summaryrefslogtreecommitdiffstats
path: root/CLI/ruby
diff options
context:
space:
mode:
authora14m <[email protected]>2020-12-03 13:33:02 +0100
committera14m <[email protected]>2020-12-03 15:03:09 +0100
commit27b78771d819c4d9c1fe8537429d16fbc41d6a34 (patch)
treecd67f8de5e29e65463b5684c7e9e3477c9cdbcae /CLI/ruby
parent05730269d4d2d690d871548ae3930d3ce20cdcad (diff)
Add the GameOfLife module API for generating universes and running them
Diffstat (limited to 'CLI/ruby')
-rw-r--r--CLI/ruby/lib/game_of_life.rb52
-rw-r--r--CLI/ruby/spec/game_of_life_spec.rb72
2 files changed, 121 insertions, 3 deletions
diff --git a/CLI/ruby/lib/game_of_life.rb b/CLI/ruby/lib/game_of_life.rb
index 7f70933..f8eba24 100644
--- a/CLI/ruby/lib/game_of_life.rb
+++ b/CLI/ruby/lib/game_of_life.rb
@@ -1,11 +1,61 @@
# frozen_string_literal: true
require "game_of_life/version"
+require "game_of_life/plane"
require "game_of_life/universe"
require "game_of_life/cell"
+# Game of Life module entry point
module GameOfLife
- class Error < StandardError; end
+ class << self
+ # Generate a universe based on the options (from an input file/URI) or using a random/passed seed
+ # This method also sets the Classes/Modules constants for the run defining the options for
+ # Cell (Dead/Live) state representations, and the delay introduced after rendering each universe/generation
+ # and the banner for the info/options
+ #
+ # @param options [Hash] CLI options to generate initial conditions
+ # @option options [String | nil] "input" path to a local file or URL to open
+ # @option options [Numeric] "seed" number to use if no "input" is provided (defaults to random)
+ # @option options [Numeric] "width" of the universe
+ # @option options [Numeric] "height" of the universe
+ # @option options [Numeric] "delay" introduced after each cycle
+ # @option options [String] "live-cell" representation
+ # @option options [String] "dead-cell" representation
+ # @return [GameOfLife::Universe] populated with the parsed input/seed
+ def generate(options)
+ # Define the constants for Cell representation
+ GameOfLife::Cell.const_set(:LIVE_CELL, options["live-cell"])
+ GameOfLife::Cell.const_set(:DEAD_CELL, options["dead-cell"])
+ # Define the constant used for delay
+ GameOfLife.const_set(:DELAY, options["delay"].to_f)
+
+ banner = options["input"] ? "Input: #{options['input']}" : "Seed: #{options['seed']}"
+ # Define the constant used for banner info
+ GameOfLife.const_set(:BANNER, banner)
+
+ return GameOfLife::Generators::Input.new(options) if options["input"]
+ GameOfLife::Generators::Seed.new(options)
+ end
+
+ # The infinite running loop for rendering the current state of a universe
+ # and then evolving it and repeating.
+ def run(universe)
+ Kernel.loop do
+ GameOfLife.render(universe)
+ universe.evolve!
+ end
+ end
+
+ # Private method to do the terminal screen/scrollback buffer cleaning
+ # and rendering of the current universe/banner info
+ # based on the delay configured by the user
+ def render(universe)
+ puts "\33c\e[3J" # Clears the screen and scrollback buffer.
+ puts universe.to_s
+ puts BANNER
+ sleep(DELAY / 1000.0)
+ end
+ end
# Generators module for generating the universe initial state
module Generators
diff --git a/CLI/ruby/spec/game_of_life_spec.rb b/CLI/ruby/spec/game_of_life_spec.rb
index 5c93709..b4c1710 100644
--- a/CLI/ruby/spec/game_of_life_spec.rb
+++ b/CLI/ruby/spec/game_of_life_spec.rb
@@ -1,7 +1,75 @@
# frozen_string_literal: true
RSpec.describe GameOfLife do
- it "has a version number" do
- expect(GameOfLife::VERSION).not_to be nil
+ describe ".generate" do
+ let(:options) do
+ {
+ "live-cell" => "L",
+ "dead-cell" => "D",
+ "delay" => 100,
+ "width" => 10,
+ "height" => 20,
+ "seed" => 500,
+ }
+ end
+
+ context "with options['input']" do
+ before { options["input"] = "./file.txt" }
+
+ it "defines configurations constants" do
+ expect(described_class::Cell).to receive(:const_set).with(:LIVE_CELL, "L")
+ expect(described_class::Cell).to receive(:const_set).with(:DEAD_CELL, "D")
+ expect(described_class).to receive(:const_set).with(:DELAY, 100.0)
+ expect(described_class).to receive(:const_set).with(:BANNER, "Input: ./file.txt")
+
+ allow(described_class::Generators::Input).to receive(:new)
+ described_class.generate(options)
+ end
+
+ it "calls Generators::Input.new" do
+ allow(described_class::Cell).to receive(:const_set)
+ allow(described_class).to receive(:const_set)
+
+ expect(described_class::Generators::Input).to receive(:new).with(options)
+ described_class.generate(options)
+ end
+ end
+
+ context "without options['input']" do
+ before { options["seed"] = 1337 }
+
+ it "defines configurations constants" do
+ expect(described_class::Cell).to receive(:const_set).with(:LIVE_CELL, "L")
+ expect(described_class::Cell).to receive(:const_set).with(:DEAD_CELL, "D")
+ expect(described_class).to receive(:const_set).with(:DELAY, 100.0)
+ expect(described_class).to receive(:const_set).with(:BANNER, "Seed: 1337")
+
+ allow(described_class::Generators::Seed).to receive(:new)
+ described_class.generate(options)
+ end
+
+ it "calls Generators::Seed.new (and raises error)" do
+ allow(described_class::Cell).to receive(:const_set)
+ allow(described_class).to receive(:const_set)
+
+ expect { described_class.generate(options) }.to raise_error(NotImplementedError)
+ end
+ end
+ end
+
+ describe ".run" do
+ let(:universe) { GameOfLife::Universe.new(width: 3, height: 3) }
+
+ it "calls universe#evolve! and #{described_class}.run" do
+ # allow kernel loop tiwce
+ allow(Kernel).to receive(:loop).and_yield.and_yield
+
+ expect(described_class).to receive(:render).with(universe).twice
+ expect(universe).to receive(:evolve!).twice
+
+ described_class.run(universe)
+ end
end
+
+ describe.pending ".render"
end