From 279658f3e56b0b8e9d57b659e0b56a4101e67195 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 2 Oct 2025 21:45:52 -0600 Subject: [PATCH] Cleaning up and writing tests --- .gitignore | 1 + game/.rspec | 1 + game/Rakefile | 14 ++- game/game.rb | 12 ++- game/planet.rb | 17 ++++ game/player.rb | 14 +++ game/position.rb | 8 +- game/spec/api_spec.rb | 2 + game/spec/planet_spec.rb | 44 ++++++++++ game/spec/position_spec.rb | 72 +++++++++++++++ game/spec/ship_spec.rb | 55 +++++------- game/spec/spec_helper.rb | 104 ++++++++++++++++++++++ game/spec/stdout_helper.rb | 12 +++ game/spec/universe_array_spec.rb | 38 ++++++++ game/spec/universe_spec.rb | 146 +++++++++++++++++++++++++++++++ game/universe.rb | 89 +++++++++++++++---- game/universe_array.rb | 11 ++- 17 files changed, 584 insertions(+), 56 deletions(-) create mode 100644 game/.rspec create mode 100644 game/player.rb create mode 100644 game/spec/api_spec.rb create mode 100644 game/spec/planet_spec.rb create mode 100644 game/spec/position_spec.rb create mode 100644 game/spec/spec_helper.rb create mode 100644 game/spec/stdout_helper.rb create mode 100644 game/spec/universe_array_spec.rb create mode 100644 game/spec/universe_spec.rb diff --git a/.gitignore b/.gitignore index aa30a61..e1b3ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ **/.yard* **/logs **/*tmp* +**/coverage diff --git a/game/.rspec b/game/.rspec new file mode 100644 index 0000000..c99d2e7 --- /dev/null +++ b/game/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/game/Rakefile b/game/Rakefile index 3ce6633..c44472b 100644 --- a/game/Rakefile +++ b/game/Rakefile @@ -21,7 +21,15 @@ task :serve do end -desc 'Create the YARD documentation for the project' -task :doc do - res = sh 'yard doc *.rb' +namespace :doc do + desc 'Create the YARD documentation for the project' + task :yard do + res = sh 'yard doc *.rb' + end + + desc 'Create code coverage' + task :coverage do + ENV['COVERAGE'] = 'true' + Rake::Task[:test].invoke + end end diff --git a/game/game.rb b/game/game.rb index c527341..bfd4d95 100644 --- a/game/game.rb +++ b/game/game.rb @@ -1,8 +1,14 @@ # Nathan Hinton 1 Oct 2025 +# This file basiacally creates a universe and runs a simulated game in it. -# Mostly for testing right now: require './universe' +require './player' -universe = Universe.new('test universe') -universe.print_universe(simple: true) +universe = Universe.new('test universe', universe_size: 3) +universe.create_universe +universe.print_universe() +planets = universe.planets + +player = Player.new('test player') +puts player.name diff --git a/game/planet.rb b/game/planet.rb index 1f72587..b70fd25 100644 --- a/game/planet.rb +++ b/game/planet.rb @@ -5,11 +5,28 @@ PLANET_PRODUCTION_RESOURCES = [:ships] # This class holds information about a planet. class Planet attr_reader :owner + # Create a planet + # + # @param random [Random] The random generator for the game (ensures seeds can be duplicated) def initialize(random) @random = random @prod_rate = @random.rand @resource = PLANET_PRODUCTION_RESOURCES[(@random.rand * PLANET_PRODUCTION_RESOURCES.length).floor] @owner = nil end + + # Change the ownership of a planet. Should check for things like the planet + # has no defenders left and other fun stuff (later) + # + # @param owner [Player] The owner of the planet + # @return [Boolean] If the operation was successful or not + def change_owner(owner) + # For now only unclaimed planets can be owned + if @owner.nil? + @owner = owner + return true + end + return false + end end diff --git a/game/player.rb b/game/player.rb new file mode 100644 index 0000000..bb3f170 --- /dev/null +++ b/game/player.rb @@ -0,0 +1,14 @@ +# Nathan Hinton 2 Oct 2025 + + +# Class for the player. For now does not do much. +class Player + attr_reader :name + + # initializes with just the player name. + # + # @param name [String] The name of the player + def initialize(name) + @name = name + end +end diff --git a/game/position.rb b/game/position.rb index ec2f2d1..970c29b 100644 --- a/game/position.rb +++ b/game/position.rb @@ -16,7 +16,6 @@ class Position # initialize with a, b, c position raise NotImplementedError.new('(a, b, c) positioning not implimented!') else - require 'byebug';debugger raise ArgumentError.new('Creation of a position requires two or three integer args for a position.') end end @@ -35,7 +34,14 @@ class Position return [x_pos, y_pos] end + # Returns the string formatted version + # + # @return [String] Returns a string representing the point. def to_s return "(#{@x_pos}, #{y_pos})" end + + def ==(other) + return other.x_pos == @x_pos && other.y_pos == @y_pos + end end diff --git a/game/spec/api_spec.rb b/game/spec/api_spec.rb new file mode 100644 index 0000000..f7a7247 --- /dev/null +++ b/game/spec/api_spec.rb @@ -0,0 +1,2 @@ +# frozen_string_literal: true +# Nathan Hinton 2 Oct 2025 diff --git a/game/spec/planet_spec.rb b/game/spec/planet_spec.rb new file mode 100644 index 0000000..db51f30 --- /dev/null +++ b/game/spec/planet_spec.rb @@ -0,0 +1,44 @@ +# Nathan Hinton 2 Oct 2025 + +require './planet' + +require './player' + +RSpec.describe Planet do + let(:seed) { 12345 } + let(:random) { Random.new(seed) } + let(:planet) { Planet.new(random) } + let(:player_1) {Player.new('tester 01') } + let(:player_2) {Player.new('tester 02') } + + describe '#initialize' do + it 'stores the random off' do + expect(planet.instance_variable_get(:@random)).to eq(random) + end + + it 'creates a production rate' do + expect(planet.instance_variable_get(:@prod_rate)).to eq(Random.new(seed).rand) + end + + it 'sets a resource' do + expect(planet.instance_variable_get(:@resource)).to eq(PLANET_PRODUCTION_RESOURCES[(Random.new(seed).rand * PLANET_PRODUCTION_RESOURCES.length).floor]) + end + + it 'has a nil owner' do + expect(planet.instance_variable_get(:@owner)).to eq nil + end + end # describe #initialize + + describe '#change_owner' do + it 'allows an ownership change when the current owner is nil' do + expect(planet.change_owner(player_1)).to eq(true) + expect(planet.instance_variable_get(:@owner)).to eq player_1 + end + + it 'does not change when already owned' do + planet.change_owner(player_2) + expect(planet.change_owner(player_1)).to eq(false) + expect(planet.instance_variable_get(:@owner)).to eq player_2 + end + end +end diff --git a/game/spec/position_spec.rb b/game/spec/position_spec.rb new file mode 100644 index 0000000..3dd468b --- /dev/null +++ b/game/spec/position_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require './position' + +RSpec.describe Position do + let(:even_pair) { [4, 6] } # 4+6 = 10 → even + let(:odd_pair) { [3, 4] } # 3+4 = 7 → odd + + describe 'initialisation' do + context 'with two arguments' do + it 'stores the coordinates when the sum is even' do + pos = Position.new(*even_pair) + expect(pos.x_pos).to eq(even_pair[0]) + expect(pos.y_pos).to eq(even_pair[1]) + end + + it 'raises ArgumentError when the sum is odd' do + expect { Position.new(*odd_pair) } + .to raise_error(ArgumentError, /must sum to an even number/) + end + end + + context 'with three arguments' do + it 'raises NotImplementedError' do + expect { Position.new(1, 2, 3) } + .to raise_error(NotImplementedError, /positioning not implimented/) + end + end + + context 'with an unsupported number of arguments' do + it 'raises ArgumentError when no arguments are supplied' do + expect { Position.new } + .to raise_error(ArgumentError, /requires two or three integer args/) + end + + it 'raises ArgumentError when too many arguments are supplied' do + expect { Position.new(1, 2, 3, 4) } + .to raise_error(ArgumentError, /requires two or three integer args/) + end + end + end + + describe '#two_d_cartesian' do + it 'returns the original (x, y) pair' do + pos = Position.new(*even_pair) + expect(pos.two_d_cartesian).to eq(even_pair) + end + end + + describe '#two_d_hexagonal' do + it 'returns an array of nil values for a, b, c' do + pos = Position.new(*even_pair) + expect(pos.two_d_hexagonal).to eq([nil, nil, nil]) + end + end + + describe '#to_s' do + it 'puts out the coordinates' do + expect(Position.new(*even_pair).to_s).to eq('(4, 6)') + end + end + + describe '#==' do + it 'returns false when the positions differ' do + expect(Position.new(*even_pair) == Position.new(6, 8)).to eq(false) + end + + it 'returns true when the positions are the same' do + expect(Position.new(*even_pair) == Position.new(4, 6)).to eq(true) + end + end +end diff --git a/game/spec/ship_spec.rb b/game/spec/ship_spec.rb index b45bf1b..c662a24 100644 --- a/game/spec/ship_spec.rb +++ b/game/spec/ship_spec.rb @@ -1,17 +1,13 @@ -# Nathan Hinton 2 Oct 2025 +# frozen_string_literal: true -# Test file for the ship object - -require './ship' +require_relative '../ship' # <-- adjust to the actual path of ship.rb RSpec.describe Ship do - let(:initial_x) { 0 } - let(:initial_y) { 0 } - subject(:ship) { Ship.new(initial_x, initial_y) } + let(:ship) { Ship.new(0, 0) } describe '#initialize' do it 'stores the given position' do - expect(ship.instance_variable_get(:@position)).to eq([initial_x, initial_y]) + expect(ship.instance_variable_get(:@position)).to eq([0, 0]) end it 'sets the speed to 0.1' do @@ -24,23 +20,22 @@ RSpec.describe Ship do end describe '#tick' do - it 'does nothing (but does not raise an exception)' do + it 'does nothing (no exception) when called' do expect { ship.tick }.not_to raise_error end end describe '#move' do - context 'when coordinates are valid' do - it 'accepts adjacent moves whose sum is even' do - # adjacent positions that satisfy the rules - expect { ship.move(1, 1) }.not_to raise_error # sum = 2 - expect { ship.move(2, 0) }.not_to raise_error # sum = 2 - expect { ship.move(0, 2) }.not_to raise_error # sum = 2 - expect { ship.move(-1, -1) }.not_to raise_error # sum = -2 + context 'when coordinates are valid (even sum, adjacent)' do + it 'accepts positions that satisfy the rules' do + expect { ship.move(1, 1) }.not_to raise_error # sum 2 + expect { ship.move(2, 0) }.not_to raise_error # sum 2 + expect { ship.move(0, 2) }.not_to raise_error # sum 2 + expect { ship.move(-1, -1) }.not_to raise_error # sum -2 end end - context 'when the x + y sum is odd' do + context 'when the sum is odd' do it 'raises an ArgumentError' do expect { ship.move(0, 1) }.to raise_error( ArgumentError, @@ -51,7 +46,6 @@ RSpec.describe Ship do context 'when the target is not adjacent' do it 'raises an ArgumentError' do - # distance > 1: difference of sums != 2 expect { ship.move(3, 3) }.to raise_error( ArgumentError, /must be to an adjcent space/ @@ -65,23 +59,22 @@ RSpec.describe Ship do end describe '#move_direction' do - it 'accepts direction values between 0 and 5' do - (0..5).each do |direction| - expect { ship.move_direction(direction) }.not_to raise_error - # verify the @moving vector matches the spec - expected = case direction - when 0 then [2, 0] - when 1 then [1, 1] - when 2 then [-1, 1] - when 3 then [-2, 0] - when 4 then [-1, -1] - when 5 then [1, -1] - end + it 'accepts direction values 0..5 and sets @moving correctly' do + mapping = { + 0 => [2, 0], + 1 => [1, 1], + 2 => [-1, 1], + 3 => [-2, 0], + 4 => [-1, -1], + 5 => [1, -1] + } + mapping.each do |dir, expected| + expect { ship.move_direction(dir) }.not_to raise_error expect(ship.instance_variable_get(:@moving)).to eq(expected) end end - it 'raises an ArgumentError for invalid directions' do + it 'raises ArgumentError for invalid directions' do expect { ship.move_direction(-1) }.to raise_error( ArgumentError, /must be between \(0..5\)/ diff --git a/game/spec/spec_helper.rb b/game/spec/spec_helper.rb new file mode 100644 index 0000000..af77a70 --- /dev/null +++ b/game/spec/spec_helper.rb @@ -0,0 +1,104 @@ +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +if ENV['COVERAGE'] == 'true' + require 'simplecov' + SimpleCov.start +end + +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/game/spec/stdout_helper.rb b/game/spec/stdout_helper.rb new file mode 100644 index 0000000..0702e19 --- /dev/null +++ b/game/spec/stdout_helper.rb @@ -0,0 +1,12 @@ +# If you are getting an error `uninitialized constant StringIO` make sure that +# your code is in an `it` block. + +# Helper that captures STDOUT. +def capture_stdout + output = StringIO.new + $stdout = output + yield + output.string +ensure + $stdout = STDOUT +end diff --git a/game/spec/universe_array_spec.rb b/game/spec/universe_array_spec.rb new file mode 100644 index 0000000..179c871 --- /dev/null +++ b/game/spec/universe_array_spec.rb @@ -0,0 +1,38 @@ +# Nathan Hinton 2 Oct 2025 + + +require './universe_array' + +RSpec.describe UniverseArray do + describe '#[]' do + it 'works with indexes' do + uni_arr = UniverseArray.new() + uni_arr[0] = [] + uni_arr[0][0] = 'hi' + expect(uni_arr[0][0]).to eq 'hi' + end + + it 'works with positions' do + uni_arr = UniverseArray.new() + uni_arr[0] = [] + uni_arr[0][0] = 'hi' + expect(uni_arr[Position.new(0, 0)]).to eq 'hi' + end + end + + describe '#[]=' do + it 'works with indexes' do + uni_arr = UniverseArray.new() + uni_arr[0] = [] + uni_arr[0][0] = 'hi' + expect(uni_arr[0][0]).to eq 'hi' + end + + it 'works with positions' do + uni_arr = UniverseArray.new() + uni_arr[0] = [] + uni_arr[Position.new(0, 0)] = 'hi' + expect(uni_arr[Position.new(0, 0)]).to eq 'hi' + end + end +end diff --git a/game/spec/universe_spec.rb b/game/spec/universe_spec.rb new file mode 100644 index 0000000..b82057b --- /dev/null +++ b/game/spec/universe_spec.rb @@ -0,0 +1,146 @@ +# frozen_string_literal: true + +require './universe' +require './planet' +require './position' +require './universe_array' + +require './spec/stdout_helper' + +RSpec.describe Universe do + + let(:name) { 'Test Universe' } + let(:universe_size) { 3 } + let(:seed) { 12345 } + + subject(:universe) do + Universe.new( + name, + universe_size: universe_size, + seed: seed, + create_chances: { planet: 1 } # force a planet at every valid spot + ) + end + + describe '#initialize' do + it 'stores all expected instance variables' do + expect(universe.name).to eq(name) + expect(universe.instance_variable_get(:@universe_size)).to eq(universe_size) + expect(universe.instance_variable_get(:@seed)).to eq(seed) + end + + it 'creates a deterministic Random object when a seed is given' do + rng = universe.instance_variable_get(:@random) + expect(rng).to be_a(Random) + expect(rng.rand(100)).to eq(Random.new(seed).rand(100)) + end + + it 'initializes the random if no seed is passed in' do + uni = Universe.new(name) + rng = uni.instance_variable_get(:@random) + expect(rng).to be_a(Random) + end + end + + describe '#create_universe' do + + end + + describe '#distance' do + it 'is symmetric' do + a = Position.new(0, 0) + b = Position.new(4, 2) + expect(universe.distance(a, b)).to eq(universe.distance(b, a)) + end + + it 'returns 0 when both points are identical' do + p = Position.new(1, 1) + expect(universe.distance(p, p)).to eq(0) + end + + it 'correctly computes known hex distances' do + p0 = Position.new(0, 0) + p1 = Position.new(2, 0) + p2 = Position.new(4, 2) + p3 = Position.new(2, 2) + # (0,0) to (2,0) → 1 + expect(universe.distance(p0, p1)).to eq(1) + # (0,0) to (4,2) → 3 + expect(universe.distance(p0, p2)).to eq(3) + # (0,0) to (2,2) → 2 + expect(universe.distance(p0, p3)).to eq(2) + end + end + + describe '#map generation' do + it 'creates the expected dimensions' do + universe.create_universe + map = universe.instance_variable_get(:@universe_map) + # Each valid (x,y) pair inside the radius contains a Planet + # (our stub) and every other entry is nil. + expect(map).to be_a(UniverseArray) + # Check how may nodes were created: + expect(map.flatten.compact.length).to eq(19) # 1 + 1*6 + 2*6 + end + + it 'contains a planet at every valid point when create_chances is 1' do + universe.create_universe + map = universe.instance_variable_get(:@universe_map) + map.each do |row| + row.each do |idx| + # The indexes are allowed to be nil + if !idx.nil? + expect(idx).to be_a(Planet) + end + end + end + end + + it 'is deterministic when the same seed is used' do + first_universe = Universe.new( + name, + universe_size: universe_size, + seed: seed, + create_chances: { planet: 0.1 } + ) + first_map = first_universe.instance_variable_get(:@universe_map) + + second_universe = Universe.new( + name, + universe_size: universe_size, + seed: seed, + create_chances: { planet: 0.1 } + ) + second_map = second_universe.instance_variable_get(:@universe_map) + + expect(first_map).to eq(second_map) + end + end + + describe '#print_universe' do + it 'prints the center point as “O”' do + out = capture_stdout do + universe.create_universe + universe.print_universe + end + expect(out).to include('O') + end + + it 'can print blank spaces for nothing' do + out = capture_stdout do + uni = Universe.new(name, create_chances: {planet: 0.0}) + uni.create_universe + uni.print_universe + end + expect(out).to include('O') + expect(out).to include('- -') + end + end + + describe '#planets' do + it 'lists all of the generated planets' do + universe.create_universe + expect(universe.planets.length).to eq(19) + end + end +end diff --git a/game/universe.rb b/game/universe.rb index 458b75e..4733f41 100644 --- a/game/universe.rb +++ b/game/universe.rb @@ -48,9 +48,10 @@ require './universe_array' # @param universe_size [Integer] Controlls the size of the generated universe. This is a radius from the center point. class Universe # Setup logger for this class: - sh 'mkdir -p logs' @@logger = Logger.new('logs/universe.log') + attr_reader :name + def initialize(name, universe_size: 4, create_chances: {planet: 1}, seed: nil) @name = name @@ -69,12 +70,15 @@ class Universe @seed = seed @random = Random.new(@seed) end + end # Initialize + + def create_universe() # Set center of universe @center = Position.new(@universe_size + 2, @universe_size) @@logger.info "Generating Universe '#{@name}' with seed #{@seed}" - puts "Center is #{@center}" + @@logger.info "Center is #{@center}" @universe_map = UniverseArray.new() # Generate the universe. First generate the x values: (0 .. (@universe_size * 2)).each do |y_pos| @@ -90,16 +94,21 @@ class Universe next end + # Skip if it is the center + if position == @center + end + # Get what the planet should be: spot = nil if @random.rand < @create_chances[:planet] spot = Planet.new(@random) end - @@logger.debug "Creating something at (#{position.x_pos} #{position.y_pos})" - @universe_map[position.y_pos][position.x_pos] = spot + @@logger.debug "Creating something at (#{position})" + @universe_map[position] = spot end # Create y points end # Create x points - end # Function initialize + # Remove any empty nodes: + end # Function create_universe # Returns the distance between points. This is returned as a number which # indicates how far you have to go to get to a point. This is actually @@ -110,14 +119,10 @@ class Universe # @return [Integer] The distance between points in the universe. def distance(position_a, position_b) result = 0 # We can just add the abs diff of each direction. + require 'byebug';debugger if position_a.nil? dist_x = (position_b.x_pos - position_a.x_pos).abs dist_y = (position_b.y_pos - position_a.y_pos).abs - # Sanity check, our coordinates should add to be even: - if (dist_x + dist_y).odd? - raise RangeError.new('When calculating the distance we calculated a non even number. This is not allowed!') - end - res = (dist_x / 2) + dist_y if dist_x >= dist_y res = (dist_x + dist_y) / 2 @@ -128,18 +133,31 @@ class Universe # Prints the universe to the screen. def print_universe(simple: false) - (0 .. (@universe_size * 2)).each do |y_pos| + (0 .. (@universe_map.length)).each do |y_pos| # Print empty space before chart (for angles) - nextline = '' + if @universe_map[y_pos].nil? + next + end + if @universe_map[y_pos] == [] + puts '_' * (4 * @universe_size * 2) + next + end + nextline = '|' if y_pos < @universe_size nextline += ' ' * ((@universe_size - y_pos).abs - 1) else nextline += ' ' * ((@universe_size - y_pos).abs) end + if @universe_size.odd? + nextline += ' ' + end + # Print more empty space for the odd places (provides offset) if y_pos.odd? - print ' ' + print '| ' + else + print '|' end #Actually loop through everything @@ -156,9 +174,9 @@ class Universe end # Print what we are: - value = @universe_map[position.y_pos][position.x_pos] + value = @universe_map[position] nextline += ' ' - if [x_pos, y_pos] == @center + if position == @center print 'O' elsif value.class == Planet print 'P' @@ -184,6 +202,32 @@ class Universe end # For each row end # Function print_universe + # Returns all of the planets contained in the universe: + # + # @return [Array] + def planets + result = [] + @universe_map.flatten.each do |item| + result.push(item) if item.is_a? Planet + end + return result + end + + + + + +################################################################################ +################################################################################ +################################################################################ +################################################################################ +################################################################################ +################################################################################ + + + + + ################################################## ################### UNIVERSE ##################### ################################################## @@ -195,8 +239,9 @@ class Universe # # @param player [Player] The player who is asking for the position # @param position [Position] The position that is being requested + # @return [Planet, nil] Returns the thing at that point. def get_position(player, position) - + return @universe_map[position] end # Sets a position as explored for a player. This should be called as ships @@ -242,6 +287,12 @@ class Universe def remove_planet(planet, position) end + # Change the ownership of a planet + # + # @param planet [Planet] The planet to be modified + # @param player [Player] The player who should now own the planet + # @param position [Position] The position of the planet + ################################################## ##################### PLAYER ##################### ################################################## @@ -251,6 +302,12 @@ class Universe # @param player [Player] The player who should be added to the universe def add_player(player) @player_list.push(player) + # Give the player a planet + free_planets = [] + planets.each do |planet| + free_planets.push(planet) if planet.owner.nil? + end + return 'Added player to universe' end diff --git a/game/universe_array.rb b/game/universe_array.rb index 17929b1..e7b737f 100644 --- a/game/universe_array.rb +++ b/game/universe_array.rb @@ -7,12 +7,19 @@ require './position' # indexing as well as point type indexing class UniverseArray < Array def initialize - super end def [](index, *rest) if index.is_a?(Position) - return self[position.y_pos][position.x_pos] + return self[index.y_pos][index.x_pos] + else + super + end + end + + def []=(index, *rest) + if index.is_a?(Position) + return self[index.y_pos][index.x_pos] = rest[0] else super end