diff options
| author | a14m <[email protected]> | 2020-12-03 13:33:02 +0100 |
|---|---|---|
| committer | a14m <[email protected]> | 2020-12-03 15:03:09 +0100 |
| commit | 27b78771d819c4d9c1fe8537429d16fbc41d6a34 (patch) | |
| tree | cd67f8de5e29e65463b5684c7e9e3477c9cdbcae /CLI/ruby | |
| parent | 05730269d4d2d690d871548ae3930d3ce20cdcad (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.rb | 52 | ||||
| -rw-r--r-- | CLI/ruby/spec/game_of_life_spec.rb | 72 |
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 |
