terminal_game_of_life 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/game-of-life +17 -4
- data/lib/game_of_life/generators/input.rb +18 -5
- data/lib/game_of_life/generators/seed.rb +40 -1
- data/lib/game_of_life/version.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf5383033cdfa94863a1ffed02d61b746689d927821350042ada2bd3727ac031
|
4
|
+
data.tar.gz: 9e5c119a3d6b70ef61fc80db8fdc740f3233624e4284da35539fbd1c06e61ba5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94b939ec472b19c6d7d38ec0f38ada21d24756c752fba4bb99da96c79c4e2b5a170a46884611ae57eb83d9a6093d81edaa529cfe58feffdee8d7d4d2f609faa4
|
7
|
+
data.tar.gz: bdc9a52b4f4040c22c72a7583ecb2c0685a031804ad5cb28abadd89465f60a3bd431b3ba8aa424f959e75884c69d2f370865be6d918d52823685a68a8c6eb160
|
data/bin/game-of-life
CHANGED
@@ -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",
|
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",
|
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" =>
|
42
|
-
"width" =>
|
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(
|
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
|
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
|
-
|
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:
|
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
|
-
|
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
|
data/lib/game_of_life/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2020-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|