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

require "open-uri"
require "socket"

module GameOfLife
  module Generators
    # Generate a new Universe/Board with a parsed local file or remote URL/file
    module Input
      class << self
        # Generate a new Universe/Board with a parsed local file or remote URL/file
        # @param options [Hash] CLI options to generate initial conditions
        # @option options [String] "input" path to a local file or URL to open
        # @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::Input.populate
        def new(options)
          # By design, it's intentional that we use URI.open to to allow seamless file and url access
          raw = URI.open(options["input"]).read.split("\n") # rubocop:disable Security/Open

          # Parse the file and convert it to a boolean 2D array
          # where any alpha numeric character is considered a living cell (true)
          raw.map! { |row| row.chars.map { |char| char.match?(/[[:alnum:]]/) } }
          populate(parsed_input: raw, width: options["width"].to_i, height: options["height"].to_i)
        rescue ::OpenURI::HTTPError, ::SocketError
          raise GameOfLife::Error, "URL isn't avaialable, please check it again and make sure the URL is correct"
        rescue ::Errno::ENOENT
          raise GameOfLife::Error,
                "File isn't avaialable, please check it again and make sure the path to the input file is correct"
        end

        # Populate the {GameOfLife::Universe} with reference to the parsed input 2D array (True/False)
        # @param parsed_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
        private def populate(parsed_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: parsed_input.fetch(i, nil)&.fetch(j, nil) }
              universe[i][j] = ::GameOfLife::Cell.new(cell)
            end
          end
          universe
        end
      end
    end
  end
end