Skip to content

Commit

Permalink
Fix : edge cases for attackers and defenders (#4)
Browse files Browse the repository at this point in the history
* fix: remove unecessary else and ununsed variable

* Fix : edge cases for attackers and defenders

* Fix : EOF

* Fix : Attackers attack all types of Defenders

---------

Co-authored-by: Ram-20062003 <[email protected]>
  • Loading branch information
shubham-1806 and Ram-20062003 authored Mar 7, 2023
1 parent 78599d9 commit fb81556
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 139 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build/
compile_commands.json
.cache

33 changes: 7 additions & 26 deletions src/attacker/attacker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,13 @@ void Attacker::update_state() {
if (defenders.empty()) {
return std::nullopt;
}

if (this->is_aerial_type()) {
auto nearest_defender = std::min_element(
defenders.begin(), defenders.end(),
[this](const Defender a, const Defender b) {
return this->get_position().distance_to(a.get_position()) <
this->get_position().distance_to(b.get_position());
});
return std::distance(defenders.begin(), nearest_defender);
} else {
auto nearest_defender = std::min_element(
defenders.begin(), defenders.end(),
[this](const Defender a, const Defender b) {
if (a.is_aerial_type() && !b.is_aerial_type()) {
return false;
}
if (b.is_aerial_type() && !a.is_aerial_type()) {
return true;
}
return this->get_position().distance_to(a.get_position()) <
this->get_position().distance_to(b.get_position());
});
return std::distance(defenders.begin(), nearest_defender);
}

return std::nullopt;
auto nearest_defender = std::min_element(
defenders.begin(), defenders.end(),
[this](const Defender a, const Defender b) {
return this->get_position().distance_to(a.get_position()) <
this->get_position().distance_to(b.get_position());
});
return std::distance(defenders.begin(), nearest_defender);
}

void Attacker::move(Position position) {
Expand Down
35 changes: 22 additions & 13 deletions src/defender/defender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,33 @@ std::optional<size_t> Defender::get_nearest_attacker_index(
auto nearest_attacker = std::min_element(
attackers.begin(), attackers.end(),
[this](const Attacker a, const Attacker b) {
return this->get_position().distance_to(a.get_position()) <
this->get_position().distance_to(b.get_position());
});
return std::distance(attackers.begin(), nearest_attacker);
} else {
auto nearest_defender = std::min_element(
attackers.begin(), attackers.end(),
[this](const Attacker a, const Attacker b) {
if (a.is_aerial_type() && !b.is_aerial_type()) {
return false;
}
if (b.is_aerial_type() && !a.is_aerial_type()) {
if (a.is_aerial_type() && !b.is_aerial_type() &&
this->is_in_range(a)) {
return true;
}
if (b.is_aerial_type() && !a.is_aerial_type() &&
this->is_in_range(b)) {
return false;
}
return this->get_position().distance_to(a.get_position()) <
this->get_position().distance_to(b.get_position());
});
return std::distance(attackers.begin(), nearest_defender);
return std::distance(attackers.begin(), nearest_attacker);
}
auto nearest_attacker = std::min_element(
attackers.begin(), attackers.end(),
[this](const Attacker a, const Attacker b) {
if (a.is_aerial_type() && !b.is_aerial_type()) {
return false;
}
if (b.is_aerial_type() && !a.is_aerial_type()) {
return true;
}
return this->get_position().distance_to(a.get_position()) <
this->get_position().distance_to(b.get_position());
});
if (!nearest_attacker->is_aerial_type()) {
return std::distance(attackers.begin(), nearest_attacker);
}

return std::nullopt;
Expand Down
6 changes: 1 addition & 5 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ Game Game::simulate(
}

if (defender_index.has_value()) {
if ((attacker.is_in_range(defenders[*defender_index])) &&
((attacker.is_aerial_type()) ||
(!defenders[*defender_index].is_aerial_type()))) {
if ((attacker.is_in_range(defenders[*defender_index]))) {
attacker.attack(defenders[*defender_index]);
} else {
attacker.set_destination(defenders[*defender_index].get_position());
Expand Down Expand Up @@ -140,8 +138,6 @@ Game Game::simulate(
ranges::for_each(spawn_positions, [&](const auto &spawn_details) {
const auto &[position, attacker_type] = spawn_details;
const unsigned price = Attacker::attribute_dictionary[attacker_type].price;
const bool is_aerial =
Attacker::attribute_dictionary[attacker_type].is_aerial;
if (price > coins_left) {
return;
}
Expand Down
55 changes: 2 additions & 53 deletions test/attacker_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,61 +202,10 @@ SCENARIO("Ground Attacker::get_nearest_defender_index") {

auto nearest_defender = attacker.get_nearest_defender_index(defenders);

THEN("nearest ground defender is the closet defenders") {
THEN("nearest defender is the closet defenders") {
REQUIRE(nearest_defender.has_value() == true);
REQUIRE(nearest_defender.value() == 4);
REQUIRE(nearest_defender.value() == 0);
}
}
}
WHEN("The list has only aerial defenders") {
Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 0, 0, 0, 0, true)));
std::vector<Defender> defenders{
Defender::construct(DefenderType::D1, {0, 1}),
Defender::construct(DefenderType::D1, {0, 2}),
Defender::construct(DefenderType::D1, {0, 4}),
Defender::construct(DefenderType::D1, {0, 5}),
Defender::construct(DefenderType::D1, {0, 3}),
Defender::construct(DefenderType::D1, {0, 6}),
Defender::construct(DefenderType::D1, {0, 7})};

Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A1, Attributes(0, 0, 0, 0, 0, false)));
Attacker attacker = Attacker::construct(AttackerType::A1, {0, 0});

auto nearest_defender = attacker.get_nearest_defender_index(defenders);

THEN("nearest aerial defender is the closet defenders") {
REQUIRE(nearest_defender.has_value() == true);
REQUIRE(nearest_defender.value() == 0);
}
}

WHEN("The list has only ground defenders") {
Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D2, Attributes(0, 0, 0, 0, 0, false)));
std::vector<Defender> defenders{
Defender::construct(DefenderType::D2, {0, 1}),
Defender::construct(DefenderType::D2, {0, 2}),
Defender::construct(DefenderType::D2, {0, 4}),
Defender::construct(DefenderType::D2, {0, 5}),
Defender::construct(DefenderType::D2, {0, 3}),
Defender::construct(DefenderType::D2, {0, 6}),
Defender::construct(DefenderType::D2, {0, 7})};

Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A1, Attributes(0, 0, 0, 0, 0, false)));
Attacker attacker = Attacker::construct(AttackerType::A1, {0, 0});

auto nearest_defender = attacker.get_nearest_defender_index(defenders);

THEN("nearest ground defender is the closet defenders") {
REQUIRE(nearest_defender.has_value() == true);
REQUIRE(nearest_defender.value() == 0);
}
}
}
146 changes: 104 additions & 42 deletions test/defender_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,56 +69,118 @@ SCENARIO("Ground Defender::get_nearest_attacker_index") {
REQUIRE(nearest_attacker.value() == 4);
}
}
}
WHEN("The list has only aerial attackers") {
Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A1, Attributes(0, 0, 0, 0, 0, true)));
std::vector<Attacker> attackers{
Attacker::construct(AttackerType::A1, {0, 1}),
Attacker::construct(AttackerType::A1, {0, 2}),
Attacker::construct(AttackerType::A1, {0, 4}),
Attacker::construct(AttackerType::A1, {0, 5}),
Attacker::construct(AttackerType::A1, {0, 3}),
Attacker::construct(AttackerType::A1, {0, 6}),
Attacker::construct(AttackerType::A1, {0, 7})};

Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 0, 0, 0, 0, false)));
Defender defender = Defender::construct(DefenderType::D1, {0, 0});
WHEN("The list has only aerial attackers") {
Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A1, Attributes(0, 0, 0, 0, 0, true)));
std::vector<Attacker> attackers{
Attacker::construct(AttackerType::A1, {0, 1}),
Attacker::construct(AttackerType::A1, {0, 2}),
Attacker::construct(AttackerType::A1, {0, 4}),
Attacker::construct(AttackerType::A1, {0, 5}),
Attacker::construct(AttackerType::A1, {0, 3}),
Attacker::construct(AttackerType::A1, {0, 6}),
Attacker::construct(AttackerType::A1, {0, 7})};

Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 0, 0, 0, 0, false)));
Defender defender = Defender::construct(DefenderType::D1, {0, 0});

auto nearest_attacker = defender.get_nearest_attacker_index(attackers);

THEN("No attacker should be returned") {
REQUIRE(nearest_attacker.has_value() == false);
}
}

WHEN("The list has only ground attackers") {
Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A2, Attributes(0, 0, 0, 0, 0, false)));
std::vector<Attacker> attackers{
Attacker::construct(AttackerType::A2, {0, 1}),
Attacker::construct(AttackerType::A2, {0, 2}),
Attacker::construct(AttackerType::A2, {0, 4}),
Attacker::construct(AttackerType::A2, {0, 5}),
Attacker::construct(AttackerType::A2, {0, 3}),
Attacker::construct(AttackerType::A2, {0, 6}),
Attacker::construct(AttackerType::A2, {0, 7})};

auto nearest_attacker = defender.get_nearest_attacker_index(attackers);
Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 0, 0, 0, 0, false)));
Defender defender = Defender::construct(DefenderType::D1, {0, 0});

THEN("nearest aerial attacker is the closet attacker") {
REQUIRE(nearest_attacker.has_value() == true);
REQUIRE(nearest_attacker.value() == 0);
auto nearest_attacker = defender.get_nearest_attacker_index(attackers);

THEN("nearest ground attacker is the closet attacker") {
REQUIRE(nearest_attacker.has_value() == true);
REQUIRE(nearest_attacker.value() == 0);
}
}
}
}

