diff options
| author | a14m <[email protected]> | 2020-12-02 12:01:05 +0100 |
|---|---|---|
| committer | a14m <[email protected]> | 2020-12-02 13:56:48 +0100 |
| commit | fd2615b57dccff21c2d54011fa251d97a504ccd4 (patch) | |
| tree | 4391f03f387adaba0618147f8f2af593dd804675 | |
| parent | d8aff6caa4c9872de1d437faad00191c9d099a7a (diff) | |
Add the implementation for the GameOfLife Cell class
| -rw-r--r-- | CLI/ruby/lib/game_of_life/cell.rb | 96 |
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 |
