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

Resources