terminal_game_of_life 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eac2c8c38b2299e6eb97af42e51429af726ad8b3e71707a77ab9af261a736e7c
4
- data.tar.gz: 9d733ec099d7494fe862efc6c8cac5609a82e775c12523bbf7acfe70deca0147
3
+ metadata.gz: bf5383033cdfa94863a1ffed02d61b746689d927821350042ada2bd3727ac031
4
+ data.tar.gz: 9e5c119a3d6b70ef61fc80db8fdc740f3233624e4284da35539fbd1c06e61ba5
5
5
  SHA512:
6
- metadata.gz: 25a07b67279a9843072a1667dfc8be1ba73ef8ae6e6c38d6ccd837e5f77adbf9685d2a6693083fe0b0cb21ea5ccd4723f008e1615f9a666483efdf029cff6921
7
- data.tar.gz: 5062f9fbc8ce2d3c74af9692b7639cda2f3ff98f3a77e8430bc453130bd313c9ba27eef34f4573ee41a6d9a5cdce8a08ca27a24a882045f16295ea1cf7da7e48
6
+ metadata.gz: 94b939ec472b19c6d7d38ec0f38ada21d24756c752fba4bb99da96c79c4e2b5a170a46884611ae57eb83d9a6093d81edaa529cfe58feffdee8d7d4d2f609faa4
7
+ data.tar.gz: bdc9a52b4f4040c22c72a7583ecb2c0685a031804ad5cb28abadd89465f60a3bd431b3ba8aa424f959e75884c69d2f370865be6d918d52823685a68a8c6eb160
@@ -20,10 +20,10 @@ class CLI < Thor
20
20
  class_option "input", aliases: "-i", type: :string, banner: :VALUE,
21
21
  desc: "Specify the path/URL for the file to use as an initial state. (used instead of seed)"
22
22
 
23
- class_option "width", aliases: "-w", type: :numeric, banner: :WIDTH,
23
+ class_option "width", type: :numeric, banner: :WIDTH,
24
24
  desc: "Specify the width of generated universe. (default to terminal width)"
25
25
 
26
- class_option "height", aliases: "-h", type: :numeric, banner: :HEIGHT,
26
+ class_option "height", type: :numeric, banner: :HEIGHT,
27
27
  desc: "Specify the hight of generated universe. (default to terminal height)"
28
28
 
29
29
  class_option "dead-cell", type: :string, banner: :CHAR, default: "\s",
@@ -34,18 +34,31 @@ class CLI < Thor
34
34
 
35
35
  class_option "delay", aliases: "-d", type: :numeric, banner: "Milli-Seconds", default: 50,
36
36
  desc: "Specify the introduced delay between each generation"
37
+ # rubocop:disable Metrics/AbcSize
38
+ # rubocop:disable Metrics/MethodLength
37
39
  def start
40
+ max_height = IO.console.winsize[0] - 2
41
+ max_width = IO.console.winsize[1]
42
+ if options["width"]&.> max_width
43
+ fail GameOfLife::Error, "Invalid --width value, must not exceed current terminal width: #{max_width}"
44
+ end
45
+ if options["height"]&.> max_height
46
+ fail GameOfLife::Error, "Invalid --height value, must not exceed current terminal height: #{max_height}"
47
+ end
48
+
38
49
  options_with_console_defaults = {
39
50
  # Defaults the hight to less then the height of the terminal to allow banner info
40
51
  # and an extra emtpy line to avoid triggering terminal scroll while flushing
41
- "height" => IO.console.winsize[0] - 3,
42
- "width" => IO.console.winsize[1],
52
+ "height" => max_height,
53
+ "width" => max_width,
43
54
  "seed" => rand(100_000),
44
55
  }.merge(options)
45
56
 
46
57
  universe = GameOfLife.generate(options_with_console_defaults)
47
58
  GameOfLife.run(universe)
48
59
  end
60
+ # rubocop:enable Metrics/AbcSize
61
+ # rubocop:enable Metrics/MethodLength
49
62
 
50
63
  class << self
51
64
  # Allow exit with status 1 on failure
@@ -14,15 +14,27 @@ module GameOfLife
14
14
  # @option options [Numeric] "width" (floored) and used as the width of the universe
15
15
  # @option options [Numeric] "height" (floored) and used as the height of the universe
16
16
  # @return [GameOfLife::Universe] populated with the parsed input
17
+ # @see ::GameOfLife::Generators::Input.generate_input_data
17
18
  # @see ::GameOfLife::Generators::Input.populate
18
19
  def new(options)
20
+ width = options["width"].to_i
21
+ height = options["height"].to_i
22
+ uri = options["input"]
23
+ input = generate_input_data(uri: uri)
24
+ populate(input: input, width: width, height: height)
25
+ end
26
+
27
+ # Generate the seeding data for populating the {GameOfLife::Universe} from input URI
28
+ # @param uri [String] inputh path to local file or URL to open
29
+ # @return [Array<Array<True|False>>] covering the defined size of universe
30
+ # @see ::GameOfLife::Generators::Input.populate
31
+ private def generate_input_data(uri:)
19
32
  # By design, it's intentional that we use URI.open to to allow seamless file and url access
