Merge pull request #31 from mod-cpp/olafurw/fruit_tests

Adding unit tests for fruits. Also some minor cleanup.
This commit is contained in:
Ólafur Waage 2021-09-15 14:44:48 +02:00 committed by GitHub
commit 0c8bc38b7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 29 deletions

View file

@ -107,13 +107,15 @@ void Canvas::renderFruits(const Fruits & fruit, int eatenFruits) {
const auto & pos = fruit.position(); const auto & pos = fruit.position();
renderSprite(sprite, pos); renderSprite(sprite, pos);
} }
const auto x = static_cast<size_t>(LEFT_MARGIN + TARGET_MAZE_WIDTH + LEFT_MARGIN); const auto x = static_cast<size_t>(LEFT_MARGIN + TARGET_MAZE_WIDTH + LEFT_MARGIN);
const auto y = static_cast<size_t>((TARGET_MAZE_HEIGHT / 3.0) * 2); const auto y = static_cast<size_t>((TARGET_MAZE_HEIGHT / 3.0) * 2);
for (auto i = 0; i < eatenFruits + 1; i++) { for (auto i = 0; i < eatenFruits + 1; i++) {
auto sprite_position = float(i) * SPRITE_WIDTH * 1.5f; const auto sprite_position = float(i) * SPRITE_WIDTH * 1.5f;
sf::Vector2f pos{ x + sprite_position, y }; const sf::Vector2f pos{ x + sprite_position, y };
sprite.setPosition(pos.x, pos.y); sprite.setPosition(pos.x, pos.y);
window.draw(sprite); window.draw(sprite);
} }
} }

View file

@ -10,11 +10,8 @@ void Fruits::update(std::chrono::milliseconds time_delta, const GameState & game
if (time_visible > std::chrono::seconds(9)) { if (time_visible > std::chrono::seconds(9)) {
hide(); hide();
} } else if ((index == 0 && gameState.score.eatenPellets >= 70) || (index == 1 && gameState.score.eatenPellets >= 170)) {
// We show the fruit twice, once at 70 pellets and once at 170
// Two times the same fruit for each level, after 70 and 170 pellets eaten
else if ((index == 0 && gameState.score.eatenPellets >= 70)
|| (index == 1 && gameState.score.eatenPellets >= 170)) {
visible = true; visible = true;
} }
} }
@ -39,6 +36,10 @@ int Fruits::value() const {
} }
int Fruits::eat() { int Fruits::eat() {
if (!isVisible()) {
return 0;
}
hide(); hide();
return value(); return value();
} }
@ -49,5 +50,4 @@ void Fruits::hide() {
visible = false; visible = false;
} }
} // namespace pacman
}

View file

@ -9,17 +9,20 @@ struct GameState;
class Fruits { class Fruits {
public: public:
void update(std::chrono::milliseconds time_delta, const GameState & gameState); void update(std::chrono::milliseconds time_delta, const GameState & gameState);
GridPosition currentSprite() const; GridPosition currentSprite() const;
Position position() const; Position position() const;
bool isVisible() const; bool isVisible() const;
int value() const; int value() const;
int eat(); int eat();
private: private:
void hide();
bool visible = false; bool visible = false;
int index = 0; int index = 0;
std::chrono::milliseconds time_visible{ 0 }; std::chrono::milliseconds time_visible{ 0 };
void hide();
}; };
} // namespace pacman } // namespace pacman

77
test/testFruits.cpp Normal file
View file

@ -0,0 +1,77 @@
#include "Fruits.hpp"
#include "GameState.hpp"
#include <catch2/catch.hpp>
TEST_CASE("Fruit default initialization", "[fruits]") {
pacman::Fruits fruit;
REQUIRE_FALSE(fruit.isVisible());
REQUIRE(fruit.value() == 100);
REQUIRE(fruit.currentSprite().x == 3);
REQUIRE(fruit.currentSprite().y == 8);
REQUIRE(fruit.position().x == Approx(13.5));
REQUIRE(fruit.position().y == Approx(17));
}
TEST_CASE("Fruit Visibility", "[fruits]") {
pacman::GameState gameState;
REQUIRE_FALSE(gameState.fruit.isVisible());
SECTION("9 seconds but no pellets eaten") {
gameState.fruit.update(std::chrono::milliseconds(9001), gameState);
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);
REQUIRE(gameState.fruit.isVisible());
// Wait more than 9 seconds and then check the state again
gameState.fruit.update(std::chrono::milliseconds(9001), gameState);
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);
REQUIRE(gameState.fruit.isVisible());
// Wait more than 9 seconds and then check the state again
gameState.fruit.update(std::chrono::milliseconds(9001), gameState);
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);
REQUIRE(gameState.fruit.isVisible());
// Wait more than 9 seconds and then check the state again
gameState.fruit.update(std::chrono::milliseconds(9001), gameState);
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);
REQUIRE_FALSE(gameState.fruit.isVisible());
// Wait more than 9 seconds and then check the state again
gameState.fruit.update(std::chrono::milliseconds(9001), gameState);
REQUIRE_FALSE(gameState.fruit.isVisible());
}
SECTION("Eating a fruit") {
REQUIRE(gameState.fruit.eat() == 0);
gameState.score.eatenPellets = 70;
gameState.fruit.update(std::chrono::milliseconds(1), gameState);
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);
REQUIRE_FALSE(gameState.fruit.isVisible());
REQUIRE(gameState.fruit.eat() == 0);
}
}