Skip to content

Instantly share code, notes, and snippets.

Created July 13, 2010 07:28
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 anonymous/473578 to your computer and use it in GitHub Desktop.
Save anonymous/473578 to your computer and use it in GitHub Desktop.
#I have included in this file the GameOfLife class as well as the supplied
#(but slightly modified) LifeNcurses class. See end of file for example of
#running a curses demonstration of my GameOfLife class.
#Thanks for running this challenge!
class GameOfLife
attr_accessor :state, :width, :height
def initialize(*size)
if (size.length == 2 && size[0].class == Fixnum && size[1].class == Fixnum)
#if passed 2 numbers, create a grid with those sizes
@width, @height = size[0], size[1]
elsif (size.length == 1 && size[0].class == Fixnum)
#if passed 1 number, create a square grid of that size
@width = @height = size[0]
elsif (size.all? {|i| i.class == Array} && size.map{|i| i.length}.uniq.length == 1)
#if passed a grid as an array, create that grid
#checks that all elements of size are arrays and are all of the same length
@state, @height, @width = size, size.length, size[0].length
return
else
#otherwise, create a 'standard' 5x5 grid
@width = 5
@height = 5
end
#Code to create grid array
@state = []
@height.times do
arr = []
@width.times {arr << rand(2)}
@state << arr
end
end
def evolve
#return the next generation of the game of life (via evolve_internal method)
evolve_internal
end
def evolve!
#return next generation and update state to next generation
@state = evolve_internal
end
protected
def evolve_internal
#generates new 2-dimensional array of dimensions @width x @height
#that is set to values of next generation of game of life
#by checking through existing state of game via cellcheck method
arr = []
@height.times do |row|
row_array = []
@width.times {|col| row_array << cellcheck(row,col)}
arr << row_array
end
arr
end
def cellcheck(row,col)
#For a cell at row,col checks each of the eight cells surrounding it and counts all of the live cells
#Uses check_for_border method to look across the edges of the game board
count = 0
(-1..1).each do |row_offset|
(-1..1).each do |col_offset|
unless (row_offset == 0 && col_offset == 0)
row_to_check = check_for_border(row + row_offset,@height)
col_to_check = check_for_border(col + col_offset,@width)
count += 1 if (@state[row_to_check][col_to_check]) == 1
end
end
end
return 0 if @state[row][col] == 1 && (count < 2 || count > 3)
return 1 if @state[row][col] == 1 && (count == 2 || count == 3)
return 1 if @state[row][col] == 0 && count == 3
0
end
def check_for_border(position,size)
position = size - 1 if position == -1
position = 0 if position == size
position
end
end
require 'rubygems'
require 'ffi-ncurses'
class LifeNcurses
# spaces from the border of the terminal
MARGIN = 2
include FFI::NCurses
def initialize(game_of_life,iterations=100)
@stdscr = initscr
cbreak
(1..iterations).each do |generation|
clear
display_title(generation)
show game_of_life.evolve! #modified to use evolve! method rather than evolve
end
ensure
endwin
end
def show(state)
state.each_with_index do |row,row_index|
row.each_with_index do |col, col_index|
mvwaddstr @stdscr, row_index+MARGIN, col_index+MARGIN, '#' if state[row_index][col_index] == 1
end
end
refresh
sleep 1
end
def display_title(generation)
mvwaddstr @stdscr, 0, 1, "Game of life: Generation #{generation}"
end
end
#Uncomment line below and run 'ruby game_of_life.rb' to see curses demonstration
#LifeNcurses.new(GameOfLife.new(50,10),20)
# Rules
# each cell 2 possible states, life of death
# 8 neighbours
# - any life cell < 2 neighbours dies
# - any life cell > 3 neighbours dies
# - any live cell with 2 or 3 neighbours lives to next generation
# - any dead cell with exactly 3 live neighbours becomes a live cell
# first generation: apply pattern
#
### EXAMPLE ##########################################################################################################
# WRITE YOUR OWN TESTS, OF COURSE
# test-driven development is the best, this is just to show you how it should work (if it's not clear from rules)
# Plus varying parameters on initialization allows you to do cooler things, like play with different sizes, seeds, etc.
#######################################################################################################################
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_kill_with_no_neighbours
@game.state = [[1,0,0],[0,0,0],[0,0,0]]
after = @game.evolve
assert_equal after[0][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 after[1][0], 0
assert_equal after[2][0], 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 after, [[0,0,0],[0,0,0],[0,0,0]]
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 after, [[1,1,1],[1,1,1],[1,1,1]]
end
def test_single_integer_parameter_creates_square_grid_of_that_size
grid = GameOfLife.new(10)
assert_equal grid.state.length, 10
assert_equal grid.state.all? {|row| row.length == 10}, true
end
def test_two_integer_parameters_creates_rectangular_grid_of_those_sizes
grid = GameOfLife.new(6,8)
assert_equal grid.state.length, 8
assert_equal grid.state.all? {|row| row.length == 6}, true
end
def test_passing_an_array_of_arrays_creates_that_grid
grid = GameOfLife.new([1,0,1,0],[0,1,0,1],[1,0,1,0],[0,1,0,1])
assert_equal grid.state, [[1,0,1,0],[0,1,0,1],[1,0,1,0],[0,1,0,1]]
end
def test_r_pentomino
grid = GameOfLife.new([0,0,0,0,0],[0,0,1,1,0],[0,1,1,0,0],[0,0,1,0,0],[0,0,0,0,0])
assert_equal grid.evolve, [[0,0,0,0,0],[0,1,1,1,0],[0,1,0,0,0],[0,1,1,0,0],[0,0,0,0,0]]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment