# 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,
# 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] 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