WHEN("The list has only ground attackers") {
Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A2, Attributes(0, 0, 0, 0, 0, false)));
std::vector<Attacker> attackers{
Attacker::construct(AttackerType::A2, {0, 1}),
Attacker::construct(AttackerType::A2, {0, 2}),
Attacker::construct(AttackerType::A2, {0, 4}),
Attacker::construct(AttackerType::A2, {0, 5}),
Attacker::construct(AttackerType::A2, {0, 3}),
Attacker::construct(AttackerType::A2, {0, 6}),
Attacker::construct(AttackerType::A2, {0, 7})};
SCENARIO("Aerial Defender::get_nearest_attacker_index") {
GIVEN("List of different types of attackers around") {
WHEN("The list has both ground attackers and aerial attackers with aerial "
"attacker in range") {
Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A1, Attributes(0, 0, 0, 0, 0, true)));
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A2, Attributes(0, 0, 0, 0, 0, false)));
std::vector<Attacker> attackers{
Attacker::construct(AttackerType::A1, {0, 1}),
Attacker::construct(AttackerType::A1, {0, 2}),
Attacker::construct(AttackerType::A2, {0, 4}),
Attacker::construct(AttackerType::A2, {0, 5}),
Attacker::construct(AttackerType::A2, {0, 3}),
Attacker::construct(AttackerType::A2, {0, 6}),
Attacker::construct(AttackerType::A2, {0, 0})};

Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 0, 0, 0, 0, false)));
Defender defender = Defender::construct(DefenderType::D1, {0, 0});
Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 3, 0, 0, 0, true)));
Defender defender = Defender::construct(DefenderType::D1, {0, 0});

auto nearest_attacker = defender.get_nearest_attacker_index(attackers);
auto nearest_attacker = defender.get_nearest_attacker_index(attackers);

THEN("nearest ground attacker is the closet attacker") {
REQUIRE(nearest_attacker.has_value() == true);
REQUIRE(nearest_attacker.value() == 0);
THEN("closest aerial attacker is returned") {
REQUIRE(nearest_attacker.has_value() == true);
REQUIRE(nearest_attacker.value() == 0);
}
}

WHEN("The list has both ground attackers and aerial attackers with aerial "
"attacker not in range") {
Attacker::attribute_dictionary.clear();
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A1, Attributes(0, 0, 0, 0, 0, true)));
Attacker::attribute_dictionary.insert(
std::make_pair(AttackerType::A2, Attributes(0, 0, 0, 0, 0, false)));
std::vector<Attacker> attackers{
Attacker::construct(AttackerType::A1, {0, 5}),
Attacker::construct(AttackerType::A1, {0, 6}),
Attacker::construct(AttackerType::A2, {0, 8}),
Attacker::construct(AttackerType::A2, {0, 5}),
Attacker::construct(AttackerType::A2, {0, 3}),
Attacker::construct(AttackerType::A2, {0, 6}),
Attacker::construct(AttackerType::A2, {0, 0})};

Defender::attribute_dictionary.clear();
Defender::attribute_dictionary.insert(
std::make_pair(DefenderType::D1, Attributes(0, 3, 0, 0, 0, true)));
Defender defender = Defender::construct(DefenderType::D1, {0, 0});

auto nearest_attacker = defender.get_nearest_attacker_index(attackers);

THEN("closest attacker irrespective of aerial or ground is returned") {
REQUIRE(nearest_attacker.has_value() == true);
REQUIRE(nearest_attacker.value() == 6);
}
}
}
}
}

0 comments on commit fb81556

Please sign in to comment.