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.
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GameOfLife
4
+ VERSION = "0.1.0"
5
+ 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: []