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 +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
|