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)
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

View File

@ -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()) {

View File

@ -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) {

View File

@ -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

View File

@ -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<double>::infinity();
};
std::array<GridMove, 4> 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<Move, 4> 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

View File

@ -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

View File

@ -1,7 +1,7 @@
#pragma once
#include <cmath>
#include <cassert>
#include <cmath>
#include <limits>
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<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) {
// This is ok as a test unless x and y become very large.
constexpr double epsilon = std::numeric_limits<double>::epsilon();