Skip to content

Instantly share code, notes, and snippets.

@nicooga
Last active October 14, 2021 11:04
Show Gist options
  • Save nicooga/6aa5c7a8814c14549304 to your computer and use it in GitHub Desktop.
Save nicooga/6aa5c7a8814c14549304 to your computer and use it in GitHub Desktop.
Ruby gosu implementation of Cpnway's game of life
require 'gosu'
ANIMATION_INTERVAL = 0.4
module GameOfLife
class Cell
attr_accessor :x, :y
def initialize(hash)
raise 'Bad attribute keys' unless (hash.keys - [:x, :y, :alive]).empty?
hash.each { |k,v| eval "@#{k}= #{v}" }
end
def alive?; @alive; end
def alive=(bool); @alive = bool; end
def kill!; @alive = false; end
def resurrect!; @alive = true end
def to_s
alive? ? "\e[1;32mO\e[0m" : "\e[1;30mX\e[0m"
end
end
class Grid < Array
def initialize(x,y=x)
if x.is_a? String
super x.lines.map { |l| l.chomp.split '|' }
.transpose
.map.with_index do |col, x|
col.map.with_index { |cell, y| GameOfLife::Cell.new(x: x, y: y, alive: !!(cell =~ /o/i)) }
end
else
super(x) do |x|
Array.new(y) do
GameOfLife::Cell.new(alive: [true,false].sample, x: x, y: y)
end
end
end
end
def next!
cells, cells_to_kill, cells_to_resurrect = [], [], []
each_cell do |cell|
cells << [cell, around(cell).flatten.compact.count(&:alive?)]
end
# Any live cell with fewer than two live neighbours dies, as if caused by under-population.
cells_to_kill.concat cells.select { |cell,ln| cell.alive? and ln < 2 }
# Any live cell with two or three live neighbours lives on to the next generation.
# Do nothing...
# Any live cell with more than three live neighbours dies, as if by overcrowding.
cells_to_kill.concat cells.select { |cell,ln| cell.alive? and ln > 3 }
# Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
cells_to_resurrect.concat cells.select { |cell,ln| !cell.alive? and ln == 3 }
cells_to_kill.each { |cell,_| cell.kill! }
cells_to_resurrect.each { |cell,_| cell.resurrect! }
self
end
def next; dup.next!; end
def animate!(gens=Float::Infinity)
print
puts (['-']*first.size).join('-')
gens.times do
next!
print
puts (['-']*first.size).join('-')
sleep(ANIMATION_INTERVAL)
end
end
def animate(*args)
dup.animate! *args
end
NEGATIVE_SLICE = ->(ary,i){ ((0..ary.size-1).to_a*2)[ary.size+i,3] }
OVER_SLICE = ->(ary,i){ ((0..ary.size-1).to_a*2)[i%ary.size,3] }
SLICE_AROUND = ->(ary,i) do
return Array.new(3,nil) if ary.nil?
if i <= 0
NEGATIVE_SLICE[ary,i-1]
elsif i >= ary.size-1
OVER_SLICE[ary,i-1]
else
((i-1)..(i+1)).to_a
end
end
def around(x,y=nil)
x, y = x.x, x.y if %w(x y).map { |m| x.respond_to? m }.all?
ary = SLICE_AROUND[self,x].map do |xi|
SLICE_AROUND[self[xi],y].map do |yi|
self[xi][yi]
end
end
ary[1][1] = nil
self.class[*ary]
end
def each_cell
each do |col|
col.each do |cell|
yield cell
end
end
end
def expand(cols=0,rows=cols)
tmp_heigth, tmp_width = heigth, width
cols.times do |x|
self << Array.new(tmp_heigth) { |y| GameOfLife::Cell.new(x: tmp_width+x, y: y, alive: false) }
end
tmp_heigth, tmp_width = heigth, width
each.with_index do |col,x|
rows.times do |y|
col << GameOfLife::Cell.new(x: x, y: tmp_heigth+y, alive: false)
end
end
self
end
def to_s
self.transpose.map do |row|
row.map { |cell| cell.nil? ? '+' : cell.to_s }.join('|')
end.join("\n")
end
def heigth; first.size; end
def width; size; end
def print; puts self.to_s; end
end
class Window < Gosu::Window
TOP_COLOR = Gosu::Color.new(0xFFFeee)
BOTTOM_COLOR = Gosu::Color.new(0xFF1D4DB5)
def initialize(grid)
@grid = grid
@width = @grid.size*30+5
@heigth = @grid.first.size*30+5
super @width, @heigth, false
self.caption = 'Game of Life'
@last_frame = Gosu::milliseconds
end
def update
@grid.next!
@this_frame = Gosu::milliseconds
@delta = @this_frame - @last_frame
@last_frame = @this_frame
sleep ANIMATION_INTERVAL
end
def draw
## BACKGROUND
draw_quad(
0, 0, TOP_COLOR,
@width, 0, TOP_COLOR,
0, @heigth, BOTTOM_COLOR,
@width, @heigth, BOTTOM_COLOR
)
## CELLS
@grid.each_cell do |cell|
color = cell.alive? ? Gosu::Color::GREEN : Gosu::Color::BLACK
draw_quad(
cell.x*30+5, cell.y*30+5, color,
cell.x*30+30, cell.y*30+5, color,
cell.x*30+5, cell.y*30+30, color,
cell.x*30+30, cell.y*30+30, color
)
end
end
end
end
PULSAR = GameOfLife::Grid.new <<-GRID
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
X|X|X|X|O|O|O|X|X|X|O|O|O|X|X|X|X
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
X|X|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X
X|X|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X
X|X|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X
X|X|X|X|O|O|O|X|X|X|O|O|O|X|X|X|X
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
X|X|X|X|O|O|O|X|X|X|O|O|O|X|X|X|X
X|X|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X
X|X|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X
X|X|O|X|X|X|X|O|X|O|X|X|X|X|O|X|X
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
X|X|X|X|O|O|O|X|X|X|O|O|O|X|X|X|X
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X
GRID
GameOfLife::Window.new(PULSAR).show
@nicooga
Copy link
Author

nicooga commented Aug 24, 2015

This scripts requires Gosu. Basically, you need to install some dependencies in order to be able to build native extensions for gosu. Them just gem install it.

In mac I just did:

brew install sdl2
gem install gosu

In linux:

# Gosu's dependencies for both C++ and Ruby
sudo apt-get install build-essential libsdl2-dev libsdl2-ttf-dev libpango1.0-dev \
                     libgl1-mesa-dev libfreeimage-dev libopenal-dev libsndfile-dev

# To install Ruby - if you are using rvm or rbenv, please skip this step
sudo apt-get install ruby-dev

# If you are using rvm or rbenv, do not use 'sudo'
sudo gem install gosu

Then dowload this file and run it ruby game_of_life.rb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment