diff --git a/src/main/main.cpp b/src/main/main.cpp index f819b8d..3c5aaa1 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -10,6 +10,7 @@ #include "state/actor/soldier.h" #include "state/actor/villager.h" #include "state/command_giver.h" +#include "state/gold_manager/gold_manager.h" #include "state/map/map.h" #include "state/path_planner/path_planner.h" #include "state/player_state.h" @@ -90,11 +91,12 @@ unique_ptr BuildMap() { } unique_ptr BuildGoldManager() { + std::vector> gold_mines; return make_unique( array{GOLD_START, GOLD_START}, GOLD_MAX, SOLDIER_KILL_REWARD_AMOUNT, VILLAGER_KILL_REWARD_AMOUNT, - FACTORY_KILL_REWARD_AMOUNT, FACTORY_SUICIDE_PENALTY, VILLAGER_COST, - SOLDIER_COST, FACTORY_COST, MINING_REWARD); + FACTORY_KILL_REWARD_AMOUNT, VILLAGER_COST, SOLDIER_COST, FACTORY_COST, + MINING_REWARD, std::move(gold_mines)); } unique_ptr BuildPathPlanner(Map *map) { diff --git a/src/state/CMakeLists.txt b/src/state/CMakeLists.txt index d6369af..aa044f1 100644 --- a/src/state/CMakeLists.txt +++ b/src/state/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCE_FILES src/actor/factory.cpp src/map/map.cpp src/gold_manager/gold_manager.cpp + src/gold_manager/gold_mine.cpp src/actor/soldier_states/soldier_state.cpp src/actor/soldier_states/soldier_idle_state.cpp src/actor/soldier_states/soldier_attack_state.cpp diff --git a/src/state/include/state/actor/actor.fwd.h b/src/state/include/state/actor/actor.fwd.h new file mode 100644 index 0000000..93ca298 --- /dev/null +++ b/src/state/include/state/actor/actor.fwd.h @@ -0,0 +1,11 @@ +/** + * @file actor.fwd.h + * Forward declaration for the Actor class + */ + +#pragma once + +namespace state { + +class Actor; +} diff --git a/src/state/include/state/actor/actor.h b/src/state/include/state/actor/actor.h index 0f01df7..e01e543 100644 --- a/src/state/include/state/actor/actor.h +++ b/src/state/include/state/actor/actor.h @@ -6,7 +6,7 @@ #pragma once #include "physics/vector.hpp" -#include "state/gold_manager/gold_manager.h" +#include "state/interfaces/i_gold_manager.h" #include "state/interfaces/i_updatable.h" #include "state/state_export.h" #include "state/utilities.h" @@ -14,9 +14,7 @@ #include namespace state { -/** - * Actor base class - */ + class STATE_EXPORT Actor : public IUpdatable { protected: /** @@ -58,13 +56,13 @@ class STATE_EXPORT Actor : public IUpdatable { /** * Gold manager instance to perform transactions */ - GoldManager *gold_manager; + IGoldManager *gold_manager; public: Actor(); Actor(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager); + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager); virtual ~Actor() {} @@ -118,7 +116,7 @@ class STATE_EXPORT Actor : public IUpdatable { * * @return gold_manager Unit's GoldManager Pointer */ - GoldManager *GetGoldManager(); + IGoldManager *GetGoldManager(); /** * Get the maximum hp of the actor diff --git a/src/state/include/state/actor/factory.fwd.h b/src/state/include/state/actor/factory.fwd.h index 505876f..afd58f5 100644 --- a/src/state/include/state/actor/factory.fwd.h +++ b/src/state/include/state/actor/factory.fwd.h @@ -1,5 +1,5 @@ /** - * @file soldier.fwd.h + * @file factory.fwd.h * Forward declaration for the Factory class */ diff --git a/src/state/include/state/actor/factory.h b/src/state/include/state/actor/factory.h index c766e99..483ddcf 100644 --- a/src/state/include/state/actor/factory.h +++ b/src/state/include/state/actor/factory.h @@ -7,6 +7,7 @@ #include "state/actor/factory_states/factory_state.h" #include "state/actor/soldier.h" #include "state/actor/villager.h" +#include "state/interfaces/i_gold_manager.h" #include "state/state_export.h" #include "state/utilities.h" @@ -79,7 +80,7 @@ class STATE_EXPORT Factory : public Actor { Factory(); Factory(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager, + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager, int64_t construction_complete, int64_t construction_total, ActorType production_state, int64_t villager_frequency, int64_t soldier_frequency, diff --git a/src/state/include/state/actor/soldier.h b/src/state/include/state/actor/soldier.h index 89df0b7..3749efb 100644 --- a/src/state/include/state/actor/soldier.h +++ b/src/state/include/state/actor/soldier.h @@ -29,7 +29,7 @@ class STATE_EXPORT Soldier : public Unit { Soldier(); Soldier(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager, + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager, PathPlanner *path_planner, int64_t speed, int64_t attack_range, int64_t attack_damage); diff --git a/src/state/include/state/actor/unit.h b/src/state/include/state/actor/unit.h index 838601e..d7cffd2 100644 --- a/src/state/include/state/actor/unit.h +++ b/src/state/include/state/actor/unit.h @@ -79,7 +79,7 @@ class STATE_EXPORT Unit : public Actor { Unit(); Unit(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager, + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager, PathPlanner *path_planner, int64_t speed, int64_t attack_range, int64_t attack_damage); diff --git a/src/state/include/state/actor/villager.h b/src/state/include/state/actor/villager.h index c31e9eb..4fe98ca 100644 --- a/src/state/include/state/actor/villager.h +++ b/src/state/include/state/actor/villager.h @@ -9,7 +9,6 @@ #include "state/actor/factory.fwd.h" #include "state/actor/unit.h" #include "state/actor/villager_states/villager_state.h" -#include "state/gold_manager/gold_manager.h" namespace state { @@ -62,7 +61,7 @@ class STATE_EXPORT Villager : public Unit { Villager(); Villager(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager, + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager, PathPlanner *path_planner, int64_t speed, int64_t attack_range, int64_t attack_damage, int64_t build_effort, int64_t build_range, int64_t mine_range); diff --git a/src/state/include/state/gold_manager/gold_manager.h b/src/state/include/state/gold_manager/gold_manager.h index f4034c0..4c596c1 100644 --- a/src/state/include/state/gold_manager/gold_manager.h +++ b/src/state/include/state/gold_manager/gold_manager.h @@ -5,11 +5,15 @@ #pragma once +#include "state/interfaces/i_gold_manager.h" #include "state/state_export.h" #include "state/utilities.h" #include #include +#include +#include +#include namespace state { @@ -21,7 +25,7 @@ class Actor; /** * Gold Manager class that handles player gold */ -class STATE_EXPORT GoldManager { +class STATE_EXPORT GoldManager : public IGoldManager { private: /** @@ -49,11 +53,6 @@ class STATE_EXPORT GoldManager { */ int64_t factory_kill_reward_amount; - /** - * Penalty amount for a suicide of a factory - */ - int64_t factory_suicide_penalty_amount; - /** * Amount of gold to create a villager */ @@ -74,6 +73,23 @@ class STATE_EXPORT GoldManager { */ int64_t mining_reward; + /** + * Vector of the gold mines + */ + std::vector> gold_mines; + + /** + * Map to manage user build requests + * It maps each gold mine to number of requests made by the player to mine + * that location + */ + std::array, 2> mine_requests; + + /** + * Function to return gold mine given the Vec2D offset + */ + GoldMine *GetGoldMine(Vec2D offset); + public: /** * Constructor for Gold Manager class @@ -83,10 +99,10 @@ class STATE_EXPORT GoldManager { GoldManager(std::array player_gold, int64_t max_gold, int64_t soldier_kill_reward_amount, int64_t villager_kill_reward_amount, - int64_t factory_kill_reward_amount, - int64_t factory_suicide_penalty_amount, int64_t villager_cost, - int64_t soldier_cost, int64_t soldier_factory_cost, - int64_t mining_reward); + int64_t factory_kill_reward_amount, int64_t villager_cost, + int64_t soldier_cost, int64_t factory_cost, + int64_t mining_reward, + std::vector> gold_mines); /** * Method to increase player money @@ -98,7 +114,7 @@ class STATE_EXPORT GoldManager { * * @throw std::out_of_range If the amount is not positive */ - void Increase(PlayerId player_id, int64_t amount); + void Increase(PlayerId player_id, int64_t amount) override; /** * Method to decrease player money @@ -109,28 +125,28 @@ class STATE_EXPORT GoldManager { * @throw std::out_of_range If the amount is not positive * player has insufficuent balance */ - void Decrease(PlayerId player_id, int64_t amount); + void Decrease(PlayerId player_id, int64_t amount) override; /** * Reward the player for killing an enemy actor * * @param[in] enemy_actor Pointer to the killed enemy */ - void RewardKill(Actor *enemy_actor); + void RewardKill(Actor *enemy_actor) override; /** * Returns the cost for creating a particular unit * * @param[in] actor_type ActorType to fetch cost */ - int64_t GetCreateUnitCost(ActorType unit_type); + int64_t GetCreateUnitCost(ActorType unit_type) override; /** * Decreases player's gold for creating specific actor type * * @param[in] actor_type Pointer to the actor which player wants to build */ - void DeductUnitCreateCost(PlayerId player_id, Actor *actor); + void DeductUnitCreateCost(PlayerId player_id, Actor *actor) override; /** * Get the current balance amount of the PlayerId passed @@ -139,27 +155,31 @@ class STATE_EXPORT GoldManager { * * @return The balance. */ - int64_t GetBalance(PlayerId player_id); + int64_t GetBalance(PlayerId player_id) override; /** * Gets the maximum balance. * * @return The maximum possible balance. */ - int64_t GetMaxGold(); + int64_t GetMaxGold() override; /** * Penalty for player triggering suicide * * @param[in] player_id Player who triggered the suicide */ - void DeductFactorySuicidePenalty(PlayerId player_id); + void RewardMineGold(PlayerId player_id, GoldMine *gold_mine, + int64_t mining_reward) override; /** - * Penalty for player triggering suicide - * - * @param[in] player_id Player who triggered the suicide + * Function to add build request to current requests + */ + void AddMineRequest(PlayerId player_id, Vec2D offset) override; + + /** + * Function to assign amount of gold to be given to each player */ - void RewardMineGold(PlayerId player_id); + void Update() override; }; } // namespace state diff --git a/src/state/include/state/gold_manager/gold_mine.h b/src/state/include/state/gold_manager/gold_mine.h new file mode 100644 index 0000000..a51db8e --- /dev/null +++ b/src/state/include/state/gold_manager/gold_mine.h @@ -0,0 +1,31 @@ +#include "physics/vector.hpp" + +namespace state { + +class GoldMine { + public: + /** + * A vector to hold the offset where the gold mine is located + */ + Vec2D offset; + + /** + * Holds the amount of gold reserve that the gold mine has + */ + int64_t value; + + /** + * Constructors + */ + + GoldMine(); + + GoldMine(Vec2D offset, int64_t value); + + /** + * Helper function to extract valid amount of gold depending on amount of + * gold left in the gold mine + */ + int64_t ExtractGold(int64_t ext_amount); +}; +} // namespace state diff --git a/src/state/include/state/interfaces/i_gold_manager.h b/src/state/include/state/interfaces/i_gold_manager.h new file mode 100644 index 0000000..f7e4720 --- /dev/null +++ b/src/state/include/state/interfaces/i_gold_manager.h @@ -0,0 +1,83 @@ +#pragma once + +#include "state/actor/actor.fwd.h" +#include "state/gold_manager/gold_mine.h" +#include "state/state_export.h" +#include "state/utilities.h" + +namespace state { + +class STATE_EXPORT IGoldManager { + public: + /** + * Destructor + */ + virtual ~IGoldManager() {} + + /** + * Function to increase a player's gold + */ + virtual void Increase(PlayerId player_id, int64_t amount) = 0; + + /** + * Function to decrease a player's gold + */ + virtual void Decrease(PlayerId player_id, int64_t amount) = 0; + + /** + * Reward the player for killing an enemy actor + * + * @param[in] enemy_actor Pointer to the killed enemy + */ + virtual void RewardKill(Actor *enemy_actor) = 0; + + /** + * Returns the cost for creating a particular unit + * + * @param[in] actor_type ActorType to fetch cost + */ + virtual int64_t GetCreateUnitCost(ActorType unit_type) = 0; + + /** + * Decreases player's gold for creating specific actor type + * + * @param[in] actor_type Pointer to the actor which player wants to build + */ + virtual void DeductUnitCreateCost(PlayerId player_id, Actor *actor) = 0; + + /** + * Get the current balance amount of the PlayerId passed + * + * @param[in] player_id The player identifier + * + * @return The balance. + */ + virtual int64_t GetBalance(PlayerId player_id) = 0; + + /** + * Gets the maximum balance. + * + * @return The maximum possible balance. + */ + virtual int64_t GetMaxGold() = 0; + + /** + * Penalty for player triggering suicide + * + * @param[in] player_id Player who triggered the suicide + */ + virtual void RewardMineGold(PlayerId player_id, GoldMine *gold_mine, + int64_t mining_reward) = 0; + + /** + * Function to add build request to current requests + */ + virtual void AddMineRequest(PlayerId player_id, Vec2D offset) = 0; + + /** + * Function to assign amount of gold to be given to each player + */ + virtual void Update() = 0; +}; + +} // namespace state diff --git a/src/state/include/state/state.h b/src/state/include/state/state.h index 1e974c1..dabc171 100644 --- a/src/state/include/state/state.h +++ b/src/state/include/state/state.h @@ -10,8 +10,8 @@ #include "state/actor/factory.h" #include "state/actor/soldier.h" #include "state/actor/villager.h" -#include "state/gold_manager/gold_manager.h" #include "state/interfaces/i_command_taker.h" +#include "state/interfaces/i_gold_manager.h" #include "state/interfaces/i_updatable.h" #include "state/map/map.h" #include "state/path_planner/path_planner.h" @@ -34,7 +34,7 @@ class STATE_EXPORT State : public ICommandTaker { /** * Gold Manager instance to maintain player gold */ - std::unique_ptr gold_manager; + std::unique_ptr gold_manager; /** * Path Planner instance @@ -159,7 +159,7 @@ class STATE_EXPORT State : public ICommandTaker { /** * Constructor */ - State(std::unique_ptr map, std::unique_ptr gold_manager, + State(std::unique_ptr map, std::unique_ptr gold_manager, std::unique_ptr path_planner, std::array>, 2> soldiers, std::array>, 2> villagers, diff --git a/src/state/src/actor/actor.cpp b/src/state/src/actor/actor.cpp index 50cb2bb..d8ccf1c 100644 --- a/src/state/src/actor/actor.cpp +++ b/src/state/src/actor/actor.cpp @@ -14,7 +14,7 @@ Actor::Actor() { } Actor::Actor(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager) + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager) : id(id), player_id(player_id), actor_type(actor_type), hp(hp), max_hp(max_hp), position(position), gold_manager(gold_manager) {} @@ -34,7 +34,7 @@ PlayerId Actor::GetPlayerId() { return player_id; } ActorType Actor::GetActorType() { return actor_type; } -GoldManager *Actor::GetGoldManager() { return gold_manager; } +IGoldManager *Actor::GetGoldManager() { return gold_manager; } int64_t Actor::GetHp() { return hp; } diff --git a/src/state/src/actor/factory.cpp b/src/state/src/actor/factory.cpp index 3344352..36a386d 100644 --- a/src/state/src/actor/factory.cpp +++ b/src/state/src/actor/factory.cpp @@ -14,7 +14,7 @@ Factory::Factory() {} Factory::Factory(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, int64_t max_hp, DoubleVec2D position, - GoldManager *gold_manager, int64_t construction_complete, + IGoldManager *gold_manager, int64_t construction_complete, int64_t construction_total, ActorType production_state, int64_t villager_frequency, int64_t soldier_frequency, UnitProductionCallback unit_production_callback) diff --git a/src/state/src/actor/soldier.cpp b/src/state/src/actor/soldier.cpp index 0d3629a..54d4cbf 100644 --- a/src/state/src/actor/soldier.cpp +++ b/src/state/src/actor/soldier.cpp @@ -16,7 +16,7 @@ Soldier::Soldier() { } Soldier::Soldier(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, int64_t max_hp, DoubleVec2D position, - GoldManager *gold_manager, PathPlanner *path_planner, + IGoldManager *gold_manager, PathPlanner *path_planner, int64_t speed, int64_t attack_range, int64_t attack_damage) : Unit(id, player_id, actor_type, hp, max_hp, position, gold_manager, path_planner, speed, attack_range, attack_damage), diff --git a/src/state/src/actor/unit.cpp b/src/state/src/actor/unit.cpp index 9dacc83..9942093 100644 --- a/src/state/src/actor/unit.cpp +++ b/src/state/src/actor/unit.cpp @@ -15,7 +15,7 @@ Unit::Unit() { } Unit::Unit(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, - int64_t max_hp, DoubleVec2D position, GoldManager *gold_manager, + int64_t max_hp, DoubleVec2D position, IGoldManager *gold_manager, PathPlanner *path_planner, int64_t speed, int64_t attack_range, int64_t attack_damage) : Actor(id, player_id, actor_type, hp, max_hp, position, gold_manager), diff --git a/src/state/src/actor/villager.cpp b/src/state/src/actor/villager.cpp index c98e770..c431402 100644 --- a/src/state/src/actor/villager.cpp +++ b/src/state/src/actor/villager.cpp @@ -18,7 +18,7 @@ Villager::Villager() { } Villager::Villager(ActorId id, PlayerId player_id, ActorType actor_type, int64_t hp, int64_t max_hp, DoubleVec2D position, - GoldManager *gold_manager, PathPlanner *path_planner, + IGoldManager *gold_manager, PathPlanner *path_planner, int64_t speed, int64_t attack_range, int64_t attack_damage, int64_t build_effort, int64_t build_range, int64_t mine_range) diff --git a/src/state/src/actor/villager_states/villager_mine_state.cpp b/src/state/src/actor/villager_states/villager_mine_state.cpp index 08936d2..1c7d1f6 100644 --- a/src/state/src/actor/villager_states/villager_mine_state.cpp +++ b/src/state/src/actor/villager_states/villager_mine_state.cpp @@ -53,7 +53,8 @@ std::unique_ptr VillagerMineState::Update() { } // Mine gold - villager->GetGoldManager()->RewardMineGold(villager->GetPlayerId()); + villager->GetGoldManager()->AddMineRequest(villager->GetPlayerId(), + villager->GetMineTarget()); return nullptr; } diff --git a/src/state/src/gold_manager/gold_manager.cpp b/src/state/src/gold_manager/gold_manager.cpp index 5063602..683737e 100644 --- a/src/state/src/gold_manager/gold_manager.cpp +++ b/src/state/src/gold_manager/gold_manager.cpp @@ -16,16 +16,16 @@ GoldManager::GoldManager(std::array players_gold, int64_t max_gold, int64_t soldier_kill_reward_amount, int64_t villager_kill_reward_amount, int64_t factory_kill_reward_amount, - int64_t factory_suicide_penalty_amount, int64_t villager_cost, int64_t soldier_cost, - int64_t factory_cost, int64_t mining_reward) + int64_t factory_cost, int64_t mining_reward, + std::vector> gold_mines) : players_gold(players_gold), max_gold(max_gold), soldier_kill_reward_amount(soldier_kill_reward_amount), villager_kill_reward_amount(villager_kill_reward_amount), factory_kill_reward_amount(factory_kill_reward_amount), - factory_suicide_penalty_amount(factory_suicide_penalty_amount), villager_cost(villager_cost), soldier_cost(soldier_cost), - factory_cost(factory_cost), mining_reward(mining_reward) {} + factory_cost(factory_cost), mining_reward(mining_reward), + gold_mines(std::move(gold_mines)), mine_requests({}) {} void GoldManager::Increase(PlayerId player_id, int64_t amount) { @@ -112,14 +112,108 @@ int64_t GoldManager::GetBalance(PlayerId player_id) { int64_t GoldManager::GetMaxGold() { return max_gold; } -void GoldManager::DeductFactorySuicidePenalty(PlayerId player_id) { - auto suicide_penalty = factory_suicide_penalty_amount; - Decrease(player_id, suicide_penalty); +void GoldManager::RewardMineGold(PlayerId player_id, GoldMine *gold_mine, + int64_t mining_reward) { + // Extracting gold from the gold mine + int64_t extracted_amount = gold_mine->ExtractGold(mining_reward); + + // Increasing the player's gold + Increase(player_id, extracted_amount); +} + +void GoldManager::AddMineRequest(PlayerId player_id, Vec2D offset) { + int64_t id = static_cast(player_id); + auto &player_requests = this->mine_requests[id]; + auto gold_mine = GetGoldMine(offset); + + // If the gold mine dosen't already exist in the hash map, then we add an + // entry with count 1 + if (player_requests.find(gold_mine) == player_requests.end()) { + player_requests.insert(std::pair(gold_mine, 1)); + } else { + auto request = player_requests.find(gold_mine); + request->second += 1; + } } -void GoldManager::RewardMineGold(PlayerId player_id) { - auto reward_mining = mining_reward; - Increase(player_id, reward_mining); +void GoldManager::Update() { + + // Assigning the player's gold based on the mine requests + for (int id = 0; id < 2; ++id) { + int64_t id_enemy = + (id + 1) % static_cast(PlayerId::PLAYER_COUNT); + auto &player_requests = this->mine_requests[id]; + auto &enemy_requests = this->mine_requests[id_enemy]; + for (auto const &request : player_requests) { + auto gold_mine = request.first; + auto no_player_requests = request.second; + PlayerId player_id = static_cast(id); + PlayerId enemy_id = static_cast(id_enemy); + + // If enemy is trying to mine in the same gold mine, gold is split + if (enemy_requests.find(gold_mine) != enemy_requests.end()) { + int64_t no_enemy_requests = + enemy_requests.find(gold_mine)->second; + + // Checking if the gold mine has enough gold to accomodate both + // the users + int64_t total_gold_required = + this->mining_reward * + (no_player_requests + no_enemy_requests); + + // If the gold mine has enough gold to accomodate both the + // player's requests + if (gold_mine->value >= total_gold_required) { + + // Deducting the player's requested gold + int64_t mining_reward = + this->mining_reward * no_player_requests; + RewardMineGold(player_id, gold_mine, mining_reward); + } + + // If the gold mine has lesser gold than what is asked + // NOTE: One gold coin may get lost + else { + // Finding the ratio of gold that this player will recieve + float gold_ratio = (double)no_player_requests / + (no_player_requests + no_enemy_requests); + int player_gold_recieved = gold_ratio * gold_mine->value; + int enemy_gold_recieved = + (1 - gold_ratio) * gold_mine->value; + int residue = gold_mine->value - + (player_gold_recieved + enemy_gold_recieved); + + // Rewarding player's with their proportion of gold + RewardMineGold(player_id, gold_mine, player_gold_recieved); + RewardMineGold(enemy_id, gold_mine, enemy_gold_recieved); + + // Extracting the extra gold from the player + gold_mine->ExtractGold(residue); + + // Removing the request from the enemy's requests + enemy_requests.erase(gold_mine); + } + } + // Gold is not split between the villagers + else { + int gold_recieved = no_player_requests * this->mining_reward; + + // Rewarding the user gold + RewardMineGold(player_id, gold_mine, gold_recieved); + } + } + this->mine_requests[id].clear(); + } +} + +GoldMine *GoldManager::GetGoldMine(Vec2D offset) { + for (int i = 0; i < this->gold_mines.size(); ++i) { + if (this->gold_mines[i]->offset == offset) { + return this->gold_mines[i].get(); + } + } + // TODO : Return null value + // return null; } } // namespace state diff --git a/src/state/src/gold_manager/gold_mine.cpp b/src/state/src/gold_manager/gold_mine.cpp new file mode 100644 index 0000000..4dc278c --- /dev/null +++ b/src/state/src/gold_manager/gold_mine.cpp @@ -0,0 +1,19 @@ +#include "state/gold_manager/gold_mine.h" + +namespace state { + +GoldMine::GoldMine() : offset(Vec2D::null), value(0){}; + +GoldMine::GoldMine(Vec2D offset, int64_t value) + : offset(offset), value(value){}; + +int64_t GoldMine::ExtractGold(int64_t ext_amount) { + if (this->value - ext_amount < 0) { + this->value = 0; + return this->value; + } else { + this->value -= ext_amount; + return ext_amount; + } +} +} // namespace state diff --git a/src/state/src/state.cpp b/src/state/src/state.cpp index fef29f6..cfd2262 100644 --- a/src/state/src/state.cpp +++ b/src/state/src/state.cpp @@ -10,7 +10,7 @@ namespace state { State::State(std::unique_ptr map, - std::unique_ptr gold_manager, + std::unique_ptr gold_manager, std::unique_ptr path_planner, std::array>, 2> soldiers, std::array>, 2> villagers, @@ -233,7 +233,7 @@ const std::array, 2> GetRawPtrsFromUniquePtrs( auto player_actors = std::vector{}; player_actors.reserve(actors[i].size()); - // For each actor, + // For each actor for (int j = 0; j < actors[i].size(); ++j) { player_actors.push_back(actors[i][j].get()); } @@ -369,6 +369,9 @@ bool State::IsGameOver(PlayerId &winner) { // If no one died, is_game_over is false by default, leave winner alone return is_game_over; + + // Late gold manager update + gold_manager->Update(); } } // namespace state diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6ebc478..8504368 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCE_FILES state/factory_test.cpp state/state_test.cpp state/path_planner_test.cpp + state/gold_manager_test.cpp state/state_syncer_test.cpp state/command_giver_test.cpp logger/logger_test.cpp diff --git a/test/drivers/main_driver_test.cpp b/test/drivers/main_driver_test.cpp index 8ac4433..c73055f 100644 --- a/test/drivers/main_driver_test.cpp +++ b/test/drivers/main_driver_test.cpp @@ -57,7 +57,7 @@ class MainDriverTest : public testing::Test { const vector MainDriverTest::shared_memory_names = {"ShmTest1", "ShmTest2"}; const int MainDriverTest::num_turns = pow(10, 4); -const int MainDriverTest::time_limit_ms = 1000; +const int MainDriverTest::time_limit_ms = 2000; const int MainDriverTest::turn_instruction_limit = 5; const int MainDriverTest::game_instruction_limit = 10; diff --git a/test/logger/logger_test.cpp b/test/logger/logger_test.cpp index 01c3022..55a28eb 100644 --- a/test/logger/logger_test.cpp +++ b/test/logger/logger_test.cpp @@ -1,6 +1,7 @@ #include "constants/constants.h" #include "logger/logger.h" #include "state/mocks/command_taker_mock.h" +#include "state/mocks/gold_manager_mock.h" #include "state/utilities.h" #include "gtest/gtest.h" #include @@ -21,10 +22,8 @@ class LoggerTest : public testing::Test { std::unique_ptr state; std::unique_ptr logger; - std::unique_ptr gold_manager; + std::unique_ptr gold_manager; std::unique_ptr path_planner; - std::array player_gold; - int64_t max_gold; std::unique_ptr map; @@ -36,7 +35,8 @@ class LoggerTest : public testing::Test { logger(make_unique(state.get(), PLAYER_INSTRUCTION_LIMIT_TURN, PLAYER_INSTRUCTION_LIMIT_GAME, SOLDIER_MAX_HP, VILLAGER_MAX_HP, - FACTORY_MAX_HP)) { + FACTORY_MAX_HP)), + gold_manager(make_unique()) { auto map_matrix = vector>{{ {L, L, L, L, L}, {L, L, L, L, L}, @@ -45,15 +45,6 @@ class LoggerTest : public testing::Test { {W, W, W, W, W}, }}; map = make_unique(map_matrix, TEST_MAP_SIZE, ELEMENT_SIZE); - - player_gold[0] = 5000; - player_gold[1] = 5000; - max_gold = 10000; - this->gold_manager = make_unique( - player_gold, max_gold, SOLDIER_KILL_REWARD_AMOUNT, - VILLAGER_KILL_REWARD_AMOUNT, FACTORY_KILL_REWARD_AMOUNT, - FACTORY_SUICIDE_PENALTY, VILLAGER_COST, SOLDIER_COST, FACTORY_COST, - MINING_REWARD); } }; diff --git a/test/state/command_giver_test.cpp b/test/state/command_giver_test.cpp index 36390f0..d3b8075 100644 --- a/test/state/command_giver_test.cpp +++ b/test/state/command_giver_test.cpp @@ -518,6 +518,7 @@ TEST_F(CommandGiverTest, CommandExecutionTest) { EXPECT_CALL( *this->logger, LogError(PlayerId::PLAYER1, ErrorType::NO_BUILD_FACTORY_ON_WATER, _)); + // (0, 0) is a water tile this->player_states[0].villagers[0].build_position = Vec2D(0, 0); ManageActorExpectations(state_soldiers, state_villagers, state_factories, diff --git a/test/state/factory_test.cpp b/test/state/factory_test.cpp index a803c92..98a4f51 100644 --- a/test/state/factory_test.cpp +++ b/test/state/factory_test.cpp @@ -3,7 +3,7 @@ #include "state/actor/factory.h" #include "state/actor/soldier.h" #include "state/actor/villager.h" -#include "state/gold_manager/gold_manager.h" +#include "state/mocks/gold_manager_mock.h" #include "gtest/gtest.h" using namespace std; @@ -13,16 +13,10 @@ using namespace testing; class FactoryTest : public Test { protected: - array player_gold; - int64_t soldier_kill_reward_gold; - int64_t villager_kill_reward_gold; - int64_t factory_kill_reward_gold; - int64_t max_gold; - Soldier model_soldier; Villager model_villager; - unique_ptr gold_manager; + unique_ptr gold_manager; unique_ptr path_planner; unique_ptr factory; @@ -59,8 +53,6 @@ class FactoryTest : public Test { FactoryTest() { - player_gold[0] = player_gold[1] = 5000; - model_villager = Villager(2, PlayerId::PLAYER2, ActorType::VILLAGER, 100, 100, DoubleVec2D(15, 15), gold_manager.get(), @@ -70,16 +62,7 @@ class FactoryTest : public Test { 100, DoubleVec2D(10, 10), gold_manager.get(), path_planner.get(), 10, 10, 10); - this->max_gold = 10000; - this->soldier_kill_reward_gold = SOLDIER_KILL_REWARD_AMOUNT; - this->villager_kill_reward_gold = VILLAGER_KILL_REWARD_AMOUNT; - this->factory_kill_reward_gold = FACTORY_KILL_REWARD_AMOUNT; - - this->gold_manager = make_unique( - player_gold, max_gold, soldier_kill_reward_gold, - villager_kill_reward_gold, factory_kill_reward_gold, - FACTORY_SUICIDE_PENALTY, VILLAGER_COST, SOLDIER_COST, FACTORY_COST, - MINING_REWARD); + this->gold_manager = make_unique(); villager_list = std::vector>{}; soldier_list = std::vector>{}; @@ -100,16 +83,22 @@ class FactoryTest : public Test { TEST_F(FactoryTest, ConstructionTest) { ASSERT_EQ(factory->GetState(), FactoryStateName::UNBUILT); - factory->Update(); - factory->LateUpdate(); - // Build the factory factory->IncrementConstructionCompletion( factory->GetTotalConstructionCompletion()); + // Expecting calls to gold manager + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(2) + .WillRepeatedly(Return(1000)); + factory->Update(); factory->LateUpdate(); + // Factory is now in PRODUCTION state ASSERT_EQ(factory->GetState(), FactoryStateName::PRODUCTION); ASSERT_EQ(factory->GetProductionState(), ActorType::VILLAGER); } @@ -117,22 +106,47 @@ TEST_F(FactoryTest, ConstructionTest) { TEST_F(FactoryTest, UnitProductionTest) { ASSERT_EQ(factory->GetState(), FactoryStateName::UNBUILT); - factory->Update(); - factory->LateUpdate(); - // Build the factory factory->IncrementConstructionCompletion( factory->GetTotalConstructionCompletion()); + // Expecting gold manager calls when factory transitions from UNBUILD state + // to PRODUCTION state + EXPECT_CALL(*gold_manager, GetCreateUnitCost) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + + // Updating the factory state + factory->Update(); + factory->LateUpdate(); + + // Factory is now in PRODUCTION state + ASSERT_EQ(factory->GetState(), FactoryStateName::PRODUCTION); + + // Producing villagers now factory->SetProductionState(ActorType::VILLAGER); + // Expecting gold manager calls + EXPECT_CALL(*gold_manager, GetCreateUnitCost) + .Times(21 * villager_frequency) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(21 * villager_frequency) + .WillRepeatedly(Return(1000)); + + // The factory creates a soldier in the first tick itself + ASSERT_EQ(villager_list.size(), 1); + // After frequency no. of updates, a villager should have been produced for (int i = 0; i < villager_frequency; ++i) { factory->Update(); factory->LateUpdate(); } - ASSERT_EQ(villager_list.size(), 1); + ASSERT_EQ(villager_list.size(), 2); // After 20 x frequency updates, 20 more villagers should have been produced for (int i = 0; i < 20 * villager_frequency; ++i) { @@ -140,23 +154,43 @@ TEST_F(FactoryTest, UnitProductionTest) { factory->LateUpdate(); } - ASSERT_EQ(villager_list.size(), 1 + 20); + ASSERT_EQ(villager_list.size(), 2 + 20); } TEST_F(FactoryTest, SwitchUnitProductionTest) { ASSERT_EQ(factory->GetState(), FactoryStateName::UNBUILT); - factory->Update(); - factory->LateUpdate(); - // Build the factory factory->IncrementConstructionCompletion( factory->GetTotalConstructionCompletion()); factory->SetProductionState(ActorType::VILLAGER); + // Gold manager expecting calls + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(2) + .WillRepeatedly(Return(1000)); + + // Transitioning the villager from the UNBUILT state to the PRODUCTION + factory->Update(); + factory->LateUpdate(); + + ASSERT_EQ(factory->GetState(), FactoryStateName::PRODUCTION); + + // Gold manager expecting calls + // Need to check why + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(villager_frequency - 1) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(villager_frequency - 1) + .WillRepeatedly(Return(1000)); + // After frequency no. of updates, a villager should have been produced - for (int i = 0; i < villager_frequency; ++i) { + for (int i = 0; i < villager_frequency - 1; ++i) { factory->Update(); factory->LateUpdate(); } @@ -167,6 +201,13 @@ TEST_F(FactoryTest, SwitchUnitProductionTest) { factory->SetProductionState(ActorType::SOLDIER); // After 20 x frequency updates, 20 soldiers should have been produced + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(20 * soldier_frequency) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(20 * soldier_frequency) + .WillRepeatedly(Return(1000)); + for (int i = 0; i < 20 * soldier_frequency; ++i) { factory->Update(); factory->LateUpdate(); @@ -178,24 +219,49 @@ TEST_F(FactoryTest, SwitchUnitProductionTest) { TEST_F(FactoryTest, StopStartTest) { ASSERT_EQ(factory->GetState(), FactoryStateName::UNBUILT); - factory->Update(); - factory->LateUpdate(); + // Setting the production state to villager + factory->SetProductionState(ActorType::SOLDIER); // Build the factory and produce a unit factory->IncrementConstructionCompletion( factory->GetTotalConstructionCompletion()); + // Expecting calls to gold manager + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(2) + .WillRepeatedly(Return(1000)); + factory->Update(); factory->LateUpdate(); + // The factory has transitioned from unbuilt state to production state ASSERT_EQ(factory->GetState(), FactoryStateName::PRODUCTION); + // Expect calls to GoldManager for getting balance and unit creation cost + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(soldier_frequency - 1) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(soldier_frequency - 1) + .WillRepeatedly(Return(1000)); + // Produce a unit (- 1 since we ran 1 update just now) - for (int i = 0; i < villager_frequency - 1; ++i) { + for (int i = 0; i < soldier_frequency - 1; ++i) { factory->Update(); factory->LateUpdate(); } - ASSERT_EQ(villager_list.size(), 1); + + // Expecting 2 calls to gold manager for transisioning from the PRODUCTION + // state to the idle state + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost) + .Times(2) + .WillRepeatedly(Return(1000)); // Stop Production factory->Stop(); @@ -203,6 +269,18 @@ TEST_F(FactoryTest, StopStartTest) { factory->LateUpdate(); ASSERT_EQ(factory->GetState(), FactoryStateName::IDLE); + // NOTE: Factory is now in idle state + // Hence, it will call the GetBalance and the GetCreateUnitCost function + // every turn + + // Expecting 2 more calls to gold manager for resuming production + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost) + .Times(2) + .WillRepeatedly(Return(1000)); + // Resume Production factory->Start(); factory->Update(); @@ -216,19 +294,27 @@ TEST_F(FactoryTest, DeathTest) { factory->Update(); factory->LateUpdate(); + // The factory is now in the IDLE state // Build the factory factory->IncrementConstructionCompletion( factory->GetTotalConstructionCompletion()); + // Calls are made in the idle state to GetCreateUnitCost and GetBalance + EXPECT_CALL(*gold_manager, GetBalance(factory->GetPlayerId())) + .Times(2) + .WillRepeatedly(Return(1000)); + EXPECT_CALL(*gold_manager, GetCreateUnitCost(factory->GetProductionState())) + .Times(2) + .WillRepeatedly(Return(1000)); factory->Update(); factory->LateUpdate(); + // Factory is now in production state ASSERT_EQ(factory->GetState(), FactoryStateName::PRODUCTION); // Kill it factory->SetHp(0); factory->Update(); factory->LateUpdate(); - ASSERT_EQ(factory->GetState(), FactoryStateName::DEAD); } diff --git a/test/state/gold_manager_test.cpp b/test/state/gold_manager_test.cpp new file mode 100644 index 0000000..87ceed6 --- /dev/null +++ b/test/state/gold_manager_test.cpp @@ -0,0 +1,224 @@ +#include "state/gold_manager/gold_manager.h" +#include "state/map/map.h" +#include "state/mocks/command_giver_mock.h" +#include "state/mocks/command_taker_mock.h" +#include "state/mocks/gold_manager_mock.h" +#include "state/mocks/state_syncer_mock.h" +#include "gtest/gtest.h" +#include + +using namespace std; +using namespace state; + +class GoldManagerTest : public testing::Test { + public: + // Creating a path planner + unique_ptr path_planner; + unique_ptr map; + + // Gold manager's dependencies + array player_gold; + int64_t max_gold; + int64_t soldier_kill_amount; + int64_t villager_kill_amount; + int64_t factory_kill_amount; + int64_t villager_cost; + int64_t soldier_cost; + int64_t factory_cost; + int64_t mining_reward; + vector gold_mines; + + // Creating an incrementing actor id + int64_t actor_id; + + // Creating the gold manager + unique_ptr gold_manager; + + GoldManagerTest() { + + // Assigning the GoldManager constants + this->player_gold = {1000, 1000}; + this->max_gold = 10000; + this->soldier_kill_amount = 100; + this->villager_kill_amount = 50; + this->factory_kill_amount = 200; + this->villager_cost = 100; + this->soldier_cost = 200; + this->factory_cost = 100; + this->mining_reward = 10; + int64_t map_size = 5; + int64_t ele_size = 5; + + // Creating gold mines + vector> uniq_gold_mines; + auto uniq_gold_mine = make_unique(Vec2D(2, 2), 10000); + auto gold_mine = uniq_gold_mine.get(); + uniq_gold_mines.push_back(move(uniq_gold_mine)); + this->gold_mines.push_back(gold_mine); + + // Creating a rudimentary map + vector> dummy_map = CreateMap(map_size); + this->map = make_unique(dummy_map, map_size, ele_size); + + // Assinging the path planner and the gold manager + this->path_planner = make_unique(this->map.get()); + this->gold_manager = make_unique( + player_gold, max_gold, soldier_kill_amount, villager_kill_amount, + factory_kill_amount, villager_cost, soldier_cost, factory_cost, + mining_reward, move(uniq_gold_mines)); + } + + unique_ptr MakeStateSoldier(PlayerId player_id) { + return make_unique(this->actor_id++, player_id, + ActorType::SOLDIER, 100, 100, + DoubleVec2D(0, 0), this->gold_manager.get(), + this->path_planner.get(), 5, 5, 5); + } + + unique_ptr MakeStateVillager(PlayerId player_id) { + return make_unique( + this->actor_id++, player_id, ActorType::VILLAGER, 100, 100, + DoubleVec2D(0, 0), this->gold_manager.get(), + this->path_planner.get(), 5, 5, 5, 3, 3, 3); + } + + unique_ptr MakeStateFactory(PlayerId player_id) { + return make_unique( + this->actor_id++, player_id, ActorType::FACTORY, 100, 100, + DoubleVec2D(0, 0), this->gold_manager.get(), 100, 100, + ActorType::SOLDIER, 3, 3, UnitProductionCallback{}); + } + + vector> CreateMap(int64_t map_size) { + /** + * Creating this map layout + * + * W L L L L + * L W L L L + * L L G L L + * L L L W L + * L L L L W + */ + vector> dummy_map; + for (int i = 0; i < map_size; ++i) { + vector dummy_row; + for (int j = 0; j < map_size; ++j) { + if (i == j) { + if (i == map_size / 2) { + dummy_row.push_back(TerrainType::GOLD_MINE); + } else { + dummy_row.push_back(TerrainType::WATER); + } + } else { + dummy_row.push_back(TerrainType::LAND); + } + } + dummy_map.push_back(dummy_row); + } + return dummy_map; + } +}; + +// Starting tests + +// Gets called when a villager tries to mine a gold mine +TEST_F(GoldManagerTest, AddMineRequest) { + auto villager = MakeStateVillager(PlayerId::PLAYER1); + auto gold_mine = this->gold_mines[0]; + gold_mine->value = 100; + int64_t player_id = static_cast(PlayerId::PLAYER1); + villager->SetMineTarget(Vec2D(2, 2)); + + // Updating the villager's state fror IDLE to MINE + // A mine request will be sent to the GoldManager when the soldier + // transitions into MINE state + + while (villager->GetState() != VillagerStateName::MINE) { + villager->Update(); + villager->LateUpdate(); + } + + // Villager is now in the mining state + ASSERT_EQ(villager->GetState(), VillagerStateName::MINE); + + // Updating gold manager to check if player 1's gold increases + gold_manager->Update(); + + ASSERT_EQ(gold_manager->GetBalance(villager->GetPlayerId()), + this->player_gold[0] + this->mining_reward); +} + +TEST_F(GoldManagerTest, SplitGoldTest) { + // Reassigning the GoldMine gold to have 16 gold units + auto gold_mine = this->gold_mines[0]; + gold_mine->value = 16; + + // Making a villager each for player 1 and player 2 + // Since both villagers start at the same position, they will reach the mine + // at the same time + auto villager1 = MakeStateVillager(PlayerId::PLAYER1); + auto villager2 = MakeStateVillager(PlayerId::PLAYER2); + villager1->SetMineTarget(Vec2D(2, 2)); + villager2->SetMineTarget(Vec2D(2, 2)); + + // Making villager transition to MINE state + while (villager1->GetState() != VillagerStateName::MINE) { + villager1->Update(); + villager2->Update(); + villager1->LateUpdate(); + villager2->LateUpdate(); + } + + // Once in MINE state, call the gold manager update to check if the gold is + // divided in the right ratio + gold_manager->Update(); + + ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER1), + this->player_gold[0] + 8); + ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER2), + this->player_gold[1] + 8); + ASSERT_EQ(gold_mine->value, 0); + + // Updating the player gold in the test + this->player_gold[0] += 8; + this->player_gold[1] += 8; + + // Reassigning gold manager to have an odd number and checking if the + // remaining gold is removed + gold_mine->value = 28; + + // Creating an additional player 2 villager and resetting the first 2 + // villagers + villager1 = MakeStateVillager(PlayerId::PLAYER1); + villager2 = MakeStateVillager(PlayerId::PLAYER2); + auto villager3 = MakeStateVillager(PlayerId::PLAYER2); + + // Setting the villagers' mine targets + villager1->SetMineTarget(Vec2D(2, 2)); + villager2->SetMineTarget(Vec2D(2, 2)); + villager3->SetMineTarget(Vec2D(2, 2)); + + // Since all the villagers have been reset, they all are in the same + // position + while (villager1->GetState() != VillagerStateName::MINE) { + villager1->Update(); + villager1->LateUpdate(); + villager2->Update(); + villager2->LateUpdate(); + villager3->Update(); + villager3->LateUpdate(); + } + + // Once in MINE state, call the gold manager update to check if the gold is + // divided in the right ratio + gold_manager->Update(); + + // Checking if the players got the right ratio of gold + ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER1), + this->player_gold[0] + 9); + ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER2), + this->player_gold[1] + 18); + + // Checking if the extra 1 gold coin is removed + ASSERT_EQ(gold_mine->value, 0); +} diff --git a/test/state/mocks/gold_manager_mock.h b/test/state/mocks/gold_manager_mock.h new file mode 100644 index 0000000..17d1036 --- /dev/null +++ b/test/state/mocks/gold_manager_mock.h @@ -0,0 +1,20 @@ +#pragma once + +#include "gmock/gmock.h" +#include "state/interfaces/i_gold_manager.h" + +using namespace state; + +class GoldManagerMock : public IGoldManager { + public: + MOCK_METHOD2(Increase, void(PlayerId, int64_t)); + MOCK_METHOD2(Decrease, void(PlayerId, int64_t)); + MOCK_METHOD1(RewardKill, void(Actor *)); + MOCK_METHOD1(GetCreateUnitCost, int64_t(ActorType)); + MOCK_METHOD2(DeductUnitCreateCost, void(PlayerId, Actor *)); + MOCK_METHOD1(GetBalance, int64_t(PlayerId)); + MOCK_METHOD0(GetMaxGold, int64_t()); + MOCK_METHOD3(RewardMineGold, void(PlayerId, GoldMine *, int64_t)); + MOCK_METHOD2(AddMineRequest, void(PlayerId, Vec2D)); + MOCK_METHOD0(Update, void()); +}; diff --git a/test/state/soldier_test.cpp b/test/state/soldier_test.cpp index fe643a3..ae04576 100644 --- a/test/state/soldier_test.cpp +++ b/test/state/soldier_test.cpp @@ -2,7 +2,7 @@ #include "gmock/gmock.h" #include "state/actor/soldier.h" #include "state/actor/villager.h" -#include "state/gold_manager/gold_manager.h" +#include "state/mocks/gold_manager_mock.h" #include "gtest/gtest.h" using namespace std; @@ -20,13 +20,7 @@ class SoldierTest : public Test { int64_t element_size = 10; unique_ptr map; - array player_gold; - int64_t soldier_kill_reward_gold; - int64_t villager_kill_reward_gold; - int64_t factory_kill_reward_gold; - int64_t max_gold; - - unique_ptr gold_manager; + unique_ptr gold_manager; unique_ptr path_planner; unique_ptr soldier; @@ -41,22 +35,7 @@ class SoldierTest : public Test { }}; map = make_unique(map_matrix, map_size, element_size); - - for (int i = 0; i < (int)PlayerId::PLAYER_COUNT; ++i) { - player_gold[i] = 5000; // Start balance - } - - this->max_gold = 10000; - this->soldier_kill_reward_gold = SOLDIER_KILL_REWARD_AMOUNT; - this->villager_kill_reward_gold = VILLAGER_KILL_REWARD_AMOUNT; - this->factory_kill_reward_gold = FACTORY_KILL_REWARD_AMOUNT; - - this->gold_manager = make_unique( - player_gold, max_gold, soldier_kill_reward_gold, - villager_kill_reward_gold, factory_kill_reward_gold, - FACTORY_SUICIDE_PENALTY, VILLAGER_COST, SOLDIER_COST, FACTORY_COST, - MINING_REWARD); - this->path_planner = make_unique(map.get()); + gold_manager = make_unique(); this->soldier = make_unique(1, PlayerId::PLAYER1, ActorType::SOLDIER, 100, @@ -119,8 +98,13 @@ TEST_F(SoldierTest, MoveToDeadState) { DoubleVec2D(15, 15), gold_manager.get(), path_planner.get(), 10, 10, attack_damage); + // Making soldier attack the other soldier this->soldier->Attack(target_soldier); + // Expect calls to GoldManager for killing unit + EXPECT_CALL(*gold_manager, RewardKill(target_soldier)); + + // Soldier continuously attacking the other soldier until he dies while (target_soldier->GetHp() != 0) { this->soldier->Update(); target_soldier->Update(); @@ -134,7 +118,7 @@ TEST_F(SoldierTest, MoveToDeadState) { TEST_F(SoldierTest, SoldierKillReward) { int64_t initial_hp = 100; int64_t attack_damage = 10; - int64_t initial_gold = player_gold[0]; + int64_t initial_gold = 100; auto *target_soldier = new Soldier(2, PlayerId::PLAYER2, ActorType::SOLDIER, initial_hp, 100, @@ -143,28 +127,31 @@ TEST_F(SoldierTest, SoldierKillReward) { this->soldier->Attack(target_soldier); + // Expecting call to gold manager to reward kill of soldier + EXPECT_CALL(*gold_manager, RewardKill(target_soldier)); + while (target_soldier->GetHp() != 0) { this->soldier->Update(); target_soldier->Update(); this->soldier->LateUpdate(); target_soldier->LateUpdate(); } - - ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER1), - (initial_gold + soldier_kill_reward_gold)); } TEST_F(SoldierTest, PursuitAndKill) { int64_t initial_hp = 100; int64_t attack_damage = 10; - auto *target_soldier = new Soldier(2, PlayerId::PLAYER2, ActorType::SOLDIER, initial_hp, 100, - DoubleVec2D(10, 40), gold_manager.get(), path_planner.get(), + DoubleVec2D(15, 15), gold_manager.get(), path_planner.get(), 10, 10, attack_damage); this->soldier->SetAttackTarget(target_soldier); + // Expecting call to gold manager to reward the soldier for killing target + // soldier + EXPECT_CALL(*gold_manager, RewardKill(target_soldier)); + while (target_soldier->GetHp() != 0) { this->soldier->Update(); target_soldier->Update(); @@ -176,13 +163,19 @@ TEST_F(SoldierTest, PursuitAndKill) { } TEST_F(SoldierTest, SimultaneousKill) { - auto target_soldier = make_unique( - 2, PlayerId::PLAYER2, ActorType::SOLDIER, 100, 100, DoubleVec2D(35, 45), + auto *target_soldier = new Soldier( + 2, PlayerId::PLAYER2, ActorType::SOLDIER, 100, 100, DoubleVec2D(15, 15), gold_manager.get(), path_planner.get(), 10, 10, 10); - soldier->Attack(target_soldier.get()); + // Both soldiers attacking each other + soldier->Attack(target_soldier); target_soldier->Attack(soldier.get()); + // Expecting call to gold manager to reward gold to each soldier for killing + // the other + EXPECT_CALL(*gold_manager, RewardKill(target_soldier)); + EXPECT_CALL(*gold_manager, RewardKill(soldier.get())); + while (target_soldier->GetHp() != 0) { this->soldier->Update(); target_soldier->Update(); diff --git a/test/state/state_syncer_test.cpp b/test/state/state_syncer_test.cpp index 606413f..c14e3d4 100644 --- a/test/state/state_syncer_test.cpp +++ b/test/state/state_syncer_test.cpp @@ -4,6 +4,7 @@ #include "state/interfaces/i_state_syncer.h" #include "state/mocks/command_giver_mock.h" #include "state/mocks/command_taker_mock.h" +#include "state/mocks/gold_manager_mock.h" #include "state/player_state.h" #include "state/state.h" #include "state/state_syncer.h" @@ -47,7 +48,7 @@ class StateSyncerTest : public Test { array player_states; // Creating a gold manager - unique_ptr gold_manager; + unique_ptr gold_manager; StateSyncerTest() { // Creating 2 player states @@ -57,9 +58,10 @@ class StateSyncerTest : public Test { player_states[0] = player_state1; player_states[1] = player_state2; - // Creating a mock command giver and taker + // Creating a mock command giver, command taker and gold manager auto command_taker = make_unique(); auto command_giver = make_unique(); + auto gold_manager = make_unique(); this->command_taker = command_taker.get(); this->command_giver = command_giver.get(); diff --git a/test/state/state_test.cpp b/test/state/state_test.cpp index 1c361a4..d7cfb61 100644 --- a/test/state/state_test.cpp +++ b/test/state/state_test.cpp @@ -1,8 +1,10 @@ #include "constants/gold_manager.h" #include "constants/state.h" #include "gmock/gmock.h" +#include "state/mocks/gold_manager_mock.h" #include "state/state.h" #include "gtest/gtest.h" +#include using namespace std; using namespace state; @@ -28,7 +30,7 @@ class StateTest : public Test { std::vector> temp_villager_list; std::vector> temp_soldier_list; - unique_ptr gold_manager; + GoldManagerMock *gold_manager; unique_ptr path_planner; Villager model_villager; Soldier model_soldier; @@ -61,65 +63,59 @@ class StateTest : public Test { this->villager_kill_reward_gold = VILLAGER_KILL_REWARD_AMOUNT; this->factory_kill_reward_gold = FACTORY_KILL_REWARD_AMOUNT; - this->gold_manager = make_unique( - player_gold, max_gold, soldier_kill_reward_gold, - villager_kill_reward_gold, factory_kill_reward_gold, - FACTORY_SUICIDE_PENALTY, VILLAGER_COST, SOLDIER_COST, FACTORY_COST, - MINING_REWARD); + auto unique_gold_manager = make_unique(); + this->gold_manager = unique_gold_manager.get(); this->path_planner = make_unique(map.get()); this->model_villager = Villager(1, PlayerId::PLAYER1, ActorType::VILLAGER, 100, 100, - DoubleVec2D(10, 10), gold_manager.get(), - path_planner.get(), 10, 10, 10, 10, 10, 10); + DoubleVec2D(10, 10), gold_manager, path_planner.get(), 10, + 10, 10, 10, 10, 10); - this->model_soldier = - Soldier(2, PlayerId::PLAYER2, ActorType::SOLDIER, 100, 100, - DoubleVec2D(10, 10), gold_manager.get(), path_planner.get(), - 10, 10, 10); + this->model_soldier = Soldier( + 2, PlayerId::PLAYER2, ActorType::SOLDIER, 100, 100, + DoubleVec2D(10, 10), gold_manager, path_planner.get(), 10, 10, 10); int64_t villager_frequency = 5; int64_t soldier_frequency = 10; - this->model_factory = - Factory(2, PlayerId::PLAYER2, ActorType::FACTORY, 1, 100, - DoubleVec2D(15, 15), gold_manager.get(), 0, 100, - ActorType::VILLAGER, villager_frequency, soldier_frequency, - UnitProductionCallback{}); + this->model_factory = Factory( + 2, PlayerId::PLAYER2, ActorType::FACTORY, 1, 100, + DoubleVec2D(15, 15), gold_manager, 0, 100, ActorType::VILLAGER, + villager_frequency, soldier_frequency, UnitProductionCallback{}); soldiers = {}; villagers = {}; factories = {}; // Player1 has 2 villagers say.. - villagers[0].push_back(make_unique( - 1, PlayerId::PLAYER1, ActorType::VILLAGER, 100, 100, - DoubleVec2D(10, 10), gold_manager.get(), path_planner.get(), 10, 10, - 10, 10, 10, 10)); - villagers[0].push_back(make_unique( - 3, PlayerId::PLAYER1, ActorType::VILLAGER, 100, 100, - DoubleVec2D(10, 10), gold_manager.get(), path_planner.get(), 10, 10, - 10, 10, 10, 10)); + villagers[0].push_back( + make_unique(1, PlayerId::PLAYER1, ActorType::VILLAGER, + 100, 100, DoubleVec2D(10, 10), gold_manager, + path_planner.get(), 10, 10, 10, 10, 10, 10)); + villagers[0].push_back( + make_unique(3, PlayerId::PLAYER1, ActorType::VILLAGER, + 100, 100, DoubleVec2D(10, 10), gold_manager, + path_planner.get(), 10, 10, 10, 10, 10, 10)); // Player 2 has 1 villager - villagers[1].push_back(make_unique( - 4, PlayerId::PLAYER1, ActorType::VILLAGER, 100, 100, - DoubleVec2D(10, 10), gold_manager.get(), path_planner.get(), 10, 10, - 10, 10, 10, 10)); + villagers[1].push_back( + make_unique(4, PlayerId::PLAYER1, ActorType::VILLAGER, + 100, 100, DoubleVec2D(10, 10), gold_manager, + path_planner.get(), 10, 10, 10, 10, 10, 10)); // Player2 has 1 soldier, say.. - soldiers[1].push_back( - make_unique(2, PlayerId::PLAYER2, ActorType::SOLDIER, 100, - 100, DoubleVec2D(10, 10), gold_manager.get(), - path_planner.get(), 10, 10, 10)); + soldiers[1].push_back(make_unique( + 2, PlayerId::PLAYER2, ActorType::SOLDIER, 100, 100, + DoubleVec2D(10, 10), gold_manager, path_planner.get(), 10, 10, 10)); Actor::SetActorIdIncrement(4); this->state = make_unique( - move(map), move(gold_manager), move(path_planner), move(soldiers), - move(villagers), move(factories), move(model_villager), - move(model_soldier), move(model_factory)); + move(map), move(unique_gold_manager), move(path_planner), + move(soldiers), move(villagers), move(factories), + move(model_villager), move(model_soldier), move(model_factory)); } }; @@ -181,6 +177,7 @@ TEST_F(StateTest, SoldierAttackEnemyTest) { } TEST_F(StateTest, CreateFactoryTest) { + EXPECT_CALL(*gold_manager, DeductUnitCreateCost); state->CreateFactory(PlayerId::PLAYER1, 1, Vec2D(0, 0)); auto curr_villagers = state->GetVillagers(); auto curr_factories = state->GetFactories(); @@ -200,13 +197,12 @@ TEST_F(StateTest, CreateFactoryTest) { auto factory = curr_factories[0].front(); ASSERT_EQ(curr_factories[0].size(), 1); ASSERT_EQ(factory->GetState(), FactoryStateName::UNBUILT); - - auto gold_manager = villager->GetGoldManager(); - ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER1), 5000 - FACTORY_COST); + ASSERT_EQ(curr_factories[0].front()->GetState(), FactoryStateName::UNBUILT); } TEST_F(StateTest, BuildFactoryTest) { // Repeat CreateFactory as above.. + EXPECT_CALL(*gold_manager, DeductUnitCreateCost); state->CreateFactory(PlayerId::PLAYER1, 1, Vec2D(0, 0)); auto curr_villagers = state->GetVillagers(); auto curr_factories = state->GetFactories(); @@ -222,13 +218,20 @@ TEST_F(StateTest, BuildFactoryTest) { // Getting the updated factories from state curr_factories = state->GetFactories(); auto factory = curr_factories[0].front(); + + // Updating state + state->Update(); + + // Adding expectation for gold manager as state is updated + + ASSERT_EQ(curr_villagers[0].front()->GetState(), VillagerStateName::BUILD); ASSERT_EQ(curr_factories[0].size(), 1); ASSERT_EQ(factory->GetState(), FactoryStateName::UNBUILT); // Test BuildFactory for the same villager.. state->BuildFactory(PlayerId::PLAYER1, 1, factory->GetActorId()); - // Updating state again + // Updating state state->Update(); ASSERT_EQ(curr_villagers[0].front()->GetState(), VillagerStateName::BUILD); @@ -237,6 +240,7 @@ TEST_F(StateTest, BuildFactoryTest) { // Test BuildFactory for the other villager.. state->BuildFactory(PlayerId::PLAYER1, 3, factory->GetActorId()); + // Updating state state->Update(); ASSERT_EQ(curr_villagers[0][1]->GetState(), VillagerStateName::BUILD); @@ -245,6 +249,7 @@ TEST_F(StateTest, BuildFactoryTest) { TEST_F(StateTest, FactoryProductionTest) { // Repeat CreateFactory as above.. + EXPECT_CALL(*gold_manager, DeductUnitCreateCost); state->CreateFactory(PlayerId::PLAYER1, 1, Vec2D(0, 0)); auto curr_villagers = state->GetVillagers(); auto curr_factories = state->GetFactories(); @@ -255,6 +260,9 @@ TEST_F(StateTest, FactoryProductionTest) { state->Update(); } + // Updating state + state->Update(); + ASSERT_EQ(villager->GetState(), VillagerStateName::BUILD); // Getting the updated state factories @@ -269,6 +277,7 @@ TEST_F(StateTest, FactoryProductionTest) { state->SetFactoryProduction(PlayerId::PLAYER1, factory->GetActorId(), ActorType::SOLDIER); + // Updating state state->Update(); ASSERT_EQ(factory->GetProductionState(), ActorType::SOLDIER); @@ -276,6 +285,7 @@ TEST_F(StateTest, FactoryProductionTest) { TEST_F(StateTest, FactoryDeathTest) { // Repeat CreateFactory as above.. + EXPECT_CALL(*gold_manager, DeductUnitCreateCost); state->CreateFactory(PlayerId::PLAYER1, 1, Vec2D(0, 0)); auto curr_villagers = state->GetVillagers(); auto curr_factories = state->GetFactories(); @@ -287,6 +297,9 @@ TEST_F(StateTest, FactoryDeathTest) { state->Update(); } + // Updating state + state->Update(); + ASSERT_EQ(villager->GetState(), VillagerStateName::BUILD); // Updating current factories from the state @@ -304,7 +317,9 @@ TEST_F(StateTest, FactoryDeathTest) { ASSERT_EQ(factory->GetState(), FactoryStateName::DEAD); ASSERT_EQ(factory->GetHp(), 0); - // Updating the state + // Kill factory + auto curr_factory = curr_factories[0].front(); + curr_factory->SetHp(0); state->Update(); auto new_facs = state->GetFactories(); diff --git a/test/state/villager_test.cpp b/test/state/villager_test.cpp index 531b003..f9216b6 100644 --- a/test/state/villager_test.cpp +++ b/test/state/villager_test.cpp @@ -3,7 +3,7 @@ #include "state/actor/factory.h" #include "state/actor/soldier.h" #include "state/actor/villager.h" -#include "state/gold_manager/gold_manager.h" +#include "state/mocks/gold_manager_mock.h" #include "gtest/gtest.h" using namespace std; @@ -21,13 +21,7 @@ class VillagerTest : public Test { int64_t element_size = 10; unique_ptr map; - array player_gold; - int64_t soldier_kill_reward_gold; - int64_t villager_kill_reward_gold; - int64_t factory_kill_reward_gold; - int64_t max_gold; - - unique_ptr gold_manager; + unique_ptr gold_manager; unique_ptr path_planner; unique_ptr villager; @@ -49,20 +43,7 @@ class VillagerTest : public Test { }}; map = make_unique(map_matrix, map_size, element_size); - for (int i = 0; i < (int)PlayerId::PLAYER_COUNT; ++i) { - player_gold[i] = 5000; // Start balance - } - - this->max_gold = 10000; - this->soldier_kill_reward_gold = SOLDIER_KILL_REWARD_AMOUNT; - this->villager_kill_reward_gold = VILLAGER_KILL_REWARD_AMOUNT; - this->factory_kill_reward_gold = FACTORY_KILL_REWARD_AMOUNT; - - this->gold_manager = make_unique( - player_gold, max_gold, soldier_kill_reward_gold, - villager_kill_reward_gold, factory_kill_reward_gold, - FACTORY_SUICIDE_PENALTY, VILLAGER_COST, SOLDIER_COST, FACTORY_COST, - MINING_REWARD); + this->gold_manager = make_unique(); this->path_planner = make_unique(map.get()); @@ -100,6 +81,8 @@ TEST_F(VillagerTest, MoveToDestination) { } TEST_F(VillagerTest, TransitionToMineState) { + EXPECT_CALL(*this->gold_manager, + AddMineRequest(PlayerId::PLAYER1, Vec2D(10, 10))); this->villager->Mine(Vec2D(10, 10)); this->villager->Update(); @@ -129,6 +112,9 @@ TEST_F(VillagerTest, TransitionToDeadState) { this->villager->Attack(target_villager.get()); + // GoldManager expects call for villager killing target villager + EXPECT_CALL(*gold_manager, RewardKill(target_villager.get())); + while (target_villager->GetHp() != 0) { this->villager->Update(); target_villager->Update(); @@ -140,7 +126,12 @@ TEST_F(VillagerTest, TransitionToDeadState) { } TEST_F(VillagerTest, MoveToMine) { - this->villager->SetMineTarget(Vec2D(25, 30)); + ASSERT_EQ(this->villager->GetState(), VillagerStateName::IDLE); + this->villager->SetMineTarget(Vec2D(20, 10)); + + // A build request will be added to the gold manager when the villager tries + // to mine + EXPECT_CALL(*gold_manager, AddMineRequest); while (this->villager->GetState() != VillagerStateName::MINE) { this->villager->Update(); @@ -149,12 +140,12 @@ TEST_F(VillagerTest, MoveToMine) { ASSERT_EQ(this->villager->IsMineTargetInRange(), true); } - +// This is a test for gold manager, not for villager TEST_F(VillagerTest, VillagerKillReward) { - int64_t initial_gold = player_gold[0]; auto target_villager = MakeTestVillager(); + EXPECT_CALL(*this->gold_manager, RewardKill(target_villager.get())); this->villager->Attack(target_villager.get()); while (target_villager->GetHp() != 0) { @@ -163,9 +154,6 @@ TEST_F(VillagerTest, VillagerKillReward) { this->villager->LateUpdate(); target_villager->LateUpdate(); } - - ASSERT_EQ(gold_manager->GetBalance(PlayerId::PLAYER1), - (initial_gold + villager_kill_reward_gold)); } TEST_F(VillagerTest, SimultaneousKill) { @@ -174,6 +162,11 @@ TEST_F(VillagerTest, SimultaneousKill) { villager->Attack(target_villager.get()); target_villager->Attack(villager.get()); + // Expecting call to gold manager for rewarding gold to both villagers for + // killing each other + EXPECT_CALL(*gold_manager, RewardKill(target_villager.get())); + EXPECT_CALL(*gold_manager, RewardKill(this->villager.get())); + while (target_villager->GetHp() != 0) { this->villager->Update(); target_villager->Update();