Change World to variable-height columns and API

This commit is contained in:
2026-04-26 17:08:35 +02:00
parent 35436ef4a8
commit 94ceebb50a
2 changed files with 147 additions and 99 deletions
+32 -37
View File
@@ -1,56 +1,51 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <memory>
class Robot;
/* A 3-D grid world where robots mine positive-value blocks and trigger effects. */ /* A 3-D grid world where robots mine positive-value blocks and trigger effects. */
class World { class World {
public: public:
explicit World ( int x = 5, int y = 5, int z = 10 ); 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 getSizeX () const { return _sizeX; }
int getSizeY () const { return _sizeY; } int getSizeY () const { return _sizeY; }
int getSizeZ () const { return _sizeZ; } int getSizeZ () const { return _sizeZ; }
/* Rearrange each column by applying a random operation int getValue ( int x, int y, int z ) const;
(shuffle, ascending sort, or descending sort) to non-zero entries. void setValue ( int x, int y, int z, int value );
Complexity: O(sizeX * sizeY * sizeZ log sizeZ) */ void setColumn ( int x, int y, const std::vector<int>& values );
std::vector<int> 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 (); void rearrange ();
/* Find and consume one negative effect value in column (x, y). void display () const;
Returns 0 if no effect exists. void display ( const std::vector<std::unique_ptr<Robot>>& robots) const;
Complexity: O(sizeZ) */
int checkEffects ( int x, int y );
private: private:
int _sizeX, _sizeY, _sizeZ; int sizeX_ = 5;
std::vector<std::vector<std::vector<int>>> _grid; int sizeY_ = 5;
int sizeZ_ = 10;
std::vector<std::vector<std::vector<int>>> 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 ();
}; };
+115 -62
View File
@@ -1,4 +1,5 @@
#include "../include/World.h" #include "../include/World.h"
#include "../include/Robot.h"
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
@@ -9,8 +10,9 @@
/* Construct a 3D grid world and initialize all cells. /* Construct a 3D grid world and initialize all cells.
Complexity: O(x × y × z) */ Complexity: O(x × y × z) */
World::World ( int x, int y, int z ) World::World ( int x, int y, int z )
: _sizeX ( x ), _sizeY ( y ), _sizeZ ( z ), : sizeX_ ( x ), sizeY_ ( y ), sizeZ_ ( z ),
_grid ( x, std::vector<std::vector<int>> ( y, std::vector<int> ( z, 0 ) ) ) { grid_ ( x, std::vector<std::vector<int>> ( y ) ) {
if ( x <= 0 || y <= 0 || z <= 0 ) throw std::invalid_argument( "World dimensions must be positive.");
init(); init();
} }
@@ -21,61 +23,124 @@ void World::init () {
std::mt19937 rng ( std::random_device {}() ); std::mt19937 rng ( std::random_device {}() );
std::uniform_int_distribution<int> valueDist ( 1, 9 ); std::uniform_int_distribution<int> valueDist ( 1, 9 );
std::uniform_int_distribution<int> effectChance ( 0, 9 ); std::uniform_int_distribution<int> effectChance ( 0, 9 );
std::uniform_int_distribution<int> effectType ( -3, -1 ); std::uniform_int_distribution<int> effectDist ( 3, 1 );
for ( int x = 0; x < _sizeX; ++x ) { for ( int x = 0; x < sizeX_; ++x ) {
for ( int y = 0; y < _sizeY; ++y ) { for ( int y = 0; y < sizeY_; ++y ) {
for ( int z = 0; z < _sizeZ; ++z ) { auto& col = grid_[x][y];
_grid[ x ][ y ][ z ] = ( effectChance ( rng ) == 1 ) ? effectType ( rng ) : valueDist ( rng ); 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. /* validate coordinates */
Complexity: O(1) */ 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 { int World::getValue ( int x, int y, int z ) const {
if ( x < 0 || x >= _sizeX || y < 0 || y >= _sizeY || z < 0 || z >= _sizeZ ) { validateXY( x, y );
throw std::out_of_range ( "Coordinates out of bounds" ); validateZ ( z );
} const auto& col = grid_[x][y];
return _grid[ x ][ y ][ z ]; return z < static_cast<int> ( 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 ) { void World::setValue ( int x, int y, int z, int value ) {
if ( x < 0 || x >= _sizeX || y < 0 || y >= _sizeY || z < 0 || z >= _sizeZ ) { validateXY( x, y );
throw std::out_of_range ( "Coordinates out of bounds" ); validateZ( z );
} auto& col = grid_[x][y];
_grid[ x ][ y ][ z ] = value; if ( value == 0 ) {
} if ( z < static_cast<int> (col.size ())) {
col.erase( col.begin() + z );
/* Return the highest z with a positive value in column (x, y). return;
Negative effect cells are ignored for mining surface purposes. }
Complexity: O(z) */ if ( z > static_cast<int> ( col.size())) {
int World::getSurfaceLevel ( int x, int y ) const { throw std::logic_error( "setValue would result in holes, use SetColumn." );
for ( int z = _sizeZ - 1; z >= 0; --z ) { }
if ( _grid[ x ][ y ][ z ] > 0 ) { if ( z == static_cast<int> ( col.size() ) ) {
return z; col.push_back( value );
} else {
col[z] = value;
} }
} }
return -1;
} }
/* Mine one topmost positive block from column (x, y) and return its value. void World::setColumn( int x, int y, const std::vector<int>& value ) {
Returns 0 when the column has no mineable block. validateXY( x, y );
Complexity: O(z) */ if ( static_cast<int> ( value.size()) > sizeZ_ ) {
int World::mine ( int x, int y ) { throw std::out_of_range( "Column is too deep." );
const int z = getSurfaceLevel ( x, y );
if ( z == -1 ) {
return 0;
} }
grid_[x][y] = value;
}
const int value = _grid[ x ][ y ][ z ]; std::vector<int> World::getColumn( int x, int y ) const {
if ( value > 0 ) { validateXY( x, y );
_grid[ x ][ y ][ z ] = 0; 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<int> ( 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. /* 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 << " "; std::cout << " ";
for ( int y = 0; y < _sizeY; ++y ) { for ( int y = 0; y < sizeY_; ++y ) {
std::cout << std::setw ( 4 ) << y; std::cout << std::setw ( 4 ) << y;
} }
std::cout << "\n"; std::cout << "\n";
for ( int x = 0; x < _sizeX; ++x ) { for ( int x = 0; x < sizeX_; ++x ) {
std::cout << std::setw ( 2 ) << 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 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 hasP1 = ( x == p1x && y == p1y );
const bool hasP2 = ( x == p2x && y == p2y ); const bool hasP2 = ( x == p2x && y == p2y );
@@ -125,14 +190,14 @@ void World::rearrange () {
std::mt19937 rng ( std::random_device {}() ); std::mt19937 rng ( std::random_device {}() );
std::uniform_int_distribution<int> opDist ( 0, 2 ); std::uniform_int_distribution<int> opDist ( 0, 2 );
for ( int x = 0; x < _sizeX; ++x ) { for ( int x = 0; x < sizeX_; ++x ) {
for ( int y = 0; y < _sizeY; ++y ) { for ( int y = 0; y < sizeY_; ++y ) {
std::vector<int> values; std::vector<int> values;
std::vector<int> positions; std::vector<int> positions;
for ( int z = 0; z < _sizeZ; ++z ) { for ( int z = 0; z < sizeZ_; ++z ) {
if ( _grid[ x ][ y ][ z ] > 0 ) { // only minable blocks; effect cells stay in place if ( grid_[ x ][ y ][ z ] > 0 ) { // only minable blocks; effect cells stay in place
values.push_back ( _grid[ x ][ y ][ z ] ); values.push_back ( grid_[ x ][ y ][ z ] );
positions.push_back ( z ); positions.push_back ( z );
} }
} }
@@ -150,7 +215,7 @@ void World::rearrange () {
} }
for ( std::size_t i = 0; i < positions.size(); ++i ) { 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). /* Find and consume the first negative effect value in column (x, y).
Returns 0 when no effect exists in that column. Returns 0 when no effect exists in that column.
Complexity: O(z) */ 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;
}