20
- raw = URI.open(options["input"]).read.split("\n") # rubocop:disable Security/Open
33
+ raw = URI.open(uri).read.split("\n") # rubocop:disable Security/Open
21
34
 
22
35
  # Parse the file and convert it to a boolean 2D array
23
36
  # where any alpha numeric character is considered a living cell (true)
24
37
  raw.map! { |row| row.chars.map { |char| char.match?(/[[:alnum:]]/) } }
25
- populate(parsed_input: raw, width: options["width"].to_i, height: options["height"].to_i)
26
38
  rescue ::OpenURI::HTTPError, ::SocketError
27
39
  raise GameOfLife::Error, "URL isn't avaialable, please check it again and make sure the URL is correct"
28
40
  rescue ::Errno::ENOENT
@@ -31,16 +43,17 @@ module GameOfLife
31
43
  end
32
44
 
33
45
  # Populate the {GameOfLife::Universe} with reference to the parsed input 2D array (True/False)
34
- # @param parsed_input [Array<Array<True|False>>]
46
+ # @param input [Array<Array<True|False>>]
35
47
  # @param width [Integer] width of the generated cyclic universe
36
48
  # @param height [Integer] height of the generated cyclic universe
37
49
  # @return [GameOfLife::Universe] populated with the parsed input
38
- private def populate(parsed_input:, width:, height:)
50
+ # @see ::GameOfLife::Generators::Input.generate_input_data
51
+ private def populate(input:, width:, height:)
39
52
  # Generate a universe based on the parsed raw file and fill it with the cells
40
53
  universe = GameOfLife::Universe.new(height: height, width: width)
41
54
  (0...height).each do |i|
42
55
  (0...width).each do |j|
43
- cell = { x: j, y: i, alive: parsed_input.fetch(i, nil)&.fetch(j, nil) }
56
+ cell = { x: j, y: i, alive: input.fetch(i, nil)&.fetch(j, nil) }
44
57
  universe[i][j] = ::GameOfLife::Cell.new(cell)
45
58
  end
46
59
  end
@@ -11,8 +11,47 @@ module GameOfLife
11
11
  # @option options [Numeric] "width" (floored) and used as the width of the universe
12
12
  # @option options [Numeric] "height" (floored) and used as the height of the universe
13
13
  # @return [GameOfLife::Universe] populated with the parsed input
14
+ # @see ::GameOfLife::Generators::Seed.generate_seed_data
15
+ # @see ::GameOfLife::Generators::Seed.populate
14
16
  def new(options)
15
- fail ::NotImplementedError, "Seed option isn't implemented yet, please use --input option"
17
+ width = options["width"].to_i
18
+ height = options["height"].to_i
19
+ seed = options["seed"].to_i
20
+ input = generate_seed_data(seed: seed, width: width, height: height)
21
+ populate(input: input, width: width, height: height)
22
+ end
23
+
24
+ # Generate the seeding data for populating the {GameOfLife::Universe}
25
+ # @param seed [Integer] used to generate {Array<"0"|"1">} using Std ruby Random
26
+ # @param width [Integer] width of the generated cyclic universe
27
+ # @param height [Integer] height of the generated cyclic universe
28
+ # @return [Array<Array<True|False>>] covering the defined size of universe
29
+ # @see ::GameOfLife::Generators::Seed.populate
30
+ # @see https://ruby-doc.org/core-2.4.0/Random.html
31
+ private def generate_seed_data(seed:, width:, height:)
32
+ # Create a pseudo(stable) random generator from the seed
33
+ # Generate a sequence of bytes to cover the Game of Life Universe plane
34
+ # each byte can be unpacked into binary data string (ex. "01101011")
35
+ raw = Random.new(seed).bytes(width * height).unpack1("B*").split("").each_slice(width)
36
+ raw.map { |row| row.map { |char| char.eql?("1") } }
37
+ end
38
+
39
+ # Populate the {GameOfLife::Universe} with reference to the parsed input 2D array (True/False)
40
+ # @param input [Array<Array<True|False>>]
41
+ # @param width [Integer] width of the generated cyclic universe
42
+ # @param height [Integer] height of the generated cyclic universe
43
+ # @return [GameOfLife::Universe] populated with the parsed input
44
+ # @see ::GameOfLife::Generators::Seed.generate_seed_data
45
+ private def populate(input:, width:, height:)
46
+ # Generate a universe based on the parsed raw file and fill it with the cells
47
+ universe = GameOfLife::Universe.new(height: height, width: width)
48
+ (0...height).each do |i|
49
+ (0...width).each do |j|
50
+ cell = { x: j, y: i, alive: input.fetch(i, nil)&.fetch(j, nil) }
51
+ universe[i][j] = ::GameOfLife::Cell.new(cell)
52
+ end
53
+ end
54
+ universe
16
55
  end
17
56
  end
18
57
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GameOfLife
4
- VERSION = "0.1.2"
4
+ # Game of life version number
5
+ VERSION = "0.2.0"
5
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terminal_game_of_life
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - a14m
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-09 00:00:00.000000000 Z
11
+ date: 2020-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor