Skip to content

Instantly share code, notes, and snippets.

@CPFB
Created July 2, 2010 01:58
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 CPFB/bff42e44d35ca554ac67 to your computer and use it in GitHub Desktop.
Save CPFB/bff42e44d35ca554ac67 to your computer and use it in GitHub Desktop.
####################################
# RCPFN 11: Game of Life
#
# Christopher Fortenberry
#
# http://twitter.com/cpfortenberry
# http://github.com/CPFB
#
#
# NOTES
#
# I went a little overboard with
# the tests. I'm hoping that I
# didn't overlook anything.
####################################
class GameOfLife
attr_accessor :grid, :step
attr_reader :rows, :columns
def initialize(rows, columns, seeds = 0)
# establish which cells get seeds (defaults to 1/4 the number of cells in the grid)
number_of_cells = (rows * columns)
seeds = (number_of_cells - (number_of_cells % 4)) / 4 if seeds == 0
# build and shuffle seed_array
seed_array = []
seeds.times { seed_array << 1 }
(number_of_cells - seeds).times { seed_array << 0 }
seed_array = seed_array.sort_by { rand }
# build grid for GameOfLife
grid = []
rows.times do
row = []
columns.times { row << seed_array.pop }
grid << row
end
@grid = grid
@step = 0
@rows = rows
@columns = columns
end
def evolve
# evolved_array will store the new array after evolution
evolved_array = []
# the upto loops grab the coordinates for each cell
0.upto(@rows-1) do |x|
new_row = []
0.upto(@columns-1) do |y|
# checks to see if the grabbed cell should evolve to living or dead
living_cells = self.number_of_living_cells(x, y)
if @grid[x][y] == 1
(living_cells == 2 || living_cells == 3) ? new_row << 1 : new_row << 0
else
living_cells == 3 ? new_row << 1 : new_row << 0
end
end
evolved_array << new_row
end
@step += 1
@grid = evolved_array
end
def state=(arrays)
new_num_rows = arrays.size
new_num_columns = arrays[0].size
# check to make sure that the arrays given are in a rectangular shape
arrays.each { |a| raise ArgumentError, "Game state must be in rectangular form" if a.size != new_num_columns }
# set the variables
@grid = arrays
@step = 0
@columns = new_num_columns
@rows = new_num_rows
end
# parameters are the coordinates for the cell
def get_neighbors(row, col)
# [
# [NW], [N], [NE],
# [W], [E],
# [SW], [S], [SE]
# ]
[
[x_coord(row-1),y_coord(col-1)], [x_coord(row-1),y_coord(col)], [x_coord(row-1),y_coord(col+1)],
[x_coord(row),y_coord(col-1)], [x_coord(row),y_coord(col+1)],
[x_coord(row+1),y_coord(col-1)], [x_coord(row+1),y_coord(col)], [x_coord(row+1),y_coord(col+1)]
]
end
# makes sure that the coordinate is correct when adding or subtracting from the original
def get_coordinate(value, array_size)
return (array_size - 1) if value < 0
return 0 if value >= array_size
return value
end
# grabs x-coordinate
def x_coord(value)
get_coordinate(value, @rows)
end
# grabs y-coordinate
def y_coord(value)
get_coordinate(value, @columns)
end
# counts the number of living cells by finding the neighbors and counting them
def number_of_living_cells(row, column)
living_cells = 0
get_neighbors(row, column).each { |x,y| living_cells += @grid[x][y] }
return living_cells
end
end
######################################
# RCPFN 11: Game of Life #
#
# Christopher Fortenberry
#
# http://twitter.com/cpfortenberry
# http://github.com/CPFB
#
#
# NOTES
#
# I refactored the evolve tests,
# leaving fragments of the old tests
# to explain how they work.
######################################
require File.join(File.dirname(__FILE__), 'game_of_life')
require 'rubygems'
require 'test/unit'
class GameOfLifeTest < Test::Unit::TestCase
def setup
@game = GameOfLife.new(6, 5)
end
def test_should_find_neighbors_with_no_fold_over
assert_equal @game.get_neighbors(3, 3), [ [2,2], [2,3], [2,4],
[3,2], [3,4],
[4,2], [4,3], [4,4]
]
end
def test_should_find_neighbors_with_north_fold_over
assert_equal @game.get_neighbors(0, 3), [ [5,2], [5,3], [5,4],
[0,2], [0,4],
[1,2], [1,3], [1,4]
]
end
def test_should_find_neighbors_with_south_fold_over
assert_equal @game.get_neighbors(5, 3), [ [4,2], [4,3], [4,4],
[5,2], [5,4],
[0,2], [0,3], [0,4]
]
end
def test_should_find_neighbors_with_west_fold_over
assert_equal @game.get_neighbors(3, 0), [ [2,4], [2,0], [2,1],
[3,4], [3,1],
[4,4], [4,0], [4,1]
]
end
def test_should_find_neighbors_with_east_fold_over
assert_equal @game.get_neighbors(3, 4), [ [2,3], [2,4], [2,0],
[3,3], [3,0],
[4,3], [4,4], [4,0]
]
end
def test_should_find_neighbors_with_northwest_fold_over
assert_equal @game.get_neighbors(0, 0), [ [5,4], [5,0], [5,1],
[0,4], [0,1],
[1,4], [1,0], [1,1]
]
end
def test_should_find_neighbors_with_northeast_fold_over
assert_equal @game.get_neighbors(0, 4), [ [5,3], [5,4], [5,0],
[0,3], [0,0],
[1,3], [1,4], [1,0]
]
end
def test_should_find_neighbors_with_southwest_fold_over
assert_equal @game.get_neighbors(5, 0), [ [4,4], [4,0], [4,1],
[5,4], [5,1],
[0,4], [0,0], [0,1]
]
end
def test_should_find_neighbors_with_southeast_fold_over
assert_equal @game.get_neighbors(5, 4), [ [4,3], [4,4], [4,0],
[5,3], [5,0],
[0,3], [0,4], [0,0]
]
end
def test_should_have_no_living_neighbors
@game.state = [ [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 0
end
def test_should_have_one_living_neighbor
@game.state = [ [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 1
end
def test_should_have_two_living_neighbors
@game.state = [ [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 1, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 2
end
def test_should_have_three_living_neighbors
@game.state = [ [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 3
end
def test_should_have_four_living_neighbors
@game.state = [ [0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 4
end
def test_should_have_five_living_neighbors
@game.state = [ [0, 0, 0, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 5
end
def test_should_have_six_living_neighbors
@game.state = [ [0, 0, 1, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 6
end
def test_should_have_seven_living_neighbors
@game.state = [ [0, 0, 1, 1, 0],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 7
end
def test_should_have_eight_living_neighbors
@game.state = [ [0, 0, 1, 1, 1],
[0, 0, 1, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
assert_equal @game.number_of_living_cells(1, 3), 8
end
def evolve_base(x_coord, y_coord, cell_status, living_neighbors, expected)
# initialize state of array
state = [ [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
# sets the dimensions of the grid
@game.state = state
state[x_coord][y_coord] = cell_status
# shuffling mechanism to randomize the order of the neighbors to check for inconsistencies in testing
coord_array = @game.get_neighbors(x_coord, y_coord)
# CAN BE COMMENTED OUT FOR FASTER TESTING
coord_array = coord_array.sort_by { rand }
# populates state with appropriate number of living neighbors
0.upto(living_neighbors-1) do |index|
coords = coord_array[index]
state[coords[0]][coords[1]] = 1
end
# do the test
@game.state = state
after = @game.evolve
assert_equal after[x_coord][y_coord], expected
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_no_living_neighbors
evolve_base(1, 3, 1, 0, 0)
# @game.state = [ [0, 0, 0, 0, 0],
# [0, 0, 0, 1, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[1][3], 0
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_one_living_neighbor
evolve_base(1, 3, 1, 1, 0)
end
def test_living_cell_with_no_fold_over_neighbors_should_live_with_two_living_neighbors
evolve_base(1, 3, 1, 2, 1)
end
def test_living_cell_with_no_fold_over_neighbors_should_live_with_three_living_neighbors
evolve_base(1, 3, 1, 3, 1)
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_four_living_neighbors
evolve_base(1, 3, 1, 4, 0)
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_five_living_neighbors
evolve_base(1, 3, 1, 5, 0)
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_six_living_neighbors
evolve_base(1, 3, 1, 6, 0)
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_seven_living_neighbors
evolve_base(1, 3, 1, 7, 0)
end
def test_living_cell_with_no_fold_over_neighbors_should_die_with_eight_living_neighbors
evolve_base(1, 3, 1, 8, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_no_living_neighbors
evolve_base(1, 3, 0, 0, 0)
# @game.state = [ [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[1][3], 0
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_one_living_neighbor
evolve_base(1, 3, 0, 1, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_two_living_neighbors
evolve_base(1, 3, 0, 2, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_regenerate_with_three_living_neighbors
evolve_base(1, 3, 0, 3, 1)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_four_living_neighbors
evolve_base(1, 3, 0, 4, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_five_living_neighbors
evolve_base(1, 3, 0, 5, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_six_living_neighbors
evolve_base(1, 3, 0, 6, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_seven_living_neighbors
evolve_base(1, 3, 0, 7, 0)
end
def test_dead_cell_with_no_fold_over_neighbors_should_stay_dead_with_eight_living_neighbors
evolve_base(1, 3, 0, 8, 0)
end
def test_living_cell_with_north_fold_over_should_die_with_no_living_neighbors
evolve_base(0, 1, 1, 0, 0)
# @game.state = [ [0, 1, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[0][1], 0
end
def test_living_cell_with_north_fold_over_should_die_with_one_living_neighbor
evolve_base(0, 1, 1, 1, 0)
end
def test_living_cell_with_north_fold_over_should_stay_living_with_two_living_neighbors
evolve_base(0, 1, 1, 2, 1)
end
def test_living_cell_with_north_fold_over_should_stay_living_with_three_living_neighbors
evolve_base(0, 1, 1, 3, 1)
end
def test_living_cell_with_north_fold_over_should_die_with_four_living_neighbors
evolve_base(0, 1, 1, 4, 0)
end
def test_living_cell_with_north_fold_over_should_die_with_five_living_neighbors
evolve_base(0, 1, 1, 5, 0)
end
def test_living_cell_with_north_fold_over_should_die_with_six_living_neighbors
evolve_base(0, 1, 1, 6, 0)
end
def test_living_cell_with_north_fold_over_should_die_with_seven_living_neighbors
evolve_base(0, 1, 1, 7, 0)
end
def test_living_cell_with_north_fold_over_should_die_with_eight_living_neighbors
evolve_base(0, 1, 1, 8, 0)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_no_living_neighbors
evolve_base(0, 1, 0, 0, 0)
# @game.state = [ [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[0][1], 0
end
def test_dead_cell_with_north_fold_over_stays_dead_with_one_living_neighbor
evolve_base(0, 1, 0, 1, 0)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_two_living_neighbors
evolve_base(0, 1, 0, 2, 0)
end
def test_dead_cell_with_north_fold_over_regenerates_with_three_living_neighbors
evolve_base(0, 1, 0, 3, 1)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_four_living_neighbors
evolve_base(0, 1, 0, 4, 0)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_five_living_neighbors
evolve_base(0, 1, 0, 5, 0)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_six_living_neighbors
evolve_base(0, 1, 0, 6, 0)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_seven_living_neighbors
evolve_base(0, 1, 0, 7, 0)
end
def test_dead_cell_with_north_fold_over_stays_dead_with_eight_living_neighbors
evolve_base(0, 1, 0, 8, 0)
end
def test_living_cell_with_west_fold_over_dies_with_no_living_neighbors
evolve_base(1, 0, 1, 0, 0)
# @game.state = [ [0, 0, 0, 0, 0],
# [1, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[1][0], 0
end
def test_living_cell_with_west_fold_over_dies_with_one_living_neighbor
evolve_base(1, 0, 1, 1, 0)
end
def test_living_cell_with_west_fold_over_dies_with_two_living_neighbors
evolve_base(1, 0, 1, 2, 1)
end
def test_living_cell_with_west_fold_over_dies_with_three_living_neighbors
evolve_base(1, 0, 1, 3, 1)
end
def test_living_cell_with_west_fold_over_dies_with_four_living_neighbors
evolve_base(1, 0, 1, 4, 0)
end
def test_living_cell_with_west_fold_over_dies_with_five_living_neighbors
evolve_base(1, 0, 1, 5, 0)
end
def test_living_cell_with_west_fold_over_dies_with_six_living_neighbors
evolve_base(1, 0, 1, 6, 0)
end
def test_living_cell_with_west_fold_over_dies_with_seven_living_neighbors
evolve_base(1, 0, 1, 7, 0)
end
def test_living_cell_with_west_fold_over_dies_with_eight_living_neighbors
evolve_base(1, 0, 1, 8, 0)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_no_neighbors
evolve_base(1, 0, 0, 0, 0)
# @game.state = [ [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[1][0], 0
end
def test_dead_cell_with_west_fold_over_stays_dead_with_one_neighbor
evolve_base(1, 0, 0, 1, 0)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_two_neighbors
evolve_base(1, 0, 0, 2, 0)
end
def test_dead_cell_with_west_fold_over_regenerates_with_three_neighbors
evolve_base(1, 0, 0, 3, 1)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_four_neighbors
evolve_base(1, 0, 0, 4, 0)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_five_neighbors
evolve_base(1, 0, 0, 5, 0)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_six_neighbors
evolve_base(1, 0, 0, 6, 0)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_seven_neighbors
evolve_base(1, 0, 0, 7, 0)
end
def test_dead_cell_with_west_fold_over_stays_dead_with_eight_neighbors
evolve_base(1, 0, 0, 8, 0)
end
def test_living_cell_with_northwest_fold_over_dies_with_no_neighbors
evolve_base(0, 0, 1, 0, 0)
# @game.state = [ [1, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[0][0], 0
end
def test_living_cell_with_northwest_fold_over_dies_with_one_neighbor
evolve_base(0, 0, 1, 1, 0)
end
def test_living_cell_with_northwest_fold_over_stays_living_with_two_neighbors
evolve_base(0, 0, 1, 2, 1)
end
def test_living_cell_with_northwest_fold_over_stays_living_with_three_neighbors
evolve_base(0, 0, 1, 3, 1)
end
def test_living_cell_with_northwest_fold_over_dies_with_four_neighbors
evolve_base(0, 0, 1, 4, 0)
end
def test_living_cell_with_northwest_fold_over_dies_with_five_neighbors
evolve_base(0, 0, 1, 5, 0)
end
def test_living_cell_with_northwest_fold_over_dies_with_six_neighbors
evolve_base(0, 0, 1, 6, 0)
end
def test_living_cell_with_northwest_fold_over_dies_with_seven_neighbors
evolve_base(0, 0, 1, 7, 0)
end
def test_living_cell_with_northwest_fold_over_dies_with_eight_neighbors
evolve_base(0, 0, 1, 8, 0)
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_no_neighbors
evolve_base(0, 0, 0, 0, 0)
# @game.state = [ [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]
# ]
# after = @game.evolve
# assert_equal after[0][0], 0
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_one_neighbor
evolve_base(0, 0, 0, 1, 0)
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_two_neighbors
evolve_base(0, 0, 0, 2, 0)
end
def test_dead_cell_with_northwest_fold_over_regenerates_with_three_neighbors
evolve_base(0, 0, 0, 3, 1)
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_four_neighbors
evolve_base(0, 0, 0, 4, 0)
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_five_neighbors
evolve_base(0, 0, 0, 5, 0)
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_six_neighbors
evolve_base(0, 0, 0, 6, 0)
end
def test_dead_cell_with_northwest_fold_over_stays_dead_with_seven_neighbors
evolve_base(0, 0, 0, 7, 0)
end
def test_living_cell_with_northwest_fold_over_stays_dead_with_eight_neighbors
evolve_base(0, 0, 0, 8, 0)
end
def test_state_should_update_rows_and_columns
@game.state = [ [0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]
]
assert_equal @game.rows, 5
assert_equal @game.columns, 7
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment