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)
|
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
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
108
lib/PacManAI.cpp
108
lib/PacManAI.cpp
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue