implement mining functionality in SortBot and add .gitignore
This commit is contained in:
+8
-1
@@ -12,4 +12,11 @@ add_executable(deep_miner
|
||||
include/World.h
|
||||
src/BaseRobot.cpp
|
||||
src/SortBot.cpp
|
||||
src/World.cpp)
|
||||
src/World.cpp
|
||||
include/Game.h
|
||||
include/DigDeepBot.h
|
||||
include/RandomBot.h
|
||||
src/DigDeepBot.cpp
|
||||
src/Game.cpp
|
||||
src/RandomBot.cpp
|
||||
main.cpp)
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ class Game {
|
||||
void setup ();
|
||||
void play ( Robot& robot, bool isPlayer );
|
||||
void checkRearrange ( Robot& robot );
|
||||
void applyEffects ( Robot& robot, int effect );
|
||||
void applyEffect ( Robot& robot, int effect );
|
||||
bool isGameOver() const;
|
||||
void printScores() const;
|
||||
void printResult() const;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ class World {
|
||||
int getSurfaceLevel ( int x, int y ) const;
|
||||
int mine ( int x, int y );
|
||||
|
||||
void display () const;
|
||||
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; }
|
||||
|
||||
+49
-2
@@ -2,6 +2,7 @@
|
||||
#include "../include/World.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
BaseRobot::BaseRobot ( std::string name, int startX, int startY )
|
||||
: x_ ( startX ), y_ ( startY ), score_ ( 0 ), name_ ( std::move ( name ) ) {}
|
||||
@@ -26,5 +27,51 @@ void BaseRobot::move ( int direction, const World &world ) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// int BaseRobot::decideNextMove ( const World &world ) const
|
||||
int BaseRobot::decideNextMove ( const World &world ) const {
|
||||
struct Option {
|
||||
int dir, val;
|
||||
};
|
||||
std::vector<Option> options;
|
||||
options.reserve ( 5 );
|
||||
|
||||
auto evaluate = [ & ] ( int dx, int dy, int dir ) {
|
||||
int nx = x_ + dx, ny = y_ + dy;
|
||||
if ( nx < 0 || nx >= world.getSizeX() )
|
||||
return;
|
||||
if ( ny < 0 || ny >= world.getSizeY() )
|
||||
return;
|
||||
int surf = world.getSurfaceLevel ( nx, ny );
|
||||
if ( surf < 0 )
|
||||
return;
|
||||
options.push_back ( { dir, world.getValue ( nx, ny, surf ) } );
|
||||
};
|
||||
|
||||
evaluate ( 0, 0, 0 );
|
||||
evaluate ( 1, 0, 1 );
|
||||
evaluate ( -1, 0, 2 );
|
||||
evaluate ( 0, 1, 3 );
|
||||
evaluate ( 0, -1, 4 );
|
||||
|
||||
if ( options.empty() ) {
|
||||
// All immediate neighbours are empty – scan the whole grid for the
|
||||
// nearest non-empty column and step one cell toward it.
|
||||
int bestDist = INT_MAX, tx = x_, ty = y_;
|
||||
for ( int cx = 0; cx < world.getSizeX(); ++cx ) {
|
||||
for ( int cy = 0; cy < world.getSizeY(); ++cy ) {
|
||||
if ( world.getSurfaceLevel ( cx, cy ) >= 0 ) {
|
||||
int dist = std::abs ( cx - x_ ) + std::abs ( cy - y_ );
|
||||
if ( dist < bestDist ) { bestDist = dist; tx = cx; ty = cy; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( tx == x_ && ty == y_ ) return 0; // grid is truly empty
|
||||
if ( tx > x_ ) return 1;
|
||||
if ( tx < x_ ) return 2;
|
||||
if ( ty > y_ ) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
auto best = std::max_element ( options.begin(), options.end(),
|
||||
[] ( const Option &a, const Option &b ) { return a.val < b.val; } );
|
||||
return best->dir;
|
||||
}
|
||||
|
||||
+63
-4
@@ -10,9 +10,9 @@ int validateInput ( const std::string& prompt, int min, int max );
|
||||
Game::Game() : world_ ( 5, 5, 10 ) {}
|
||||
|
||||
void Game::run () {
|
||||
std::cout << "=== DEEP MINER ===/n/n";
|
||||
std::cout << "=== DEEP MINER ===\n\n";
|
||||
setup();
|
||||
world_.display ();
|
||||
world_.display ( player_->getX(), player_->getY(), computer_->getX(), computer_->getY() );
|
||||
|
||||
int round = 1;
|
||||
while ( !isGameOver() ) {
|
||||
@@ -26,7 +26,7 @@ void Game::run () {
|
||||
std::cout << "[Computer: " << computer_->getName() << "]\n";
|
||||
play ( *computer_, false );
|
||||
|
||||
world_.display();
|
||||
world_.display ( player_->getX(), player_->getY(), computer_->getX(), computer_->getY() );
|
||||
printScores();
|
||||
}
|
||||
printResult();
|
||||
@@ -59,6 +59,65 @@ std::unique_ptr < Robot > Game::createRobot ( int choice, int x, int y ) const {
|
||||
}
|
||||
}
|
||||
|
||||
void Game::play ( Robot& robot, bool isPlayer ) {
|
||||
int direction = ( !isPlayer || autoMode_ ) ? robot.decideNextMove( world_ )
|
||||
: validateInput ( "Direction (0 =stay 1=+x 2=-x 3=+y 4=-y): ", 0, 4);
|
||||
robot.move( direction, world_ );
|
||||
|
||||
int effect = world_.checkEffects( robot.getX(), robot.getY() );
|
||||
if ( effect < 0 ) {
|
||||
applyEffect( robot, effect );
|
||||
}
|
||||
if ( effect != -1 ) { // -1 = blocked; all other effects still allow mining
|
||||
int mined = robot.mine( world_ );
|
||||
std::cout << " " << robot.getName() << " mined " << mined << " points.\n";
|
||||
}
|
||||
checkRearrange( robot );
|
||||
}
|
||||
|
||||
void Game::checkRearrange(Robot& robot) {
|
||||
int score = robot.getScore();
|
||||
int threshold = (score / 50) * 50;
|
||||
if (threshold > 0 && threshold > lastThreshold_) {
|
||||
lastThreshold_ = threshold;
|
||||
std::cout << "*** " << robot.getName() << " reached " << threshold
|
||||
<< " points! World rearranged! ***\n";
|
||||
world_.rearrange();
|
||||
world_.display ( player_->getX(), player_->getY(), computer_->getX(), computer_->getY() );
|
||||
}
|
||||
}
|
||||
|
||||
void Game::applyEffect(Robot& robot, int effect) {
|
||||
switch (effect) {
|
||||
case -1:
|
||||
std::cout << " [EFFECT -1] " << robot.getName() << " blocked this round!\n";
|
||||
break;
|
||||
|
||||
case -2: {
|
||||
int minVal = std::numeric_limits<int>::max(), bx = 0, by = 0;
|
||||
for (int x = 0; x < world_.getSizeX(); ++x)
|
||||
for (int y = 0; y < world_.getSizeY(); ++y) {
|
||||
int surf = world_.getSurfaceLevel(x, y);
|
||||
if (surf < 0) continue;
|
||||
int val = world_.getValue(x, y, surf);
|
||||
if (val < minVal) { minVal = val; bx = x; by = y; }
|
||||
}
|
||||
std::cout << " [EFFECT -2] " << robot.getName()
|
||||
<< " teleported to (" << bx << "," << by << ")!\n";
|
||||
robot.setPosition(bx, by);
|
||||
break;
|
||||
}
|
||||
|
||||
case -3:
|
||||
std::cout << " [EFFECT -3] " << robot.getName() << " loses half score!\n";
|
||||
robot.addScore(-(robot.getScore() / 2));
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << " [EFFECT] Unknown effect " << effect << " ignored.\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool Game::isGameOver () const {
|
||||
for ( int x = 0; x < world_.getSizeX (); ++x ) {
|
||||
for ( int y = 0; y < world_.getSizeY(); ++y ){
|
||||
@@ -87,7 +146,7 @@ void Game::printResult() const {
|
||||
std::cout << "DRAW!\n";
|
||||
}
|
||||
|
||||
int getValidatedInput(const std::string& prompt, int min, int max) {
|
||||
int validateInput (const std::string& prompt, int min, int max) {
|
||||
int val;
|
||||
while (true) {
|
||||
std::cout << prompt;
|
||||
|
||||
+3
-2
@@ -9,8 +9,9 @@ BaseRobot ( "RandomBot", startX, startY ) {}
|
||||
int RandomBot::mine ( World& world ) {
|
||||
int total = 0;
|
||||
int grabbed = 0;
|
||||
const int limit = randomNumber(); // computed once; calling it every iteration re-rolls the limit
|
||||
|
||||
for ( int z = world.getSizeZ() -1; z >= 0 && grabbed <= randomNumber(); --z ) {
|
||||
for ( int z = world.getSizeZ() -1; z >= 0 && grabbed < limit; --z ) {
|
||||
int value = world.getValue ( x_, y_, z );
|
||||
if ( value > 0 ) {
|
||||
total += value;
|
||||
@@ -22,7 +23,7 @@ int RandomBot::mine ( World& world ) {
|
||||
return total;
|
||||
}
|
||||
|
||||
int randomNumber () {
|
||||
int RandomBot::randomNumber () {
|
||||
static std ::mt19937 rng ( std::random_device{}());
|
||||
std::uniform_int_distribution < int > distr ( 0, 9 );
|
||||
return distr ( rng );
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ int SortBot::mine ( World &world ) {
|
||||
if ( v > 0 ) values.emplace_back(v, z );
|
||||
}
|
||||
if ( values.empty()) return 0;
|
||||
// sort descending
|
||||
std::sort ( values.begin(), values.end(), [] ( const auto& a, const auto& b ) { return a.first > b.first;} );
|
||||
// sort ascending so highest value ends up at top (surface) and gets mined
|
||||
std::sort ( values.begin(), values.end(), [] ( const auto& a, const auto& b ) { return a.first < b.first;} );
|
||||
|
||||
for ( auto& [x, z] : values ) {
|
||||
world.setValue ( x_, y_, z, 0);
|
||||
|
||||
+39
-14
@@ -16,7 +16,7 @@ void World::init () {
|
||||
std::mt19937 rng ( std::random_device {}() ); // random number generator (RNG)
|
||||
std::uniform_int_distribution<int> distribution ( 1, 9 );
|
||||
std::uniform_int_distribution<int> effectProbability ( 0, 9 ); // 10% chance for an effect
|
||||
std::uniform_int_distribution<int> effectType ( 1, 3 ); // 3 types of effects
|
||||
std::uniform_int_distribution<int> effectType ( -3, -1 ); // 3 types of effects (negative flags)
|
||||
|
||||
for ( int x = 0; x < _sizeX; ++x ) {
|
||||
for ( int y = 0; y < _sizeY; ++y ) {
|
||||
@@ -45,7 +45,7 @@ void World::setValue ( int x, int y, int z, int value ) {
|
||||
|
||||
int World::getSurfaceLevel ( int x, int y ) const {
|
||||
for ( int z = _sizeZ - 1; z >= 0; --z ) {
|
||||
if ( _grid[ x ][ y ][ z ] != 0 ) {
|
||||
if ( _grid[ x ][ y ][ z ] > 0 ) { // effects (negative) are not part of the minable surface
|
||||
return z;
|
||||
}
|
||||
}
|
||||
@@ -63,23 +63,41 @@ int World::mine ( int x, int y ) {
|
||||
return value;
|
||||
}
|
||||
|
||||
void World::display () const {
|
||||
for ( int y = 0; y < _sizeY; ++y ) {
|
||||
void World::display ( int p1x, int p1y, int p2x, int p2y ) const {
|
||||
std::cout << "=== SURFACE VIEW ===\n";
|
||||
|
||||
// legend – only shown when at least one robot position is valid
|
||||
if ( p1x >= 0 || p2x >= 0 )
|
||||
std::cout << " P = Player C = Computer ! = both\n";
|
||||
|
||||
// column-index header
|
||||
std::cout << " ";
|
||||
for ( int y = 0; y < _sizeY; ++y )
|
||||
std::cout << std::setw ( 4 ) << y;
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
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 ) {
|
||||
int surface = getSurfaceLevel ( x, y );
|
||||
std::cout << std::setw ( 4 ) << ( surface >= 0 ? _grid[ x ][ y ][ surface ] : 0 );
|
||||
int val = ( surface >= 0 ? _grid[ x ][ y ][ surface ] : 0 );
|
||||
bool hasP1 = ( x == p1x && y == p1y );
|
||||
bool hasP2 = ( x == p2x && y == p2y );
|
||||
|
||||
if ( hasP1 && hasP2 )
|
||||
std::cout << std::setw ( 3 ) << val << '!';
|
||||
else if ( hasP1 )
|
||||
std::cout << std::setw ( 3 ) << val << 'P';
|
||||
else if ( hasP2 )
|
||||
std::cout << std::setw ( 3 ) << val << 'C';
|
||||
else
|
||||
std::cout << std::setw ( 4 ) << val;
|
||||
}
|
||||
std::cout << "\n";
|
||||
std::cout << "=================================\n";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// int World::checkEffects ( int x, int y ) const
|
||||
void World::rearrange () {
|
||||
std::mt19937 rng ( std::random_device {}() );
|
||||
std::uniform_int_distribution<int> opDist ( 0, 2 );
|
||||
@@ -96,9 +114,15 @@ void World::rearrange() {
|
||||
}
|
||||
|
||||
switch ( opDist ( rng ) ) {
|
||||
case 0: std::shuffle(values.begin(), values.end(), rng); break;
|
||||
case 1: std::sort(values.begin(), values.end()); break;
|
||||
case 2: std::sort(values.begin(), values.end(), std::greater<int>()); break;
|
||||
case 0:
|
||||
std::shuffle ( values.begin(), values.end(), rng );
|
||||
break;
|
||||
case 1:
|
||||
std::sort ( values.begin(), values.end() );
|
||||
break;
|
||||
case 2:
|
||||
std::sort ( values.begin(), values.end(), std::greater<int>() );
|
||||
break;
|
||||
}
|
||||
|
||||
for ( std::size_t i = 0; i < positions.size(); ++i )
|
||||
@@ -110,8 +134,9 @@ void World::rearrange() {
|
||||
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;
|
||||
if ( it == col.end() )
|
||||
return 0;
|
||||
int effect = *it;
|
||||
*it = 0; // consume: the effect fires exactly once
|
||||
*it = 0; // consume
|
||||
return effect;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user