Skip to content

Instantly share code, notes, and snippets.

@edvardm
Created April 3, 2013 10:27
Show Gist options
  • Save edvardm/5300071 to your computer and use it in GitHub Desktop.
Save edvardm/5300071 to your computer and use it in GitHub Desktop.
simple curses-based gof
require 'curses'
class GameOfLife
LIVE_CHAR = "0"
DEAD_CHAR = '.'
def initialize(nrows, ncols, opts={}, &block)
@nrows, @ncols = nrows, ncols
@opts = opts
@rows = (0..@nrows).each_with_object([]) { |i, acc| @ncols.times { (acc[i] ||= []) << DEAD_CHAR } }
block.call.each { |i, j| @rows[i][j] = LIVE_CHAR } if block
@generations = 0
end
class Finished < StandardError; end
def run
curses_context do
begin
loop do
show
next_iteration
sleep @opts.fetch(:sleep_int, 0.25)
end
rescue Finished
print " Finished, press key when ready..."
STDIN.getc
end
end
end
private
def curses_context(&block)
Curses.init_screen
block.call
ensure
Curses.close_screen
end
def write_ch(row, col, char)
Curses.setpos(row, col)
Curses.addch(char)
end
def show
each_cell { |i, j| write_ch i, j, @rows[i][j] }
Curses.setpos(@nrows, 0)
Curses.addstr(@generations.to_s)
@generations += 1
Curses.refresh
end
def each_cell
@nrows.times { |i| @ncols.times { |j| yield [i, j] } }
end
def next_iteration
@prev_state = @rows.flatten
new_rows = (0...@nrows).each_with_object([]) { |i, a| a << @rows[i].to_a }
each_cell do |i, j|
if (alive?(i, j) && live_neigh_count(i, j) < 2) || (alive?(i, j) && live_neigh_count(i, j) > 3)
new_rows[i][j] = DEAD_CHAR
elsif (alive?(i, j) && [2, 3].include?(live_neigh_count(i, j))) || (!alive?(i, j) && live_neigh_count(i, j) == 3)
new_rows[i][j] = LIVE_CHAR
end
end
@rows = new_rows
raise Finished if @rows.flatten == @prev_state
end
def alive?(i, j)
@rows[i][j] == LIVE_CHAR
end
def live_neigh_count(i, j)
neighbours(i, j).select { |i, j| alive?(i, j) }.size
end
def neighbours(i, j)
[[i-1, j-1], [i-1, j], [i-1, j+1], [i, j+1], [i+1, j+1], [i+1, j], [i+1, j-1], [i, j-1]].select { |i, j| valid_cell?(i, j) }
end
def valid_cell?(x, y)
0 <= x && x < @nrows && 0 <= y && y < @ncols
end
end
rows = 20
cols = 40
offset = [rows, cols].min / 2
count = 20
GameOfLife.new(rows, cols, sleep_int: 0.1) do
(1..count).map { |_| [rand(rows/4)+offset, rand(cols/4)+offset] }
end.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment