terminal_game_of_life 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []