From f45f73cb2703550f45fed465918d672402ded4f7 Mon Sep 17 00:00:00 2001 From: Florian Egger Date: Mon, 20 Apr 2026 12:06:33 +0200 Subject: [PATCH] Refactor code style, comments, and robot implementations Move UML PDF to doc/ and update .gitignore. Normalize includes, whitespace and function signatures across headers/sources and add concise class/function documentation. Fix RandomBot so the mine limit is computed once and simplify DigDeepBot/SortBot mining logic. Tidy World/Game initialization, error handling, and minor const/var clarifications for readability and correctness --- .gitignore | 3 +- uml_diagram.pdf => doc/uml_diagram.pdf | Bin include/BaseRobot.h | 25 +-- include/DigDeepBot.h | 11 +- include/Game.h | 39 +++-- include/RandomBot.h | 13 +- include/Robot.h | 2 +- include/SortBot.h | 3 +- include/World.h | 34 +++- main.cpp | 7 +- src/BaseRobot.cpp | 28 +++- src/DigDeepBot.cpp | 15 +- src/Game.cpp | 222 +++++++++++++++---------- src/RandomBot.cpp | 23 ++- src/SortBot.cpp | 49 ++++-- src/World.cpp | 96 +++++++---- 16 files changed, 353 insertions(+), 217 deletions(-) rename uml_diagram.pdf => doc/uml_diagram.pdf (100%) diff --git a/.gitignore b/.gitignore index 7e7b227..33fdbed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ uml_diagram.tex .zed/ .idea/ -cmake-build-debug/ +.ignore/ +cmake-build-debug diff --git a/uml_diagram.pdf b/doc/uml_diagram.pdf similarity index 100% rename from uml_diagram.pdf rename to doc/uml_diagram.pdf diff --git a/include/BaseRobot.h b/include/BaseRobot.h index 918714c..57ec24b 100644 --- a/include/BaseRobot.h +++ b/include/BaseRobot.h @@ -4,25 +4,28 @@ #include +/* Base robot implementation shared by all concrete robot types. */ class BaseRobot : public Robot { - /* A robot base class that implements functions common to all robots. */ public: - BaseRobot ( std::string name, int startX, int startY ); + BaseRobot(std::string name, int startX, int startY); - void move ( int direction, const World &world ) override; - int decideNextMove ( const World &world ) const override; + void move(int direction, const World& world) override; + int decideNextMove(const World& world) const override; - void setPosition ( int x, int y ) override { + void setPosition(int x, int y) override { x_ = x; y_ = y; } - int getScore () const override { return score_; } - void addScore ( int points ) override { score_ += points; } - int getX () const override { return x_; } - int getY () const override { return y_; } - std::string getName () const override { return name_; } + + int getScore() const override { return score_; } + void addScore(int points) override { score_ += points; } + int getX() const override { return x_; } + int getY() const override { return y_; } + std::string getName() const override { return name_; } protected: - int x_, y_, score_; + int x_; + int y_; + int score_; std::string name_; }; diff --git a/include/DigDeepBot.h b/include/DigDeepBot.h index 6db7097..d22048c 100644 --- a/include/DigDeepBot.h +++ b/include/DigDeepBot.h @@ -1,11 +1,12 @@ #pragma once -#include "../include/BaseRobot.h" -#include +#include "BaseRobot.h" + class World; +/* Robot that mines up to three positive blocks per turn from its current column. */ class DigDeepBot : public BaseRobot { - public: - DigDeepBot ( const int startX, const int startY ); - int mine ( World& world ) override; + public: + DigDeepBot ( int startX, int startY ); + int mine ( World &world ) override; }; diff --git a/include/Game.h b/include/Game.h index bffbcd4..eba4fd4 100644 --- a/include/Game.h +++ b/include/Game.h @@ -2,30 +2,29 @@ #include -#include "World.h" #include "Robot.h" +#include "World.h" - +/* Coordinates game setup, turn order, world updates, and final scoring output. */ class Game { - public: - Game (); - void run (); + public: + Game (); + void run (); - private: - World world_; - std::unique_ptr< Robot > player_; - std::unique_ptr < Robot > computer_; - bool autoMode_ = false; - int lastThreshold_ = 0; + private: + World world_; + std::unique_ptr player_; + std::unique_ptr computer_; + bool autoMode_ = false; + int lastThreshold_ = 0; - void setup (); - void play ( Robot& robot, bool isPlayer ); - void checkRearrange ( Robot& robot ); - void applyEffect ( Robot& robot, int effect ); - bool isGameOver() const; - void printScores() const; - void printResult() const; - - std::unique_ptr < Robot > createRobot ( int choice, int x, int y ) const; + void setup (); + void play ( Robot &robot, bool isPlayer ); + void checkRearrange ( Robot &robot ); + void applyEffect ( Robot &robot, int effect ); + bool isGameOver () const; + void printScores () const; + void printResult () const; + std::unique_ptr createRobot ( int choice, int x, int y ) const; }; diff --git a/include/RandomBot.h b/include/RandomBot.h index db4ff37..30f15c6 100644 --- a/include/RandomBot.h +++ b/include/RandomBot.h @@ -4,11 +4,12 @@ class World; +/* Robot that mines a random number of positive blocks from its current column. */ class RandomBot : public BaseRobot { -/* A robot that digs at a random depth */ - public: - RandomBot ( const int startX, const int startY ); - int mine ( World& world ) override; - private: - int randomNumber(); + public: + RandomBot ( int startX, int startY ); + int mine ( World &world ) override; + + private: + int randomNumber (); }; diff --git a/include/Robot.h b/include/Robot.h index 280600c..d1ac61b 100644 --- a/include/Robot.h +++ b/include/Robot.h @@ -6,8 +6,8 @@ class World; +/* Virtual interface implemented by all robot types. */ class Robot { - /* A virtual interface for a robot */ public: virtual ~Robot () = default; Robot ( const Robot & ) = default; diff --git a/include/SortBot.h b/include/SortBot.h index c4c7a89..ef181ae 100644 --- a/include/SortBot.h +++ b/include/SortBot.h @@ -2,9 +2,10 @@ #include "../include/BaseRobot.h" +/* Robot that reorders the current column so the highest value reaches the surface, + then mines that top block. */ class SortBot : public BaseRobot { public: SortBot ( int startX, int startY ); int mine ( World &world ) override; }; - diff --git a/include/World.h b/include/World.h index b9b94df..4df0a22 100644 --- a/include/World.h +++ b/include/World.h @@ -2,33 +2,55 @@ #include +/* A 3-D grid world where robots mine positive-value blocks and trigger effects. */ class World { - /* A 3D grid world where robots can mine blocks. - Blocks have a value, some have special effects. */ - 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 ); - void display ( int p1x = -1, int p1y = -1, int p2x = -1, int p2y = -1 ) const; // player positions initialized to -1 -> outside of surface grid + /* 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; } - // stage 2 + /* 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) */ void rearrange (); - // stage 3 + /* 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 ); private: int _sizeX, _sizeY, _sizeZ; std::vector>> _grid; + /* Initialize the grid with random positive values and occasional effects. + Complexity: O(sizeX * sizeY * sizeZ) */ void init (); }; diff --git a/main.cpp b/main.cpp index 68d6410..81e761b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,13 @@ -#include #include "include/Game.h" +#include int main () { try { Game game; - game.run (); - } catch ( const std::exception& e ) { + game.run(); + } catch ( const std::exception &e ) { std::cerr << "Fatal Error: " << e.what() << "\n"; return 1; } return 0; - } diff --git a/src/BaseRobot.cpp b/src/BaseRobot.cpp index 1c599f2..6e97cd6 100644 --- a/src/BaseRobot.cpp +++ b/src/BaseRobot.cpp @@ -7,8 +7,9 @@ BaseRobot::BaseRobot ( std::string name, int startX, int startY ) : x_ ( startX ), y_ ( startY ), score_ ( 0 ), name_ ( std::move ( name ) ) {} +/* Move one step in the requested direction while clamping to world bounds. + Complexity: O(1) */ void BaseRobot::move ( int direction, const World &world ) { - switch ( direction ) { case 1: x_ = std::min ( x_ + 1, world.getSizeX() - 1 ); @@ -27,6 +28,10 @@ void BaseRobot::move ( int direction, const World &world ) { } } +/* Choose the next move by maximizing immediate surface value among + current/adjacent cells; if none are mineable nearby, step toward the + nearest non-empty column in the grid. + Complexity: O(1) for local evaluation, worst O(sizeX * sizeY) fallback scan */ int BaseRobot::decideNextMove ( const World &world ) const { struct Option { int dir, val; @@ -53,21 +58,28 @@ int BaseRobot::decideNextMove ( const World &world ) const { 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 ( 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; + + 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; } diff --git a/src/DigDeepBot.cpp b/src/DigDeepBot.cpp index 42571d0..bc06432 100644 --- a/src/DigDeepBot.cpp +++ b/src/DigDeepBot.cpp @@ -1,21 +1,24 @@ #include "../include/DigDeepBot.h" #include "../include/World.h" -DigDeepBot::DigDeepBot ( const int startX, const int startY ) : -BaseRobot( "DigDeepBot", startX, startY) {} +DigDeepBot::DigDeepBot ( const int startX, const int startY ) : BaseRobot ( "DigDeepBot", startX, startY ) {} -int DigDeepBot::mine ( World& world ) { +int DigDeepBot::mine ( World &world ) { + /* Mine up to three positive blocks from the current column, scanning + from top depth to bottom depth. + Complexity: O(z) where z = column depth */ int total = 0; int grabbed = 0; for ( int z = world.getSizeZ() - 1; z >= 0 && grabbed < 3; --z ) { - int v = world.getValue ( x_, y_, z ); - if ( v > 0 ) { - total += v; + const int value = world.getValue ( x_, y_, z ); + if ( value > 0 ) { + total += value; world.setValue ( x_, y_, z, 0 ); ++grabbed; } } + score_ += total; return total; } diff --git a/src/Game.cpp b/src/Game.cpp index a6be6d0..ef60518 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,14 +1,19 @@ #include "../include/Game.h" -#include "../include/SortBot.h" #include "../include/DigDeepBot.h" #include "../include/RandomBot.h" +#include "../include/SortBot.h" + #include #include -int validateInput ( const std::string& prompt, int min, int max ); +int validateInput ( const std::string &prompt, int min, int max ); -Game::Game() : world_ ( 5, 5, 10 ) {} +/* Initialize game state with default world dimensions. + Complexity: O(1) */ +Game::Game () : world_ ( 5, 5, 10 ) {} +/* Run the full game loop: setup, alternating turns, and final result output. + Complexity: O(r * C) where r = rounds until depletion, C = per-round world/robot work */ void Game::run () { std::cout << "=== DEEP MINER ===\n\n"; setup(); @@ -16,145 +21,182 @@ void Game::run () { int round = 1; while ( !isGameOver() ) { - std::cout << "\n--- Round " << round ++ << " ---\n"; + std::cout << "\n--- Round " << round++ << " ---\n"; - // human player - std::cout << "[Player: " << player_->getName () << "]\n"; + std::cout << "[Player: " << player_->getName() << "]\n"; play ( *player_, true ); - // computer std::cout << "[Computer: " << computer_->getName() << "]\n"; play ( *computer_, false ); world_.display ( player_->getX(), player_->getY(), computer_->getX(), computer_->getY() ); printScores(); - } + } + printResult(); } +/* Collect mode/robot selections and create both participants. + Complexity: O(1) plus input wait time */ void Game::setup () { - autoMode_ = ( validateInput("Mode - 1: Player vs. Computer 2: Computer vs. Computer: ", 1, 2) == 2 ); + autoMode_ = ( validateInput ( "Mode - 1: Player vs. Computer 2: Computer vs. Computer: ", 1, 2 ) == 2 ); - std::cout << "\nRobots: \n" - << " 1 = SortBot (sorts column and mines highest value)\n" - << " 2 = DigDeepBot (grabs the top 3 values)]\n" - << " 3 = RandomBot (grabs a random Number of values)\n"; + std::cout << "\nRobots: \n" + << " 1 = SortBot (sorts column and mines highest value)\n" + << " 2 = DigDeepBot (grabs the top 3 values)\n" + << " 3 = RandomBot (grabs a random number of values)\n"; - player_ = createRobot ( validateInput( - "Your robot: ", 1, 3), 0, 0 ); - computer_ = createRobot ( validateInput( - "Computer robot: ", 1, 3 ), world_.getSizeX() - 1, world_.getSizeY() - 1 ); + player_ = createRobot ( validateInput ( "Your robot: ", 1, 3 ), 0, 0 ); + computer_ = + createRobot ( validateInput ( "Computer robot: ", 1, 3 ), world_.getSizeX() - 1, world_.getSizeY() - 1 ); } -std::unique_ptr < Robot > Game::createRobot ( int choice, int x, int y ) const { +/* Instantiate a robot implementation from a menu choice. + Complexity: O(1) */ +std::unique_ptr Game::createRobot ( int choice, int x, int y ) const { switch ( choice ) { - case 1: - return std::make_unique < SortBot > ( x, y ); - case 2 : - return std::make_unique < DigDeepBot > ( x, y ); - case 3 : - return std::make_unique < RandomBot > ( x, y ); - default: - return std::make_unique < SortBot > ( x, y ); + case 1: + return std::make_unique ( x, y ); + case 2: + return std::make_unique ( x, y ); + case 3: + return std::make_unique ( x, y ); + default: + return std::make_unique ( x, y ); } } -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_ ); +/* Execute one turn: movement, tile effects, optional mining, and rearrange checks. + Complexity: O(W) in the worst case due to effect handling that scans the world */ +void Game::play ( Robot &robot, bool isPlayer ) { + const int direction = ( !isPlayer || autoMode_ ) + ? robot.decideNextMove ( world_ ) + : validateInput ( "Direction (0=stay 1=+x 2=-x 3=+y 4=-y): ", 0, 4 ); - int effect = world_.checkEffects( robot.getX(), robot.getY() ); + robot.move ( direction, world_ ); + + const int effect = world_.checkEffects ( robot.getX(), robot.getY() ); if ( effect < 0 ) { - applyEffect( robot, effect ); + applyEffect ( robot, effect ); } - if ( effect != -1 ) { // -1 = blocked; all other effects still allow mining - int mined = robot.mine( world_ ); + + if ( effect != -1 ) { // -1 blocks mining for this turn + const int mined = robot.mine ( world_ ); std::cout << " " << robot.getName() << " mined " << mined << " points.\n"; } - checkRearrange( robot ); + + checkRearrange ( robot ); } -void Game::checkRearrange(Robot& robot) { - int score = robot.getScore(); - int threshold = (score / 50) * 50; - if (threshold > 0 && threshold > lastThreshold_) { +/* Rearrange the world each time a robot crosses a new 50-point threshold. + Complexity: O(W) where W = number of cells in the world */ +void Game::checkRearrange ( Robot &robot ) { + const int score = robot.getScore(); + const int threshold = ( score / 50 ) * 50; + + if ( threshold > 0 && threshold > lastThreshold_ ) { lastThreshold_ = threshold; - std::cout << "*** " << robot.getName() << " reached " << threshold - << " points! World rearranged! ***\n"; + 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; +/* Apply special tile effects: + -1 block turn, -2 teleport to lowest visible surface value, -3 lose half score. + Complexity: O(W) worst case for teleport target search */ +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::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; } + case -2: { + int minVal = std::numeric_limits::max(); + int bx = 0; + int by = 0; + + for ( int x = 0; x < world_.getSizeX(); ++x ) { + for ( int y = 0; y < world_.getSizeY(); ++y ) { + const int surf = world_.getSurfaceLevel ( x, y ); + if ( surf < 0 ) + continue; + + const 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; + std::cout << " [EFFECT -2] " << robot.getName() << " teleported to (" << bx << "," << by << ")!\n"; + robot.setPosition ( bx, by ); + break; + } - default: - std::cout << " [EFFECT] Unknown effect " << effect << " ignored.\n"; + 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"; + break; } } +/* Return true when no minable surface block remains anywhere. + Complexity: O(X * Y * Z) via repeated surface checks */ bool Game::isGameOver () const { - for ( int x = 0; x < world_.getSizeX (); ++x ) { - for ( int y = 0; y < world_.getSizeY(); ++y ){ - if ( world_.getSurfaceLevel( x, y) >= 0 ) + for ( int x = 0; x < world_.getSizeX(); ++x ) { + for ( int y = 0; y < world_.getSizeY(); ++y ) { + if ( world_.getSurfaceLevel ( x, y ) >= 0 ) { return false; + } } } return true; } -void Game::printScores() const { - std::cout << "Score: " - << player_->getName () << " = " << player_->getScore() << " | " - << computer_->getName() << " = " << computer_->getScore() << "\n"; +/* Print current scores for both robots. + Complexity: O(1) */ +void Game::printScores () const { + std::cout << "Score: " << player_->getName() << " = " << player_->getScore() << " | " << computer_->getName() + << " = " << computer_->getScore() << "\n"; } -void Game::printResult() const { +/* Print final scores and winner once the world is depleted. + Complexity: O(1) */ +void Game::printResult () const { std::cout << "=== GAME OVER ===\n"; printScores(); - int scorePlayer = player_->getScore(), scoreComputer = computer_->getScore(); - if ( scorePlayer > scoreComputer ) - std::cout << player_->getName() << " WINS!\n"; - else if ( scoreComputer > scorePlayer ) - std::cout << computer_->getName() << " WINS!\n"; - else - std::cout << "DRAW!\n"; -} -int validateInput (const std::string& prompt, int min, int max) { - int val; - while (true) { - std::cout << prompt; - if (std::cin >> val && val >= min && val <= max) - return val; - std::cin.clear(); - std::cin.ignore(std::numeric_limits::max(), '\n'); - std::cout << " Invalid. Enter a number between " - << min << " and " << max << ": "; + const int scorePlayer = player_->getScore(); + const int scoreComputer = computer_->getScore(); + + if ( scorePlayer > scoreComputer ) { + std::cout << player_->getName() << " WINS!\n"; + } else if ( scoreComputer > scorePlayer ) { + std::cout << computer_->getName() << " WINS!\n"; + } else { + std::cout << "DRAW!\n"; + } +} + +/* Read and validate bounded integer input from stdin. + Complexity: O(1) per successful read attempt */ +int validateInput ( const std::string &prompt, int min, int max ) { + int val; + while ( true ) { + std::cout << prompt; + if ( std::cin >> val && val >= min && val <= max ) { + return val; + } + + std::cin.clear(); + std::cin.ignore ( std::numeric_limits::max(), '\n' ); + std::cout << " Invalid. Enter a number between " << min << " and " << max << ": "; } } diff --git a/src/RandomBot.cpp b/src/RandomBot.cpp index f5e6c1d..44432e5 100644 --- a/src/RandomBot.cpp +++ b/src/RandomBot.cpp @@ -3,28 +3,33 @@ #include -RandomBot::RandomBot ( const int startX, const int startY ): -BaseRobot ( "RandomBot", startX, startY ) {} +RandomBot::RandomBot ( const int startX, const int startY ) : BaseRobot ( "RandomBot", startX, startY ) {} -int RandomBot::mine ( World& world ) { +int RandomBot::mine ( World &world ) { + /* Mine up to a random number of positive blocks from the current column, + scanning from top to bottom and stopping once the random limit is reached. + Complexity: O(z) where z = world height */ int total = 0; int grabbed = 0; - const int limit = randomNumber(); // computed once; calling it every iteration re-rolls the limit + const int limit = randomNumber(); - for ( int z = world.getSizeZ() -1; z >= 0 && grabbed < limit; --z ) { - int value = world.getValue ( x_, y_, z ); + for ( int z = world.getSizeZ() - 1; z >= 0 && grabbed < limit; --z ) { + const int value = world.getValue ( x_, y_, z ); if ( value > 0 ) { total += value; world.setValue ( x_, y_, z, 0 ); ++grabbed; } } + score_ += total; return total; } int RandomBot::randomNumber () { - static std ::mt19937 rng ( std::random_device{}()); - std::uniform_int_distribution < int > distr ( 0, 9 ); + /* Return a uniformly distributed mining limit in [0, 9]. + Complexity: O(1) */ + static std::mt19937 rng ( std::random_device {}() ); + std::uniform_int_distribution distr ( 0, 9 ); return distr ( rng ); -}; +} diff --git a/src/SortBot.cpp b/src/SortBot.cpp index 98024ce..683bb4d 100644 --- a/src/SortBot.cpp +++ b/src/SortBot.cpp @@ -4,28 +4,43 @@ #include #include -SortBot::SortBot ( const int startX, const int startY ) : BaseRobot ( "SortBot", startX, startY ) {} +SortBot::SortBot ( int startX, int startY ) : BaseRobot ( "SortBot", startX, startY ) {} int SortBot::mine ( World &world ) { - // create ( value, z ) pairs for positive values ( exclude mined blocks and effects ) + /* Collect positive blocks in the current column, sort them so the + highest value becomes the surface block, then mine that block. + Complexity: O(z log z) where z = world depth at this column */ std::vector> values; - for ( int z = 0; z < world.getSizeZ(); ++z ) { - int v = world.getValue(x_, y_, z ); - if ( v > 0 ) values.emplace_back(v, z ); - } - if ( values.empty()) return 0; - // 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;} ); + values.reserve ( static_cast ( world.getSizeZ() ) ); -for ( auto& [x, z] : values ) { - world.setValue ( x_, y_, z, 0); -} - for ( std::size_t i = 0; i < values.size(); ++i ) { - world.setValue( x_, y_, static_cast(i), values[i].first ); + for ( int z = 0; z < world.getSizeZ(); ++z ) { + const int value = world.getValue ( x_, y_, z ); + if ( value > 0 ) { + values.emplace_back ( value, z ); + } } - int surface = world.getSurfaceLevel(x_, y_); - if ( surface < 0 ) return 0; - int mined = world.getValue ( x_, y_, surface ); + + if ( values.empty() ) { + return 0; + } + + std::sort ( values.begin(), values.end(), [] ( const auto &a, const auto &b ) { return a.first < b.first; } ); + + for ( const auto &[ value, z ] : values ) { + ( void )value; + world.setValue ( x_, y_, z, 0 ); + } + + for ( std::size_t i = 0; i < values.size(); ++i ) { + world.setValue ( x_, y_, static_cast ( i ), values[ i ].first ); + } + + const int surface = world.getSurfaceLevel ( x_, y_ ); + if ( surface < 0 ) { + return 0; + } + + const int mined = world.getValue ( x_, y_, surface ); world.setValue ( x_, y_, surface, 0 ); score_ += mined; return mined; diff --git a/src/World.cpp b/src/World.cpp index 975881d..d3312f1 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1,33 +1,39 @@ #include "../include/World.h" + +#include #include #include #include +#include -#include - +/* 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 ) ) ) { init(); } +/* Fill the world with random positive values and occasional effect cells. + Effects are encoded as negative values in [-3, -1]. + Complexity: O(x × y × z) */ void World::init () { - std::mt19937 rng ( std::random_device {}() ); // random number generator (RNG) - std::uniform_int_distribution distribution ( 1, 9 ); - std::uniform_int_distribution effectProbability ( 0, 9 ); // 10% chance for an effect - std::uniform_int_distribution effectType ( -3, -1 ); // 3 types of effects (negative flags) + 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 ); 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 ] = ( effectProbability ( rng ) == 1 ) - ? effectType ( rng ) - : distribution ( rng ); // 10% chance for effect + _grid[ x ][ y ][ z ] = ( effectChance ( rng ) == 1 ) ? effectType ( rng ) : valueDist ( rng ); } } } -} // init +} +/* Return the value at (x, y, z), throwing on invalid coordinates. + Complexity: O(1) */ 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" ); @@ -35,6 +41,8 @@ int World::getValue ( int x, int y, int z ) const { return _grid[ x ][ y ][ z ]; } +/* 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" ); @@ -42,61 +50,77 @@ void World::setValue ( int x, int y, int z, int value ) { _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 ) { // effects (negative) are not part of the minable surface + if ( _grid[ x ][ y ][ z ] > 0 ) { return z; } } - return -1; // no surface found + 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 ) { - int z = getSurfaceLevel ( x, y ); - if ( z == -1 ) + const int z = getSurfaceLevel ( x, y ); + if ( z == -1 ) { return 0; - int value = _grid[ x ][ y ][ z ]; + } + + const int value = _grid[ x ][ y ][ z ]; if ( value > 0 ) { - _grid[ x ][ y ][ z ] = 0; // mine the block + _grid[ x ][ y ][ z ] = 0; } return value; } +/* Print a 2D surface view with optional player/computer markers. + Marker legend is shown when at least one robot position is provided. + Complexity: O(x × y × z) due to per-cell surface lookup */ 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 ) + 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 ) + 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 << " "; for ( int y = 0; y < _sizeY; ++y ) { - int surface = getSurfaceLevel ( x, y ); - int val = ( surface >= 0 ? _grid[ x ][ y ][ surface ] : 0 ); - bool hasP1 = ( x == p1x && y == p1y ); - bool hasP2 = ( x == p2x && y == p2y ); + const int surface = getSurfaceLevel ( x, y ); + const int val = ( surface >= 0 ? _grid[ x ][ y ][ surface ] : 0 ); + const bool hasP1 = ( x == p1x && y == p1y ); + const bool hasP2 = ( x == p2x && y == p2y ); - if ( hasP1 && hasP2 ) + if ( hasP1 && hasP2 ) { std::cout << std::setw ( 3 ) << val << '!'; - else if ( hasP1 ) + } else if ( hasP1 ) { std::cout << std::setw ( 3 ) << val << 'P'; - else if ( hasP2 ) + } else if ( hasP2 ) { std::cout << std::setw ( 3 ) << val << 'C'; - else + } else { std::cout << std::setw ( 4 ) << val; + } } std::cout << "\n"; std::cout << "=========================\n"; } } +/* Rearrange each column by applying one random operation: + shuffle, ascending sort, or descending sort. + Zero cells remain untouched; only non-zero entries are rearranged in place. + Complexity: O(x × y × z log z) in the worst case */ void World::rearrange () { std::mt19937 rng ( std::random_device {}() ); std::uniform_int_distribution opDist ( 0, 2 ); @@ -105,6 +129,7 @@ void World::rearrange () { 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 ) { values.push_back ( _grid[ x ][ y ][ z ] ); @@ -124,18 +149,25 @@ void World::rearrange () { break; } - 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 ]; + } } } } +/* 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() ) + + if ( it == col.end() ) { return 0; - int effect = *it; - *it = 0; // consume + } + + const int effect = *it; + *it = 0; return effect; }