Work on the AI project. Not sure if this is good as an exercise.
This commit is contained in:
parent
0712db77bb
commit
9031609348
|
@ -79,6 +79,36 @@ bool isPortal(GridPosition point, Direction direction) {
|
||||||
(cellAtPosition(point) == Cell::right_portal && direction == Direction::RIGHT);
|
(cellAtPosition(point) == Cell::right_portal && direction == Direction::RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GridPosition iterateGridPosition(GridPosition point, Direction direction) {
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::LEFT: {
|
||||||
|
if (point.x != 0) {
|
||||||
|
point.x -= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Direction::RIGHT: {
|
||||||
|
point.x += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Direction::UP: {
|
||||||
|
if (point.y != 0) {
|
||||||
|
point.y -= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Direction::DOWN: {
|
||||||
|
point.y += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Direction::NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
GridPosition teleport(GridPosition point) {
|
GridPosition teleport(GridPosition point) {
|
||||||
size_t right = COLUMNS - 1;
|
size_t right = COLUMNS - 1;
|
||||||
size_t left = 0;
|
size_t left = 0;
|
||||||
|
|
|
@ -7,7 +7,8 @@ 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) {
|
||||||
pacMan.update(delta, inputState.direction());
|
pacManAI.update(pacMan, pellets, superPellets, blinky, clyde, inky, pinky);
|
||||||
|
pacMan.update(delta, pacManAI.suggestedDirection());
|
||||||
|
|
||||||
if (isPacManDying()) {
|
if (isPacManDying()) {
|
||||||
handleDeathAnimation(delta);
|
handleDeathAnimation(delta);
|
||||||
|
@ -18,7 +19,7 @@ void GameState::step(std::chrono::milliseconds delta) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
blinky.update(delta, *this); // waage: urgh, I wanna remove this
|
blinky.update(delta, *this); // waage: urgh, I wanna remove this
|
||||||
pinky.update(delta, *this); // ghosts know what they want, which is usually pacman's location
|
pinky.update(delta, *this); // ghosts know what they want, which is usually pacman's location
|
||||||
inky.update(delta, *this);
|
inky.update(delta, *this);
|
||||||
clyde.update(delta, *this);
|
clyde.update(delta, *this);
|
||||||
fruit.update(delta, *this);
|
fruit.update(delta, *this);
|
||||||
|
@ -80,14 +81,14 @@ void GameState::eatPellets() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameState::eatFruit() {
|
void GameState::eatFruit() {
|
||||||
const auto pos = pacMan.positionInGrid();
|
const auto pos = pacMan.positionInGrid();
|
||||||
const auto fruitpos = positionToGridPosition(fruit.position());
|
const auto fruitpos = positionToGridPosition(fruit.position());
|
||||||
|
|
||||||
// TODO: hitboxes based collision
|
// TODO: hitboxes based collision
|
||||||
if(fruit.isVisible() && pos == fruitpos) {
|
if (fruit.isVisible() && pos == fruitpos) {
|
||||||
score.points += fruit.eat();
|
score.points += fruit.eat();
|
||||||
score.eatenFruits++;
|
score.eatenFruits++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameState::killPacMan() {
|
void GameState::killPacMan() {
|
||||||
|
@ -100,4 +101,4 @@ bool GameState::isPacManDying() const {
|
||||||
return timeSinceDeath.count() != 0;
|
return timeSinceDeath.count() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace pacman
|
||||||
|
|
|
@ -1,4 +1,98 @@
|
||||||
#include "PacManAI.hpp"
|
#include "PacManAI.hpp"
|
||||||
|
|
||||||
|
#include "Board.hpp"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
namespace pacman {
|
namespace pacman {
|
||||||
}
|
Direction PacManAI::suggestedDirection() const {
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacManAI::update(const PacMan pacMan, const Pellets & pellets, const SuperPellets & superPellets, const Blinky & blinky, const Clyde & clyde, const Inky & inky, const Pinky & pinky) {
|
||||||
|
const GridPosition pacManGridPos = pacMan.positionInGrid();
|
||||||
|
const GridPosition currentGridPos = positionToGridPosition(pos);
|
||||||
|
if (currentGridPos == pacManGridPos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = gridPositionToPosition(pacManGridPos);
|
||||||
|
|
||||||
|
if (!isIntersection(pacManGridPos)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GridMove {
|
||||||
|
Direction direction = Direction::NONE;
|
||||||
|
GridPosition position;
|
||||||
|
bool hasGhost = false;
|
||||||
|
size_t pelletCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto & move : possibleMoves) {
|
||||||
|
if (!isWalkableForPacMan(move.position)) {
|
||||||
|
move.direction = Direction::NONE;
|
||||||
|
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) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pellets.isPellet(move.position) || superPellets.isPellet(move.position)) {
|
||||||
|
direction = move.direction;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
} // namespace pacman
|
|
@ -6,6 +6,14 @@ namespace pacman {
|
||||||
Pellets::Pellets()
|
Pellets::Pellets()
|
||||||
: positions(initialPelletPositions()) {}
|
: positions(initialPelletPositions()) {}
|
||||||
|
|
||||||
|
bool Pellets::isPellet(GridPosition p) const {
|
||||||
|
auto match = [&p](GridPosition pellet) {
|
||||||
|
return p.x == pellet.x && p.y == pellet.y;
|
||||||
|
};
|
||||||
|
|
||||||
|
return std::any_of(positions.begin(), positions.end(), match);
|
||||||
|
}
|
||||||
|
|
||||||
bool Pellets::eatPelletAtPosition(GridPosition p) {
|
bool Pellets::eatPelletAtPosition(GridPosition p) {
|
||||||
auto it = std::find(positions.begin(), positions.end(), p);
|
auto it = std::find(positions.begin(), positions.end(), p);
|
||||||
if (it == positions.end())
|
if (it == positions.end())
|
||||||
|
|
|
@ -6,6 +6,14 @@ namespace pacman {
|
||||||
SuperPellets::SuperPellets()
|
SuperPellets::SuperPellets()
|
||||||
: positions(initialSuperPelletPositions()) {}
|
: positions(initialSuperPelletPositions()) {}
|
||||||
|
|
||||||
|
bool SuperPellets::isPellet(GridPosition p) const {
|
||||||
|
auto match = [&p](GridPosition pellet) {
|
||||||
|
return p.x == pellet.x && p.y == pellet.y;
|
||||||
|
};
|
||||||
|
|
||||||
|
return std::any_of(positions.begin(), positions.end(), match);
|
||||||
|
}
|
||||||
|
|
||||||
bool SuperPellets::eatPelletAtPosition(GridPosition p) {
|
bool SuperPellets::eatPelletAtPosition(GridPosition p) {
|
||||||
auto it = std::find(positions.begin(), positions.end(), p);
|
auto it = std::find(positions.begin(), positions.end(), p);
|
||||||
if (it == positions.end())
|
if (it == positions.end())
|
||||||
|
|
|
@ -12,13 +12,21 @@ bool isWalkableForPacMan(GridPosition point);
|
||||||
bool isWalkableForGhost(GridPosition point, GridPosition origin, bool isEyes);
|
bool isWalkableForGhost(GridPosition point, GridPosition origin, bool isEyes);
|
||||||
bool isInPen(GridPosition point);
|
bool isInPen(GridPosition point);
|
||||||
bool isPortal(GridPosition point, Direction direction);
|
bool isPortal(GridPosition point, Direction direction);
|
||||||
|
GridPosition iterateGridPosition(GridPosition point, Direction direction);
|
||||||
GridPosition teleport(GridPosition point);
|
GridPosition teleport(GridPosition point);
|
||||||
|
|
||||||
std::vector<GridPosition> initialPelletPositions();
|
std::vector<GridPosition> initialPelletPositions();
|
||||||
|
|
||||||
std::vector<GridPosition> initialSuperPelletPositions();
|
std::vector<GridPosition> initialSuperPelletPositions();
|
||||||
|
|
||||||
inline Position penDoorPosition() { return { 13, 11 }; }
|
inline Position penDoorPosition() {
|
||||||
inline Position initialPacManPosition() { return { 13.5, 23 }; }
|
return { 13, 11 };
|
||||||
|
}
|
||||||
|
inline Position initialPacManPosition() {
|
||||||
|
return { 13.5, 23 };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWalkableStraightLine(GridPosition pointA, GridPosition pointB);
|
||||||
|
bool isIntersection(GridPosition point);
|
||||||
|
|
||||||
} // namespace pacman
|
} // namespace pacman
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Blinky.hpp"
|
||||||
|
#include "Clyde.hpp"
|
||||||
|
#include "Direction.hpp"
|
||||||
|
#include "Inky.hpp"
|
||||||
|
#include "PacMan.hpp"
|
||||||
|
#include "Pellets.hpp"
|
||||||
|
#include "Pinky.hpp"
|
||||||
|
#include "Position.hpp"
|
||||||
|
#include "SuperPellets.hpp"
|
||||||
|
|
||||||
namespace pacman {
|
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);
|
||||||
|
Direction suggestedDirection() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Position pos;
|
||||||
|
Direction direction = Direction::RIGHT;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pacman
|
} // namespace pacman
|
||||||
|
|
|
@ -17,6 +17,7 @@ public:
|
||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPellet(GridPosition p) const;
|
||||||
bool eatPelletAtPosition(GridPosition p);
|
bool eatPelletAtPosition(GridPosition p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -17,6 +17,7 @@ public:
|
||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPellet(GridPosition p) const;
|
||||||
bool eatPelletAtPosition(GridPosition p);
|
bool eatPelletAtPosition(GridPosition p);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in New Issue