-
-
Save coxy/8dcb8d3365b5228fb7d6 to your computer and use it in GitHub Desktop.
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
# 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 |
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
# 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