ruby GTK tcp producing core dumps - ruby
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
Related
Any way to optimize this character counter i wrote in ruby for String class
# Character Counter class String def count_lcases count(('a'..'z').to_a.join('')) end def count_upcases count(('A'..'Z').to_a.join('')) end def count_num count((0..9).to_a.join('')) end def count_spl_chars length - count_lcases - count_upcases - count_num end end input = ARGV[0] if ARGV.empty? puts 'Please provide an input' exit end puts 'Lowercase characters = %d' % [input.count_lcases] puts 'Uppercase characters = %d' % [input.count_upcases] puts 'Numeric characters = %d' % [input.count_num] puts 'Special characters = %d' % [input.count_spl_chars] I used ranges to count characters but count function is called 3 times. I can always use loops and count it one by one.I was wondering is there any way to optimize this?...
If you are using Ruby 2.7 you could use tally; the string's chars are just iterated one time. def classify_char(c) case c when /[a-z]/ then :lcase when /[A-Z]/ then :ucase when /\d/ then :digit else :other end end p "asg3456 ERTYaeth".chars.map{|c| classify_char(c) }.tally # => {:lcase=>7, :digit=>4, :other=>2, :ucase=>4}
If Ruby 2.3...2.7, this will work: CHAR_CLASSES = { lcase: ?a..?z, ucase: ?A..?Z, digit: ?0..?9, } p "asg3456 ERTYaeth".each_char.with_object(Hash.new(0)) { |c, o| o[CHAR_CLASSES.find { |label, group| group === c }&.first || :other] += 1 } For < 2.3, p "asg3456 ERTYaeth".each_char.with_object(Hash.new(0)) { |c, o| p = CHAR_CLASSES.find { |label, group| group === c } o[p ? p.first : :other] += 1 }
Why an obvious condition is not fulfilled?
I work on task about schedule and have class and .yml file where i want to retrieve data as "start" point, "end" point, "price" etc. And in my class I have method, where i want to determine if stations equal to user choice: class Train require 'yaml' def initialize(time) #time = YAML.load_file(time) end def calc(begin:, end:) ary = #time['time'] start = begin stop = end res = [] loop do tmp = ary.find { |h| h['begin'] == start } break unless tmp res << tmp start = tmp['end'] break if start == stop end end end But it always fails on condition break unless tmp For example if write instance variable a = Train.new("my_path_to yml") a.calc(begin: ':washington', end: ':tokyo') It executes nothing. Even if I refactor loop block and write "for" iterator it throw "else" condition: for i in ary if i['begin'] == 'washington' puts "good" else puts "no way" end end Here is my .yml file time: - begin: :washington end: :briston time: 6 price: 3 - begin: :briston end: :dallas time: 4 price: 2 - begin: :dallas end: :tokyo time: 3.5 price: 3 - begin: :tokyo end: :chicago time: 3.5 price: 3 - begin: :chicago end: :dellawer time: 3.5 price: 3 Thanks in advance!
Try this change, check comments in code: def calc(begin_:, end_:) # <-- don't call variables begin or end, they are reserved words ary = #time['time'] start = begin_ stop = end_ res = [] loop do tmp = ary.find { |h| h['begin'] == start } break unless tmp res << tmp start = tmp['end'] break if start == stop end res # <-- return something end Call as: train.calc(begin_: :washington, end_: :tokyo) #=> [{"begin"=>:washington, "end"=>:briston, "time"=>6, "price"=>3}, {"begin"=>:briston, "end"=>:dallas, "time"=>4, "price"=>2}, {"begin"=>:dallas, "end"=>:tokyo, "time"=>3.5, "price"=>3}] Take care not messing up strings with symbols. ary.each do |i| # for i in ary <-- pythonic! :) if i['begin'] == :washington # <-- should be a symbol to pass, not a string puts "good" else puts "no way" end 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
Ruby Minesweeper
I am trying to get this Ruby game I made to loop after the first round, and end with a "game over" if the player hits a mine. I have it to where it will build the board, which is a pre-built layout in a .txt file, and it will loop, but the game doesn't end. Here is my code def load_board mine_field = [] File.open("mines.txt", 'r') do |f| f.gets f.each_line do |line| mine_field.push line.chomp.split('') end end return mine_field end def create_hint_board(board) hint = Array.new for i in (0..board.size-1) hint[i] = [] for j in (0..board[i].size-1) hint if board[i][j] == '*' hint[i].push '*' else bombs = 0 for x in (-1..1) for y in (-1..1) if i-x >= 0 && i-x < board.size && j-y >= 0 && j-y < board[i].size && board [i-x][j-y] == '*' bombs += 1 end end end hint[i][j] = bombs end end end return hint end def print_board (board) board.each do |row| p row end end def pp_board (board) puts Array.new(board[0].size*2+1, '-').join('') board.each do |row| puts "|" + row.join('|') + "|" puts Array.new(row.size*2+1, '-').join('') end end def copy_to_blank(board) blank = Array.new for i in 0..(board.size-1) blank << Array.new(board[i].size, '.') end blank end def play(board) puts "Welcome to Minesweeper!" puts puts puts hint = create_hint_board(board) game = copy_to_blank(board) puts "Starting" pp_board game #copy coords user puts in coords = gets.chomp.split(' ') i = coords[0].to_i j = coords[1].to_i game[i][j] = hint[i][j] pp_board game puts puts "Hint" pp_board hint end end #start the game play load_board Also, mines.txt looks like this... 4 3 *... ..*. .... I would appreciate any feedback you can give.
This will get you looping: # start while loop while true coords = gets.chomp.split i = coords[0].to_i j = coords[1].to_i if hint[i][j] == "*" puts "you lose" break end game[i][j] = hint[i][j] pp_board game puts "\nHint" pp_board hint end #end while loop Ctrl-c to bust out. You can also use break to exit the loop: break if game_over?
A very specific Conway's Game of Life (Ruby beginner)
Looking for feedback on obvious logic errors on this, not optimizing. I keep getting weird tick counts on the end game message (ex: 1 tick turns into 11 ticks) The largest error I can spot while running the code is on the 2nd tick, a very large amount of alive cells appear. I am too new to this to understand why, but it seems like the #alive_cells is not resetting back to 0 after each check. Here is my entire code, its large but it should be child's play to anyone with experience. class CellGame def initialize puts "How big do you want this game?" #size = gets.chomp.to_i #cell_grid = Array.new(#size) { Array.new(#size) } #grid_storage = Array.new(#size) { Array.new(#size) } #tick_count = 0 fill_grid_with_random_cells end def fill_grid_with_random_cells #cell_grid.each do |row| row.map! do |cell| roll = rand(10) if roll > 9 "•" else " " end end end check_cells_for_future_state end def check_for_any_alive_cells #cell_grid.each do |row| if row.include?("•") check_cells_for_future_state break else end_game_print_result end end end def check_cells_for_future_state #cell_grid.each_with_index do |row, row_index| row.each_with_index do |cell, cell_index| #live_neighbors = 0 add_row_shift = (row_index + 1) if add_row_shift == #size add_row_shift = 0 end add_cell_shift = (cell_index + 1) if add_cell_shift == #size add_cell_shift = 0 end def does_this_include_alive(cell) if cell.include?("•") #live_neighbors +=1 end end top_left_cell = #cell_grid[(row_index - 1)][(cell_index - 1)] does_this_include_alive(top_left_cell) top_cell = #cell_grid[(row_index - 1)][(cell_index)] does_this_include_alive(top_cell) top_right_cell = #cell_grid[(row_index - 1)][(add_cell_shift)] does_this_include_alive(top_right_cell) right_cell = #cell_grid[(row_index)][(add_cell_shift)] does_this_include_alive(right_cell) bottom_right_cell = #cell_grid[(add_row_shift)][(add_cell_shift)] does_this_include_alive(bottom_right_cell) bottom_cell = #cell_grid[(add_row_shift)][(cell_index)] does_this_include_alive(bottom_cell) bottom_left_cell = #cell_grid[(add_row_shift)][(cell_index - 1)] does_this_include_alive(bottom_left_cell) left_cell = #cell_grid[(row_index)][(cell_index - 1)] does_this_include_alive(left_cell) if #live_neighbors == 2 || #live_neighbors == 3 #grid_storage[row_index][cell_index] = "•" else #grid_storage[row_index][cell_index] = " " end end end update_cell_grid end def update_cell_grid #cell_grid = #grid_storage print_cell_grid_and_counter end def print_cell_grid_and_counter system"clear" #cell_grid.each do |row| row.each do |cell| print cell + " " end print "\n" end #tick_count += 1 print "\n" print "Days passed: #{#tick_count}" sleep(0.25) check_for_any_alive_cells end def end_game_print_result print "#{#tick_count} ticks were played, end of game." exit end end
I couldn't see where your code went wrong. It does have a recursive call which can easily cause strange behavior. Here is what I came up with: class CellGame def initialize(size) #size = size; #archive = [] #grid = Array.new(size) { Array.new(size) { rand(3).zero? } } end def lives_on?(row, col) neighborhood = (-1..1).map { |r| (-1..1).map { |c| #grid[row + r] && #grid[row + r][col + c] } } its_alive = neighborhood[1].delete_at(1) neighbors = neighborhood.flatten.count(true) neighbors == 3 || neighbors == 2 && its_alive end def next_gen (0...#size).map { |row| (0...#size).map { |col| lives_on?(row, col) } } end def play tick = 0; incr = 1 loop do #archive.include?(#grid) ? incr = 0 : #archive << #grid sleep(0.5); system "clear"; #grid = next_gen puts "tick - #{tick += incr}" puts #grid.map { |row| row.map { |cell| cell ? '*' : ' ' }.inspect } end end end cg = CellGame.new 10 cg.play The tick count stops but the program keeps running through the oscillator at the end.
I wanted to revisit this and confidently say I have figured it out! Here is my new solution - still super beginner focused. Hope it helps someone out. class Game # Uses constants for values that won't change LIVE = "🦄" DEAD = " " WIDTH = 68 HEIGHT = 34 def initialize # Sets our grid to a new empty grid (set by method below) #grid = empty_grid # Randomly fills our grid with live cells #grid.each do |row| # Map will construct our new array, we use map! to edit the #grid row.map! do |cell| if rand(10) == 1 LIVE # Place a live cell else DEAD # Place a dead cell end end end # Single line implimentation # #grid.each {|row|row.map! {|cell|rand(10) == 1 ? LIVE : DEAD}} loop_cells #start the cycle end def empty_grid Array.new(HEIGHT) do # Creates an array with HEIGHT number of empty arrays Array.new(WIDTH) do # Fills each array with a dead cell WIDTH number of times DEAD end end # Single line implimentation # Array.new(HEIGHT){ Array.new(WIDTH) { DEAD } } end def print_grid # Prints our grid to the terminal system "clear" # Clears the terminal window # Joins cells in each row with an empty space rows = #grid.map do |row| row.join(" ") end # Print rows joined by a new line print rows.join("\n") # Single line implimentation # print #grid.map{|row| row.join(" ")}.join("\n") end def loop_cells print_grid # Start by printing the current grid new_grid = empty_grid # Set an empty grid (this will be the next life cycle) # Loop through every cell in every row #grid.each_with_index do |row, row_index| row.each_with_index do |cell, cell_index| # Find the cells friends friends = find_friends(row_index, cell_index) # Apply life or death rules if cell == LIVE state = friends.size.between?(2,3) else state = friends.size == 3 end # Set cell in new_grid for the next cycle new_grid[row_index][cell_index] = state ? LIVE : DEAD end end # Replace grid and start over #grid = new_grid start_over end def find_friends(row_index, cell_index) # Ruby can reach backwards through arrays and start over at the end - but it cannot reach forwards. If we're going off the grid, start over at 0 row_fix = true if (row_index + 1) == HEIGHT cell_fix = true if (cell_index + 1) == WIDTH # You'll see below I will use 0 if one of these values is truthy when checking cells to the upper right, right, lower right, lower, and lower left. # Check each neighbor, use 0 if we're reaching too far friends = [ #grid[(row_index - 1)][(cell_index - 1)], #grid[(row_index - 1)][(cell_index)], #grid[(row_index - 1)][(cell_fix ? 0 : cell_index + 1)], #grid[(row_index)][(cell_fix ? 0 : cell_index + 1)], #grid[(row_fix ? 0 : row_index + 1)][(cell_fix ? 0 : cell_index + 1)], #grid[(row_fix ? 0 : row_index + 1)][(cell_index)], #grid[(row_fix ? 0 : row_index + 1)][(cell_index - 1)], #grid[(row_index)][(cell_index - 1)] ] # Maps live neighbors into an array, removes nil values friends.map{|x| x if x == LIVE}.compact end def start_over sleep 0.1 loop_cells end end # Start game when file is run Game.new