Change World to variable-height columns and API
This commit is contained in:
+32
-37
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user