diff --git a/lib/Board.cpp b/lib/Board.cpp index ef8dc5d..583e4e9 100644 --- a/lib/Board.cpp +++ b/lib/Board.cpp @@ -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 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 diff --git a/lib/Ghost.cpp b/lib/Ghost.cpp index b524a6c..1388dda 100644 --- a/lib/Ghost.cpp +++ b/lib/Ghost.cpp @@ -1,8 +1,8 @@ #include "Ghost.hpp" +#include #include #include #include -#include 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(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; } diff --git a/lib/PacManAI.cpp b/lib/PacManAI.cpp new file mode 100644 index 0000000..98d280e --- /dev/null +++ b/lib/PacManAI.cpp @@ -0,0 +1,4 @@ +#include "PacManAI.hpp" + +namespace pacman { +} \ No newline at end of file diff --git a/lib/include/GameState.hpp b/lib/include/GameState.hpp index 7a74779..f0b0c92 100644 --- a/lib/include/GameState.hpp +++ b/lib/include/GameState.hpp @@ -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; diff --git a/lib/include/Ghost.hpp b/lib/include/Ghost.hpp index 01401dd..ceac37e 100644 --- a/lib/include/Ghost.hpp +++ b/lib/include/Ghost.hpp @@ -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; diff --git a/lib/include/PacManAI.hpp b/lib/include/PacManAI.hpp new file mode 100644 index 0000000..c426e53 --- /dev/null +++ b/lib/include/PacManAI.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace pacman { + +class PacManAI { +public: +}; + +} // namespace pacman