summaryrefslogtreecommitdiffstats
path: root/CLI/ruby/lib/game_of_life/generators/seed.rb
blob: 63999459bc731afb68b22833dc6f154e877c2ca7 (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
# frozen_string_literal: true

module GameOfLife
  module Generators
    # Generate a new Universe/Board from a seed number
    module Seed
      class << self
        # Generate a new Universe/Board from a seed number
        # @param options [Hash] CLI options to generate initial conditions
        # @option options [Numeric] "seed" number to use for generation
        # @option options [Numeric] "width" (floored) and used as the width of the universe
        # @option options [Numeric] "height" (floored) and used as the height of the universe
        # @return [GameOfLife::Universe] populated with the parsed input
        # @see ::GameOfLife::Generators::Seed.generate_seed_data
        # @see ::GameOfLife::Generators::Seed.populate
        def new(options)
          width  = options["width"].to_i
          height = options["height"].to_i
          seed   = options["seed"].to_i
          input  = generate_seed_data(seed: seed, width: width, height: height)
          populate(input: input, width: width, height: height)
        end

        # Generate the seeding data for populating the {GameOfLife::Universe}
        # @param seed [Integer] used to generate {Array<"0"|"1">} using Std ruby Random
        # @param width [Integer] width of the generated cyclic universe
        # @param height [Integer] height of the generated cyclic universe
        # @return [Array<Array<True|False>>] covering the defined size of universe
        # @see ::GameOfLife::Generators::Seed.populate
        # @see https://ruby-doc.org/core-2.4.0/Random.html
        private def generate_seed_data(seed:, width:, height:)
          # Create a pseudo(stable) random generator from the seed
          # Generate a sequence of bytes to cover the Game of Life Universe plane
          # each byte can be unpacked into binary data string (ex. "01101011")
          raw = Random.new(seed).bytes(width * height).unpack1("B*").split("").each_slice(width)
          raw.map { |row| row.map { |char| char.eql?("1") } }
        end

        # Populate the {GameOfLife::Universe} with reference to the parsed input 2D array (True/False)
        # @param input [Array<Array<True|False>>]
        # @param width [Integer] width of the generated cyclic universe
        # @param height [Integer] height of the generated cyclic universe
        # @return [GameOfLife::Universe] populated with the parsed input
        # @see ::GameOfLife::Generators::Seed.generate_seed_data
        private def populate(input:, width:, height:)
          # Generate a universe based on the parsed raw file and fill it with the cells
          universe = GameOfLife::Universe.new(height: height, width: width)
          (0...height).each do |i|
            (0...width).each do |j|
              cell = { x: j, y: i, alive: input.fetch(i, nil)&.fetch(j, nil) }
              universe[i][j] = ::GameOfLife::Cell.new(**cell)
            end
          end
          universe
        end
      end
    end
  end
end