Class: GameOfLife::Board
- Inherits:
-
Object
- Object
- GameOfLife::Board
- Defined in:
- lib/game_of_life/board.rb,
lib/game_of_life/cell.rb
Overview
The board used in the game. Holds the Cells.
Defined Under Namespace
Classes: Cell
Instance Attribute Summary collapse
- #cells ⇒ Object readonly
Instance Method Summary collapse
-
#cell_at(x, y) ⇒ Cell
Find the cell at a given pair of co-ordinates.
-
#coords_of_neighbors(x, y) ⇒ Array<Integer, Integer>
private
Calculates the co-ordinates of neighbors of a given pair of co-ordinates.
- #each_cell(&block) ⇒ Object
- #each_row(&block) ⇒ Object
- #each_row_with_index(&block) ⇒ Object
-
#initialize(seed_data) ⇒ Board
constructor
Creates the board.
- #mark_and_sweep_for_next_generation! ⇒ Object
- #mark_changes_for_next_generation ⇒ Object private
-
#neighbors_of_cell_at(x, y) ⇒ Array<Cell>
Finds the neighbors of a given Cell‘s co-ordinates.
-
#reformat_for_next_generation! ⇒ Object
This is the first stage in a Game’s #tick.
- #seed_with!(data) ⇒ Object private
-
#shed_dead_weight! ⇒ Object
This is the third and last stage in a Game’s #tick.
- #sweep_changes_for_next_generation! ⇒ Object private
- #validate ⇒ Object private
- #view(outputter = Outputters::SimpleStringOutputter.new) ⇒ Object
Constructor Details
#initialize(seed_data) ⇒ Board
Creates the board
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/game_of_life/board.rb', line 23 def initialize(seed_data) @cells = Array.new(Array.new) seed_with!(seed_data) begin validate rescue InvalidBoardError => ex # Add the seed_data into the error message, so the caller gets a clue raise InvalidBoardError, ex. + " [seed data was: #{seed_data.inspect}]" end end |
Instance Attribute Details
#cells ⇒ Object (readonly)
Use #each_cell, #each_row etc. methods to access the cells individually.
The Cells in this board,internally maintained as a 2D Array of Cells. The x-coordinate increases horizontally and is always positive. The y-coordinate increases vertically and is always positive. Internally, the cells are arranged as a 2D array. The first-level Array indexed with the y-coordinates. It contains an Array of Cells, whose position is the x-coordinate.
15 16 17 |
# File 'lib/game_of_life/board.rb', line 15 def cells @cells end |
Instance Method Details
#cell_at(x, y) ⇒ Cell
Find the cell at a given pair of co-ordinates. Following Array symantics, this method returns nil if nothing exists at that location or if the location is out of the board. To avoid Array’s negative index symantics it returns nill if a negative index is passed.
58 59 60 61 |
# File 'lib/game_of_life/board.rb', line 58 def cell_at(x, y) return nil if (x < 0 || y < 0) @cells[y][x] if @cells[y] end |
#coords_of_neighbors(x, y) ⇒ Array<Integer, Integer> (private)
This method returns all possible co-ordinate pairs of neighbors, so it can contain coordinates of cells not in the board, or negative ones.
Calculates the co-ordinates of neighbors of a given pair of co-ordinates.
179 180 181 182 183 184 185 186 187 188 |
# File 'lib/game_of_life/board.rb', line 179 def coords_of_neighbors(x, y) coords_of_neighbors = [] (x - 1).upto(x + 1).each do |neighbors_x| (y - 1).upto(y + 1).each do |neighbors_y| next if (x == neighbors_x) && (y == neighbors_y) coords_of_neighbors << [neighbors_x, neighbors_y] end end coords_of_neighbors end |
#each_cell(&block) ⇒ Object
47 48 49 |
# File 'lib/game_of_life/board.rb', line 47 def each_cell(&block) @cells.flatten.each { |cell| yield cell } end |
#each_row(&block) ⇒ Object
39 40 41 |
# File 'lib/game_of_life/board.rb', line 39 def each_row(&block) @cells.each { |row| yield row } end |
#each_row_with_index(&block) ⇒ Object
43 44 45 |
# File 'lib/game_of_life/board.rb', line 43 def each_row_with_index(&block) @cells.each_with_index { |row, i| yield row, i } end |
#mark_and_sweep_for_next_generation! ⇒ Object
98 99 100 101 |
# File 'lib/game_of_life/board.rb', line 98 def mark_and_sweep_for_next_generation! mark_changes_for_next_generation sweep_changes_for_next_generation! end |
#mark_changes_for_next_generation ⇒ Object (private)
152 153 154 155 156 157 158 159 |
# File 'lib/game_of_life/board.rb', line 152 def mark_changes_for_next_generation self.each_row_with_index do |cells, y| cells.each_with_index do |cell, x| cell.should_live_in_next_generation = Rules.should_cell_live?(self, cell, x, y) end end end |
#neighbors_of_cell_at(x, y) ⇒ Array<Cell>
Finds the neighbors of a given Cell‘s co-ordinates. The neighbors are the eight cells that surround the given one.
68 69 70 71 |
# File 'lib/game_of_life/board.rb', line 68 def neighbors_of_cell_at(x, y) neighbors = coords_of_neighbors(x, y).map { |x, y| self.cell_at(x, y) } neighbors.reject {|n| n.nil?} end |
#reformat_for_next_generation! ⇒ Object
This is the first stage in a Game’s #tick.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/game_of_life/board.rb', line 75 def reformat_for_next_generation! # create an array of dead cells and insert it as the first and last row of cells dead_cells = (1..@cells.first.size).map { Cell.new } # don't forget to deep copy the dead_cells @cells.unshift Marshal.load(Marshal.dump(dead_cells)) @cells.push Marshal.load(Marshal.dump(dead_cells)) # also insert a dead cell at the left and right of each row @cells.each do |row| row.unshift Cell.new row.push Cell.new end # validate to see if we broke the board validate end |
#seed_with!(data) ⇒ Object (private)
143 144 145 146 147 148 149 150 |
# File 'lib/game_of_life/board.rb', line 143 def seed_with!(data) data.each_with_index do |row, y| @cells << [] row.each_with_index do |state, x| @cells[y] << Cell.new(state) end end end |
#shed_dead_weight! ⇒ Object
This is the third and last stage in a Game’s #tick.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/game_of_life/board.rb', line 105 def shed_dead_weight! # Remove the first and last rows if all cells are dead @cells.shift if @cells.first.all? { |cell| cell.dead? } @cells.pop if @cells.last.all? { |cell| cell.dead? } # Remove the first cell of every row, if they are all dead first_columns = @cells.map { |row| row.first } if first_columns.all? { |cell| cell.dead? } @cells.each { |row| row.shift } end # Remove the last cell of every row, if they are all dead last_columns = @cells.map { |row| row.last } if last_columns.all? { |cell| cell.dead? } @cells.each { |row| row.pop } end validate end |
#sweep_changes_for_next_generation! ⇒ Object (private)
161 162 163 |
# File 'lib/game_of_life/board.rb', line 161 def sweep_changes_for_next_generation! self.each_cell { |cell| cell.change_state_if_needed! } end |
#validate ⇒ Object (private)
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/game_of_life/board.rb', line 126 def validate num_o_rows = @cells.size columns_in_each_row = @cells.map(&:size) unless columns_in_each_row.uniq.size == 1 msg = "Unequal number of columns, #{columns_in_each_row.inspect} in different rows found" raise InvalidBoardError, msg end num_o_columns = columns_in_each_row.uniq.first num_o_elements = @cells.flatten.reject {|d| d.nil? }.size unless (num_o_rows * num_o_columns) == num_o_elements msg = "Not a rectangular shape: " + "rows(#{num_o_rows}) x columns(#{num_o_columns}) != total elements(#{num_o_elements})]. " raise InvalidBoardError, msg end end |
#view(outputter = Outputters::SimpleStringOutputter.new) ⇒ Object
35 36 37 |
# File 'lib/game_of_life/board.rb', line 35 def view(outputter = Outputters::SimpleStringOutputter.new) outputter.render(self) end |