Ruby gets input goes to the console command line after program terminates - ruby
I am trying to set up a simple ruby program with a two additional threads.
One thread is to check the serial port for data and populate a variable if anything is found.
The second thread is is a call to 'gets', which when returned will stop the program.
The main thread simply ouputs the values stored by the first additional thread to the console (The program is a console based data logger).
My problem is that the second thread starts ok, as you can see from the "Press Enter..." note in the output.
D:\Documents and Settings\JMEDDING\My Documents\Ruby scripts>c-scope.rb -t
"Press 'Enter' to end process"
511, 511, 485 | | | | | + | | | | | 536
511, 511, 485 | | | | | + | | | | | 536
512, 511, 485 | | | | | XO | | | | | 536
512, 512, 485 | | | | | |+ | | | | | 536
but any data returned from
input = gets
does not make it into the program. Instead, it can be seen on the console command line after the program is killed with ctrl-c.
I am running this in the ruby console on windows. This used to work properly before, but I re-arranged the code at one point and now it is not cooperating. I have copied the entire program below. I guess this is something very simple, but I'm totally stuck, so thanks for taking a look.
Jon
require 'serialport'
TEST = true if ARGV[0] == '--test' || ARGV[0] == '-t'
# Edit the two settings below
port = "COM6" #"/dev/tty.usbserial-A600b1Q6"
baud = 9600
begin
if RUBY_PLATFORM =~ /win32/ || RUBY_PLATFORM == "i386-mingw32"
require 'Win32/Console/ANSI'
end
rescue LoadError
raise "You must 'gem install win32console' to use color on Windows"
end
if TEST
sp = nil
else
sp = SerialPort.new port, baud, 8, 1, SerialPort::NONE
end
symbols = ["X", "O", "#", "#"]
vals = [] #max four values to plot
input = nil
len = 50
max = 0
min = 1200
if TEST
vals = [511, 511]
end
def colorize(text, color_code)
"\033[1;#{color_code}m#{text}\033[0m"
end
def red(text); colorize(text, "31"); end
def green(text); colorize(text, "32"); end
def blue(text); colorize(text, "34"); end
def color_the_symbols(text, symbols)
color = 31 # "+" = red, then green, yellow, blue, pink
chars = ["+"] + symbols
chars.each_with_index do |s,i|
text.gsub!(s, colorize(s, "#{color + i}"))
end
text
end
def base_string (len, min, max, vals, symbols)
s = ""
(0..len).each {|i| s += i%5 == 0 ? "|" : " "}
vals.each_with_index do |val, i|
pos = len * (val - min)/ (max - min)
char = symbols.include?(s[pos] ) ? "+" : symbols[i]
s[pos] = char
end
color_the_symbols s, symbols
end
#listen to STDIN for stop signal
Thread.new do
p "Press 'Enter' to end process"
input = gets #pressing enter will stop the other loops
end
sleep(1)
#listening thread
Thread.new do
while !input
if TEST
(0...vals.count).each {|i| vals[i] += rand(3) - 1}
else
c = ""
word = ""
until c == "\n"
c = sp.getc
word << c.to_s
#p "Flushing buffer #{c}"
end
#p word.chomp
vals[0] = Integer(word.chomp)
end
sleep(0.5)
end
end
while !input #will be true until enter key is pressed
while vals.count == 0
#wait for firs data to show up
sleep (0.1)
end
for i in (0...vals.count) do
#validate min and max
max = (vals[i] * 1.05).to_i if vals[i] > max
min = (vals[i] * 0.95).to_i if vals[i] < min
end
output = base_string(len, min, max, vals, symbols)
output = " #{red(min.to_s)} " + output + " #{red(max.to_s)}"
color = 32
vals.each_with_index do |val,i|
output = " #{colorize("%4d," % val, "#{color + i}")}" + output
end
puts output
sleep(1)
end
I think there was a error somewhere, but the notification was being obscured by the threading behavior. I made some unrelated changes and after some debugging, it started working again. Sorry I can't be more specific.
You need to have
thread = Thread.new
# blah
end
thread.join
If you want that thread to finish its work before the program finishes.
Related
How to delete double quotation mark from ruby array?
Based on the link I tried to delete "" in the array on ruby However still not get what I want, if anyone knows, please advice me a = gets lines = [] aaa = [] b = [] bb =[] while line = gets do lines << line.chomp.split(' ') end for k in 0..(lines.size - 1) do b << lines[k][1].to_i + 1 end for i in 0..(lines.size - 1)do bb << lines[i][0] + ' ' + b[i].to_s end for l in 0..(lines.size - 1)do p bb[l] end Input 3 Tanaka 18 Sato 50 Suzuki 120 Output [["Tanaka", "18"], ["Sato", "50"], ["Suzuki", "120"]] "Tanaka 19" "Tanaka 19" "Sato 51" "Suzuki 121"
As pointed out in the comments, you can get rid of the quotation marks by replacing p (Ruby's inspect/print) with puts. While we're at it, you can make this much more "Ruby-ish" by using .readlines to scoop up all the input into an array, and by replacing the multiple counting loops with .map or .each iterators. The following is more concise, and allows you to lose the first input line which you're just throwing away anyway. lines = STDIN.readlines(chomp: true).map do |line| l = line.split(' ') [l[0], l[1].to_i + 1].join(' ') # or # "#{l[0]} #{l[1].to_i + 1}" end lines.each { |line| puts line } With Ruby 3, you can use rightward-assignment for the first part if you find it more readable: STDIN.readlines(chomp: true).map do |line| l = line.split(' ') "#{l[0]} #{l[1].to_i + 1}" end => lines
ruby GTK tcp producing core dumps
i've problems with core dumps using ruby, gtk and tcp connections. my application is much larger, but here a minimized version: GTK code (is also attached): ##################################################### require 'gtk3' require 'cairo' class DummyArray def initialize #array=[ "M",2,48.83,1.95,"L",8,50.55,0.0,62.27,0.0,63.91,5.0,48.83,1.95,"M",2,0.0,22.42,"L",8,0.0,16.95,9.77,13.2,7.27,28.44,0.0,22.42,"M",2,5.39,49.61,"L",8,0.0,48.52,0.0,35.63,0.47,35.08,5.39,49.61,"M",2,0.0,80.7,"L",6,0.0,68.28,9.38,70.31,0.0,80.7,"M",2,90.23,46.56,"L",6,100.0,35.55,100.0,48.52,90.23,46.56,"M",2,100.0,16.95,"L",6,100.0,22.42,95.47,18.67,100.0,16.95,"M",2,100.0,68.28, "L",8,100.0,80.78,99.14,81.72,94.45,67.11,100.0,68.28,"M",2,50.55,100.0,"L",6,58.98,90.47,62.27,100.0,50.55,100.0,"M",2,23.44,78.67,"L",6,33.59,67.19,38.52,81.72,23.44,78.67,"M",2,40.47,15.39,"L",6,25.39,12.34,35.63,0.86,40.47,15.39,"M",2,59.61,39.77,"L",6,59.14,24.38,72.66,31.56,59.61,39.77,"M",2,45.78,36.95,"L",6,55.23,36.48,50.94,45.0,45.78,36.95, "M",2,48.98,24.38,"L",6,43.67,16.41,53.2,15.86,48.98,24.38,"M",2,22.58,10.08,"L",6,13.52,13.13,15.47,3.75,22.58,10.08,"M",2,18.28,30.47,"L",6,21.33,39.53,11.95,37.58,18.28,30.47,"M",2,25.23,40.86,"L",6,26.41,25.55,39.06,34.22,25.23,40.86,"M",2,13.67,63.13,"L",6,13.13,47.73,26.64,54.92,13.67,63.13,"M",2,17.66,68.52,"L",6,21.72,77.19,12.11,76.25,17.66,68.52,"M",2,52.34,49.06, "L",6,44.14,62.11,36.95,48.52,52.34,49.06,"M",2,52.42,79.84,"L",6,60.08,66.56,67.73,79.84,52.42,79.84,"M",2,47.42,76.25,"L",6,42.19,68.28,51.72,67.73,47.42,76.25,"M",2,79.14,31.48,"L",6,87.11,26.17,87.73,35.7,79.14,31.48,"M",2,81.64,20.08,"L",6,74.92,6.25,90.23,7.34,81.64,20.08,"M",2,67.97,10.86,"L",6,68.52,20.39,59.92,16.09,67.97,10.86,"M",2,73.59,73.36,"L",6,81.48,68.2,82.11,77.73,73.59,73.36, "M",2,63.59,49.06,"L",6,64.22,58.59,55.62,54.3,63.59,49.06,"M",2,79.53,43.83,"L",6,84.45,58.36,69.38,55.31,79.53,43.83,"M",2,90.23,95.16,"L",6,92.19,85.78,99.3,92.11,90.23,95.16,"M",2,86.17,81.48,"L",6,82.34,96.33,71.41,85.55,86.17,81.48,"M",2,22.11,97.34,"L",6,6.8,97.34,14.45,84.06,22.11,97.34,"M",2,38.2,93.05,"L",6,29.14,96.09,31.09,86.72,38.2,93.05, "Z",0 ] end def render(cr) i= 0 size= #array.size while i < size e= #array[i] case e when 'M', 'L' count= #array[i+1] j= i + 2 i= j + count while j < i x= #array[j] y= #array[j+1] if e == 'M' cr.move_to(x, y) else cr.line_to(x, y) end j += 2 end when 'Z' count= #array[i+1] j= i + 2 i= j + count cr.close_path else raise end end end end class ArtScrollCanvas < Gtk::Grid def initialize super #user_zoom= 1.0 #darea = Gtk::DrawingArea.new #darea.hexpand= true #darea.vexpand= true #darea.signal_connect('configure-event') do print "\n darea_configure_callback" update_adjustments_and_paint(0.0, 0.0) end #darea.signal_connect "draw" do |_, cr| paint(cr) end attach(#darea, 0, 0, 1, 1) update_adjustments_and_paint(0.0, 0.0) end def update_adjustments_and_paint(dx= 0.0, dy= 0.0) #darea.queue_draw_area(0, 0, #darea.allocation.width, #darea.allocation.height) end def paint(cr) cr.set_source_rgba(1, 1, 1, 1) cr.paint cr.set_source_rgba(1, 0, 0, 1) DummyArray.new.render(cr) #DummyDirect.new.render(cr) cr.fill end end class ArtApplicationWindow < Gtk::ApplicationWindow def initialize(application) super(application) signal_connect('destroy') do #Gtk.main_quit application.quit end set_title 'Art' set_size_request(600, 400) #canvas = ArtScrollCanvas.new #vbox = Gtk::Box.new(:vertical, 0) #vbox.pack_start(#canvas, :expand => true, :fill => true, :padding => 0) add(#vbox) show_all end end # ArtApplicationWindow class ArtApp < Gtk::Application def initialize super("org.gtk.exampleapp", :handles_open) signal_connect('activate') do |application| window = ArtApplicationWindow.new(application) window.present end end end begin ss = Gio::SocketService.new ss.add_inet_port(4321) ss.signal_connect("incoming") do |_, conn, _| print "\n#############################################################" print "\nconn.local_address: ", conn.local_address print "\nconn.local_address.family: ", conn.local_address.family print "\nconn.remote_address: ", conn.remote_address print "\nconn.class: ", conn.class print "\nconn.input_stream.fd: ", conn.input_stream.fd print "\nconn.input_stream.socket.fd: ", conn.input_stream.socket.fd # prevents closing the connection (at least in c code) Gtk::Loader.reference_gobject(conn) Gtk::Loader.reference_gobject(conn.input_stream) Gtk::Loader.reference_gobject(conn.input_stream.socket) Gtk::Loader.reference_gobject(conn.output_stream) Gtk::Loader.reference_gobject(conn.output_stream.socket) channel= GLib::IOChannel.new(conn.input_stream.socket.fd) channel.add_watch(GLib::IOChannel::IN) do print "\n>>> ", conn.input_stream.socket.fd len= conn.input_stream.socket.available_bytes input= conn.input_stream.read(len) print "\ninput= ", input conn.output_stream.write("OK\n") GLib::Source::CONTINUE true end true end ss.start end GLib::Timeout.add(10000) do print "\ntimer" GLib::Source::CONTINUE end app = ArtApp.new app.run ################################################# SIMPLE client: require 'socket' class Comm def initialize(socket, verbose: false) #socket= socket #sio= StringIO.new #verbose= verbose end def close #socket.close end def <<(str) #sio << str end def send str= #sio.string #socket.write(str) print "\n>>>\nsending: ", str if #verbose #sio.reopen res= #socket.readline print "\nres= \"", res, "\"" if #verbose res end end server_socket = TCPSocket.open("localhost", 4321) comm= Comm.new(server_socket, verbose: true) i= 1000000 while true i += 1 comm << i.to_s comm.send sleep(0.025) # 40 per s end in the beginning everything seems ok, but after the client is launched resizing the GTK window leads to core dumps after approx. 1000 messages (sometimes much less, but sometimes much later). Shortening the #array in DummyArray leads to less core dumps, therefore the array is that large. i've read https://ruby-gnome2.osdn.jp/hiki.cgi?tips_threads but don't know how this would apply to my program ... cheers artur
using a global array $channels= [] and adding each new channel to it channel= GLib::IOChannel.new(conn.input_stream.socket.fd) $channels << channel seems to prevent garbage collecting the channel (not well documented :-(, this answer came from the ruby-gnome2 mailing list
Keep Score method for tic tac toe is not updating score variables properly
I am currently adding a 'keep score' method to the below tic tac toe game. The method game_score works insofar as it increments after the game, but always resets back to 0. I think the problem is something very simple, and there are similar questions already answered but I couldn't apply them to my code. I would appreciate any help. Thanks. INITIAL_MARKER = ' ' PLAYER_MARKER = 'X' COMPUTER_MARKER = 'O' player_score = 0 computer_score = 0 def prompt(msg) puts "=>#{msg}" end def display_board(brd) system 'clear' puts "You're a #{PLAYER_MARKER} Computer is #{COMPUTER_MARKER}" puts "" puts" | |" puts" #{brd[1]} | #{brd[2]} | #{brd[3]}" puts" | |" puts"-----+-----+-----" puts" | |" puts" #{brd[4]} | #{brd[5]} | #{brd[6]}" puts" | |" puts"-----+-----+-----" puts" | |" puts" #{brd[7]} | #{brd[8]} | #{brd[9]}" puts" | |" end def initialize_board new_board = {} (1..9).each {|num| new_board[num] = INITIAL_MARKER} new_board end def empty_squares(brd) brd.keys.select{|num| brd[num] == INITIAL_MARKER} end def player_places_piece!(brd) square = INITIAL_MARKER loop do prompt ("Choose a square (#{empty_squares(brd).join(',')})") square = gets.chomp.to_i break if empty_squares(brd).include?(square) prompt "Sorry, that's not a valid choice." end brd[square] = PLAYER_MARKER end def computer_places_piece(brd) square = empty_squares(brd).sample brd[square] = COMPUTER_MARKER end def board_full(brd) empty_squares(brd).empty? end def someone_won?(brd) !!detect_winner(brd) end def detect_winner(brd) #computer_score = winning_lines = [[1,2,3],[4,5,6],[7,8,9]] + [[1,4,7],[2,5,8],[3,6,9]] + [[1,5,9],[3,5,7]] winning_lines.each do |line| if brd[line[0]] == PLAYER_MARKER && brd[line[1]] == PLAYER_MARKER && brd[line[2]] == PLAYER_MARKER return 'Player' player_score += 1 elsif brd[line[0]] == COMPUTER_MARKER && brd[line[1]] == COMPUTER_MARKER && brd[line[2]] == COMPUTER_MARKER return 'Computer' computer_score += 1 else end end nil end def game_score (player_score, computer_score, board) player_score += 1 if detect_winner(board) == 'Player' computer_score += 1 if detect_winner(board) == 'Computer' prompt("Player Score is: #{player_score}") prompt("Computer Score is: #{computer_score}") end loop do board = initialize_board loop do display_board(board) player_places_piece!(board) break if someone_won?(board) || board_full(board) display_board(board) computer_places_piece(board) display_board(board) break if someone_won?(board) || board_full(board) end if someone_won?(board) prompt "#{detect_winner(board)} won!" #player_score += 1 if detect_winner(board)== 'Player' #computer_score += 1 if detect_winner(board)== 'Computer' #display("Player Score: #{player_score}") #display("Computer Score: #{computer_score}") else prompt "It's a tie!" end game_score(player_score, computer_score, board) binding pry prompt("would you like to play again?") input = gets.chomp break unless input == 'y' end prompt("bye")
When you pass in player_score and computer_score into the game_score method, then you get new variables that are scoped to that method. They are not the same as your global variables that you declare at the top. May I suggest that you try to write some classes that encapsulates the proper abstractions (like game, board, score etc). I think that would make it easier to figure out how you should save your state.
Stuck in while loop even though condition has been met
I'm writing this script to build a tic-tac-toe game. This is only the beginning (no turns yet). I want to let the user input again if the previous input is invalid. def display_board(board) first_row = " #{board[0]} | #{board[1]} | #{board[2]} " second_row = " #{board[3]} | #{board[4]} | #{board[5]} " third_row = " #{board[6]} | #{board[7]} | #{board[8]} " row_divider = "-----------" puts first_row puts row_divider puts second_row puts row_divider puts third_row end def valid_move?(board,index) if (index >= 0) && (index <= board.length - 1) && (position_taken?(board,index) != FALSE) return TRUE else return FALSE end end def input_to_index(user_input) index = user_input.to_i - 1 return index end def move(board, index, character = "X") board[index] = character end def position_taken?(board,index) if (board[index] == "X") || (board[index]=="O") return FALSE end end def turn(board) puts "Please enter 1-9:" user_input = gets.strip index = input_to_index(user_input) while valid_move?(board,index) == FALSE puts "invalid" turn(board) end move(board, index, character = "X") display_board(board) end I am stuck on the while loop. If I input an invalid input and then a valid input, it runs through the while loop instead of ending the program. It should be true. The problem is fixed if I use an if statement instead of a while loop, but I want to learn to use while loops better.
Because you call "turn" (internal) from "turn" (external) and then the internal "turn" called is valid but the board and index on the external "turn" didn't change. try this: def turn(board) loop do puts "Please enter 1-9:" user_input = gets.strip index = input_to_index(user_input) if valid_move?(board,index) == TRUE break end puts "invalid" end move(board, index, character = "X") display_board(board) end
How to correctly write a ruby method
i'm trying ruby. I want to make a program which can continue the sequence. 1 11 21 1211 111221 (which next line is a calculated each number of previous line) For example last line is(see previous) one of 1, one of 2, and two of 1. I make a code and it works fine: 5.times do result_seq = [] count = 1 puts initial_seq.join initial_seq.size.times do if (value = initial_seq.shift) == initial_seq.first count += 1 else result_seq << count << value count = 1 end end initial_seq = result_seq end But now I want to write a simple method called Next I want to make: sec = Sequence.new(1) sec.next -> will return 11 sec.next.next -> will return 21 sec.next.next.next -> will return 1211 How can i write it correctly using my code? UPD I wrote the tests for it: require "spec_helper" require "sequence" describe Sequence do let(:sequence) { Sequence.new("1") } describe "to_s" do it "return initial value" do expect(sequence.to_s).to eql "1" end end describe "next" do it "generate next state" do expect(sequence.next.to_s).to eql "11" end it "return Sequence instance" do expect(sequence.next).to be_an_instance_of(Sequence) end it "generate next state 2 times" do expect(sequence.next.next.to_s).to eql "21" end it "generate next state 3 times" do expect(sequence.next.next.next.to_s).to eql "1211" end end end
class Sequence attr_reader :initial_seq def initialize(initial_seq = []) #initial_seq = initial_seq print_next end def print_next result_seq = [] count = 1 puts initial_seq.join initial_seq.size.times do if (value = initial_seq.shift) == initial_seq.first count += 1 else result_seq << count << value count = 1 end end #initial_seq = result_seq self #<===== The most important part for being able to chain `print_next` end end Usage: Sequence.new([1]).print_next.print_next.print_next.print_next 1 11 21 1211 111221 edit If you want to initialize it with integer argument, not array: def initialize(number) #initial_seq = [number] print_next end Sequence.new(1).print_next.print_next 1 11 21 Or, if you do not want initialize to accept an argument (assuming, it will always start with 1): def initialize #initial_seq = [1] print_next end Sequence.new.print_next.print_next 1 11 21
Ruby provides Enumerators, which behave almost like in OP. Leaving the original code almost unaltered: seq = Enumerator.new do |yielder| initial_seq = [1] loop do #endless loop, but don't worry, its lazy result_seq = [] count = 1 yielder << initial_seq.join initial_seq.size.times do if (value = initial_seq.shift) == initial_seq.first count += 1 else result_seq << count << value count = 1 end end initial_seq = result_seq end end 5.times{puts seq.next} puts seq.next