diff --git a/include/World.h b/include/World.h index 4df0a22..ee8bc60 100644 --- a/include/World.h +++ b/include/World.h @@ -1,56 +1,51 @@ #pragma once #include +#include + +class Robot; /* A 3-D grid world where robots mine positive-value blocks and trigger effects. */ class World { public: explicit World ( int x = 5, int y = 5, int z = 10 ); - /* Return the value at (x, y, z). - Throws std::out_of_range for invalid coordinates. - Complexity: O(1) */ - int getValue ( int x, int y, int z ) const; - - /* Set the value at (x, y, z). - Throws std::out_of_range for invalid coordinates. - Complexity: O(1) */ - void setValue ( int x, int y, int z, int value ); - - /* Return the highest z in column (x, y) containing a positive value. - Returns -1 if no minable block exists in the column. - Complexity: O(sizeZ) */ - int getSurfaceLevel ( int x, int y ) const; - - /* Mine the current surface block at (x, y), set it to 0, and return its value. - Returns 0 if the column has no minable surface. - Complexity: O(sizeZ) */ - int mine ( int x, int y ); - - /* Print a 2-D surface view of the world with optional robot markers. - Pass default coordinates (-1, -1) to omit a robot marker. - Complexity: O(sizeX * sizeY * sizeZ) */ - void display ( int p1x = -1, int p1y = -1, int p2x = -1, int p2y = -1 ) const; - int getSizeX () const { return _sizeX; } int getSizeY () const { return _sizeY; } int getSizeZ () const { return _sizeZ; } - /* Rearrange each column by applying a random operation - (shuffle, ascending sort, or descending sort) to non-zero entries. - Complexity: O(sizeX * sizeY * sizeZ log sizeZ) */ + int getValue ( int x, int y, int z ) const; + void setValue ( int x, int y, int z, int value ); + void setColumn ( int x, int y, const std::vector& values ); + std::vector getColumn ( int x, int y ) const; + void clear (); + + int getSurfaceLevel ( int x, int y ) const; + int getSurfaceValue ( int x, int y ) const; + bool hasPositiveValues () const; + int remainingPositiveSum () const; + + + int mine ( int x, int y ); + int checkEffects ( int x, int y ); + int mineAllPositive ( int x, int y ); + + double posiveAverage ( int x, int y ) const; + int topPositiveSum ( int x, int y, int blocks ) const; + void sortPositiveValuesInColumnAscending ( int x, int y ); void rearrange (); - /* Find and consume one negative effect value in column (x, y). - Returns 0 if no effect exists. - Complexity: O(sizeZ) */ - int checkEffects ( int x, int y ); + void display () const; + void display ( const std::vector>& robots) const; private: - int _sizeX, _sizeY, _sizeZ; - std::vector>> _grid; + int sizeX_ = 5; + int sizeY_ = 5; + int sizeZ_ = 10; + std::vector>> grid_; + + void init(); + void validateXY ( int x, int y ) const; + void validateZ ( int z ) const; - /* Initialize the grid with random positive values and occasional effects. - Complexity: O(sizeX * sizeY * sizeZ) */ - void init (); }; diff --git a/src/World.cpp b/src/World.cpp index 789a2f8..6a66dc9 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1,4 +1,5 @@ #include "../include/World.h" +#include "../include/Robot.h" #include #include @@ -9,8 +10,9 @@ /* Construct a 3D grid world and initialize all cells. Complexity: O(x × y × z) */ World::World ( int x, int y, int z ) - : _sizeX ( x ), _sizeY ( y ), _sizeZ ( z ), - _grid ( x, std::vector> ( y, std::vector ( z, 0 ) ) ) { + : sizeX_ ( x ), sizeY_ ( y ), sizeZ_ ( z ), + grid_ ( x, std::vector> ( y ) ) { + if ( x <= 0 || y <= 0 || z <= 0 ) throw std::invalid_argument( "World dimensions must be positive."); init(); } @@ -21,61 +23,124 @@ void World::init () { std::mt19937 rng ( std::random_device {}() ); std::uniform_int_distribution valueDist ( 1, 9 ); std::uniform_int_distribution effectChance ( 0, 9 ); - std::uniform_int_distribution effectType ( -3, -1 ); + std::uniform_int_distribution effectDist ( 3, 1 ); - for ( int x = 0; x < _sizeX; ++x ) { - for ( int y = 0; y < _sizeY; ++y ) { - for ( int z = 0; z < _sizeZ; ++z ) { - _grid[ x ][ y ][ z ] = ( effectChance ( rng ) == 1 ) ? effectType ( rng ) : valueDist ( rng ); + for ( int x = 0; x < sizeX_; ++x ) { + for ( int y = 0; y < sizeY_; ++y ) { + auto& col = grid_[x][y]; + col.clear(); + col.reserve ( sizeZ_ ); + for ( int z = 0; z < sizeZ_; ++z ) { + col.push_back( effectChance (rng ) == 1 ? -effectDist (rng ) : valueDist (rng ) ); } } } } -/* Return the value at (x, y, z), throwing on invalid coordinates. - Complexity: O(1) */ +/* validate coordinates */ +void World::validateXY ( int x, int y ) const { + if ( x < 0 || x >= sizeX_ || y < 0 || y >= sizeY_ ) throw std::out_of_range( "World coordinate out of range." ); +} + +void World::validateZ( int z ) const { + if ( z < 0 || z >= sizeZ_ ) throw std::out_of_range ( "World depth out of range." ); +} + +/* public API */ int World::getValue ( int x, int y, int z ) const { - if ( x < 0 || x >= _sizeX || y < 0 || y >= _sizeY || z < 0 || z >= _sizeZ ) { - throw std::out_of_range ( "Coordinates out of bounds" ); - } - return _grid[ x ][ y ][ z ]; + validateXY( x, y ); + validateZ ( z ); + const auto& col = grid_[x][y]; + return z < static_cast ( col.size() ) ? col[z] : 0; } -/* Set the value at (x, y, z), throwing on invalid coordinates. - Complexity: O(1) */ void World::setValue ( int x, int y, int z, int value ) { - if ( x < 0 || x >= _sizeX || y < 0 || y >= _sizeY || z < 0 || z >= _sizeZ ) { - throw std::out_of_range ( "Coordinates out of bounds" ); - } - _grid[ x ][ y ][ z ] = value; -} - -/* Return the highest z with a positive value in column (x, y). - Negative effect cells are ignored for mining surface purposes. - Complexity: O(z) */ -int World::getSurfaceLevel ( int x, int y ) const { - for ( int z = _sizeZ - 1; z >= 0; --z ) { - if ( _grid[ x ][ y ][ z ] > 0 ) { - return z; + validateXY( x, y ); + validateZ( z ); + auto& col = grid_[x][y]; + if ( value == 0 ) { + if ( z < static_cast (col.size ())) { + col.erase( col.begin() + z ); + return; + } + if ( z > static_cast ( col.size())) { + throw std::logic_error( "setValue would result in holes, use SetColumn." ); + } + if ( z == static_cast ( col.size() ) ) { + col.push_back( value ); + } else { + col[z] = value; } } - return -1; } -/* Mine one topmost positive block from column (x, y) and return its value. - Returns 0 when the column has no mineable block. - Complexity: O(z) */ -int World::mine ( int x, int y ) { - const int z = getSurfaceLevel ( x, y ); - if ( z == -1 ) { - return 0; +void World::setColumn( int x, int y, const std::vector& value ) { + validateXY( x, y ); + if ( static_cast ( value.size()) > sizeZ_ ) { + throw std::out_of_range( "Column is too deep." ); } + grid_[x][y] = value; +} - const int value = _grid[ x ][ y ][ z ]; - if ( value > 0 ) { - _grid[ x ][ y ][ z ] = 0; +std::vector World::getColumn( int x, int y ) const { + validateXY( x, y ); + return grid_[x][y]; +} + +void World::clear() { + for ( auto& row : grid_ ) { + for ( auto& col : row ) { + col.clear(); + } } - return value; +} + +int World::getSurfaceLevel ( int x, int y ) const { + validateXY( x, y ); + const auto& col = grid_[x][y]; + return col.empty() ? -1 : static_cast ( col.size()) -1; +} + +int World::getSurfaceValue( int x, int y ) const { + validateXY( x, y ); + const auto& col = grid_[x][y]; + return col.empty() ? 0 : col.back(); +} + +int World::remainingPositiveSum() const { + int sum = 0; + for ( const auto& row : grid_ ) { + for ( const auto& col : row ) { + for ( int v : col ) { + if ( v > 0 ) sum += v; + } + } + } + return sum; +} + +bool World::hasPositiveValues() const { + return remainingPositiveSum() > 0; +} + +int World::checkEffects ( int x, int y ) { + validateXY( x, y ); + auto& col = grid_[x][y]; + if ( !col.empty() && col.back() < 0 ) { + int effect = col.back(); + col.pop_back(); + return effect; + } + return 0; +} + +int World::mine ( int x, int y ) { + validateXY( x, y ); + auto& col = grid_[x][y]; + if ( col.empty() ) return 0; + int value = col.back(); + col.pop_back(); + return value > 0 ? value : 0; } /* Print a 2D surface view with optional player/computer markers. @@ -89,16 +154,16 @@ void World::display ( int p1x, int p1y, int p2x, int p2y ) const { } std::cout << " "; - for ( int y = 0; y < _sizeY; ++y ) { + for ( int y = 0; y < sizeY_; ++y ) { std::cout << std::setw ( 4 ) << y; } std::cout << "\n"; - for ( int x = 0; x < _sizeX; ++x ) { + for ( int x = 0; x < sizeX_; ++x ) { std::cout << std::setw ( 2 ) << x << " "; - for ( int y = 0; y < _sizeY; ++y ) { + for ( int y = 0; y < sizeY_; ++y ) { const int surface = getSurfaceLevel ( x, y ); - const int val = ( surface >= 0 ? _grid[ x ][ y ][ surface ] : 0 ); + const int val = ( surface >= 0 ? grid_[ x ][ y ][ surface ] : 0 ); const bool hasP1 = ( x == p1x && y == p1y ); const bool hasP2 = ( x == p2x && y == p2y ); @@ -125,14 +190,14 @@ void World::rearrange () { std::mt19937 rng ( std::random_device {}() ); std::uniform_int_distribution opDist ( 0, 2 ); - for ( int x = 0; x < _sizeX; ++x ) { - for ( int y = 0; y < _sizeY; ++y ) { + for ( int x = 0; x < sizeX_; ++x ) { + for ( int y = 0; y < sizeY_; ++y ) { std::vector values; std::vector positions; - for ( int z = 0; z < _sizeZ; ++z ) { - if ( _grid[ x ][ y ][ z ] > 0 ) { // only minable blocks; effect cells stay in place - values.push_back ( _grid[ x ][ y ][ z ] ); + for ( int z = 0; z < sizeZ_; ++z ) { + if ( grid_[ x ][ y ][ z ] > 0 ) { // only minable blocks; effect cells stay in place + values.push_back ( grid_[ x ][ y ][ z ] ); positions.push_back ( z ); } } @@ -150,7 +215,7 @@ void World::rearrange () { } for ( std::size_t i = 0; i < positions.size(); ++i ) { - _grid[ x ][ y ][ positions[ i ] ] = values[ i ]; + grid_[ x ][ y ][ positions[ i ] ] = values[ i ]; } } } @@ -159,15 +224,3 @@ void World::rearrange () { /* Find and consume the first negative effect value in column (x, y). Returns 0 when no effect exists in that column. Complexity: O(z) */ -int World::checkEffects ( int x, int y ) { - auto &col = _grid[ x ][ y ]; - auto it = std::find_if ( col.begin(), col.end(), [] ( int v ) { return v < 0; } ); - - if ( it == col.end() ) { - return 0; - } - - const int effect = *it; - *it = 0; - return effect; -}