summaryrefslogtreecommitdiffstats
path: root/CLI/ruby/lib/game_of_life/universe.rb
blob: f4296ddbfbb5a0b5eda76ff37ee8b31cb13e861a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
# frozen_string_literal: true

require "forwardable"

module GameOfLife
  # Representation of the current and future state of the game of life universe
  class Universe
    extend Forwardable
    delegate :[] => :@current
    delegate :[]= => :@current

    # Initialize the empty initial universe 2D plane (circular arrays)
    # @param width [Integer] the width of the universe array
    # @param height [Integer] the height of the universe array
    def initialize(width:, height:)
      @current = GameOfLife::Plane.new(height) { GameOfLife::Plane.new(width) }
      @future  = GameOfLife::Plane.new(height) { GameOfLife::Plane.new(width) }
    end

    # Representation of the game of life universe
    # @return [String] representing the current state
    # @see GameOfLife::Cell#to_s
    def to_s
      @current.map(&:join).join("\n")
    end

    # Update the current state of the universe (according to the game of life laws) and
    # set the current state to the next future generation
    #
    # This have computational order `O(n^2) = O(width * height)`
    # It loops over all the universe cells and evolves them to the next generation
    # @see #neighbors
    def evolve!
      @current.each do |row|
        row.each do |cell|
          @future[cell.y][cell.x] = cell.evolve!(neighbors(cell))
        end
      end
      @current = Marshal.load(Marshal.dump(@future))
    end

    # Return the array of all the neighboring cells using the circular universe array, <br/>
    #   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  |  .....  |
    #
    # @param cell [GameOfLife::Cell] the to return the neighboring cells from the universe current plane
    # @return [Array<GameOfLife::Cell>] of all the immediate neighboring cells
    # @smell AbcSize higher than configuration, but needed to return the neighboring cells array
    #
    # rubocop:disable Metrics/AbcSize
    private def neighbors(cell)
      x, y = cell.x, cell.y # rubocop:disable Style/ParallelAssignment
      [
        @current[y - 1][x - 1], @current[y - 1][x], @current[y - 1][x + 1],
        @current[y][x - 1], @current[y][x + 1],
        @current[y + 1][x - 1], @current[y + 1][x], @current[y + 1][x + 1]
      ]
    end
    # rubocop:enable Metrics/AbcSize
  end
end