diff --git a/lib/Blinky.cpp b/lib/Blinky.cpp index 69aee36..6f2375b 100644 --- a/lib/Blinky.cpp +++ b/lib/Blinky.cpp @@ -8,7 +8,7 @@ Blinky::Blinky() pos = initialPosition(); } -double Blinky::speed(const GameState &) const { +double Blinky::speed() const { if (state == State::Eyes) return 2; if (state == State::Frightened) @@ -16,15 +16,18 @@ double Blinky::speed(const GameState &) const { return 0.75; } -Position Blinky::target(const GameState & gameState) const { - if (state == State::Eyes) - return initialPosition(); +void Blinky::setTarget(Position pacManPos) { + if (state == State::Eyes) { + target = initialPosition(); + return; + } - if (isInPen()) - return penDoorPosition(); + if (isInPen()) { + target = penDoorPosition(); + return; + } - const auto pacManPosition = gridPositionToPosition(gameState.pacMan.positionInGrid()); - return state == State::Chase ? pacManPosition : scatterTarget(); + target = state == State::Chase ? pacManPos : scatterTarget(); } Position Blinky::initialPosition() const { @@ -35,4 +38,4 @@ Position Blinky::scatterTarget() const { return { 25, -3 }; } -} +} // namespace pacman diff --git a/lib/Clyde.cpp b/lib/Clyde.cpp index 269185e..d084769 100644 --- a/lib/Clyde.cpp +++ b/lib/Clyde.cpp @@ -8,7 +8,7 @@ Clyde::Clyde() pos = initialPosition(); } -double Clyde::speed(const GameState &) const { +double Clyde::speed() const { if (state == State::Eyes) return 2; if (state == State::Frightened) @@ -16,24 +16,27 @@ double Clyde::speed(const GameState &) const { return 0.75; } -Position Clyde::target(const GameState & gameState) const { - if (state == State::Eyes) - return initialPosition(); +void Clyde::setTarget(Position pacManPos) { + if (state == State::Eyes) { + target = initialPosition(); + return; + } - if (isInPen()) - return penDoorPosition(); + if (isInPen()) { + target = penDoorPosition(); + return; + } // Clyde always target its scatter target, unless pacman is further than 8 tiles away - auto targetPosition = scatterTarget(); - if (state == State::Scatter) - return targetPosition; + target = scatterTarget(); + if (state == State::Scatter) { + return; + } - const auto pacManPosition = gameState.pacMan.position(); - const auto distanceFomPacMan = std::hypot(pos.x - double(pacManPosition.x), pos.y - double(pacManPosition.y)); - if (distanceFomPacMan > 8) - targetPosition = pacManPosition; - - return targetPosition; + const auto distanceFomPacMan = std::hypot(pos.x - pacManPos.x, pos.y - pacManPos.y); + if (distanceFomPacMan > 8) { + target = pacManPos; + } } Position Clyde::initialPosition() const { diff --git a/lib/Fruits.cpp b/lib/Fruits.cpp index b47b700..e22edcf 100644 --- a/lib/Fruits.cpp +++ b/lib/Fruits.cpp @@ -3,14 +3,14 @@ namespace pacman { -void Fruits::update(std::chrono::milliseconds time_delta, const GameState & gameState) { +void Fruits::update(std::chrono::milliseconds time_delta, int eatenPellets) { if (visible) { time_visible += time_delta; } if (time_visible > std::chrono::seconds(9)) { hide(); - } else if ((index == 0 && gameState.score.eatenPellets >= 70) || (index == 1 && gameState.score.eatenPellets >= 170)) { + } else if ((index == 0 && eatenPellets >= 70) || (index == 1 && eatenPellets >= 170)) { // We show the fruit twice, once at 70 pellets and once at 170 visible = true; } diff --git a/lib/GameState.cpp b/lib/GameState.cpp index b8f8a10..e9d0000 100644 --- a/lib/GameState.cpp +++ b/lib/GameState.cpp @@ -18,11 +18,16 @@ void GameState::step(std::chrono::milliseconds delta) { if (!pacMan.hasDirection()) return; - 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 - inky.update(delta, *this); - clyde.update(delta, *this); - fruit.update(delta, *this); + blinky.setTarget(pacMan.position()); + blinky.update(delta); + pinky.setTarget(pacMan.positionInGrid(), pacMan.currentDirection()); + pinky.update(delta); + inky.setTarget(pacMan.positionInGrid(), pacMan.currentDirection(), blinky.positionInGrid()); + inky.update(delta); + clyde.setTarget(pacMan.position()); + clyde.update(delta); + + fruit.update(delta, score.eatenPellets); checkCollision(blinky); checkCollision(pinky); diff --git a/lib/Ghost.cpp b/lib/Ghost.cpp index 69622f0..3d9819e 100644 --- a/lib/Ghost.cpp +++ b/lib/Ghost.cpp @@ -70,7 +70,7 @@ Direction Ghost::currentDirection() const { return direction; } -void Ghost::update(std::chrono::milliseconds time_delta, const GameState & gameState) { +void Ghost::update(std::chrono::milliseconds time_delta) { if (state == State::Eyes && isInPen()) state = State::Scatter; @@ -90,17 +90,17 @@ void Ghost::update(std::chrono::milliseconds time_delta, const GameState & gameS } updateAnimation(time_delta); - updatePosition(time_delta, gameState); + updatePosition(time_delta); } bool Ghost::isInPen() const { return pacman::isInPen(positionInGrid()); } -void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState & gameState) { - updateDirection(gameState); +void Ghost::updatePosition(std::chrono::milliseconds time_delta) { + updateDirection(); - double position_delta = (0.004 * double(time_delta.count())) * speed(gameState); + double position_delta = (0.004 * double(time_delta.count())) * speed(); const auto old_position = pos; const GridPosition old_grid_position = positionToGridPosition(old_position); @@ -150,7 +150,7 @@ void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState * In the scatter state, each ghost tries to reach an unreachable position outside of the map. * This makes ghosts run in circle around the island at each of the 4 map corner. */ -void Ghost::updateDirection(const GameState & gameState) { +void Ghost::updateDirection() { const auto current_grid_position = positionInGrid(); if (current_grid_position == last_grid_position) return; @@ -170,8 +170,6 @@ void Ghost::updateDirection(const GameState & gameState) { Move{ Direction::RIGHT, { x + 1, y } } }; - const Position target_position = target(gameState); - for (auto & move : possible_moves) { if (isPortal(current_grid_position, move.direction)) move.position = gridPositionToPosition(teleport(current_grid_position)); @@ -189,7 +187,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 = std::hypot(move.position.x - target.x, move.position.y - target.y); } const auto optimal_move = std::min_element(possible_moves.begin(), possible_moves.end(), [](const auto & a, const auto & b) { diff --git a/lib/Inky.cpp b/lib/Inky.cpp index 56c3a48..7d6dd45 100644 --- a/lib/Inky.cpp +++ b/lib/Inky.cpp @@ -8,7 +8,7 @@ Inky::Inky() pos = initialPosition(); } -double Inky::speed(const GameState &) const { +double Inky::speed() const { if (state == State::Eyes) return 2; if (state == State::Frightened) @@ -16,19 +16,25 @@ double Inky::speed(const GameState &) const { return 0.75; } -Position Inky::target(const GameState & gameState) const { - if (state == State::Eyes) - return initialPosition(); +void Inky::setTarget(GridPosition pacManPos, Direction pacManDir, GridPosition blinkyPos) { + if (state == State::Eyes) { + target = initialPosition(); + return; + } - if (isInPen()) - return penDoorPosition(); + if (isInPen()) { + target = penDoorPosition(); + return; + } - if (state == State::Scatter) - return scatterTarget(); + if (state == State::Scatter) { + target = scatterTarget(); + return; + } // Inky first selects a position 2 cell away from pacman in his direction. - GridPosition targetPosition = gameState.pacMan.positionInGrid(); - switch (gameState.pacMan.currentDirection()) { + GridPosition targetPosition = pacManPos; + switch (pacManDir) { case Direction::LEFT: targetPosition.x -= 2; break; @@ -48,15 +54,14 @@ Position Inky::target(const GameState & gameState) const { } // Then it calculates the distance between Blinky and this position - const auto & blinkyPosition = gameState.blinky.positionInGrid(); - const double distanceBetweenBlinkyAndTarget = std::hypot(blinkyPosition.x - targetPosition.x, blinkyPosition.y - targetPosition.y); + const double distanceBetweenBlinkyAndTarget = std::hypot(blinkyPos.x - targetPosition.x, blinkyPos.y - targetPosition.y); - // And selects a point on the line crossing blinky and this position that is at twice that distance - // away from blinky - targetPosition.x += std::size_t((double(targetPosition.x) - double(blinkyPosition.x)) / distanceBetweenBlinkyAndTarget) * 2; - targetPosition.y += std::size_t((double(targetPosition.y) - double(blinkyPosition.y)) / distanceBetweenBlinkyAndTarget) * 2; + // And selects a point on the line crossing blinky and + // this position that is at twice that distance away from blinky + targetPosition.x += std::size_t((double(targetPosition.x) - double(blinkyPos.x)) / distanceBetweenBlinkyAndTarget) * 2; + targetPosition.y += std::size_t((double(targetPosition.y) - double(blinkyPos.y)) / distanceBetweenBlinkyAndTarget) * 2; - return gridPositionToPosition(targetPosition); + target = gridPositionToPosition(targetPosition); } Position Inky::initialPosition() const { diff --git a/lib/Pinky.cpp b/lib/Pinky.cpp index 8a0dc4a..71ce353 100644 --- a/lib/Pinky.cpp +++ b/lib/Pinky.cpp @@ -8,7 +8,7 @@ Pinky::Pinky() pos = initialPosition(); } -double Pinky::speed(const GameState &) const { +double Pinky::speed() const { if (state == State::Eyes) return 2; if (state == State::Frightened) @@ -16,19 +16,25 @@ double Pinky::speed(const GameState &) const { return 0.75; } -Position Pinky::target(const GameState & gameState) const { - if (state == State::Eyes) - return initialPosition(); +void Pinky::setTarget(GridPosition pacManPos, Direction pacManDir) { + if (state == State::Eyes) { + target = initialPosition(); + return; + } - if (isInPen()) - return penDoorPosition(); + if (isInPen()) { + target = penDoorPosition(); + return; + } - if (state == State::Scatter) - return scatterTarget(); + if (state == State::Scatter) { + target = scatterTarget(); + return; + } // Inky first selects a position 2 cell away from pacman in his direction. - GridPosition targetPosition = gameState.pacMan.positionInGrid(); - switch (gameState.pacMan.currentDirection()) { + GridPosition targetPosition = pacManPos; + switch (pacManDir) { case Direction::LEFT: targetPosition.x -= 4; break; @@ -46,7 +52,8 @@ Position Pinky::target(const GameState & gameState) const { assert(false && "Pacman should be moving"); break; } - return gridPositionToPosition(targetPosition); + + target = gridPositionToPosition(targetPosition); } Position Pinky::initialPosition() const { @@ -57,4 +64,4 @@ Position Pinky::scatterTarget() const { return { 3, -2 }; } -} +} // namespace pacman diff --git a/lib/include/Blinky.hpp b/lib/include/Blinky.hpp index 5aad764..94b91e3 100644 --- a/lib/include/Blinky.hpp +++ b/lib/include/Blinky.hpp @@ -7,10 +7,10 @@ namespace pacman { class Blinky final : public Ghost { public: Blinky(); + void setTarget(Position pacManPos); protected: - double speed(const GameState & gameState) const override; - Position target(const GameState & gameState) const override; + double speed() const override; Position initialPosition() const override; private: diff --git a/lib/include/Clyde.hpp b/lib/include/Clyde.hpp index fc2e7f0..bdb0953 100644 --- a/lib/include/Clyde.hpp +++ b/lib/include/Clyde.hpp @@ -7,10 +7,10 @@ namespace pacman { class Clyde final : public Ghost { public: explicit Clyde(); + void setTarget(Position pacManPos); protected: - double speed(const GameState & gameState) const override; - Position target(const GameState & gameState) const override; + double speed() const override; Position initialPosition() const override; private: diff --git a/lib/include/Fruits.hpp b/lib/include/Fruits.hpp index d89bc53..3ccee42 100644 --- a/lib/include/Fruits.hpp +++ b/lib/include/Fruits.hpp @@ -8,7 +8,7 @@ struct GameState; class Fruits { public: - void update(std::chrono::milliseconds time_delta, const GameState & gameState); + void update(std::chrono::milliseconds time_delta, int eatenPellets); GridPosition currentSprite() const; Position position() const; diff --git a/lib/include/Ghost.hpp b/lib/include/Ghost.hpp index cbee277..5623abe 100644 --- a/lib/include/Ghost.hpp +++ b/lib/include/Ghost.hpp @@ -27,7 +27,7 @@ public: GridPosition positionInGrid() const; Direction currentDirection() const; - void update(std::chrono::milliseconds time_delta, const GameState & gameState); + void update(std::chrono::milliseconds time_delta); void frighten(); void die(); bool isFrightened() const; @@ -36,8 +36,8 @@ public: private: void updateAnimation(std::chrono::milliseconds time_delta); - void updatePosition(std::chrono::milliseconds time_delta, const GameState & gameState); - void updateDirection(const GameState & gameState); + void updatePosition(std::chrono::milliseconds time_delta); + void updateDirection(); protected: Atlas::Ghost spriteSet; @@ -48,12 +48,12 @@ protected: std::chrono::milliseconds timeFrighten = {}; std::chrono::milliseconds timeChase = {}; Position pos; + Position target; GridPosition last_grid_position = { 0, 0 }; State defaultStateAtDuration(std::chrono::seconds seconds); - virtual double speed(const GameState & gameState) const = 0; - virtual Position target(const GameState & gameState) const = 0; + virtual double speed() const = 0; virtual Position initialPosition() const = 0; bool isInPen() const; diff --git a/lib/include/Inky.hpp b/lib/include/Inky.hpp index a6a828c..ba30914 100644 --- a/lib/include/Inky.hpp +++ b/lib/include/Inky.hpp @@ -7,10 +7,10 @@ namespace pacman { class Inky final : public Ghost { public: explicit Inky(); + void setTarget(GridPosition pacManPos, Direction pacManDir, GridPosition blinkyPos); protected: - double speed(const GameState & gameState) const override; - Position target(const GameState & gameState) const override; + double speed() const override; Position initialPosition() const override; private: diff --git a/lib/include/Pinky.hpp b/lib/include/Pinky.hpp index de6abc9..4bacd66 100644 --- a/lib/include/Pinky.hpp +++ b/lib/include/Pinky.hpp @@ -7,10 +7,10 @@ namespace pacman { class Pinky final : public Ghost { public: explicit Pinky(); + void setTarget(GridPosition pacManPos, Direction pacManDir); protected: - double speed(const GameState & gameState) const override; - Position target(const GameState & gameState) const override; + double speed() const override; Position initialPosition() const override; private: diff --git a/test/testFruits.cpp b/test/testFruits.cpp index f0832e7..00f7fd6 100644 --- a/test/testFruits.cpp +++ b/test/testFruits.cpp @@ -17,47 +17,47 @@ TEST_CASE("Fruit Visibility", "[fruits]") { REQUIRE_FALSE(gameState.fruit.isVisible()); SECTION("9 seconds but no pellets eaten") { - gameState.fruit.update(std::chrono::milliseconds(9001), gameState); + gameState.fruit.update(std::chrono::milliseconds(9001), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); } SECTION("9 seconds and 70 pellets eaten") { // "Eat 70 pellets", do an update and check the state gameState.score.eatenPellets = 70; - gameState.fruit.update(std::chrono::milliseconds(1), gameState); + gameState.fruit.update(std::chrono::milliseconds(1), gameState.score.eatenPellets); REQUIRE(gameState.fruit.isVisible()); // Wait more than 9 seconds and then check the state again - gameState.fruit.update(std::chrono::milliseconds(9001), gameState); + gameState.fruit.update(std::chrono::milliseconds(9001), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); } SECTION("70 and 170 pellets eaten") { // "Eat 70 pellets", do an update and check the state gameState.score.eatenPellets = 70; - gameState.fruit.update(std::chrono::milliseconds(1), gameState); + gameState.fruit.update(std::chrono::milliseconds(1), gameState.score.eatenPellets); REQUIRE(gameState.fruit.isVisible()); // Wait more than 9 seconds and then check the state again - gameState.fruit.update(std::chrono::milliseconds(9001), gameState); + gameState.fruit.update(std::chrono::milliseconds(9001), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); // "Eat 170 pellets", do an update and check the state gameState.score.eatenPellets = 170; - gameState.fruit.update(std::chrono::milliseconds(1), gameState); + gameState.fruit.update(std::chrono::milliseconds(1), gameState.score.eatenPellets); REQUIRE(gameState.fruit.isVisible()); // Wait more than 9 seconds and then check the state again - gameState.fruit.update(std::chrono::milliseconds(9001), gameState); + gameState.fruit.update(std::chrono::milliseconds(9001), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); // We should never get a visible state again, since we only show 2 fruits gameState.score.eatenPellets = 1000; - gameState.fruit.update(std::chrono::milliseconds(1), gameState); + gameState.fruit.update(std::chrono::milliseconds(1), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); // Wait more than 9 seconds and then check the state again - gameState.fruit.update(std::chrono::milliseconds(9001), gameState); + gameState.fruit.update(std::chrono::milliseconds(9001), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); } @@ -65,12 +65,12 @@ TEST_CASE("Fruit Visibility", "[fruits]") { REQUIRE(gameState.fruit.eat() == 0); gameState.score.eatenPellets = 70; - gameState.fruit.update(std::chrono::milliseconds(1), gameState); + gameState.fruit.update(std::chrono::milliseconds(1), gameState.score.eatenPellets); REQUIRE(gameState.fruit.isVisible()); REQUIRE(gameState.fruit.eat() == gameState.fruit.value()); // Wait more than 9 seconds and then check the state again - gameState.fruit.update(std::chrono::milliseconds(9001), gameState); + gameState.fruit.update(std::chrono::milliseconds(9001), gameState.score.eatenPellets); REQUIRE_FALSE(gameState.fruit.isVisible()); REQUIRE(gameState.fruit.eat() == 0); }