Skip to content

Instantly share code, notes, and snippets.

@tylerdooling
Created October 9, 2012 20:22
Show Gist options
  • Save tylerdooling/3861192 to your computer and use it in GitHub Desktop.
Save tylerdooling/3861192 to your computer and use it in GitHub Desktop.
Game of Life
# The Game of Life
#
# Rules
# Any live cell with fewer than two live neighbors dies, as if caused by under-population.
# Any live cell with two or three live neighbors lives on to the next generation.
# Any live cell with more than three live neighbors dies, as if by overcrowding.
# Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
# Example usage
# Game.new(150,50).randomize.run 1000
require 'rspec'
class Point < Struct.new :x, :y
def neighbors
@neighbors ||= (((-1..1).to_a * 2).combination(2).to_a.uniq - [[0,0]]).map { |n|
Point.new *([[x,y], n].transpose.map { |e| e.reduce :+ })
}
end
end
class Node
attr_reader :coordinate, :container
attr_accessor :state
def initialize(x, y, container)
@coordinate = Point.new x, y
@container = container
@state = :off
end
def neighbors
@neighbors ||= @coordinate.neighbors.map { |n| self.container.node_at n }.compact
end
def capture_state
@captured_live_neighbors = self.neighbors.select { |n| n.state == :on }.size
end
def evolve
case captured_live_neighbors
when 2; # do nothing
when 3; self.state = :on
else self.state = :off
end
end
def show
self.state == :on ? '*' : " "
end
private
def captured_live_neighbors
@captured_live_neighbors || []
end
end
class Game
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
build_grid
end
def build_grid
@grid = Array.new(y){ |xc| Array.new(x){ |yc| Node.new(xc, yc, self) }}
end
def randomize
@grid.each do |r| r.each do |c| c.tap { |n| n.state = rand > 0.8 ? :on : :off } end end; self
end
def node_at(point)
return unless point && point.x > 0 && point.y > 0
@grid.fetch(point.x, [])[point.y]
end
def run(generations)
generations.times do
self.tick
puts self.show
end
end
def tick
@grid.each do |r| r.each do |c| c.capture_state end end
@grid.each do |r| r.each do |c| c.evolve end end
end
def show
@grid.map { |r| r.map { |n| n.show }.join('')}.join "\n"
end
end
describe Point do
it "returns an array of 8 neighbors" do
Point.new(0,0).neighbors.size.should be 8
end
end
describe Game do
it "returns nil if node is out of range" do
g = Game.new 20, 20
mock_point = mock :point, :x => -1, :y => -1
g.node_at(mock_point).should be nil
end
end
describe Node do
context "that is alive" do
it "dies with fewer than 2 live neighbors" do
n = Node.new 3, 3, nil
n.state = :on
mock_node = mock :node, :state => :on
n.stub(:neighbors).and_return(Array.new(1, mock_node))
n.capture_state
n.evolve
n.state.should be :off
end
it "lives with 2 or 3 live neighbors" do
n = Node.new 3, 3, nil
n.state = :on
mock_node = mock :node, :state => :on
n.stub(:neighbors).and_return(Array.new(2, mock_node))
n.capture_state
n.evolve
n.state.should be :on
end
it "dies with more than 3 live neighbors" do
n = Node.new 3, 3, nil
n.state = :on
mock_node = mock :node, :state => :on
n.stub(:neighbors).and_return(Array.new(4, mock_node))
n.capture_state
n.evolve
n.state.should be :off
end
end
context "that is dead" do
it "lives with 3 live neighbors" do
n = Node.new 3, 3, nil
n.state = :off
mock_node = mock :node, :state => :on
n.stub(:neighbors).and_return(Array.new(3, mock_node))
n.capture_state
n.evolve
n.state.should be :on
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment