ruby driver.rb
rspec game_of_life_spec.rb
def tick(grid) | |
n = grid.flat_map{ |c| neighbors(*c) } | |
cells = grid | n | |
cells.reduce(grid) do |new_grid, cell| | |
if grid.include?(cell) | |
new_grid & kill(cell, grid) | |
else | |
new_grid | birth(cell, grid) - grid | |
end | |
end | |
end | |
def neighbors(x,y) | |
range = (-1..1).to_a | |
offsets = range.product(range) | |
offsets.delete([0,0]) | |
offsets.map do |(ox,oy)| | |
[x-ox, y-oy] | |
end | |
end | |
def living_neighbors(cell, grid) | |
neighbors(*cell).select do |n| | |
grid.include?(n) | |
end | |
end | |
def kill(cell, grid) | |
living_neighbors = living_neighbors(cell, grid) | |
grid.dup.tap do |g| | |
if living_neighbors.length < 2 || living_neighbors.length > 3 | |
g.delete(cell) | |
end | |
end | |
end | |
def birth(cell, grid) | |
living_neighbors = living_neighbors(cell, grid) | |
grid.dup.tap do |g| | |
if living_neighbors.length == 3 | |
g << cell | |
end | |
end | |
end |
require 'set' | |
require_relative 'conway' | |
require_relative 'game_of_life_renderer' | |
grid = Set[ | |
[0,1], [1,1], [2,1] # blinker | |
#[1,0], [2,1], [0,2], [1,2], [2,2] # glider | |
#[1,1], [1,2], [1,3], [2,0], [2,1], [2,2] # toad | |
] | |
renderer = GameOfLifeRenderer.new(10,10) | |
while true | |
renderer.render_grid(grid) | |
grid = tick(grid) | |
sleep(1) | |
end |
class GameOfLifeRenderer | |
attr_reader :width, :height, :io | |
def initialize(width, height, io = $stdout) | |
@width, @height = width, height | |
@io = io | |
end | |
def render_grid(grid) | |
clear_screen | |
width.times do |x| | |
height.times do |y| | |
render_coord([x, y], grid) | |
end | |
io.puts | |
end | |
io.flush | |
end | |
private | |
def render_coord(coord, grid) | |
if grid.include?(coord) | |
io.print('0') | |
else | |
io.print('.') | |
end | |
end | |
def clear_screen | |
io.puts("\e[H\e[2J") | |
end | |
end |
require_relative 'conway' | |
describe 'game of life' do | |
it 'works for blinker' do | |
# x x x | |
# o o o | |
# x x x | |
grid_before = [[0,1], [1,1], [2,1]] | |
# x o x | |
# x o x | |
# x o x | |
grid_after = [[1,0], [1,1], [1,2]] | |
expect(tick(grid_before)).to match_array(grid_after) | |
end | |
describe 'kill' do | |
it 'kills cell with less than 2 neighbors' do | |
cell = [0,0] | |
grid_before = [cell] | |
expect(kill(cell, grid_before)).to eq([]) | |
end | |
it 'kills cell with more than 3 neighbors' do | |
cell = [1,1] | |
# o o o | |
# o 0 x | |
grid_before = [[0,0], [0,1], [0,2], [1,0], cell] | |
grid_after = [[0,0], [0,1], [0,2], [1,0]] | |
expect(kill(cell, grid_before)).to match_array(grid_after) | |
end | |
it 'does nothing with cell having 2 neighbors' do | |
cell = [0,0] | |
# 0 o | |
# o x | |
grid_before = [cell, [0,1], [1,0]] | |
expect(kill(cell, grid_before)).to match_array(grid_before) | |
end | |
it 'does nothing with cell having 3 neighbors' do | |
cell = [0,0] | |
# 0 o | |
# o o | |
grid_before = [cell, [0,1], [1,0], [1,1]] | |
expect(kill(cell, grid_before)).to match_array(grid_before) | |
end | |
end | |
describe 'birth' do | |
it 'births cell with exactly 3 neighbors' do | |
cell = [0,0] | |
# x o | |
# o o | |
grid_before = [[1,0], [0,1], [1,1]] | |
grid_after = [[1,0], [0,1], [1,1], cell] | |
expect(birth(cell, grid_before)).to match_array(grid_after) | |
end | |
it 'does nothing with cell having less than 3 neighbors' do | |
cell = [0,0] | |
grid_before = [] | |
expect(birth(cell, grid_before)).to eq([]) | |
end | |
it 'does nothing with cell having more than 3 neighbors' do | |
cell = [1,1] | |
# o o o | |
# o 0 x | |
grid_before = [[0,0], [0,1], [0,2], [1,0]] | |
expect(birth(cell, grid_before)).to match_array(grid_before) | |
end | |
end | |
end |