terminal_game_of_life 0.1.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 +7 -0
- data/README.md +52 -0
- data/bin/game-of-life +65 -0
- data/game_of_life.gemspec +35 -0
- data/lib/game_of_life.rb +65 -0
- data/lib/game_of_life/cell.rb +92 -0
- data/lib/game_of_life/generators/input.rb +46 -0
- data/lib/game_of_life/generators/seed.rb +20 -0
- data/lib/game_of_life/plane.rb +27 -0
- data/lib/game_of_life/universe.rb +67 -0
- data/lib/game_of_life/version.rb +5 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e653045a66a53af6c2b4c5b58f0330ec18d8125e7ce70b2b9c8803e8d7f67b2a
|
4
|
+
data.tar.gz: 75ffbbd48c0b9cb883a2cd1aab1f0b7e7606856223a18e88c4dd0a9916a5afc6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4ae52be24a7bbadf45997c129b1e4b407f3b6f47ecc1cb6c6c63c002d57add5f6ac231581ef024458f366189555f73fad4995a279d4b9b92c0fc5e2d5721141
|
7
|
+
data.tar.gz: b3f717f56369f87ed173a57ff9f79364e3400af441c543c58a64a244fa8b9a328304fd00a9de8d82593fbb6aeb0795f81d15ceb67b29a698d1b0ebe0133ff1ea
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# GameOfLife
|
2
|
+
|
3
|
+
The implementation of the [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) ruby CLI gem
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
```bash
|
7
|
+
$ gem install terminal_game_of_life
|
8
|
+
```
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
Check `game-of-life --help` for usage info.
|
13
|
+
|
14
|
+
```
|
15
|
+
game-of-life --version, -v # Prints the Game of Life version information
|
16
|
+
game-of-life help [COMMAND] # Describe available commands or one specific command
|
17
|
+
game-of-life start [OPTIONS] # Start the Game of Life simulations (default command)
|
18
|
+
|
19
|
+
Options:
|
20
|
+
-s, [--seed=SEED] # Specify the seed number to use as an initial state (default to random).
|
21
|
+
-i, [--input=VALUE] # Specify the path/URL for the file to use as an initial state. (used instead of seed)
|
22
|
+
-w, [--width=WIDTH] # Specify the width of generated universe. (default to terminal width)
|
23
|
+
-h, [--height=HEIGHT] # Specify the hight of generated universe. (default to terminal height)
|
24
|
+
[--dead-cell=CHAR] # Specify the dead-cell representation
|
25
|
+
# Default:
|
26
|
+
[--live-cell=CHAR] # Specify the dead-cell representation
|
27
|
+
# Default: █
|
28
|
+
-d, [--delay=Milli-Seconds] # Specify the introduced delay between each generation
|
29
|
+
# Default: 50
|
30
|
+
```
|
31
|
+
|
32
|
+
## Development
|
33
|
+
|
34
|
+
- Clone the repo.
|
35
|
+
- Navigate to the ruby CLI implementation `cd game-of-life/CLI/ruby`.
|
36
|
+
- run `bundle install` to install dependencies.
|
37
|
+
- run `bundle rake build` to build the gem/CLI into the `pkg` directory.
|
38
|
+
- run `bundle rake install` to build and install gem/CLI into system gems.
|
39
|
+
|
40
|
+
## Linting
|
41
|
+
Run `bundle exec rubocop`
|
42
|
+
|
43
|
+
## Testing
|
44
|
+
Run `bundle exec rspec` or `bundle exec rake spec`
|
45
|
+
|
46
|
+
## Documentation
|
47
|
+
Run `bundle exec yard`
|
48
|
+
|
49
|
+
## Extra information
|
50
|
+
### [Contributing](../../CONTRIBUTING.md)
|
51
|
+
### [License](../../LICENSE.md)
|
52
|
+
### [Code of Conduct](../../CODE_OF_CONDUCT.md)
|
data/bin/game-of-life
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "game_of_life"
|
5
|
+
require "thor"
|
6
|
+
require "io/console"
|
7
|
+
|
8
|
+
class CLI < Thor
|
9
|
+
map %w[-v --version] => :version
|
10
|
+
desc "--version, -v", "Prints the Game of Life version information"
|
11
|
+
def version
|
12
|
+
puts "Game of Life version #{GameOfLife::VERSION}"
|
13
|
+
end
|
14
|
+
|
15
|
+
default_task :start
|
16
|
+
desc "start [OPTIONS]", "Start the Game of Life simulations"
|
17
|
+
class_option "seed", aliases: "-s", type: :numeric, banner: :SEED,
|
18
|
+
desc: "Specify the seed number to use as an initial state (default to random)."
|
19
|
+
|
20
|
+
class_option "input", aliases: "-i", type: :string, banner: :VALUE,
|
21
|
+
desc: "Specify the path/URL for the file to use as an initial state. (used instead of seed)"
|
22
|
+
|
23
|
+
class_option "width", aliases: "-w", type: :numeric, banner: :WIDTH,
|
24
|
+
desc: "Specify the width of generated universe. (default to terminal width)"
|
25
|
+
|
26
|
+
class_option "height", aliases: "-h", type: :numeric, banner: :HEIGHT,
|
27
|
+
desc: "Specify the hight of generated universe. (default to terminal height)"
|
28
|
+
|
29
|
+
class_option "dead-cell", type: :string, banner: :CHAR, default: "\s",
|
30
|
+
desc: "Specify the dead-cell representation"
|
31
|
+
|
32
|
+
class_option "live-cell", type: :string, banner: :CHAR, default: "\u2588",
|
33
|
+
desc: "Specify the dead-cell representation"
|
34
|
+
|
35
|
+
class_option "delay", aliases: "-d", type: :numeric, banner: "Milli-Seconds", default: 50,
|
36
|
+
desc: "Specify the introduced delay between each generation"
|
37
|
+
def start
|
38
|
+
options_with_console_defaults = {
|
39
|
+
# Defaults the hight to less then the height of the terminal to allow banner info
|
40
|
+
# 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],
|
43
|
+
"seed" => rand(100_000),
|
44
|
+
}.merge(options)
|
45
|
+
|
46
|
+
universe = GameOfLife.generate(options_with_console_defaults)
|
47
|
+
GameOfLife.run(universe)
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
# Allow exit with status 1 on failure
|
52
|
+
# Ref: https://github.com/erikhuda/thor/issues/244#issue-6116190
|
53
|
+
def exit_on_failure?
|
54
|
+
true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
begin
|
60
|
+
CLI.start(ARGV)
|
61
|
+
rescue SystemExit, Interrupt
|
62
|
+
"Do nothing when user exits with Interrupt (CMD/Ctrl + C)"
|
63
|
+
rescue NotImplementedError
|
64
|
+
puts "Seed option isn't implemented yet, please use --input option"
|
65
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/game_of_life/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "terminal_game_of_life"
|
7
|
+
spec.version = GameOfLife::VERSION
|
8
|
+
spec.authors = ["a14m"]
|
9
|
+
spec.email = ["a14m@pm.me"]
|
10
|
+
|
11
|
+
spec.summary = "Game of Life CLI"
|
12
|
+
spec.description = "Conway's game of life implementation as a CLI ruby gem."
|
13
|
+
spec.homepage = "https://gitlab.com/a14m/game-of-life"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.4.0"
|
16
|
+
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = "https://gitlab.com/a14m/game-of-life/-/tree/master/CLI/ruby/"
|
20
|
+
|
21
|
+
spec.executables = ["game-of-life"]
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
spec.files = Dir["README.md", "game_of_life.gemspec", "bin/game-of-life", "lib/**/*.rb"]
|
24
|
+
|
25
|
+
spec.add_dependency "thor", "~> 1.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler"
|
28
|
+
spec.add_development_dependency "pry-byebug"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "rspec"
|
31
|
+
spec.add_development_dependency "rubocop"
|
32
|
+
# Documentation dependencies
|
33
|
+
spec.add_development_dependency "redcarpet"
|
34
|
+
spec.add_development_dependency "yard"
|
35
|
+
end
|
data/lib/game_of_life.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "game_of_life/version"
|
4
|
+
require "game_of_life/plane"
|
5
|
+
require "game_of_life/universe"
|
6
|
+
require "game_of_life/cell"
|
7
|
+
|
8
|
+
# Game of Life module entry point
|
9
|
+
module GameOfLife
|
10
|
+
class << self
|
11
|
+
# Generate a universe based on the options (from an input file/URI) or using a random/passed seed
|
12
|
+
# This method also sets the Classes/Modules constants for the run defining the options for
|
13
|
+
# Cell (Dead/Live) state representations, and the delay introduced after rendering each universe/generation
|
14
|
+
# and the banner for the info/options
|
15
|
+
#
|
16
|
+
# @param options [Hash] CLI options to generate initial conditions
|
17
|
+
# @option options [String | nil] "input" path to a local file or URL to open
|
18
|
+
# @option options [Numeric] "seed" number to use if no "input" is provided (defaults to random)
|
19
|
+
# @option options [Numeric] "width" of the universe
|
20
|
+
# @option options [Numeric] "height" of the universe
|
21
|
+
# @option options [Numeric] "delay" introduced after each cycle
|
22
|
+
# @option options [String] "live-cell" representation
|
23
|
+
# @option options [String] "dead-cell" representation
|
24
|
+
# @return [GameOfLife::Universe] populated with the parsed input/seed
|
25
|
+
def generate(options)
|
26
|
+
# Define the constants for Cell representation
|
27
|
+
GameOfLife::Cell.const_set(:LIVE_CELL, options["live-cell"])
|
28
|
+
GameOfLife::Cell.const_set(:DEAD_CELL, options["dead-cell"])
|
29
|
+
# Define the constant used for delay
|
30
|
+
GameOfLife.const_set(:DELAY, options["delay"].to_f)
|
31
|
+
|
32
|
+
banner = options["input"] ? "Input: #{options['input']}" : "Seed: #{options['seed']}"
|
33
|
+
# Define the constant used for banner info
|
34
|
+
GameOfLife.const_set(:BANNER, banner)
|
35
|
+
|
36
|
+
return GameOfLife::Generators::Input.new(options) if options["input"]
|
37
|
+
GameOfLife::Generators::Seed.new(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# The infinite running loop for rendering the current state of a universe
|
41
|
+
# and then evolving it and repeating.
|
42
|
+
def run(universe)
|
43
|
+
Kernel.loop do
|
44
|
+
GameOfLife.render(universe)
|
45
|
+
universe.evolve!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Private method to do the terminal screen/scrollback buffer cleaning
|
50
|
+
# and rendering of the current universe/banner info
|
51
|
+
# based on the delay configured by the user
|
52
|
+
def render(universe)
|
53
|
+
puts "\33c\e[3J" # Clears the screen and scrollback buffer.
|
54
|
+
puts universe.to_s
|
55
|
+
puts BANNER
|
56
|
+
sleep(DELAY / 1000.0)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generators module for generating the universe initial state
|
61
|
+
module Generators
|
62
|
+
autoload :Input, "game_of_life/generators/input.rb"
|
63
|
+
autoload :Seed, "game_of_life/generators/seed.rb"
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GameOfLife
|
4
|
+
# Class representing a cell in the GemeOfLife universe
|
5
|
+
# @!attribute [r] alive
|
6
|
+
# @return [True | False] the living state of the cell
|
7
|
+
# @!attribute [r] x
|
8
|
+
# @return [Integer] the X-axis position of the cell in the universe
|
9
|
+
# @!attribute [r] y
|
10
|
+
# @return [Integer] the Y-axis position of the cell in the universe
|
11
|
+
class Cell
|
12
|
+
attr_reader :alive, :x, :y
|
13
|
+
|
14
|
+
# Initialize a cell in the game of life universe
|
15
|
+
# @param alive [True|False] boolean indicating if the cell is considered alive
|
16
|
+
# @param x [Integer] the x position on the cyclic universe plane
|
17
|
+
# @param y [Integer] the y position on the cyclic universe plane
|
18
|
+
def initialize(alive:, x:, y:)
|
19
|
+
@alive = alive
|
20
|
+
@x = x
|
21
|
+
@y = y
|
22
|
+
end
|
23
|
+
|
24
|
+
# Representation of a cell
|
25
|
+
# Both LIVE_CELL, and DEAD_CELL constant are defined on the application start from the options
|
26
|
+
# @return [String] if a cell is alive or an empty space
|
27
|
+
def to_s
|
28
|
+
alive? ? LIVE_CELL : DEAD_CELL
|
29
|
+
end
|
30
|
+
|
31
|
+
# Evolve a cell in the game of life to the new state in the next generation
|
32
|
+
# @param neighbors [Array<GameOfLife::Cell>] that are neighboring the current cell (size of 8)
|
33
|
+
# @return [GameOfLife::Cell] representing the new state in the next generation
|
34
|
+
#
|
35
|
+
# @note There are some optimizations of returning a new object only when the state changes, otherwise returning self
|
36
|
+
# This optimization is done to limit the spacial complexity to `O(2n^2) = O(2*witdht*height)`
|
37
|
+
# in the worst case scenario, and to `O(n^2) = O(width * hight)` in the best case scenario
|
38
|
+
# (when the universe is stale and don't evolve anymore).
|
39
|
+
# @see #living_neighbors
|
40
|
+
def evolve!(neighbors)
|
41
|
+
return repopulate! if dead? && living_neighbors(neighbors) == 3
|
42
|
+
return survive! if alive? && [2, 3].include?(living_neighbors(neighbors))
|
43
|
+
die!
|
44
|
+
end
|
45
|
+
|
46
|
+
# Helper method to define the language used in GameOfLife
|
47
|
+
# @return [True | False] cell living state
|
48
|
+
def alive?
|
49
|
+
@alive
|
50
|
+
end
|
51
|
+
|
52
|
+
# Helper method to define the language used in GameOfLife
|
53
|
+
# @return [True | False] cell dead state
|
54
|
+
# @see #alive?
|
55
|
+
def dead?
|
56
|
+
!alive?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Count the living neighbor cells relatively to the current cell in the {GameOfLife::Universe}
|
60
|
+
# @param neighbors [Array<GameOfLife::Cell>] that are neighboring the current cell (size of 8)
|
61
|
+
# @return [Integer] the number of living neighbors
|
62
|
+
# @see GameOfLive::Universe#neighbors
|
63
|
+
private def living_neighbors(neighbors)
|
64
|
+
neighbors.select(&:alive?).size
|
65
|
+
end
|
66
|
+
|
67
|
+
# Helper method to define the language used in GameOfLife
|
68
|
+
# @return [GameOfLife::Cell] new living cell
|
69
|
+
# @see #survive!
|
70
|
+
# @see #die!
|
71
|
+
private def repopulate!
|
72
|
+
self.class.new(x: x, y: y, alive: true)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Helper method to define the language used in GameOfLife
|
76
|
+
# @return [GameOfLife::Cell] self (since no change in state needed)
|
77
|
+
# @see #repopulate!
|
78
|
+
# @see #die!
|
79
|
+
private def survive!
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
# Helper method to define the language used in GameOfLife
|
84
|
+
# When a cell is dead returns self otherwise return a new dead cell
|
85
|
+
# @return [GameOfLife::Cell] self (if already dead) or new dead cell
|
86
|
+
# @see #repopulate!
|
87
|
+
# @see #survive!
|
88
|
+
private def die!
|
89
|
+
dead? ? self : self.class.new(x: x, y: y, alive: false)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open-uri"
|
4
|
+
|
5
|
+
module GameOfLife
|
6
|
+
module Generators
|
7
|
+
# Generate a new Universe/Board with a parsed local file or remote URL/file
|
8
|
+
module Input
|
9
|
+
class << self
|
10
|
+
# Generate a new Universe/Board with a parsed local file or remote URL/file
|
11
|
+
# @param options [Hash] CLI options to generate initial conditions
|
12
|
+
# @option options [String] "input" path to a local file or URL to open
|
13
|
+
# @option options [Numeric] "width" (floored) and used as the width of the universe
|
14
|
+
# @option options [Numeric] "height" (floored) and used as the height of the universe
|
15
|
+
# @return [GameOfLife::Universe] populated with the parsed input
|
16
|
+
# @see ::GameOfLife::Generators::Input.populate
|
17
|
+
def new(options)
|
18
|
+
# By design, it's intentional that we use URI.open to to allow seamless file and url access
|
19
|
+
raw = URI.open(options["input"]).read.split("\n") # rubocop:disable Security/Open
|
20
|
+
|
21
|
+
# Parse the file and convert it to a boolean 2D array
|
22
|
+
# where any alpha numeric character is considered a living cell (true)
|
23
|
+
raw.map! { |row| row.chars.map { |char| char.match?(/[[:alnum:]]/) } }
|
24
|
+
populate(parsed_input: raw, width: options["width"].to_i, height: options["height"].to_i)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Populate the {GameOfLife::Universe} with reference to the parsed input 2D array (True/False)
|
28
|
+
# @param parsed_input [Array<Array<True|False>>]
|
29
|
+
# @param width [Integer] width of the generated cyclic universe
|
30
|
+
# @param height [Integer] height of the generated cyclic universe
|
31
|
+
# @return [GameOfLife::Universe] populated with the parsed input
|
32
|
+
private def populate(parsed_input:, width:, height:)
|
33
|
+
# Generate a universe based on the parsed raw file and fill it with the cells
|
34
|
+
universe = GameOfLife::Universe.new(height: height, width: width)
|
35
|
+
(0...height).each do |i|
|
36
|
+
(0...width).each do |j|
|
37
|
+
cell = { x: j, y: i, alive: parsed_input.fetch(i, nil)&.fetch(j, nil) }
|
38
|
+
universe[i][j] = ::GameOfLife::Cell.new(cell)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
universe
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GameOfLife
|
4
|
+
module Generators
|
5
|
+
# Generate a new Universe/Board from a seed number
|
6
|
+
module Seed
|
7
|
+
class << self
|
8
|
+
# Generate a new Universe/Board from a seed number
|
9
|
+
# @param options [Hash] CLI options to generate initial conditions
|
10
|
+
# @option options [Numeric] "seed" number to use for generation
|
11
|
+
# @option options [Numeric] "width" (floored) and used as the width of the universe
|
12
|
+
# @option options [Numeric] "height" (floored) and used as the height of the universe
|
13
|
+
# @return [GameOfLife::Universe] populated with the parsed input
|
14
|
+
def new(options)
|
15
|
+
fail NotImplementedError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GameOfLife
|
4
|
+
# Custom Plane class that represent a cyclic/circular plane/array
|
5
|
+
# This class would always return an element unless the array is empty
|
6
|
+
class Plane < Array
|
7
|
+
# Extending the default array behaviour by treating the array as cyclic/circular object
|
8
|
+
#
|
9
|
+
# @example working with normal array
|
10
|
+
# a = [0, 1, 2, 3]
|
11
|
+
# a[-6] == a[-2] == a[2]
|
12
|
+
# a[-5] == a[-1] == a[3]
|
13
|
+
# a[-4] == a[0] == a[4]
|
14
|
+
# a[-3] == a[1] == a[5]
|
15
|
+
# a[-2] == a[2] == a[6]
|
16
|
+
# a[-1] == a[3] == a[7]
|
17
|
+
# a[0] == a[4] == a[8]
|
18
|
+
#
|
19
|
+
# @example working with empty array
|
20
|
+
# b = []
|
21
|
+
# b[0] == b[1] == b[-1] == nil
|
22
|
+
def [](index)
|
23
|
+
return nil if empty?
|
24
|
+
super(index % size)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module GameOfLife
|
6
|
+
# Representation of the current and future state of the game of life universe
|
7
|
+
class Universe
|
8
|
+
extend Forwardable
|
9
|
+
delegate :[] => :@current
|
10
|
+
delegate :[]= => :@current
|
11
|
+
|
12
|
+
# Initialize the empty initial universe 2D plane (circular arrays)
|
13
|
+
# @param width [Integer] the width of the universe array
|
14
|
+
# @param height [Integer] the height of the universe array
|
15
|
+
def initialize(width:, height:)
|
16
|
+
@current = GameOfLife::Plane.new(height) { GameOfLife::Plane.new(width) }
|
17
|
+
@future = GameOfLife::Plane.new(height) { GameOfLife::Plane.new(width) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Representation of the game of life universe
|
21
|
+
# @return [String] representing the current state
|
22
|
+
# @see GameOfLife::Cell#to_s
|
23
|
+
def to_s
|
24
|
+
@current.map(&:join).join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Update the current state of the universe (according to the game of life laws) and
|
28
|
+
# set the current state to the next future generation
|
29
|
+
#
|
30
|
+
# This have computational order `O(n^2) = O(width * height)`
|
31
|
+
# It loops over all the universe cells and evolves them to the next generation
|
32
|
+
# @see #neighbors
|
33
|
+
def evolve!
|
34
|
+
@current.each do |row|
|
35
|
+
row.each do |cell|
|
36
|
+
@future[cell.y][cell.x] = cell.evolve!(neighbors(cell))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
@current = Marshal.load(Marshal.dump(@future))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the array of all the neighboring cells using the circular universe array, <br/>
|
43
|
+
# given a cell "C" in a universe of cells "." the immediate neighbors would be "x" as in
|
44
|
+
# the following different examples
|
45
|
+
#
|
46
|
+
# ..... | .xxx. | .xCx. | xCx.. | Cx..x | xx..x |
|
47
|
+
# .xxx. | .xCx. | .xxx. | xxx.. | xx..x | Cx..x |
|
48
|
+
# .xCx. | .xxx. | ..... | ..... | ..... | xx..x |
|
49
|
+
# .xxx. | ..... | ..... | ..... | ..... | ..... |
|
50
|
+
# ..... | ..... | .xxx. | xxx.. | xx..x | ..... |
|
51
|
+
#
|
52
|
+
# @param cell [GameOfLife::Cell] the to return the neighboring cells from the universe current plane
|
53
|
+
# @return [Array<GameOfLife::Cell>] of all the immediate neighboring cells
|
54
|
+
# @smell AbcSize higher than configuration, but needed to return the neighboring cells array
|
55
|
+
#
|
56
|
+
# rubocop:disable Metrics/AbcSize
|
57
|
+
private def neighbors(cell)
|
58
|
+
x, y = cell.x, cell.y # rubocop:disable Style/ParallelAssignment
|
59
|
+
[
|
60
|
+
@current[y - 1][x - 1], @current[y - 1][x], @current[y - 1][x + 1],
|
61
|
+
@current[y][x - 1], @current[y][x + 1],
|
62
|
+
@current[y + 1][x - 1], @current[y + 1][x], @current[y + 1][x + 1]
|
63
|
+
]
|
64
|
+
end
|
65
|
+
# rubocop:enable Metrics/AbcSize
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: terminal_game_of_life
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- a14m
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-12-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: redcarpet
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Conway's game of life implementation as a CLI ruby gem.
|
126
|
+
email:
|
127
|
+
- a14m@pm.me
|
128
|
+
executables:
|
129
|
+
- game-of-life
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- README.md
|
134
|
+
- bin/game-of-life
|
135
|
+
- game_of_life.gemspec
|
136
|
+
- lib/game_of_life.rb
|
137
|
+
- lib/game_of_life/cell.rb
|
138
|
+
- lib/game_of_life/generators/input.rb
|
139
|
+
- lib/game_of_life/generators/seed.rb
|
140
|
+
- lib/game_of_life/plane.rb
|
141
|
+
- lib/game_of_life/universe.rb
|
142
|
+
- lib/game_of_life/version.rb
|
143
|
+
homepage: https://gitlab.com/a14m/game-of-life
|
144
|
+
licenses:
|
145
|
+
- MIT
|
146
|
+
metadata:
|
147
|
+
allowed_push_host: https://rubygems.org
|
148
|
+
homepage_uri: https://gitlab.com/a14m/game-of-life
|
149
|
+
source_code_uri: https://gitlab.com/a14m/game-of-life/-/tree/master/CLI/ruby/
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 2.4.0
|
159
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
requirements: []
|
165
|
+
rubygems_version: 3.1.4
|
166
|
+
signing_key:
|
167
|
+
specification_version: 4
|
168
|
+
summary: Game of Life CLI
|
169
|
+
test_files: []
|