Created
December 9, 2012 19:38
-
-
Save jacegu/4246676 to your computer and use it in GitHub Desktop.
Functional take on Conway's Game of Life
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Cell | |
def cell | |
{ state: :alive, neighbours: [] } | |
end | |
def dead_cell | |
{state: :dead, neighbours: []} | |
end | |
def neighbours_of(cell, neighbour_info) | |
cell.merge!(neighbours: neighbour_info[:are]) | |
end | |
def evolve_cell(cell) | |
if underpopulated?(cell) or overpopulated?(cell) | |
dead_cell | |
else | |
cell | |
end | |
end | |
def underpopulated?(cell) | |
neighbour_count(cell) < 2 | |
end | |
def overpopulated?(cell) | |
neighbour_count(cell) > 3 | |
end | |
def neighbour_count(cell) | |
cell[:neighbours].length | |
end | |
end | |
module Organism | |
include Cell | |
def organism(cells = {}) | |
cells | |
end | |
def evolved(organism) | |
organism.each_with_object({}){ |(name, cell), evolved| evolved[name] = evolve_cell(cell) } | |
end | |
def cell_named(name, organism_info) | |
organism_info[:from][name] | |
end | |
end | |
RSpec.configure do |config| | |
config.include(Organism) | |
end | |
RSpec::Matchers.define :be_alive do | |
match do |cell| | |
cell[:state] == :alive | |
end | |
end | |
RSpec::Matchers.define :be_dead do | |
match do |cell| | |
cell[:state] == :dead | |
end | |
end | |
describe 'evolution' do | |
context 'in an organism with a cell without neighbours' do | |
it 'kills that cell' do | |
cell_named(:c1, from: evolved(organism(c1: cell))).should be_dead | |
end | |
end | |
context 'in an organism with a cell with 2 neighbours' do | |
it 'keeps that cell alive' do | |
c1, c2, c3 = cell, cell, cell | |
neighbours_of c1, are: [c2, c3] | |
neighbours_of c2, are: [c1] | |
neighbours_of c3, are: [c1] | |
evolved_organism = evolved(organism(c1: c1, c2: c2, c3: c3)) | |
cell_named(:c1, from: evolved_organism).should be_alive | |
cell_named(:c2, from: evolved_organism).should be_dead | |
cell_named(:c3, from: evolved_organism).should be_dead | |
end | |
end | |
context 'in an organism with a cell with 3 neighbours' do | |
it 'keeps that cell alive' do | |
c1, c2, c3, c4 = cell, cell, cell, cell | |
neighbours_of c1, are: [c2, c3, c4] | |
neighbours_of c2, are: [c1, c3] | |
neighbours_of c3, are: [c1, c2] | |
neighbours_of c4, are: [c1] | |
evolved_organism = evolved(organism(c1: c1, c2: c2, c3: c3, c4: c4)) | |
cell_named(:c1, from: evolved_organism).should be_alive | |
cell_named(:c2, from: evolved_organism).should be_alive | |
cell_named(:c3, from: evolved_organism).should be_alive | |
cell_named(:c4, from: evolved_organism).should be_dead | |
end | |
end | |
context 'in an organism with a cell with 4 neighbours' do | |
it 'kills that cell' do | |
c1, c2, c3, c4, c5 = cell, cell, cell, cell, cell | |
neighbours_of c1, are: [c2, c3, c4, c5] | |
neighbours_of c2, are: [c1, c3] | |
neighbours_of c3, are: [c1, c2] | |
neighbours_of c4, are: [c1] | |
neighbours_of c5, are: [c1] | |
evolved_organism = evolved(organism(c1: c1, c2: c2, c3: c3, c4: c4, c5: c5)) | |
cell_named(:c1, from: evolved_organism).should be_dead | |
cell_named(:c2, from: evolved_organism).should be_alive | |
cell_named(:c3, from: evolved_organism).should be_alive | |
cell_named(:c4, from: evolved_organism).should be_dead | |
cell_named(:c5, from: evolved_organism).should be_dead | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice, quite readable.
You have a couple of bugs, though:
Also, there are two things I would do different: