diff --git a/lib/Clyde.cpp b/lib/Clyde.cpp index 877b494..e851fd7 100644 --- a/lib/Clyde.cpp +++ b/lib/Clyde.cpp @@ -28,10 +28,10 @@ Position Clyde::target(const GameState & gameState) const { if (state == State::Scatter) return targetPosition; - const auto pacManPosition = gameState.pacMan.positionInGrid(); - auto distanceFomPacMan = std::hypot(pos.x - double(pacManPosition.x), pos.y - double(pacManPosition.y)); + const auto pacManPosition = gameState.pacMan.position(); + auto distanceFomPacMan = positionDistance(pos, pacManPosition); if (distanceFomPacMan > 8) - targetPosition = gridPositionToPosition(pacManPosition); + targetPosition = pacManPosition; return targetPosition; } @@ -44,4 +44,4 @@ Position Clyde::scatterTarget() const { return { 0, 30 }; } -} +} // namespace pacman diff --git a/lib/GameState.cpp b/lib/GameState.cpp index 157dd16..6269e19 100644 --- a/lib/GameState.cpp +++ b/lib/GameState.cpp @@ -7,7 +7,7 @@ constexpr int NORMAL_PELLET_POINTS = 10; constexpr int POWER_PELLET_POINTS = 50; void GameState::step(std::chrono::milliseconds delta) { - pacManAI.update(pacMan, pellets, superPellets, blinky, clyde, inky, pinky); + pacManAI.update(pacMan, pellets, superPellets); pacMan.update(delta, pacManAI.suggestedDirection()); if (isPacManDying()) { diff --git a/lib/Ghost.cpp b/lib/Ghost.cpp index 1388dda..8ce3de5 100644 --- a/lib/Ghost.cpp +++ b/lib/Ghost.cpp @@ -189,7 +189,7 @@ void Ghost::updateDirection(const GameState & gameState) { if (!can_walk) continue; - move.distance_to_target = std::hypot(move.position.x - target_position.x, move.position.y - target_position.y); + move.distance_to_target = positionDistance(move.position, target_position); } const auto optimal_move = std::min_element(possible_moves.begin(), possible_moves.end(), [](const auto & a, const auto & b) { diff --git a/lib/Inky.cpp b/lib/Inky.cpp index c50d456..1f9baf2 100644 --- a/lib/Inky.cpp +++ b/lib/Inky.cpp @@ -49,7 +49,7 @@ Position Inky::target(const GameState & gameState) const { // Then it calculates the distance between Blinky and this position const auto & blinkyPosition = gameState.blinky.positionInGrid(); - double distanceBetweenBlinkyAndTarget = std::hypot(blinkyPosition.x - targetPosition.x, blinkyPosition.y - targetPosition.y); + const double distanceBetweenBlinkyAndTarget = positionDistance(blinkyPosition, targetPosition); // And selects a point on the line crossing blinky and this position that is at twice that distance // away from blinky @@ -67,4 +67,4 @@ Position Inky::scatterTarget() const { return { 27, 30 }; } -} +} // namespace pacman diff --git a/lib/PacManAI.cpp b/lib/PacManAI.cpp index d55399f..0bce0dd 100644 --- a/lib/PacManAI.cpp +++ b/lib/PacManAI.cpp @@ -5,10 +5,10 @@ namespace pacman { Direction PacManAI::suggestedDirection() const { - return direction; + return dir; } -void PacManAI::update(const PacMan pacMan, const Pellets & pellets, const SuperPellets & superPellets, const Blinky & blinky, const Clyde & clyde, const Inky & inky, const Pinky & pinky) { +void PacManAI::update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets) { const GridPosition pacManGridPos = pacMan.positionInGrid(); const GridPosition currentGridPos = positionToGridPosition(pos); if (currentGridPos == pacManGridPos) { @@ -21,78 +21,60 @@ void PacManAI::update(const PacMan pacMan, const Pellets & pellets, const SuperP return; } - struct GridMove { + auto pelletPositions = pellets.currentPositions(); + if (pelletPositions.empty()) { + return; + } + auto pelletSort = [&pacManGridPos](GridPosition pelletA, GridPosition pelletB) { + double distanceA = positionDistance(pacManGridPos, pelletA); + double distanceB = positionDistance(pacManGridPos, pelletB); + return distanceA < distanceB; + }; + std::sort(pelletPositions.begin(), pelletPositions.end(), pelletSort); + + const auto & target = pelletPositions[0]; + const Position targetPos{ double(target.x), double(target.y) }; + + struct Move { Direction direction = Direction::NONE; - GridPosition position; - bool hasGhost = false; - size_t pelletCount = 0; + Position position; + double distanceToTarget = std::numeric_limits::infinity(); }; - std::array possibleMoves = { - GridMove{ Direction::UP, { pacManGridPos.x, pacManGridPos.y - 1 } }, - GridMove{ Direction::LEFT, { pacManGridPos.x - 1, pacManGridPos.y } }, - GridMove{ Direction::DOWN, { pacManGridPos.x, pacManGridPos.y + 1 } }, - GridMove{ Direction::RIGHT, { pacManGridPos.x + 1, pacManGridPos.y } } - }; - - auto isGhostDangerous = [](const auto ghost, GridPosition pos) { - return !ghost.isFrightened() && ghost.positionInGrid() == pos; + const Position currentPosition = { double(pacManGridPos.x), double(pacManGridPos.y) }; + const auto [x, y] = currentPosition; + std::array possibleMoves = { + Move{ Direction::UP, { x, y - 1 } }, + Move{ Direction::LEFT, { x - 1, y } }, + Move{ Direction::DOWN, { x, y + 1 } }, + Move{ Direction::RIGHT, { x + 1, y } } }; for (auto & move : possibleMoves) { - if (!isWalkableForPacMan(move.position)) { - move.direction = Direction::NONE; + const bool invalidPosition = (move.position.x < 0 || move.position.y < 0); + if (invalidPosition) { continue; } - GridPosition posTest = move.position; - while (isWalkableForPacMan(posTest)) { - if (pellets.isPellet(posTest) || superPellets.isPellet(posTest)) { - move.pelletCount++; - } - - if (isGhostDangerous(blinky, posTest) || isGhostDangerous(clyde, posTest) || isGhostDangerous(inky, posTest) || isGhostDangerous(pinky, posTest)) { - move.hasGhost = true; - } - - const GridPosition oldPosTest = posTest; - posTest = iterateGridPosition(posTest, move.direction); - if (posTest == oldPosTest) { - break; - } - } - } - - auto sortCondition = [&pacMan](const GridMove & moveA, const GridMove & moveB) { - if (moveA.pelletCount == moveB.pelletCount) { - return (moveA.direction == pacMan.currentDirection() ? true : false); - } - return moveA.pelletCount > moveB.pelletCount; - }; - - std::sort(possibleMoves.begin(), possibleMoves.end(), sortCondition); - - for (const auto & move : possibleMoves) { - fmt::print("{}, {}, {}\n", move.pelletCount, move.hasGhost, move.direction); - } - fmt::print("\n"); - - for (const auto & move : possibleMoves) { - if (move.direction != Direction::NONE && !move.hasGhost) { - direction = move.direction; - break; - } - } - - // Then out of all of the movable directions, pick one and exit the loop - /* for (const auto & move : possibleMoves) { - if (move.direction == Direction::NONE || move.hasGhost) { + const bool isOpposite = (move.direction == oppositeDirection(dir)); + if (isOpposite) { continue; } - if (pellets.isPellet(move.position) || superPellets.isPellet(move.position)) { - direction = move.direction; - break; + + const GridPosition gridMove = { size_t(move.position.x), size_t(move.position.y) }; + const bool canWalk = isWalkableForPacMan(gridMove); + if (!canWalk) { + continue; } - }*/ + + move.distanceToTarget = positionDistance(move.position, targetPos); + } + + const auto optimalMove = std::min_element(possibleMoves.begin(), possibleMoves.end(), [](const auto & a, const auto & b) { + return a.distanceToTarget < b.distanceToTarget; + }); + + const auto & move = *optimalMove; + dir = move.direction; } } // namespace pacman \ No newline at end of file diff --git a/lib/include/PacManAI.hpp b/lib/include/PacManAI.hpp index c6b96ea..e81043c 100644 --- a/lib/include/PacManAI.hpp +++ b/lib/include/PacManAI.hpp @@ -14,12 +14,12 @@ namespace pacman { class PacManAI { public: - void update(const PacMan pacMan, const Pellets & pellets, const SuperPellets & superPellets, const Blinky & blinky, const Clyde & clyde, const Inky & inky, const Pinky & pinky); + void update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets); Direction suggestedDirection() const; private: Position pos; - Direction direction = Direction::RIGHT; + Direction dir = Direction::RIGHT; }; } // namespace pacman diff --git a/lib/include/Position.hpp b/lib/include/Position.hpp index a7eeb2f..3766dae 100644 --- a/lib/include/Position.hpp +++ b/lib/include/Position.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include namespace pacman { @@ -14,7 +14,9 @@ struct Position { struct GridPosition { size_t x; size_t y; - constexpr GridPosition(size_t x, size_t y) : x(x), y(y) {} + constexpr GridPosition(size_t x, size_t y) + : x(x), + y(y) {} }; inline GridPosition positionToGridPosition(Position pos) { @@ -34,6 +36,13 @@ constexpr bool operator!=(const GridPosition & a, const GridPosition & b) { return !(a == b); } +template +inline double positionDistance(const T & a, const T & b) { + const double first = double(a.x) - double(b.x); + const double second = double(a.y) - double(b.y); + return std::sqrt((first * first) + (second * second)); +} + inline bool operator==(const Position & a, const Position & b) { // This is ok as a test unless x and y become very large. constexpr double epsilon = std::numeric_limits::epsilon();