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
#include <vector>
#include <memory>
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<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 ();
/* 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<std::unique_ptr<Robot>>& robots) const;
private:
int _sizeX, _sizeY, _sizeZ;
std::vector<std::vector<std::vector<int>>> _grid;
int sizeX_ = 5;
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/Robot.h"
#include <algorithm>
#include <iomanip>
@@ -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<std::vector<int>> ( y, std::vector<int> ( z, 0 ) ) ) {
: sizeX_ ( x ), sizeY_ ( y ), sizeZ_ ( z ),
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();
}
@@ -21,61 +23,124 @@ void World::init () {
std::mt19937 rng ( std::random_device {}() );
std::uniform_int_distribution<int> valueDist ( 1, 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 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<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 ) {
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<int> (col.size ())) {
col.erase( col.begin() + z );
return;
}
if ( z > static_cast<int> ( col.size())) {
throw std::logic_error( "setValue would result in holes, use SetColumn." );
}
if ( z == static_cast<int> ( 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<int>& value ) {
validateXY( x, y );
if ( static_cast<int> ( 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<int> 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<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.
@@ -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<int> 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<int> values;
std::vector<int> 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;
}