summaryrefslogtreecommitdiffstats
path: root/CLI/ruby/lib
diff options
context:
space:
mode:
authora14m <[email protected]>2020-12-02 12:01:05 +0100
committera14m <[email protected]>2020-12-02 13:56:48 +0100
commitfd2615b57dccff21c2d54011fa251d97a504ccd4 (patch)
tree4391f03f387adaba0618147f8f2af593dd804675 /CLI/ruby/lib
parentd8aff6caa4c9872de1d437faad00191c9d099a7a (diff)
Add the implementation for the GameOfLife Cell class
Diffstat (limited to 'CLI/ruby/lib')
-rw-r--r--CLI/ruby/lib/game_of_life/cell.rb96
1 files changed, 93 insertions, 3 deletions
diff --git a/CLI/ruby/lib/game_of_life/cell.rb b/CLI/ruby/lib/game_of_life/cell.rb
index 62cd3a1..8f4a267 100644
--- a/CLI/ruby/lib/game_of_life/cell.rb
+++ b/CLI/ruby/lib/game_of_life/cell.rb
@@ -1,10 +1,16 @@
# frozen_string_literal: true
+require "pry-byebug"
module GameOfLife
+ # Class representing a cell in the GemeOfLife universe
+ # @!attribute [r] alive
+ # @return [True | False] the living state of the cell
+ # @!attribute [r] pos_x
+ # @return [Integer] the X-axis position of the cell in the universe
+ # @!attribute [r] pos_y
+ # @return [Integer] the Y-axis position of the cell in the universe
class Cell
- attr_accessor :alive, :pos_x, :pos_y
-
- alias_method :alive?, :alive
+ attr_reader :alive, :pos_x, :pos_y
# Initialize a cell in the game of life universe
# @param alive [True|False] boolean indicating if the cell is considered alive
@@ -22,5 +28,89 @@ module GameOfLife
def to_s
alive? ? LIVE_CELL : DEAD_CELL
end
+
+ # Evolve a cell in the game of life to the new state in the next generation
+ # @param universe [Array<GameOfLife::Cell>] the {GameOfLife::Universe#current} state of universe
+ # @param living_neighbors [Integer] number of living neighbors, defaults to the private implementation of
+ # {GameOfLife::Cell#living_neighbors} implementation by default
+ # @return [GameOfLife::Cell] representing the new state in the next generation
+ #
+ # @note There are some optimizations of returning a new object only when the state changes, otherwise returning self
+ # This optimization is done to limit the spacial complexity to O(2*witdht*height) in the worst case scenario
+ # and to O(width * hight) in the best case scenario (when the universe is stale and don't evolve anymore).
+ def evolve!(universe, living_neighbors: living_neighbors(universe))
+ return repopulate! if dead? && living_neighbors == 3
+ return survive! if alive? && [2, 3].include?(living_neighbors)
+ die!
+ end
+
+ # Helper method to define the language used in GameOfLife
+ # @return [True | False] cell living state
+ def alive?
+ @alive
+ end
+
+ # Helper method to define the language used in GameOfLife
+ # @return [True | False] cell dead state
+ # @see #alive?
+ def dead?
+ !alive?
+ end
+
+ # Return the array of all the neighboring cells using the circular universe array
+ # @param universe [Array<GameOfLife::Cell>] the {GameOfLife::Universe#current} state of universe
+ # @return [Array<GameOfLife::Cell>] of all the immediate neighboring cells
+ # @example given a cell "C" in a universe of cells "." the immediate neighbors would be "x" as in
+ # the following different examples
+ #
+ # ..... | .xxx. | .xCx. | xCx.. | Cx..x | xx..x |
+ # .xxx. | .xCx. | .xxx. | xxx.. | xx..x | Cx..x |
+ # .xCx. | .xxx. | ..... | ..... | ..... | xx..x |
+ # .xxx. | ..... | ..... | ..... | ..... | ..... |
+ # ..... | ..... | .xxx. | xxx.. | xx..x | ..... |
+ #
+ # @smell AbcSize higher than configuration, but needed to return the neighboring cells array
+ # rubocop:disable Metrics/AbcSize
+ private def neighbors(universe)
+ [
+ universe[@pos_y - 1][@pos_x - 1], universe[@pos_y - 1][@pos_x], universe[@pos_y - 1][@pos_x + 1],
+ universe[@pos_y][@pos_x - 1], universe[@pos_y][@pos_x + 1],
+ universe[@pos_y + 1][@pos_x - 1], universe[@pos_y + 1][@pos_x], universe[@pos_y + 1][@pos_x + 1]
+ ]
+ end
+ # rubocop:enable Metrics/AbcSize
+
+ # Count the living neighbor cells relatively to the current cell in the {GameOfLife::Universe}
+ # @param universe [Array<GameOfLife::Cell>] the {GameOfLife::Universe#current} state of universe
+ # @return [Integer] the number of living neighbors
+ # @see #neighbors
+ private def living_neighbors(universe)
+ neighbors(universe).select(&:alive?).size
+ end
+
+ # Helper method to define the language used in GameOfLife
+ # @return [GameOfLife::Cell] new living cell
+ # @see #survive!
+ # @see #die!
+ private def repopulate!
+ self.class.new(pos_x: @pos_x, pos_y: @pos_y, alive: true)
+ end
+
+ # Helper method to define the language used in GameOfLife
+ # @return [GameOfLife::Cell] self (since no change in state needed)
+ # @see #repopulate!
+ # @see #die!
+ private def survive!
+ self
+ end
+
+ # Helper method to define the language used in GameOfLife
+ # When a cell is dead returns self otherwise return a new dead cell
+ # @return [GameOfLife::Cell] self (if already dead) or new dead cell
+ # @see #repopulate!
+ # @see #survive!
+ private def die!
+ dead? ? self : self.class.new(pos_x: @pos_x, pos_y: @pos_y, alive: false)
+ end
end
end