Different AI implementation. Now searches for nearest pellet. No ghost checks yet.

This commit is contained in:
Ólafur Waage 2021-09-20 15:17:07 +02:00
parent 9031609348
commit db0756ca21
7 changed files with 66 additions and 75 deletions

View File

@ -28,10 +28,10 @@ Position Clyde::target(const GameState & gameState) const {
if (state == State::Scatter) if (state == State::Scatter)
return targetPosition; return targetPosition;
const auto pacManPosition = gameState.pacMan.positionInGrid(); const auto pacManPosition = gameState.pacMan.position();
auto distanceFomPacMan = std::hypot(pos.x - double(pacManPosition.x), pos.y - double(pacManPosition.y)); auto distanceFomPacMan = positionDistance(pos, pacManPosition);
if (distanceFomPacMan > 8) if (distanceFomPacMan > 8)
targetPosition = gridPositionToPosition(pacManPosition); targetPosition = pacManPosition;
return targetPosition; return targetPosition;
} }
@ -44,4 +44,4 @@ Position Clyde::scatterTarget() const {
return { 0, 30 }; return { 0, 30 };
} }
} } // namespace pacman

View File

@ -7,7 +7,7 @@ constexpr int NORMAL_PELLET_POINTS = 10;
constexpr int POWER_PELLET_POINTS = 50; constexpr int POWER_PELLET_POINTS = 50;
void GameState::step(std::chrono::milliseconds delta) { 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()); pacMan.update(delta, pacManAI.suggestedDirection());
if (isPacManDying()) { if (isPacManDying()) {

View File

@ -189,7 +189,7 @@ void Ghost::updateDirection(const GameState & gameState) {
if (!can_walk) if (!can_walk)
continue; 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) { const auto optimal_move = std::min_element(possible_moves.begin(), possible_moves.end(), [](const auto & a, const auto & b) {

View File

@ -49,7 +49,7 @@ Position Inky::target(const GameState & gameState) const {
// Then it calculates the distance between Blinky and this position // Then it calculates the distance between Blinky and this position
const auto & blinkyPosition = gameState.blinky.positionInGrid(); 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 // And selects a point on the line crossing blinky and this position that is at twice that distance
// away from blinky // away from blinky
@ -67,4 +67,4 @@ Position Inky::scatterTarget() const {
return { 27, 30 }; return { 27, 30 };
} }
} } // namespace pacman

View File

@ -5,10 +5,10 @@
namespace pacman { namespace pacman {
Direction PacManAI::suggestedDirection() const { 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 pacManGridPos = pacMan.positionInGrid();
const GridPosition currentGridPos = positionToGridPosition(pos); const GridPosition currentGridPos = positionToGridPosition(pos);
if (currentGridPos == pacManGridPos) { if (currentGridPos == pacManGridPos) {
@ -21,78 +21,60 @@ void PacManAI::update(const PacMan pacMan, const Pellets & pellets, const SuperP
return; 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; Direction direction = Direction::NONE;
GridPosition position; Position position;
bool hasGhost = false; double distanceToTarget = std::numeric_limits<double>::infinity();
size_t pelletCount = 0;
}; };
std::array<GridMove, 4> possibleMoves = { const Position currentPosition = { double(pacManGridPos.x), double(pacManGridPos.y) };
GridMove{ Direction::UP, { pacManGridPos.x, pacManGridPos.y - 1 } }, const auto [x, y] = currentPosition;
GridMove{ Direction::LEFT, { pacManGridPos.x - 1, pacManGridPos.y } }, std::array<Move, 4> possibleMoves = {
GridMove{ Direction::DOWN, { pacManGridPos.x, pacManGridPos.y + 1 } }, Move{ Direction::UP, { x, y - 1 } },
GridMove{ Direction::RIGHT, { pacManGridPos.x + 1, pacManGridPos.y } } Move{ Direction::LEFT, { x - 1, y } },
}; Move{ Direction::DOWN, { x, y + 1 } },
Move{ Direction::RIGHT, { x + 1, y } }
auto isGhostDangerous = [](const auto ghost, GridPosition pos) {
return !ghost.isFrightened() && ghost.positionInGrid() == pos;
}; };
for (auto & move : possibleMoves) { for (auto & move : possibleMoves) {
if (!isWalkableForPacMan(move.position)) { const bool invalidPosition = (move.position.x < 0 || move.position.y < 0);
move.direction = Direction::NONE; if (invalidPosition) {
continue; continue;
} }
GridPosition posTest = move.position; const bool isOpposite = (move.direction == oppositeDirection(dir));
while (isWalkableForPacMan(posTest)) { if (isOpposite) {
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) {
continue; continue;
} }
if (pellets.isPellet(move.position) || superPellets.isPellet(move.position)) {
direction = move.direction; const GridPosition gridMove = { size_t(move.position.x), size_t(move.position.y) };
break; 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 } // namespace pacman

View File

@ -14,12 +14,12 @@ namespace pacman {
class PacManAI { class PacManAI {
public: 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; Direction suggestedDirection() const;
private: private:
Position pos; Position pos;
Direction direction = Direction::RIGHT; Direction dir = Direction::RIGHT;
}; };
} // namespace pacman } // namespace pacman

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <cmath>
#include <cassert> #include <cassert>
#include <cmath>
#include <limits> #include <limits>
namespace pacman { namespace pacman {
@ -14,7 +14,9 @@ struct Position {
struct GridPosition { struct GridPosition {
size_t x; size_t x;
size_t y; 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) { inline GridPosition positionToGridPosition(Position pos) {
@ -34,6 +36,13 @@ constexpr bool operator!=(const GridPosition & a, const GridPosition & b) {
return !(a == b); return !(a == b);
} }
template<typename T>
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) { inline bool operator==(const Position & a, const Position & b) {
// This is ok as a test unless x and y become very large. // This is ok as a test unless x and y become very large.
constexpr double epsilon = std::numeric_limits<double>::epsilon(); constexpr double epsilon = std::numeric_limits<double>::epsilon();