Class: LifeCalculator

Inherits:
Object
  • Object
show all
Defined in:
lib/life_game_viewer/model/life_calculator.rb

Overview

Performs calculations relating to determination of a cell’s neighbors and the next generation. Generally, ‘next_generation’ will be the only method that will need to be called, but the others are provided publicly, since the Game of Life is all about experimentation.

See Wikipedia for more information about Conway’s Game of Life.

Rules distilled:

1) Any live cell with fewer than two live neighbours dies, as if caused by under-population. 2) Any live cell with two or three live neighbours lives on to the next generation. 3) Any live cell with more than three live neighbours dies, as if by overcrowding. 4) Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.

Instance Method Summary collapse

Instance Method Details

#dead_cell_should_become_alive(model, row, col) ⇒ Object



99
100
101
# File 'lib/life_game_viewer/model/life_calculator.rb', line 99

def dead_cell_should_become_alive(model, row, col)
  num_living_neighbors(model, row, col) == 3
end

#live_cell_should_continue_to_live(model, row, col) ⇒ Object



95
96
97
# File 'lib/life_game_viewer/model/life_calculator.rb', line 95

def live_cell_should_continue_to_live(model, row, col)
  (2..3).include?(num_living_neighbors(model, row, col))
end

#neighbors(model, row, col) ⇒ Object

Returns an array of [row, col] tuples corresponding to the cells neighboring the specified cell location. “Neighbor” is defined as a cell with up/down/left/right/diagonal adjacency to the specified cell.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/life_game_viewer/model/life_calculator.rb', line 27

def neighbors(model, row, col)

  neighbors = []

  at_left_edge   = col == 0
  at_right_edge  = col == model.column_count - 1
  at_top_edge    = row == 0
  at_bottom_edge = row == model.row_count - 1

  col_to_left  = col - 1
  col_to_right = col + 1
  row_above    = row - 1
  row_below    = row + 1

  # In its own row, return the cell to the left and right as possible.
  unless at_left_edge
    neighbors << [row, col_to_left]
  end
  unless at_right_edge
    neighbors << [row, col_to_right]
  end

  # Process the row above
  unless at_top_edge
    unless at_left_edge
      neighbors << [row_above, col_to_left]
    end
    neighbors << [row_above, col]
    unless at_right_edge
      neighbors << [row_above, col_to_right]
    end
  end

  # Process the row below
  unless at_bottom_edge
    unless at_left_edge
      neighbors << [row_below, col_to_left]
    end
    neighbors << [row_below, col]
    unless at_right_edge
      neighbors << [row_below, col_to_right]
    end
  end

  neighbors

end

#next_generation(old_model) ⇒ Object

Returns a new model with the next generation’s data.



18
19
20
21
22
# File 'lib/life_game_viewer/model/life_calculator.rb', line 18

def next_generation(old_model)
  old_model.class.send(:create, old_model.row_count, old_model.column_count) do |row, col|
    should_live(old_model, row, col)
  end
end

#num_living_neighbors(model, row, col) ⇒ Object

Returns an array of [row, col] tuples corresponding to those neighbor cells that are alive.



78
79
80
81
82
83
84
# File 'lib/life_game_viewer/model/life_calculator.rb', line 78

def num_living_neighbors(model, row, col)
  neighbors(model, row, col).inject(0) do |num_living, neighbor|
    neighbor_row, neighbor_column = neighbor
    num_living += 1 if model.alive?(neighbor_row, neighbor_column)
    num_living
  end
end

#should_live(model, row, col) ⇒ Object

Returns whether or not (as true or false) the specified cell should continue to live in the next generation.



89
90
91
92
93
# File 'lib/life_game_viewer/model/life_calculator.rb', line 89

def should_live(model, row, col)
  model.alive?(row, col) \
      ? live_cell_should_continue_to_live(model, row, col) \
      : dead_cell_should_become_alive(model, row, col)
end