Skip to content

Instantly share code, notes, and snippets.

@elle
Last active February 27, 2022 23:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elle/9bade1cc1d0fe55dfb737a8f6ea04039 to your computer and use it in GitHub Desktop.
Save elle/9bade1cc1d0fe55dfb737a8f6ea04039 to your computer and use it in GitHub Desktop.
Conway's game of life
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