# 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