-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: unit tests for pvpgame and pvpattacker (#3)
* wip: pvp tests * feat: unit tests for pvp
- Loading branch information
1 parent
c93fee4
commit b448a5d
Showing
3 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
#include "attacker/pvpattacker/pvpattacker.hpp" | ||
|
||
#include <catch2/catch.hpp> | ||
#include <utility> | ||
#include <vector> | ||
|
||
SCENARIO("PvPAttacker::get_nearest_defender_index") { | ||
GIVEN("a list of defenders") { | ||
PvPAttacker::attribute_dictionary.clear(); | ||
PvPAttacker::attribute_dictionary.insert( | ||
std::make_pair(AttackerType::A1, Attributes(0, 2, 0, 4, 0, false))); | ||
PvPAttacker::attribute_dictionary.insert( | ||
std::make_pair(AttackerType::A2, Attributes(0, 4, 0, 2, 0, false))); | ||
PvPAttacker::attribute_dictionary.insert( | ||
std::make_pair(AttackerType::A3, Attributes(0, 4, 0, 4, 0, true))); | ||
std::vector<PvPAttacker> attackers{ | ||
PvPAttacker::construct(AttackerType::A1, {0, 1}, Owner::PLAYER1), | ||
PvPAttacker::construct(AttackerType::A2, {0, 2}, Owner::PLAYER1), | ||
PvPAttacker::construct(AttackerType::A3, {0, 4}, Owner::PLAYER1), | ||
}; | ||
WHEN("the list is empty") { | ||
std::vector<PvPAttacker> defenders; | ||
|
||
PvPAttacker currentAttacker = attackers[0]; | ||
|
||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
|
||
THEN("nearest defender does not exist") { | ||
REQUIRE(nearest_defender_index.has_value() == false); | ||
} | ||
} | ||
|
||
WHEN("list has defenders") { | ||
WHEN("the attacker is aerial") { | ||
PvPAttacker currentAttacker = attackers[2]; | ||
WHEN("the defender is aerial") { | ||
std::vector<PvPAttacker> defenders{ | ||
PvPAttacker::construct(AttackerType::A3, {17, 3}, Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A3, {10, 2}, Owner::PLAYER2), | ||
}; | ||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
THEN("nearest defender is the closest defender") { | ||
REQUIRE(nearest_defender_index.has_value() == true); | ||
REQUIRE(nearest_defender_index.value() == 1); | ||
} | ||
} | ||
WHEN("the defender is ground") { | ||
std::vector<PvPAttacker> defenders{ | ||
PvPAttacker::construct(AttackerType::A1, {4, 4}, Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A2, {0, 9}, Owner::PLAYER2), | ||
}; | ||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
THEN("nearest defender is the closest defender") { | ||
REQUIRE(nearest_defender_index.has_value() == true); | ||
REQUIRE(nearest_defender_index.value() == 0); | ||
} | ||
} | ||
WHEN("the defender can be either aerial or ground") { | ||
std::vector<PvPAttacker> defenders{ | ||
PvPAttacker::construct(AttackerType::A1, {3, 0}, Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A3, {1, 4}, Owner::PLAYER2), | ||
}; | ||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
THEN("nearest defender is the aerial defender in range") { | ||
REQUIRE(nearest_defender_index.has_value() == true); | ||
REQUIRE(nearest_defender_index.value() == 1); | ||
} | ||
} | ||
} | ||
|
||
WHEN("the attacker is ground") { | ||
PvPAttacker currentAttacker = attackers[1]; | ||
WHEN("the defender is aerial") { | ||
std::vector<PvPAttacker> defenders{ | ||
PvPAttacker::construct(AttackerType::A3, {1, 2}, Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A3, {7, 3}, Owner::PLAYER2), | ||
}; | ||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
THEN("nearest defender does not exist") { | ||
REQUIRE(nearest_defender_index.has_value() == false); | ||
} | ||
} | ||
WHEN("the defender is ground") { | ||
std::vector<PvPAttacker> defenders{ | ||
PvPAttacker::construct(AttackerType::A1, {0, 9}, Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A2, {4, 4}, Owner::PLAYER2), | ||
}; | ||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
THEN("nearest defender is the closest defender") { | ||
REQUIRE(nearest_defender_index.has_value() == true); | ||
REQUIRE(nearest_defender_index.value() == 1); | ||
} | ||
} | ||
WHEN("the defender can be either aerial or ground") { | ||
std::vector<PvPAttacker> defenders{ | ||
PvPAttacker::construct(AttackerType::A1, {6, 1}, Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A3, {3, 0}, Owner::PLAYER2), | ||
}; | ||
auto nearest_defender_index = | ||
currentAttacker.get_nearest_defender_index(defenders); | ||
THEN("nearest defender is the ground defender") { | ||
REQUIRE(nearest_defender_index.has_value() == true); | ||
REQUIRE(nearest_defender_index.value() == 0); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
#include "attacker/pvpattacker/pvpattacker.hpp" | ||
#include "game/pvpgame.hpp" | ||
#include "utils/game_map.hpp" | ||
|
||
#include <catch2/catch.hpp> | ||
|
||
SCENARIO("PVPGame::simulate") { | ||
|
||
GIVEN("a game with 2 attackers each") { | ||
Map::no_of_rows = 10; | ||
Map::no_of_cols = 10; | ||
Map game_map({ | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
}); | ||
PvPAttacker::attribute_dictionary.clear(); | ||
PvPAttacker::attribute_dictionary.insert(std::make_pair( | ||
AttackerType::A1, | ||
Attributes( | ||
10, 1, 5, 4, 50, | ||
false))); // hp = 10, range = 1, damage = 5, speed = 4, cost = 50 | ||
PvPAttacker::attribute_dictionary.insert(std::make_pair( | ||
AttackerType::A2, | ||
Attributes( | ||
20, 1, 5, 4, 100, | ||
false))); // hp = 20, range = 1, damage = 5, speed = 4, cost = 100 | ||
PvPAttacker::attribute_dictionary.insert(std::make_pair( | ||
AttackerType::A3, | ||
Attributes( | ||
20, 1, 5, 4, 150, | ||
true))); // hp = 20, range = 1, damage = 5, speed = 4, cost = 150 | ||
std::vector<std::pair<Position, AttackerType>> first_turn_attackers{ | ||
{Position(0, 2), AttackerType::A1}, | ||
{Position(0, 4), AttackerType::A3}, | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> first_turn_defenders{ | ||
{Position(9, 2), AttackerType::A2}, | ||
{Position(9, 4), AttackerType::A3}, | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> second_turn_attackers{ | ||
{Position(0, 3), AttackerType::A2}, | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> second_turn_defenders{ | ||
{Position(9, 6), AttackerType::A1}, | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> third_turn_attackers{ | ||
{Position(3, 3), AttackerType::A2}, // won't spawn as invalid position | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> third_turn_defenders; | ||
std::vector<std::pair<Position, AttackerType>> fourth_turn_attackers{ | ||
{Position(0, 7), AttackerType::A2}, | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> fourth_turn_defenders{ | ||
{Position(9, 9), AttackerType::A2}, | ||
}; | ||
std::vector<std::pair<Position, AttackerType>> fifth_turn_attackers; | ||
std::vector<std::pair<Position, AttackerType>> fifth_turn_defenders; | ||
std::vector<std::pair<Position, AttackerType>> sixth_turn_attackers; | ||
std::vector<std::pair<Position, AttackerType>> sixth_turn_defenders; | ||
std::vector<PvPAttacker> attackers; | ||
std::vector<PvPAttacker> defenders; | ||
PvPGame pvpgame(attackers, defenders); | ||
pvpgame.FIXED_COINS_PER_TURN = 500; | ||
|
||
// FIRST TURN | ||
PvPGame first_turn_state = | ||
pvpgame.simulate({{0, 0}, {1, 1}}, first_turn_attackers, | ||
{{0, 0}, {1, 1}}, first_turn_defenders); | ||
|
||
first_turn_state.FIXED_COINS_PER_TURN = 500; | ||
// SECOND TURN | ||
PvPGame second_turn_state = first_turn_state.simulate( | ||
{}, second_turn_attackers, {}, second_turn_defenders); | ||
second_turn_state.FIXED_COINS_PER_TURN = 500; | ||
// THIRD TURN | ||
PvPGame third_turn_state = second_turn_state.simulate( | ||
{}, third_turn_attackers, {}, third_turn_defenders); | ||
third_turn_state.FIXED_COINS_PER_TURN = | ||
60; // won't spawn as insufficient coins | ||
// FOURTH TURN | ||
PvPGame fourth_turn_state = third_turn_state.simulate( | ||
{}, fourth_turn_attackers, {}, fourth_turn_defenders); | ||
|
||
// FIFTH TURN | ||
PvPGame fifth_turn_state = fourth_turn_state.simulate( | ||
{}, fifth_turn_attackers, {}, fifth_turn_defenders); | ||
|
||
// SIXTH TURN | ||
PvPGame sixth_turn_state = fifth_turn_state.simulate( | ||
{}, sixth_turn_attackers, {}, sixth_turn_defenders); | ||
|
||
WHEN("first turn is simulated") { | ||
THEN("all troops are in their respective positions") { | ||
REQUIRE(first_turn_state.get_player_1_attackers() == | ||
std::vector<PvPAttacker>{ | ||
PvPAttacker::construct(AttackerType::A1, {0, 2}, | ||
Owner::PLAYER1), | ||
PvPAttacker::construct(AttackerType::A3, {0, 4}, | ||
Owner::PLAYER1), | ||
}); | ||
} | ||
THEN("set targets are invalid") { | ||
REQUIRE( | ||
std::ranges::none_of(first_turn_state.get_player_1_attackers(), | ||
[](const PvPAttacker &pvpattacker) { | ||
return pvpattacker.is_target_set_by_player(); | ||
})); | ||
REQUIRE( | ||
std::ranges::none_of(first_turn_state.get_player_2_attackers(), | ||
[](const PvPAttacker &pvpattacker) { | ||
return pvpattacker.is_target_set_by_player(); | ||
})); | ||
} | ||
THEN("nearest defender is identified correctly") { | ||
REQUIRE(first_turn_state.get_player_1_attackers()[0] | ||
.get_nearest_defender_index( | ||
first_turn_state.get_player_2_attackers()) | ||
.value() == 0); | ||
REQUIRE(first_turn_state.get_player_1_attackers()[1] | ||
.get_nearest_defender_index( | ||
first_turn_state.get_player_2_attackers()) | ||
.value() == 1); | ||
} | ||
} | ||
WHEN("second turn is simulated") { | ||
THEN("all troops of both players are alive and new troops are spawned") { | ||
REQUIRE(second_turn_state.get_player_1_attackers() == | ||
std::vector<PvPAttacker>{ | ||
PvPAttacker::construct(AttackerType::A1, {4, 2}, | ||
Owner::PLAYER1), | ||
PvPAttacker::construct(AttackerType::A3, {4, 4}, | ||
Owner::PLAYER1), | ||
PvPAttacker::construct(AttackerType::A2, {0, 3}, | ||
Owner::PLAYER1), | ||
}); | ||
REQUIRE(second_turn_state.get_player_2_attackers() == | ||
std::vector<PvPAttacker>{ | ||
PvPAttacker::construct(AttackerType::A2, {5, 2}, | ||
Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A3, {5, 4}, | ||
Owner::PLAYER2), | ||
PvPAttacker::construct(AttackerType::A1, {9, 6}, | ||
Owner::PLAYER2), | ||
}); | ||
} | ||
THEN("no troops have attacked yet") { | ||
for (int i = 0; i < 2; i++) { | ||
REQUIRE(second_turn_state.get_player_1_attackers()[i].get_hp() == | ||
first_turn_state.get_player_1_attackers()[i].get_hp()); | ||
REQUIRE(second_turn_state.get_player_2_attackers()[i].get_hp() == | ||
first_turn_state.get_player_2_attackers()[i].get_hp()); | ||
} | ||
} | ||
THEN("troops spawned in the previous turn are in the MOVING state and " | ||
"troops spawned in this turn are in the SPAWNED state") { | ||
REQUIRE(second_turn_state.get_player_1_attackers()[2].get_state() == | ||
Attacker::State::SPAWNED); | ||
REQUIRE(second_turn_state.get_player_2_attackers()[2].get_state() == | ||
Attacker::State::SPAWNED); | ||
for (int index = 0; index < 2; index++) { | ||
REQUIRE( | ||
second_turn_state.get_player_1_attackers()[index].get_state() == | ||
Attacker::State::MOVING); | ||
REQUIRE( | ||
second_turn_state.get_player_2_attackers()[index].get_state() == | ||
Attacker::State::MOVING); | ||
} | ||
} | ||
} | ||
WHEN("third turn is simulated") { | ||
THEN("all troops of both players are alive but no new troops are " | ||
"spawned") { | ||
REQUIRE(third_turn_state.get_player_1_attackers().size() == 3); | ||
REQUIRE(third_turn_state.get_player_2_attackers().size() == 3); | ||
} | ||
THEN("troops spawned in first turn have started attacking and " | ||
"troops spawned in second turn have not started attacking yet") { | ||
for (int i = 0; i < 2; i++) { | ||
REQUIRE(second_turn_state.get_player_1_attackers()[i].get_hp() > | ||
third_turn_state.get_player_1_attackers()[i].get_hp()); | ||
REQUIRE(second_turn_state.get_player_2_attackers()[i].get_hp() > | ||
third_turn_state.get_player_2_attackers()[i].get_hp()); | ||
} | ||
REQUIRE(second_turn_state.get_player_1_attackers()[2].get_hp() == | ||
third_turn_state.get_player_1_attackers()[2].get_hp()); | ||
REQUIRE(second_turn_state.get_player_2_attackers()[2].get_hp() == | ||
third_turn_state.get_player_2_attackers()[2].get_hp()); | ||
} | ||
} | ||
WHEN("fourth turn is simulated") { | ||
THEN("some troops are gone and no new troops are spawned") { | ||
REQUIRE(fourth_turn_state.get_player_1_attackers().size() == 2); | ||
REQUIRE(fourth_turn_state.get_player_2_attackers().size() == 3); | ||
} | ||
THEN("first attacker is dead for player 1") { | ||
REQUIRE(fourth_turn_state.get_player_1_attackers()[0].get_id() != | ||
third_turn_state.get_player_1_attackers()[0].get_id()); | ||
} | ||
} | ||
WHEN("fifth turn is simulated") { | ||
THEN("some troops are gone") { | ||
REQUIRE(fifth_turn_state.get_player_1_attackers().size() == 2); | ||
REQUIRE(fifth_turn_state.get_player_2_attackers().size() == 2); | ||
} | ||
THEN("first attacker is dead for player 2") { | ||
REQUIRE(fifth_turn_state.get_player_2_attackers()[0].get_id() != | ||
fourth_turn_state.get_player_2_attackers()[0].get_id()); | ||
} | ||
} | ||
WHEN("sixth turn is simulated") { | ||
THEN("some troops are gone") { | ||
REQUIRE(sixth_turn_state.get_player_1_attackers().size() == 1); | ||
REQUIRE(sixth_turn_state.get_player_2_attackers().size() == 1); | ||
} | ||
THEN("only the last spawned attacker is aliive for both players") { | ||
REQUIRE(sixth_turn_state.get_player_1_attackers()[0].get_id() == | ||
second_turn_state.get_player_1_attackers()[2].get_id()); | ||
REQUIRE(sixth_turn_state.get_player_2_attackers()[0].get_id() == | ||
second_turn_state.get_player_2_attackers()[2].get_id()); | ||
} | ||
} | ||
} | ||
} |