Different AI implementation. Now searches for nearest pellet. No ghost checks yet.
This commit is contained in:
parent
9031609348
commit
db0756ca21
7 changed files with 66 additions and 75 deletions
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
108
lib/PacManAI.cpp
108
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<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
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue