HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Ruby implementation of Conway's Game of Life

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
conwaygamerubylifeimplementation

Problem

I'm just starting with Ruby and decided to do two different implementations of Conway's Game of Life. In this first one I'm not using cells as a separate class and instead just track everything as a location on the board, using a state pattern I guess.

Anyway, I want this code to display good Object-Oriented programming basics as well as construction via test-driven development, using rspec.

Please critique away and tell me if it's under/over engineered for the problem, not extensible enough or too extended, as well as if my TDD progression was worthwhile, did I do enough or too few tests, or maybe not the right tests.

board.rb:

```
class Board
attr_accessor :width,:height

def initialize(cells=[], width = 3, height = 3)
@cells = cells
@height = height
@width = width
end

def live_cells
@cells
end

def is_alive?(x,y)
@cells.include?([x,y])
end

def neighbors_to(x,y)
@cells.select do |cell_x, cell_y|
dx = (x-cell_x).abs
dy = (y-cell_y).abs
#If the tuple x,y is a neighbor of that cell then the following is true
dx <= 1 and dy <= 1 and not (dx == 0 and dy == 0)
end.length
#We only care how many there are and do not care which cells in particular
end

def evolve
survivors = []
#Scan the entire board (may be able to be more clever here but you need to check each dead cell for ressurection)
@width.times do |x|
@height.times do |y|
survivors << [x,y] if should_survive?(x,y) or should_ressurect?(x,y)
end
end
#Populating the new board from survivors allows reference to the current generation while populating the next generation
Board.new(survivors, self.width,self.height)
end

def should_survive?(x,y)
(neighbors_to(x,y) == 2 or neighbors_to(x,y) == 3) and is_alive?(x,y)
end

def should_ressurect?(x,y)
neighbors_to(x,y) == 3 and not is_alive?(x,y)
end

def print_board
puts "Current Generation"
puts
@width.times do |x|
@hei

Solution

I won't talk about style, because @tokland's comments are spot-on.

However, some quick notes about efficiency. For large board sizes, this implementation is inefficient.

For example, neighbors_to scans every cell on the board, and it is called multiple times. It is called twice from the should_survive? method:

def should_survive?(x,y)
  (neighbors_to(x,y) == 2 or neighbors_to(x,y) == 3) and is_alive?(x,y)
end


You can reduce the call to one, and swap the order so you don't have to call it at all if the cell is alive:

def should_survive?(x,y)
  is_alive?(x,y) and [2,3].include? neighbors_to(x,y)
end


The implementation of neighbors_to should simply collect each of the surrounding eight (maximum) spaces rather than scan the entire set of live cells.

This also means you need to be able to query a cell directly, rather than scan all live cells as your is_alive? method currently does. I would recommend representing a board using a 2D array of spaces. I'd probably rename @cells to @live_cells to avoid confusion.

Code Snippets

def should_survive?(x,y)
  (neighbors_to(x,y) == 2 or neighbors_to(x,y) == 3) and is_alive?(x,y)
end
def should_survive?(x,y)
  is_alive?(x,y) and [2,3].include? neighbors_to(x,y)
end

Context

StackExchange Code Review Q#20402, answer score: 5

Revisions (0)

No revisions yet.