Merge pull request #26 from mod-cpp/owaage_test_refactor

Testing and Code refactor.
This commit is contained in:
Ólafur Waage 2021-09-10 10:23:17 +02:00 committed by GitHub
commit 434875e344
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 362 additions and 204 deletions

View file

@ -54,7 +54,7 @@ constexpr std::array<std::array<int, COLUMNS>, ROWS> board = {{
// clang-format on
static Cell cellAtPosition(GridPosition point) {
if (point.x >= COLUMNS || point.y >= ROWS)
if (point.x < 0 || point.x >= COLUMNS || point.y < 0 || point.y >= ROWS)
return Cell::wall;
return Cell(board[point.y][point.x]);
}
@ -64,10 +64,10 @@ bool isWalkableForPacMan(GridPosition point) {
}
bool isWalkableForGhost(GridPosition point, GridPosition origin, bool isEyes) {
Cell cell = cellAtPosition(point);
const Cell cell = cellAtPosition(point);
if (cell == Cell::wall)
return false;
return isEyes || (isInPen(origin) || !isInPen(point));
return isEyes || isInPen(origin) || !isInPen(point);
}
bool isInPen(GridPosition point) {

View file

@ -5,7 +5,7 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
find_package(OpenGL REQUIRED COMPONENTS OpenGL GLX)
endif ()
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp" "*.hpp")
add_library(libpacman ${sources})

View file

@ -29,7 +29,7 @@ Canvas::Canvas()
game_font = loadFont("retro_font.ttf");
}
void Canvas::update(const GameState & gameState, const Score & score) {
void Canvas::update(const GameState & gameState) {
clear();
renderMaze();
@ -41,8 +41,8 @@ void Canvas::update(const GameState & gameState, const Score & score) {
renderGhost(gameState.inky);
renderGhost(gameState.clyde);
renderScore(score.points);
renderLives(score.lives);
renderScore(gameState.score.points);
renderLives(gameState.score.lives);
renderPacMan(gameState.pacMan);

View file

@ -5,125 +5,33 @@
namespace pacman {
constexpr int DEFAULT_LIVES = 3;
constexpr int NORMAL_PELLET_POINTS = 10;
constexpr int POWER_PELLET_POINTS = 50;
constexpr int GHOST_POINTS = 200;
Game::Game() {
score.lives = DEFAULT_LIVES;
}
void Game::run() {
const std::chrono::milliseconds delta_time(1000 / 60);
std::chrono::milliseconds accumulator(0);
auto current_time = std::chrono::system_clock::now();
InputState inputState;
while (true) {
auto newTime = std::chrono::system_clock::now();
auto frameTime = std::chrono::duration_cast<std::chrono::milliseconds>(newTime - current_time);
const auto newTime = std::chrono::system_clock::now();
const auto frameTime = std::chrono::duration_cast<std::chrono::milliseconds>(newTime - current_time);
current_time = newTime;
accumulator += frameTime;
processEvents(inputState);
if (inputState.close)
processEvents(gameState.inputState);
if (gameState.inputState.close)
return;
while (accumulator >= delta_time) {
step(delta_time, inputState);
gameState.step(delta_time);
accumulator -= delta_time;
}
canvas.update(gameState, score);
}
}
void Game::killPacMan() {
gameState.pacMan.die();
score.lives--;
timeSinceDeath = std::chrono::milliseconds(1);
}
bool Game::pacManDying() const {
return timeSinceDeath.count() != 0;
}
void Game::handleDeathAnimation(std::chrono::milliseconds delta) {
timeSinceDeath += delta;
if (timeSinceDeath.count() > 1000) {
gameState.blinky.reset();
gameState.pinky.reset();
gameState.inky.reset();
gameState.clyde.reset();
gameState.pacMan.reset();
timeSinceDeath = std::chrono::milliseconds(0);
}
}
void Game::step(std::chrono::milliseconds delta, InputState inputState) {
gameState.pacMan.update(delta, inputState.direction());
if (pacManDying()) {
handleDeathAnimation(delta);
return;
}
if (!gameState.pacMan.hasDirection())
return;
gameState.blinky.update(delta, gameState);
gameState.pinky.update(delta, gameState);
gameState.inky.update(delta, gameState);
gameState.clyde.update(delta, gameState);
checkCollision(gameState.blinky);
checkCollision(gameState.pinky);
checkCollision(gameState.inky);
checkCollision(gameState.clyde);
eatPellets();
}
void Game::checkCollision(Ghost & ghost) {
if (pacManDying() || ghost.isEyes())
return;
if (ghost.positionInGrid() != gameState.pacMan.positionInGrid())
return;
if (ghost.isFrightened()) {
ghost.die();
score.points += GHOST_POINTS;
} else {
killPacMan();
}
}
void Game::eatPellets() {
const auto pos = gameState.pacMan.positionInGrid();
if (gameState.pellets.eatPelletAtPosition(pos)) {
score.eatenPellets++;
score.points += NORMAL_PELLET_POINTS;
}
if (gameState.superPellets.eatPelletAtPosition(pos)) {
score.eatenPellets++;
score.points += POWER_PELLET_POINTS;
gameState.blinky.frighten();
gameState.pinky.frighten();
gameState.inky.frighten();
gameState.clyde.frighten();
canvas.update(gameState);
}
}
void Game::processEvents(InputState & inputState) {
auto event = canvas.pollEvent();
const auto event = canvas.pollEvent();
if (event && event.value().type == sf::Event::Closed) {
inputState.close = true;
return;

89
lib/GameState.cpp Normal file
View file

@ -0,0 +1,89 @@
#include "GameState.hpp"
namespace pacman {
constexpr int GHOST_POINTS = 200;
constexpr int NORMAL_PELLET_POINTS = 10;
constexpr int POWER_PELLET_POINTS = 50;
void GameState::step(std::chrono::milliseconds delta) {
pacMan.update(delta, inputState.direction());
if (isPacManDying()) {
handleDeathAnimation(delta);
return;
}
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);
checkCollision(blinky);
checkCollision(pinky);
checkCollision(inky);
checkCollision(clyde);
eatPellets();
}
void GameState::checkCollision(Ghost & ghost) {
if (isPacManDying() || ghost.isEyes())
return;
if (ghost.positionInGrid() != pacMan.positionInGrid())
return;
if (ghost.isFrightened()) {
ghost.die();
score.points += GHOST_POINTS;
} else {
killPacMan();
}
}
void GameState::handleDeathAnimation(std::chrono::milliseconds delta) {
timeSinceDeath += delta;
if (timeSinceDeath.count() > 1000) {
blinky.reset();
pinky.reset();
inky.reset();
clyde.reset();
pacMan.reset();
timeSinceDeath = std::chrono::milliseconds(0);
}
}
void GameState::eatPellets() {
const auto pos = pacMan.positionInGrid();
if (pellets.eatPelletAtPosition(pos)) {
score.eatenPellets++;
score.points += NORMAL_PELLET_POINTS;
}
if (superPellets.eatPelletAtPosition(pos)) {
score.eatenPellets++;
score.points += POWER_PELLET_POINTS;
blinky.frighten();
pinky.frighten();
inky.frighten();
clyde.frighten();
}
}
void GameState::killPacMan() {
pacMan.die();
score.lives--;
timeSinceDeath = std::chrono::milliseconds(1);
}
bool GameState::isPacManDying() const {
return timeSinceDeath.count() != 0;
}
}

View file

@ -75,7 +75,7 @@ void Ghost::update(std::chrono::milliseconds time_delta, const GameState & gameS
if (state == State::Scatter || state == State::Chase) {
timeChase += time_delta;
auto newState = defaultStateAtDuration(std::chrono::duration_cast<std::chrono::seconds>(timeChase));
const auto newState = defaultStateAtDuration(std::chrono::duration_cast<std::chrono::seconds>(timeChase));
if (newState != state) {
direction = oppositeDirection(direction);
state = newState;
@ -95,6 +95,9 @@ void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState
double position_delta = (0.004 * time_delta.count()) * speed(gameState);
const auto old_position = pos;
const GridPosition old_grid_position = positionToGridPosition(old_position);
switch (direction) {
case Direction::NONE:
break;
@ -119,6 +122,10 @@ void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState
if (isPortal(positionInGrid(), direction)) {
pos = gridPositionToPosition(teleport(positionInGrid()));
}
else if (!isWalkableForGhost(positionInGrid(), old_grid_position, isEyes())) {
pos = old_position;
direction = oppositeDirection(direction);
}
}
/*
@ -160,7 +167,6 @@ void Ghost::updateDirection(const GameState & gameState) {
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));
@ -172,7 +178,7 @@ void Ghost::updateDirection(const GameState & gameState) {
if (opposite_direction)
continue;
const GridPosition grid_position = { size_t(move.position.x), size_t(move.position.y) };
const GridPosition grid_position = { int64_t(move.position.x), int64_t(move.position.y) };
const bool can_walk = isWalkableForGhost(grid_position, current_grid_position, isEyes());
if (!can_walk)
continue;
@ -184,7 +190,7 @@ void Ghost::updateDirection(const GameState & gameState) {
return a.distance_to_target < b.distance_to_target;
});
auto move = *optimal_move;
const auto & move = *optimal_move;
direction = move.direction;
last_grid_position = current_grid_position;
}
@ -204,13 +210,17 @@ void Ghost::updateAnimation(std::chrono::milliseconds time_delta) {
Ghost::State Ghost::defaultStateAtDuration(std::chrono::seconds seconds) {
// This array denotes the duration of each state, alternating between scatter and chase
std::array changes = { /*scatter*/ 7, 20, 7, 20, 5, 20, 5 };
// To know the current state we first compute the cumulative time using std::partial_sum
// This gives us {7, 27, 34, 54, 59, 79, 84}
std::partial_sum(std::begin(changes), std::end(changes), std::begin(changes));
// Then we look for the first value in the array greater than the time spent in chase/scatter states
auto it = std::upper_bound(std::begin(changes), std::end(changes), seconds.count());
// We get the position of that iterator in the array
auto count = std::distance(std::begin(changes), it);
// Because the first positition is scatter, all the even positions will be scatter
// all the odd positions will be chase
return count % 2 == 0 ? State::Scatter : State::Chase;

View file

@ -53,8 +53,8 @@ Position Inky::target(const GameState & gameState) const {
// And selects a point on the line crossing blinky and this position that is at twice that distance
// away from blinky
targetPosition.x += size_t((targetPosition.x - blinkyPosition.x) / distanceBetweenBlinkyAndTarget) * 2;
targetPosition.y += size_t((targetPosition.y - blinkyPosition.y) / distanceBetweenBlinkyAndTarget) * 2;
targetPosition.x += int64_t((targetPosition.x - blinkyPosition.x) / distanceBetweenBlinkyAndTarget) * 2;
targetPosition.y += int64_t((targetPosition.y - blinkyPosition.y) / distanceBetweenBlinkyAndTarget) * 2;
return gridPositionToPosition(targetPosition);
}

View file

@ -18,6 +18,7 @@ GridPosition PacMan::positionInGrid() const {
void PacMan::die() {
if (dead)
return;
dead = true;
}
@ -33,8 +34,10 @@ void PacMan::update(std::chrono::milliseconds time_delta, Direction input_direct
updateAnimationPosition(time_delta, false);
return;
}
if (input_direction != Direction::NONE)
desired_direction = input_direction;
const auto old = pos;
updateMazePosition(time_delta);
const bool paused = pos == old;
@ -50,7 +53,6 @@ void PacMan::updateAnimationPosition(std::chrono::milliseconds time_delta, bool
}
void PacMan::updateMazePosition(std::chrono::milliseconds time_delta) {
if (isPortal(positionInGrid(), direction)) {
pos = gridPositionToPosition(teleport(positionInGrid()));
return;
@ -62,13 +64,13 @@ void PacMan::updateMazePosition(std::chrono::milliseconds time_delta) {
auto moveToPosition = [position_delta](Position point, Direction move_direction) {
switch (move_direction) {
case Direction::LEFT:
return GridPosition{ std::size_t(point.x - position_delta), std::size_t(point.y) };
return GridPosition{ int64_t(point.x - position_delta), int64_t(point.y) };
case Direction::RIGHT:
return GridPosition{ std::size_t(point.x + pacman_size), std::size_t(point.y) };
return GridPosition{ int64_t(point.x + pacman_size), int64_t(point.y) };
case Direction::UP:
return GridPosition{ std::size_t(point.x), std::size_t(point.y - position_delta) };
return GridPosition{ int64_t(point.x), int64_t(point.y - position_delta) };
case Direction::DOWN:
return GridPosition{ std::size_t(point.x), std::size_t(point.y + pacman_size) };
return GridPosition{ int64_t(point.x), int64_t(point.y + pacman_size) };
case Direction::NONE:
default:
return positionToGridPosition(point);
@ -79,31 +81,34 @@ void PacMan::updateMazePosition(std::chrono::milliseconds time_delta) {
return isWalkableForPacMan(moveToPosition(pos, move_direction));
};
if (canGo(desired_direction)) {
if (desired_direction != direction && canGo(desired_direction)) {
direction = desired_direction;
}
if (canGo(direction)) {
switch (direction) {
case Direction::NONE:
break;
case Direction::LEFT:
pos.x -= position_delta;
pos.y = std::floor(pos.y);
break;
case Direction::RIGHT:
pos.x += position_delta;
pos.y = std::floor(pos.y);
break;
case Direction::UP:
pos.x = std::floor(pos.x);
pos.y -= position_delta;
break;
case Direction::DOWN:
pos.x = std::floor(pos.x);
pos.y += position_delta;
break;
}
if (!canGo(direction)) {
return;
}
switch (direction) {
case Direction::LEFT:
pos.x -= position_delta;
pos.y = std::floor(pos.y);
break;
case Direction::RIGHT:
pos.x += position_delta;
pos.y = std::floor(pos.y);
break;
case Direction::UP:
pos.x = std::floor(pos.x);
pos.y -= position_delta;
break;
case Direction::DOWN:
pos.x = std::floor(pos.x);
pos.y += position_delta;
break;
case Direction::NONE:
default:
break;
}
}

View file

@ -32,15 +32,13 @@ void PacManAnimation::updateAnimationPosition(std::chrono::milliseconds time_del
return;
animation_position_delta += (0.02) * double(time_delta.count());
animation_position = int(animation_position + animation_position_delta);
animation_position = int64_t(animation_position + animation_position_delta);
if (!dead)
animation_position = animation_position % 4;
if(animation_position_delta > 1)
animation_position_delta = animation_position_delta - 1;
}
void PacManAnimation::pause() {

View file

@ -47,8 +47,8 @@ constexpr GridPosition eyeSprite(Direction direction) {
constexpr GridPosition ghostSprite(Ghost ghost, Direction direction, bool alternative) {
assert(ghost >= Ghost::blinky && ghost <= Ghost::clyde && "Invalid Ghost");
auto y = static_cast<size_t>(ghost);
size_t x = 0;
auto y = static_cast<int64_t>(ghost);
int64_t x = 0;
switch (direction) {
case Direction::RIGHT:
x = 0;

View file

@ -1,7 +1,5 @@
#pragma once
#include <SFML/Graphics.hpp>
#include "Direction.hpp"
#include "Position.hpp"
#include <array>
@ -20,8 +18,7 @@ std::vector<GridPosition> initialPelletPositions();
std::vector<GridPosition> initialSuperPelletPositions();
inline Position penDoorPosition() {
return { 13, 11 }; }
inline Position initialPacManPosition() { return { 13.5, 23 }; }
inline Position penDoorPosition() { return { 13, 11 }; }
inline Position initialPacManPosition() { return { 13.5, 23 }; }
} // namespace pacman
} // namespace pacman

View file

@ -1,16 +1,20 @@
#pragma once
#include <SFML/Graphics.hpp>
#include "GameState.hpp"
#include "Position.hpp"
#include "Score.hpp"
#include <optional>
namespace pacman {
using Rect = sf::Rect<int>;
using Sprite = sf::Sprite;
class Canvas {
public:
Canvas();
void update(const GameState & gameState, const Score & score);
void update(const GameState & gameState);
std::optional<sf::Event> pollEvent();
private:

View file

@ -1,29 +1,20 @@
#pragma once
#include "Canvas.hpp"
#include "GameState.hpp"
#include "InputState.hpp"
#include "Canvas.hpp"
namespace pacman {
class Game {
public:
Game();
void run();
private:
Canvas canvas;
GameState gameState;
Score score;
std::chrono::milliseconds timeSinceDeath{};
void step(std::chrono::milliseconds delta, InputState inputState);
void eatPellets();
void processEvents(InputState & inputState);
void checkCollision(Ghost & ghost);
void killPacMan();
bool pacManDying() const;
void handleDeathAnimation(std::chrono::milliseconds delta);
};
} // namespace pacman

View file

@ -9,17 +9,31 @@
#include "Pinky.hpp"
#include "Score.hpp"
#include "SuperPellets.hpp"
#include "InputState.hpp"
namespace pacman {
struct GameState {
void step(std::chrono::milliseconds delta);
Blinky blinky;
Pinky pinky;
Inky inky;
Clyde clyde;
PacMan pacMan;
InputState inputState;
Pellets pellets;
SuperPellets superPellets;
Score score;
std::chrono::milliseconds timeSinceDeath{};
void checkCollision(Ghost & ghost);
void handleDeathAnimation(std::chrono::milliseconds delta);
void eatPellets();
void killPacMan();
bool isPacManDying() const;
};
} // namespace pacman

View file

@ -23,9 +23,7 @@ public:
virtual ~Ghost() = default;
GridPosition currentSprite() const;
Position position() const;
GridPosition positionInGrid() const;
void update(std::chrono::milliseconds time_delta, const GameState & gameState);
@ -41,12 +39,6 @@ private:
void updateDirection(const GameState & gameState);
protected:
State defaultStateAtDuration(std::chrono::seconds seconds);
virtual double speed(const GameState & gameState) const = 0;
virtual Position target(const GameState & gameState) const = 0;
virtual Position initialPosition() const = 0;
Atlas::Ghost spriteSet;
Direction direction = Direction::NONE;
double timeForAnimation = 0;
@ -56,6 +48,13 @@ protected:
std::chrono::milliseconds timeChase = {};
Position pos;
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 Position initialPosition() const = 0;
bool isInPen() const;
};

View file

@ -8,15 +8,10 @@
namespace pacman {
class Board;
class InputState;
class PacMan {
public:
GridPosition currentSprite() const;
Position position() const;
GridPosition positionInGrid() const;
void update(std::chrono::milliseconds time_delta, Direction input_direction);

View file

@ -18,7 +18,7 @@ public:
void pause();
private:
size_t animation_position = 0;
int64_t animation_position = 0;
double animation_position_delta = 0.0;
};

View file

@ -1,6 +1,5 @@
#pragma once
#include <SFML/Graphics.hpp>
#include <cmath>
namespace pacman {
@ -11,17 +10,13 @@ struct Position {
};
struct GridPosition {
size_t x;
size_t y;
constexpr GridPosition(size_t x, size_t y) : x(x), y(y) {}
int64_t x;
int64_t y;
constexpr GridPosition(int64_t x, int64_t y) : x(x), y(y) {}
};
using Rect = sf::Rect<int>;
using Sprite = sf::Sprite;
inline GridPosition positionToGridPosition(Position pos) {
return { size_t(std::round(pos.x)), size_t(std::round(pos.y)) };
return { int64_t(std::round(pos.x)), int64_t(std::round(pos.y)) };
}
inline Position gridPositionToPosition(GridPosition pos) {
@ -36,14 +31,14 @@ constexpr bool operator!=(const GridPosition & a, const GridPosition & b) {
return !(a == b);
}
constexpr bool operator==(const Position & a, const Position & b) {
return a.x == b.x && a.y == b.y;
inline bool operator==(const Position & a, const Position & b) {
// This is ok as a test unless x and y become very large.
constexpr double epsilon = std::numeric_limits<double>::epsilon();
return std::abs(a.x - b.x) <= epsilon && std::abs(a.y - b.y) <= epsilon;
}
constexpr bool operator!=(const Position & a, const Position & b) {
inline bool operator!=(const Position & a, const Position & b) {
return !(a == b);
}
} // namespace pacman

View file

@ -1,8 +1,10 @@
#pragma once
namespace pacman {
constexpr int DEFAULT_LIVES = 3;
struct Score {
int lives = 0;
int lives = DEFAULT_LIVES;
int points = 0;
int eatenPellets = 0;
};

View file

@ -3,8 +3,9 @@ find_package(GTest REQUIRED)
include(GoogleTest)
add_executable(pacman_tests tests.cpp)
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
add_executable(pacman_tests ${sources})
target_link_libraries(pacman_tests GTest::GTest libpacman)
gtest_discover_tests(pacman_tests TEST_PREFIX pacman:)
add_test(NAME monolithic COMMAND pacman_tests)
gtest_discover_tests(pacman_tests)

26
test/testGameState.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "GameState.hpp"
#include <gtest/gtest.h>
#include <fmt/format.h>
TEST(GameStateTest, Fuzz) {
pacman::GameState gameState;
//fmt::print("{}\n", gameState.pellets.currentPositions().size());
int pacManDeathCount = 0;
bool canCountDeath = false;
for (std::size_t i = 0; i < 50000; ++i) {
gameState.inputState.up = i % 7 ? true : false;
gameState.inputState.down = i % 11 ? true : false;
gameState.inputState.left = i % 13 ? true : false;
gameState.inputState.right = i % 17 ? true : false;
canCountDeath = !gameState.isPacManDying();
gameState.step(std::chrono::milliseconds(1000 / 30));
if (canCountDeath && gameState.isPacManDying()) {
pacManDeathCount++;
}
}
//fmt::print("{}\n", pacManDeathCount);
//fmt::print("{}\n", gameState.pellets.currentPositions().size());
}

77
test/testGhost.cpp Normal file
View file

@ -0,0 +1,77 @@
#include "Blinky.hpp"
#include "Clyde.hpp"
#include "Inky.hpp"
#include "Pinky.hpp"
#include <gtest/gtest.h>
template<typename T>
static void ghostInitHelper(const T& ghost, const double x, const double y) {
const pacman::Position pos{ x, y };
EXPECT_EQ(ghost.position(), pos);
const pacman::GridPosition gridPos = pacman::positionToGridPosition(pos);
EXPECT_EQ(ghost.positionInGrid(), gridPos);
EXPECT_FALSE(ghost.isEyes());
EXPECT_FALSE(ghost.isFrightened());
}
TEST(GhostTest, Init) {
pacman::Blinky blinky;
ghostInitHelper(blinky, 13.5, 11);
pacman::Clyde clyde;
ghostInitHelper(clyde, 15.5, 14);
pacman::Inky inky;
ghostInitHelper(inky, 13.5, 14);
pacman::Pinky pinky;
ghostInitHelper(pinky, 11.5, 14);
}
template<typename T>
static void ghostFrightenHelper(T& ghost) {
EXPECT_FALSE(ghost.isFrightened());
ghost.frighten();
EXPECT_TRUE(ghost.isFrightened());
ghost.reset();
EXPECT_FALSE(ghost.isFrightened());
}
TEST(GhostTest, Frighten) {
pacman::Blinky blinky;
ghostFrightenHelper(blinky);
pacman::Clyde clyde;
ghostFrightenHelper(clyde);
pacman::Inky inky;
ghostFrightenHelper(inky);
pacman::Pinky pinky;
ghostFrightenHelper(pinky);
}
template<typename T>
static void ghostDeadHelper(T & ghost) {
EXPECT_FALSE(ghost.isEyes());
ghost.die();
EXPECT_TRUE(ghost.isEyes());
ghost.reset();
EXPECT_FALSE(ghost.isEyes());
}
TEST(GhostTest, Dead) {
pacman::Blinky blinky;
ghostDeadHelper(blinky);
pacman::Clyde clyde;
ghostDeadHelper(clyde);
pacman::Inky inky;
ghostDeadHelper(inky);
pacman::Pinky pinky;
ghostDeadHelper(pinky);
}

8
test/testPacMan.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "PacMan.hpp"
#include <gtest/gtest.h>
TEST(PacManTest, InitialPosition) {
pacman::PacMan pacMan;
EXPECT_EQ(pacMan.position().x, 13.5);
EXPECT_EQ(pacMan.position().y, 23);
}

46
test/testPosition.cpp Normal file
View file

@ -0,0 +1,46 @@
#include "Position.hpp"
#include <gtest/gtest.h>
TEST(PositionTest, PositionInit) {
pacman::Position pos;
EXPECT_DOUBLE_EQ(pos.x, 0.0);
EXPECT_DOUBLE_EQ(pos.y, 0.0);
pacman::Position pos2{ 10.0, 20.0 };
EXPECT_DOUBLE_EQ(pos2.x, 10.0);
EXPECT_DOUBLE_EQ(pos2.y, 20.0);
}
TEST(PositionTest, GridPositionInit) {
pacman::GridPosition gridPos{ 10, 20 };
EXPECT_EQ(gridPos.x, 10);
EXPECT_EQ(gridPos.y, 20);
}
TEST(PositionTest, ConvertPositionToGridPosition) {
pacman::Position pos{ 10.0, 20.0 };
const auto gridPos = pacman::positionToGridPosition(pos);
EXPECT_EQ(gridPos.x, 10);
EXPECT_EQ(gridPos.y, 20);
}
TEST(PositionTest, ConvertGridPositionToPosition) {
pacman::GridPosition gridPos{ 10, 20 };
const auto pos = pacman::gridPositionToPosition(gridPos);
EXPECT_DOUBLE_EQ(pos.x, 10.0);
EXPECT_DOUBLE_EQ(pos.y, 20.0);
}
TEST(PositionTest, PositionEquality) {
pacman::Position pos1{ 10.0, 20.0 };
pacman::Position pos2{ 10.0, 20.0 };
EXPECT_TRUE(pos1 == pos2);
pacman::Position pos3{ 9.9, 19.9 };
EXPECT_FALSE(pos1 == pos3);
pos3.x += 0.1;
pos3.y += 0.1;
EXPECT_TRUE(pos1 == pos3);
}

View file

@ -1,12 +1,5 @@
#include "PacMan.hpp"
#include <gtest/gtest.h>
TEST(PacManTest, InitialPosition) {
pacman::PacMan pacMan;
EXPECT_EQ(pacMan.position().x, 13.5);
EXPECT_EQ(pacMan.position().y, 23);
}
int main(int argc, char * argv[]) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();