Skip to content

Instantly share code, notes, and snippets.

@coxy
Created June 30, 2010 08:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coxy/8dcb8d3365b5228fb7d6 to your computer and use it in GitHub Desktop.
Save coxy/8dcb8d3365b5228fb7d6 to your computer and use it in GitHub Desktop.
# game_of_life.rb for Ruby Challenge - http://rubylearning.com/blog/2010/06/28/rpcfn-the-game-of-life-11/
# Andrew Cox
# 3coxy4@gmail.com
DEFAULT_SIZE = 5
class GameOfLife
attr_reader :size
# init and randomize state
def initialize(size = DEFAULT_SIZE)
@size = size
randomize_state
end
# generate random state
def randomize_state
@cells = ([[0]*@size]*@size).map do |x|
x.map{ Cell.new(rand.round) }
end
set_neighbours
end
# set to specific state
def state=(state)
@cells = state.map do |x|
x.map do |y|
Cell.new(y)
end
end
set_neighbours
end
# flatten cell array - useful for iterating through all cells
def cells
@cells.flatten
end
# iterate through each cell with their x,y co-ordinates
def each_cell_with_index(&block)
@cells.each_with_index do |x,i|
x.each_with_index do |y,j|
block.call(y,i,j)
end
end
end
# evolve cells based on specific rules of the game
def evolve
cells.each do |cell|
# any life cell < 2 neighbours dies
cell.dead! if cell.alive? and cell.live_neighbours < 2
# any life cell > 3 neighbours dies
cell.dead! if cell.alive? and cell.live_neighbours > 3
# any live cell with 2 or 3 neighbours lives to next generation
# no action needed
# any dead cell with exactly 3 live neighbours becomes a live cell
cell.alive! if cell.dead? and cell.live_neighbours == 3
end
# propigate changes
cells.each{|cell| cell.save_state! }
state_as_int_array
end
private
# calls 'state' method on each Cell
def state_as_int_array
@cells.map{|r| r.map{|c| c.state } }
end
def maximum_index
@size - 1
end
# joins edges together so that the board folds onto itself
def no_edges(n)
n < 0 ? maximum_index : (n > maximum_index ? 0 : n)
end
# works out all neighbours of a given point on the board
def neighbours_of(x,y)
[[x-1,y-1], [x,y-1], [x+1,y-1],
[x-1,y ], [x+1,y ],
[x-1,y+1], [x,y+1], [x+1,y+1]].map do |n|
@cells[no_edges(n[0])][no_edges(n[1])]
end
end
# loop through each Cell and set it's neighbours
def set_neighbours
each_cell_with_index do |cell,x,y|
cell.neighbours = neighbours_of(x,y)
end
end
end
# Represents each 'cell' on the board
class Cell
attr_accessor :state, :neighbours
def initialize(state)
@state = state
@neighbours = []
end
def alive?
@state == 1
end
def dead?
@state == 0
end
def alive!
@nextstate = 1
end
def dead!
@nextstate = 0
end
# update this Cell's current state with the next state after an evolution
def save_state!
@state = @nextstate || @state
end
# counts all neighbours that are currently 'alive' - i.e. have a state of '1'
def live_neighbours
@neighbours.map{|c| c.state }.inject{|a,b| a + b }
end
end
# game_of_life_test.rb for Ruby Challenge - http://rubylearning.com/blog/2010/06/28/rpcfn-the-game-of-life-11/
# Andrew Cox
# 3coxy4@gmail.com
require File.join(File.dirname(__FILE__), 'game_of_life')
require 'rubygems'
require 'test/unit'
class GameOfLifeTest < Test::Unit::TestCase
def setup
@game = GameOfLife.new(3)
end
def test_should_default_size_to_5x5
new_game = GameOfLife.new
assert_equal 5, new_game.size
end
def test_should_generate_random_state_on_init
new_game = GameOfLife.new(20)
assert_not_nil new_game.cells
assert_equal 400, new_game.cells.size
end
def test_should_return_state
@game.state = [[0,0,0],[0,1,0],[0,0,0]]
assert_equal 1, @game.cells[4].state
end
def test_should_all_have_8_neighbours
@game.state = [[1,0,0],[0,0,0],[0,0,0]]
@game.cells.each do |cell|
assert_equal 8, cell.neighbours.size
end
end
def test_should_calculate_live_neighbours
@game.state = [[1,0,0],[0,0,0],[0,0,0]]
assert_equal 0, @game.cells[0].live_neighbours
@game.state = [[1,0,1],[0,0,0],[0,0,0]]
assert_equal 1, @game.cells[0].live_neighbours
@game.state = [[1,0,1],[0,0,0],[0,0,1]]
assert_equal 2, @game.cells[0].live_neighbours
@game.state = [[1,0,1],[0,0,0],[1,0,1]]
assert_equal 3, @game.cells[0].live_neighbours
@game.state = [[1,1,1],[0,0,0],[1,0,1]]
assert_equal 4, @game.cells[0].live_neighbours
@game.state = [[1,1,1],[1,0,0],[1,0,1]]
assert_equal 5, @game.cells[0].live_neighbours
@game.state = [[1,1,1],[1,1,0],[1,0,1]]
assert_equal 6, @game.cells[0].live_neighbours
@game.state = [[1,1,1],[1,1,1],[1,0,1]]
assert_equal 7, @game.cells[0].live_neighbours
@game.state = [[1,1,1],[1,1,1],[1,1,1]]
assert_equal 8, @game.cells[0].live_neighbours
end
def test_should_kill_with_no_neighbours
@game.state = [[1,0,0],[0,0,0],[0,0,0]]
after = @game.evolve
assert_equal 0, after[0][0]
end
def test_should_kill_with_just_one_neighbour
@game.state = [[0,0,0],[1,0,0],[1,0,0]]
after = @game.evolve
assert_equal 0, after[1][0]
assert_equal 0, after[2][0]
end
def test_should_kill_with_more_than_3_neighbours
@game.state = [[1,1,1],[1,1,1],[1,1,1]]
after = @game.evolve
assert_equal [[0,0,0],[0,0,0],[0,0,0]], after
end
def test_should_give_birth_if_3_neighbours
@game.state = [[1,0,0],[1,1,0],[0,0,0]]
after = @game.evolve
assert_equal [[1,1,1],[1,1,1],[1,1,1]], after
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment