Last active
February 27, 2022 23:24
-
-
Save elle/9bade1cc1d0fe55dfb737a8f6ea04039 to your computer and use it in GitHub Desktop.
Conway's game of life
This file contains 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
class LiveCell | |
TRANSITION_RULES = { | |
2 => LiveCell.new, | |
3 => LiveCell.new, | |
} | |
def next_generation(living_neighbours_count) | |
TRANSITION_RULES.fetch(living_neighbours_count) { DeadCell.new } | |
end | |
end | |
class DeadCell | |
TRANSITION_RULES = { | |
3 => LiveCell.new | |
} | |
def next_generation(living_neighbours_count) | |
TRANSITION_RULES.fetch(living_neighbours_count) { DeadCell.new } | |
end | |
end | |
class Location | |
attr_reader :x, :y, :cells, :length | |
def initialize(x, y, cells) | |
@x = x | |
@y = y | |
@cells = cells | |
@length = cells.length - 1 | |
end | |
def live_neighbours | |
neighbours.count { |n| n.instance_of? LiveCell } | |
end | |
private | |
def neighbours | |
@neighbours ||= [nw, n, ne, e, se, s, sw, w] | |
.reject { |x,y| x < 0 || y < 0 } # reject negative positions beyond the grid | |
.reject { |x,y| x > length || y > length } # reject positions beyond the grid | |
.map { |x,y| cells[x][y] } | |
end | |
def nw | |
[x-1, y+1] | |
end | |
def n | |
[x, y+1] | |
end | |
def ne | |
[x+1, y+1] | |
end | |
def e | |
[x+1, y] | |
end | |
def se | |
[x+1, y-1] | |
end | |
def s | |
[x, y-1] | |
end | |
def sw | |
[x-1, y-1] | |
end | |
def w | |
[x-1, y] | |
end | |
end | |
class World | |
attr_reader :state, :columns, :rows | |
def initialize(state) | |
@state = state | |
@columns = @rows = state.length | |
end | |
def tick | |
new_state = (0...columns).map do |x| | |
(0...rows).map do |y| | |
loc = Location.new(x, y, state) | |
cell = state[x][y] | |
cell.next_generation(loc.live_neighbours) | |
end | |
end | |
World.new(new_state) | |
end | |
end | |
RSpec.describe World do | |
describe "#tick" do | |
context "with 3 living neighbours" do | |
it "brings a cell alive" do | |
initial_state = [[DeadCell.new, LiveCell.new], [LiveCell.new, LiveCell.new]] | |
world = World.new(initial_state) | |
new_world = world.tick | |
live_count = new_world.state.flatten.count { |c| c.instance_of? LiveCell } | |
expect(live_count).to eq 4 | |
end | |
end | |
context "with one living neighbour" do | |
it "is not enough to keep the living alive" do | |
initial_state = [[DeadCell.new, LiveCell.new], [DeadCell.new, DeadCell.new]] | |
world = World.new(initial_state) | |
new_world = world.tick | |
dead_count = new_world.state.flatten.count { |c| c.instance_of? DeadCell } | |
expect(dead_count).to eq 4 | |
end | |
end | |
context "with a bigger world" do | |
it "ticks according to the rules" do | |
initial_state = [ | |
[DeadCell.new, LiveCell.new, LiveCell.new, DeadCell.new], | |
[LiveCell.new, LiveCell.new, DeadCell.new, DeadCell.new], | |
[DeadCell.new, DeadCell.new, LiveCell.new, DeadCell.new], | |
[LiveCell.new, LiveCell.new, DeadCell.new, DeadCell.new], | |
] | |
world = World.new(initial_state) | |
new_world = world.tick | |
# [L, L, L, D] | |
# [L, D, D, D] | |
# [D, D, L, D] | |
# [D, L, D, D] | |
dead_count = new_world.state.flatten.count { |c| c.instance_of? DeadCell } | |
expect(dead_count).to eq 10 | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment