Initial helper functions for PacMan AI. Not complete.

This commit is contained in:
Ólafur Waage 2021-09-16 16:36:57 +02:00
parent 8415048809
commit 0712db77bb
6 changed files with 102 additions and 25 deletions

View file

@ -6,14 +6,14 @@ const std::size_t ROWS = 31;
const std::size_t COLUMNS = 28;
enum class Cell {
wall = 0,
pellet = 1,
// nothing = 2,
// missing 3,
power_pellet = 4,
pen = 5,
left_portal = 6,
right_portal = 7
wall = 0,
pellet = 1,
// nothing = 2,
// missing 3,
power_pellet = 4,
pen = 5,
left_portal = 6,
right_portal = 7
};
// clang-format off
@ -124,4 +124,60 @@ std::vector<GridPosition> initialSuperPelletPositions() {
return positions;
}
// AI
bool isIntersection(GridPosition point) {
if (!isWalkableForPacMan(point) || cellAtPosition(point) == Cell::left_portal || cellAtPosition(point) == Cell::right_portal) {
return false;
}
const GridPosition right{ point.x + 1, point.y };
const bool rightWalkable = isWalkableForPacMan(right);
const GridPosition left{ point.x - 1, point.y };
const bool leftWalkable = isWalkableForPacMan(left);
const GridPosition top{ point.x, point.y - 1 };
const bool topWalkable = isWalkableForPacMan(top);
const GridPosition bottom{ point.x, point.y + 1 };
const bool bottomWalkable = isWalkableForPacMan(bottom);
return (topWalkable && rightWalkable) || (rightWalkable && bottomWalkable) || (bottomWalkable && leftWalkable) || (leftWalkable && topWalkable);
}
bool isWalkableStraightLine(GridPosition pointA, GridPosition pointB) {
// Points with no shared x,y have no straight line between them
if (pointA.x != pointB.x && pointA.y != pointB.y) {
return false;
}
// this is std::all_of
if (pointA.x == pointB.x) {
const size_t startY = (pointA.y > pointB.y ? pointB.y : pointA.y);
const size_t endY = (pointA.y > pointB.y ? pointA.y : pointB.y);
for (size_t y = startY; y <= endY; y++) {
const GridPosition test{ pointA.x, y };
if (!isWalkableForPacMan(test)) {
return false;
}
}
return true;
}
if (pointA.y == pointB.y) {
const size_t startX = (pointA.x > pointB.x ? pointB.x : pointA.x);
const size_t endX = (pointA.x > pointB.x ? pointA.x : pointB.x);
for (size_t x = startX; x <= endX; x++) {
const GridPosition test{ x, pointA.y };
if (!isWalkableForPacMan(test)) {
return false;
}
}
return true;
}
return false;
}
} // namespace pacman

View file

@ -1,8 +1,8 @@
#include "Ghost.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <numeric>
#include <algorithm>
namespace pacman {
@ -13,7 +13,8 @@ Ghost::Ghost(Atlas::Ghost spriteSet)
void Ghost::frighten() {
if (state > State::Scatter)
return;
direction = oppositeDirection(direction);
dir = oppositeDirection(dir);
state = State::Frightened;
timeFrighten = {};
}
@ -29,7 +30,8 @@ bool Ghost::isEyes() const {
void Ghost::die() {
if (state == State::Eyes)
return;
direction = oppositeDirection(direction);
dir = oppositeDirection(dir);
state = State::Eyes;
timeFrighten = {};
timeChase = {};
@ -42,12 +44,12 @@ void Ghost::reset() {
timeChase = {};
}
GridPosition Ghost::currentSprite() const {
GridPosition Ghost::currentSprite() const {
switch (state) {
default:
return Atlas::ghostSprite(spriteSet, direction, (animationIndex % 2) == 0);
return Atlas::ghostSprite(spriteSet, dir, (animationIndex % 2) == 0);
case State::Eyes:
return Atlas::eyeSprite(direction);
return Atlas::eyeSprite(dir);
case State::Frightened:
if (timeFrighten.count() < 3500)
return Atlas::initialFrightened(animationIndex);
@ -64,6 +66,10 @@ GridPosition Ghost::positionInGrid() const {
return positionToGridPosition(pos);
}
Direction Ghost::direction() const {
return dir;
}
void Ghost::update(std::chrono::milliseconds time_delta, const GameState & gameState) {
if (state == State::Eyes && isInPen())
state = State::Scatter;
@ -78,7 +84,7 @@ void Ghost::update(std::chrono::milliseconds time_delta, const GameState & gameS
timeChase += time_delta;
const auto newState = defaultStateAtDuration(std::chrono::duration_cast<std::chrono::seconds>(timeChase));
if (newState != state) {
direction = oppositeDirection(direction);
dir = oppositeDirection(dir);
state = newState;
}
}
@ -99,7 +105,7 @@ void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState
const auto old_position = pos;
const GridPosition old_grid_position = positionToGridPosition(old_position);
switch (direction) {
switch (dir) {
case Direction::NONE:
break;
case Direction::LEFT:
@ -120,12 +126,11 @@ void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState
break;
}
if (isPortal(positionInGrid(), direction)) {
if (isPortal(positionInGrid(), dir)) {
pos = gridPositionToPosition(teleport(positionInGrid()));
}
else if (!isWalkableForGhost(positionInGrid(), old_grid_position, isEyes())) {
} else if (!isWalkableForGhost(positionInGrid(), old_grid_position, isEyes())) {
pos = old_position;
direction = oppositeDirection(direction);
dir = oppositeDirection(dir);
}
}
@ -175,7 +180,7 @@ void Ghost::updateDirection(const GameState & gameState) {
if (invalid_position)
continue;
const bool opposite_direction = (move.direction == oppositeDirection(direction));
const bool opposite_direction = (move.direction == oppositeDirection(dir));
if (opposite_direction)
continue;
@ -192,7 +197,7 @@ void Ghost::updateDirection(const GameState & gameState) {
});
const auto & move = *optimal_move;
direction = move.direction;
dir = move.direction;
last_grid_position = current_grid_position;
}

4
lib/PacManAI.cpp Normal file
View file

@ -0,0 +1,4 @@
#include "PacManAI.hpp"
namespace pacman {
}

View file

@ -2,15 +2,16 @@
#include "Blinky.hpp"
#include "Clyde.hpp"
#include "Fruits.hpp"
#include "Ghost.hpp"
#include "Inky.hpp"
#include "InputState.hpp"
#include "PacMan.hpp"
#include "PacManAI.hpp"
#include "Pellets.hpp"
#include "Pinky.hpp"
#include "Score.hpp"
#include "SuperPellets.hpp"
#include "Fruits.hpp"
#include "InputState.hpp"
namespace pacman {
@ -23,6 +24,7 @@ struct GameState {
Clyde clyde;
PacMan pacMan;
PacManAI pacManAI;
InputState inputState;
Pellets pellets;
SuperPellets superPellets;

View file

@ -25,6 +25,7 @@ public:
GridPosition currentSprite() const;
Position position() const;
GridPosition positionInGrid() const;
Direction direction() const;
void update(std::chrono::milliseconds time_delta, const GameState & gameState);
void frighten();
@ -40,7 +41,7 @@ private:
protected:
Atlas::Ghost spriteSet;
Direction direction = Direction::NONE;
Direction dir = Direction::NONE;
double timeForAnimation = 0;
std::size_t animationIndex = 0;
State state = State::Chase;

9
lib/include/PacManAI.hpp Normal file
View file

@ -0,0 +1,9 @@
#pragma once
namespace pacman {
class PacManAI {
public:
};
} // namespace pacman