Skip to content

Instantly share code, notes, and snippets.

@steven-s
Created January 29, 2012 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save steven-s/1699989 to your computer and use it in GitHub Desktop.
Save steven-s/1699989 to your computer and use it in GitHub Desktop.
Conway's Game of Life with Ruby and RSpec
require 'rspec'
class World
attr_accessor :cells
def initialize
@cells = []
end
def tick
new_world = World.new
# determine if new life should be created, live on, or die
cells.each do |cell|
# north
if !cell_at(cell.x, cell.y + 1) && !new_world.cell_at(cell.x, cell.y + 1) && create_live_cell?(cell.x, cell.y + 1)
new_world.cells << Cell.new(cell.x, cell.y + 1)
end
# south
if !cell_at(cell.x, cell.y - 1) && !new_world.cell_at(cell.x, cell.y - 1) && create_live_cell?(cell.x, cell.y - 1)
new_world.cells << Cell.new(cell.x, cell.y - 1)
end
# east
if !cell_at(cell.x + 1, cell.y) && !new_world.cell_at(cell.x + 1, cell.y) && create_live_cell?(cell.x + 1, cell.y)
new_world.cells << Cell.new(cell.x + 1, cell.y)
end
# west
if !cell_at(cell.x - 1, cell.y) && !new_world.cell_at(cell.x - 1, cell.y) && create_live_cell?(cell.x - 1, cell.y)
new_world.cells << Cell.new(cell.x - 1, cell.y)
end
# northeast
if !cell_at(cell.x + 1, cell.y + 1) && !new_world.cell_at(cell.x + 1, cell.y + 1) && create_live_cell?(cell.x + 1, cell.y + 1)
new_world.cells << Cell.new(cell.x + 1, cell.y + 1)
end
# northwest
if !cell_at(cell.x - 1, cell.y + 1) && !new_world.cell_at(cell.x - 1, cell.y + 1) && create_live_cell?(cell.x - 1, cell.y + 1)
new_world.cells << Cell.new(cell.x - 1, cell.y + 1)
end
neighbor_count = cell_neighbors(cell.x, cell.y).count
unless neighbor_count < 2 || neighbor_count > 3
new_world.cells << Cell.new(cell.x, cell.y)
end
end
new_world
end
def cell_at(x, y)
cells.each do |cell|
if cell.x == x && cell.y == y
return cell
end
end
nil
end
def cell_neighbors(x, y)
neighbors = []
cells.each do |cell|
unless cell.x == x && cell.y == y
if (cell.x - x).abs <= 1 && (cell.y - y).abs <= 1
neighbors << cell
end
end
end
neighbors
end
def create_live_cell?(x, y)
if cell_neighbors(x, y).count == 3
return true
end
false
end
end
class Cell
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
end
end
describe 'game_of_life' do
context 'world methods' do
subject { World.new }
it 'create empty new worlds' do
subject.cells.count.should == 0
end
it 'add cells to worlds' do
subject.cells << Cell.new(1, 3)
subject.cells.count.should == 1
end
it 'create new world when determining next state' do
new_world = subject.tick
new_world.should_not == subject
end
it 'locate nothing at coordinates without cells' do
subject.cell_at(1, 3).should be_nil
end
it 'locate cells at coordinates' do
subject.cells << Cell.new(1, 3)
subject.cell_at(1, 3).should_not be_nil
end
it 'determine that a cell has no neighbors' do
subject.cells << Cell.new(0, 0)
subject.cell_neighbors(0, 0).count.should == 0
end
it 'determine that a cell has a neighbor to the east' do
subject.cells << Cell.new(0, 0) << Cell.new(1, 0)
subject.cell_neighbors(0, 0).count.should == 1
end
it 'determine that a cell has a neighbor to the west' do
subject.cells << Cell.new(0, 0) << Cell.new(1, 0)
subject.cell_neighbors(1, 0).count.should == 1
end
it 'determine that a cell has a neighbor to the north' do
subject.cells << Cell.new(0, 0) << Cell.new(0, 1)
subject.cell_neighbors(0, 0).count.should == 1
end
it 'determine that a cell has a neighbor to the south' do
subject.cells << Cell.new(0, 0) << Cell.new(0, 1)
subject.cell_neighbors(0, 1).count.should == 1
end
it 'determine that a cell has a neighbor to the northeast' do
subject.cells << Cell.new(0, 0) << Cell.new(1, 1)
subject.cell_neighbors(0, 0).count.should == 1
end
it 'determine that a cell has a neighbor to the southwest' do
subject.cells << Cell.new(0, 0) << Cell.new(1, 1)
subject.cell_neighbors(1, 1).count.should == 1
end
it 'determine that a cell has a neighbor to the northwest' do
subject.cells << Cell.new(0, 0) << Cell.new(-1, 1)
subject.cell_neighbors(0, 0).count.should == 1
end
it 'determine that a cell has a neighbor to the southeast' do
subject.cells << Cell.new(0, 0) << Cell.new(-1, 1)
subject.cell_neighbors(-1, 1).count.should == 1
end
it 'determine that a cell should not come to life' do
subject.cells << Cell.new(-1, 0) << Cell.new(1, 0)
subject.create_live_cell?(0, 0).should be_false
end
it 'determine that a cell should come to life' do
subject.cells << Cell.new(0, 0) << Cell.new(1, 0) << Cell.new(0, 1)
subject.create_live_cell?(1, 1).should be_true
end
end
context 'cell methods' do
subject { Cell.new(1, 3) }
it 'give coordinates' do
subject.x.should == 1
subject.y.should == 3
end
end
let(:world) { World.new }
it 'follows Rule 1.1: Any live cell with no live neighbors dies, as if caused by under-population' do
world.cells << Cell.new(0, 0)
new_world = world.tick
new_world.cell_at(0, 0).should be_nil
end
it 'follows Rule 1.2: Any live cell with only one live neighbor dies, as if caused by under-population' do
world.cells << Cell.new(0, 0) << Cell.new(1, 0)
new_world = world.tick
new_world.cell_at(0, 0).should be_nil
new_world.cell_at(1, 0).should be_nil
end
it 'follows Rule 2.1: Any live cell with two live neighbors lives on to the next generation' do
world.cells << Cell.new(0, 0) << Cell.new(-1, 0) << Cell.new(1, 0)
new_world = world.tick
new_world.cell_at(0, 0).should_not be_nil
end
it 'follows Rule 2.2: Any live cell with three live neighbors lives on to the next generation' do
world.cells << Cell.new(0, 0) << Cell.new(-1, 0) << Cell.new(1, 0) << Cell.new(0, 1)
new_world = world.tick
new_world.cell_at(0, 0).should_not be_nil
end
it 'follows Rule 3: Any live cell with more than three live neighbors dies, as if by overcrowding' do
world.cells << Cell.new(0, 0) << Cell.new(-1, 0) << Cell.new(1, 0) << Cell.new(0, 1) << Cell.new(0, -1)
new_world = world.tick
new_world.cell_at(0, 0).should be_nil
end
it 'follows Rule 4.1: Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction (north/south)' do
world.cells << Cell.new(0, 0) << Cell.new(-1, 0) << Cell.new(1, 0)
new_world = world.tick
new_world.cell_at(0, 1).should_not be_nil
new_world.cell_at(0, -1).should_not be_nil
end
it 'follows Rule 4.2: Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction (east/west)' do
world.cells << Cell.new(0, 0) << Cell.new(0, 1) << Cell.new(0, -1)
new_world = world.tick
new_world.cell_at(-1, 0).should_not be_nil
new_world.cell_at(1, 0).should_not be_nil
end
it 'follows Rule 4.3: Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction (northeast)' do
world.cells << Cell.new(-1, 1) << Cell.new(1, 1) << Cell.new(-1, -1)
new_world = world.tick
new_world.cell_at(0, 0).should_not be_nil
end
it 'follows Rule 4.4: Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction (northwest)' do
world.cells << Cell.new(-1, 1) << Cell.new(1, 1) << Cell.new(1, -1)
new_world = world.tick
new_world.cell_at(0, 0).should_not be_nil
end
it 'follows Rule 4.5: Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction (southeast)' do
world.cells << Cell.new(-1, -1) << Cell.new(1, -1) << Cell.new(-1, 1)
new_world = world.tick
new_world.cell_at(0, 0).should_not be_nil
end
it 'follows Rule 4.6: Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction (southwest)' do
world.cells << Cell.new(-1, -1) << Cell.new(1, -1) << Cell.new(1, 1)
new_world = world.tick
new_world.cell_at(0, 0).should_not be_nil
end
it 'creates still life: behive' do
world.cells << Cell.new(0, 0) << Cell.new(1, 1) << Cell.new(2, 1) << Cell.new(3, 0) << Cell.new(2, -1) << Cell.new(1, -1)
new_world = world.tick
new_world.cells.count.should == 6
new_world.cell_at(0, 0).should_not be_nil
new_world.cell_at(1 ,1).should_not be_nil
new_world.cell_at(2, 1).should_not be_nil
new_world.cell_at(3, 0).should_not be_nil
new_world.cell_at(2, -1).should_not be_nil
new_world.cell_at(1, -1).should_not be_nil
end
it 'creates still life: block' do
world.cells << Cell.new(0, 0) << Cell.new(1, 0) << Cell.new(0, 1) << Cell.new(1, 1)
new_world = world.tick
new_world.cells.count.should == 4
new_world.cell_at(0, 0).should_not be_nil
new_world.cell_at(1, 0).should_not be_nil
new_world.cell_at(0, 1).should_not be_nil
new_world.cell_at(1, 1).should_not be_nil
end
it 'creates oscillator: blinker' do
world.cells << Cell.new(0, 0) << Cell.new(1, 0) << Cell.new(-1, 0)
new_world = world.tick
new_world.cells.count.should == 3
new_world.cell_at(0, 0).should_not be_nil
new_world.cell_at(0, 1).should_not be_nil
new_world.cell_at(0, -1).should_not be_nil
next_iteration = new_world.tick
next_iteration.cells.count.should == 3
next_iteration.cell_at(0, 0).should_not be_nil
next_iteration.cell_at(1, 0).should_not be_nil
next_iteration.cell_at(-1, 0).should_not be_nil
end
it 'creates oscillator: beacon' do
world.cells << Cell.new(0, 2) << Cell.new(0, 3) << Cell.new(1, 3)
world.cells << Cell.new(2, 0) << Cell.new(3, 0) << Cell.new(3, 1)
new_world = world.tick
new_world.cells.count.should == 8
new_world.cell_at(0, 2).should_not be_nil
new_world.cell_at(0, 3).should_not be_nil
new_world.cell_at(1, 3).should_not be_nil
new_world.cell_at(2, 0).should_not be_nil
new_world.cell_at(3, 0).should_not be_nil
new_world.cell_at(3, 1).should_not be_nil
new_world.cell_at(1, 2).should_not be_nil
new_world.cell_at(2, 1).should_not be_nil
next_iteration = new_world.tick
next_iteration.cells.count.should == 6
next_iteration.cell_at(0, 2).should_not be_nil
next_iteration.cell_at(0, 3).should_not be_nil
next_iteration.cell_at(1, 3).should_not be_nil
next_iteration.cell_at(2, 0).should_not be_nil
next_iteration.cell_at(3, 0).should_not be_nil
next_iteration.cell_at(3, 1).should_not be_nil
end
end
@mattdsteele
Copy link

The first rule of Coderetreat Club is that you do not save anything from Coderetreat Club.

@steven-s
Copy link
Author

This one is all my own mistakes, had to work on the problem again the next day lol

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