1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Nullkiller: initial cleanup of unused code

This commit is contained in:
Andrii Danylchenko
2019-12-15 15:47:21 +02:00
committed by Andrii Danylchenko
parent 5344df51c6
commit af0dcf97c4
67 changed files with 378 additions and 2913 deletions

View File

@@ -13,8 +13,6 @@
AIhelper::AIhelper() AIhelper::AIhelper()
{ {
resourceManager.reset(new ResourceManager());
buildingManager.reset(new BuildingManager());
pathfindingManager.reset(new PathfindingManager()); pathfindingManager.reset(new PathfindingManager());
armyManager.reset(new ArmyManager()); armyManager.reset(new ArmyManager());
heroManager.reset(new HeroManager()); heroManager.reset(new HeroManager());
@@ -24,15 +22,8 @@ AIhelper::~AIhelper()
{ {
} }
bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal)
{
return resourceManager->notifyGoalCompleted(goal);
}
void AIhelper::init(CPlayerSpecificInfoCallback * CB) void AIhelper::init(CPlayerSpecificInfoCallback * CB)
{ {
resourceManager->init(CB);
buildingManager->init(CB);
pathfindingManager->init(CB); pathfindingManager->init(CB);
armyManager->init(CB); armyManager->init(CB);
heroManager->init(CB); heroManager->init(CB);
@@ -40,8 +31,6 @@ void AIhelper::init(CPlayerSpecificInfoCallback * CB)
void AIhelper::setAI(VCAI * AI) void AIhelper::setAI(VCAI * AI)
{ {
resourceManager->setAI(AI);
buildingManager->setAI(AI);
pathfindingManager->setAI(AI); pathfindingManager->setAI(AI);
armyManager->setAI(AI); armyManager->setAI(AI);
heroManager->setAI(AI); heroManager->setAI(AI);
@@ -53,106 +42,6 @@ void AIhelper::update()
heroManager->update(); heroManager->update();
} }
bool AIhelper::getBuildingOptions(const CGTownInstance * t)
{
return buildingManager->getBuildingOptions(t);
}
BuildingID AIhelper::getMaxPossibleGoldBuilding(const CGTownInstance * t)
{
return buildingManager->getMaxPossibleGoldBuilding(t);
}
boost::optional<PotentialBuilding> AIhelper::immediateBuilding() const
{
return buildingManager->immediateBuilding();
}
boost::optional<PotentialBuilding> AIhelper::expensiveBuilding() const
{
return buildingManager->expensiveBuilding();
}
boost::optional<BuildingID> AIhelper::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
{
return buildingManager->canBuildAnyStructure(t, buildList, maxDays);
}
Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal)
{
return resourceManager->whatToDo(res, goal);
}
Goals::TSubgoal AIhelper::whatToDo() const
{
return resourceManager->whatToDo();
}
bool AIhelper::containsObjective(Goals::TSubgoal goal) const
{
return resourceManager->containsObjective(goal);
}
bool AIhelper::hasTasksLeft() const
{
return resourceManager->hasTasksLeft();
}
bool AIhelper::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal&)> predicate)
{
return resourceManager->removeOutdatedObjectives(predicate);
}
bool AIhelper::canAfford(const TResources & cost) const
{
return resourceManager->canAfford(cost);
}
TResources AIhelper::reservedResources() const
{
return resourceManager->reservedResources();
}
TResources AIhelper::freeResources() const
{
return resourceManager->freeResources();
}
TResource AIhelper::freeGold() const
{
return resourceManager->freeGold();
}
TResources AIhelper::allResources() const
{
return resourceManager->allResources();
}
TResource AIhelper::allGold() const
{
return resourceManager->allGold();
}
Goals::TGoalVec AIhelper::howToVisitTile(const int3 & tile, bool allowGatherArmy) const
{
return pathfindingManager->howToVisitTile(tile, allowGatherArmy);
}
Goals::TGoalVec AIhelper::howToVisitObj(ObjectIdRef obj, bool allowGatherArmy) const
{
return pathfindingManager->howToVisitObj(obj, allowGatherArmy);
}
Goals::TGoalVec AIhelper::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
{
return pathfindingManager->howToVisitTile(hero, tile, allowGatherArmy);
}
Goals::TGoalVec AIhelper::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
{
return pathfindingManager->howToVisitObj(hero, obj, allowGatherArmy);
}
std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
{ {
return pathfindingManager->getPathsToTile(hero, tile); return pathfindingManager->getPathsToTile(hero, tile);

View File

@@ -14,8 +14,6 @@
!!! Note: Include THIS file at the end of include list to avoid "undefined base class" error !!! Note: Include THIS file at the end of include list to avoid "undefined base class" error
*/ */
#include "ResourceManager.h"
#include "BuildingManager.h"
#include "ArmyManager.h" #include "ArmyManager.h"
#include "HeroManager.h" #include "HeroManager.h"
#include "Pathfinding/PathfindingManager.h" #include "Pathfinding/PathfindingManager.h"
@@ -24,14 +22,11 @@ class ResourceManager;
class BuildingManager; class BuildingManager;
//indirection interface for various modules //TODO: remove class, put managers to engine
class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager, public IArmyManager, public IHeroManager class DLL_EXPORT AIhelper : public IPathfindingManager, public IArmyManager, public IHeroManager
{ {
friend class VCAI; //std::shared_ptr<ResourceManager> resourceManager;
friend struct SetGlobalState; //mess? //std::shared_ptr<BuildingManager> buildingManager;
std::shared_ptr<ResourceManager> resourceManager;
std::shared_ptr<BuildingManager> buildingManager;
std::shared_ptr<PathfindingManager> pathfindingManager; std::shared_ptr<PathfindingManager> pathfindingManager;
std::shared_ptr<ArmyManager> armyManager; std::shared_ptr<ArmyManager> armyManager;
std::shared_ptr<HeroManager> heroManager; std::shared_ptr<HeroManager> heroManager;
@@ -40,29 +35,6 @@ public:
AIhelper(); AIhelper();
~AIhelper(); ~AIhelper();
bool canAfford(const TResources & cost) const;
TResources reservedResources() const override;
TResources freeResources() const override;
TResource freeGold() const override;
TResources allResources() const override;
TResource allGold() const override;
Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) override;
Goals::TSubgoal whatToDo() const override;
bool containsObjective(Goals::TSubgoal goal) const override;
bool hasTasksLeft() const override;
bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
bool getBuildingOptions(const CGTownInstance * t) override;
BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
boost::optional<PotentialBuilding> immediateBuilding() const override;
boost::optional<PotentialBuilding> expensiveBuilding() const override;
boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override; std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
std::vector<AIPath> getPathsToTile(const int3 & tile) const override; std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override; void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
@@ -95,9 +67,6 @@ public:
void update() override; void update() override;
private:
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
void init(CPlayerSpecificInfoCallback * CB) override; void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override; void setAI(VCAI * AI) override;
}; };

View File

@@ -91,6 +91,11 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1) if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
continue; continue;
if(path.specialAction && !path.specialAction->canAct(path.targetHero))
{
auto subGoal = path.specialAction->whatToDo(path.targetHero);
}
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger); auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
#ifdef AI_TRACE_LEVEL >= 2 #ifdef AI_TRACE_LEVEL >= 2

View File

@@ -148,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(treatIsUnderControl) if(treatIsUnderControl)
continue; continue;
if(cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST) if(!town->visitingHero
&& town->hasBuilt(BuildingID::TAVERN)
&& cb->getResourceAmount(Res::GOLD) > GameConstants::HERO_GOLD_COST)
{ {
auto heroesInTavern = cb->getAvailableHeroes(town); auto heroesInTavern = cb->getAvailableHeroes(town);

View File

@@ -56,6 +56,8 @@ Goals::TGoalVec GatherArmyBehavior::getTasks()
{ {
vstd::concatenate(tasks, upgradeArmy(town)); vstd::concatenate(tasks, upgradeArmy(town));
} }
return tasks;
} }
Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const

View File

@@ -1,257 +0,0 @@
/*
* BuildingManager.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "BuildingManager.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/MapObjects.h"
bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays)
{
if (maxDays == 0)
{
logAi->warn("Request to build building %d in 0 days!", building.toEnum());
return false;
}
if (!vstd::contains(t->town->buildings, building))
return false; // no such building in town
if (t->hasBuilt(building)) //Already built? Shouldn't happen in general
return true;
const CBuilding * buildPtr = t->town->buildings.at(building);
auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID)
{
return t->hasBuilt(buildID);
});
toBuild.push_back(building);
for (BuildingID buildID : toBuild)
{
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
if (canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER)
return false; //we won't be able to build this
}
if (maxDays && toBuild.size() > maxDays)
return false;
//TODO: calculate if we have enough resources to build it in maxDays?
for (const auto & buildID : toBuild)
{
const CBuilding * b = t->town->buildings.at(buildID);
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
if (canBuild == EBuildingState::ALLOWED)
{
PotentialBuilding pb;
pb.bid = buildID;
pb.price = t->getBuildingCost(buildID);
immediateBuildings.push_back(pb); //these are checked again in try
return true;
}
else if (canBuild == EBuildingState::PREREQUIRES)
{
// can happen when dependencies have their own missing dependencies
if (tryBuildThisStructure(t, buildID, maxDays - 1))
return true;
}
else if (canBuild == EBuildingState::MISSING_BASE)
{
if (tryBuildThisStructure(t, b->upgrade, maxDays - 1))
return true;
}
else if (canBuild == EBuildingState::NO_RESOURCES)
{
//we may need to gather resources for those
PotentialBuilding pb;
pb.bid = buildID;
pb.price = t->getBuildingCost(buildID);
expensiveBuildings.push_back(pb); //these are checked again in try
return false;
}
else
return false;
}
return false;
}
bool BuildingManager::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
{
for (const auto & building : buildList)
{
if (t->hasBuilt(building))
continue;
return tryBuildThisStructure(t, building, maxDays);
}
return false; //Can't build anything
}
boost::optional<BuildingID> BuildingManager::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
{
for (const auto & building : buildList)
{
if (t->hasBuilt(building))
continue;
switch (cb->canBuildStructure(t, building))
{
case EBuildingState::ALLOWED:
case EBuildingState::NO_RESOURCES: //TODO: allow this via optional parameter?
return boost::optional<BuildingID>(building);
break;
}
}
return boost::optional<BuildingID>(); //Can't build anything
}
bool BuildingManager::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
{
for (const auto & building : buildList)
{
if (t->hasBuilt(building))
continue;
return tryBuildThisStructure(t, building, maxDays);
}
return false; //Nothing to build
}
void BuildingManager::init(CPlayerSpecificInfoCallback * CB)
{
cb = CB;
}
void BuildingManager::setAI(VCAI * AI)
{
ai = AI;
}
//Set of buildings for different goals. Does not include any prerequisites.
static const std::vector<BuildingID> essential = { BuildingID::TAVERN, BuildingID::TOWN_HALL };
static const std::vector<BuildingID> basicGoldSource = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL };
static const std::vector<BuildingID> capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL };
static const std::vector<BuildingID> unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 };
static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP };
static const std::vector<BuildingID> unitGrowth = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR };
static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 };
static const std::vector<BuildingID> extra = { BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
BuildingID::SPECIAL_4, BuildingID::SHIPYARD }; // all remaining buildings
bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
{
//TODO make *real* town development system
//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
//TODO: build resource silo, defences when needed
//Possible - allow "locking" on specific building (build prerequisites and then building itself)
//TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal.
immediateBuildings.clear();
expensiveBuildings.clear();
//below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal
//changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings
TResources currentRes = cb->getResourceAmount();
TResources currentIncome = t->dailyIncome();
if(tryBuildAnyStructure(t, essential))
return true;
//the more gold the better and less problems later //TODO: what about building mage guild / marketplace etc. with city hall disabled in editor?
if(tryBuildNextStructure(t, basicGoldSource))
return true;
//workaround for mantis #2696 - build capitol with separate algorithm if it is available
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
{
if(tryBuildNextStructure(t, capitolAndRequirements))
return true;
}
if(!t->hasBuilt(BuildingID::FORT)) //in vast majority of situations fort is top priority building if we already have city hall, TODO: unite with unitGrowth building chain
if(tryBuildThisStructure(t, BuildingID::FORT))
return true;
if (cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
{
if (tryBuildNextStructure(t, unitGrowth, 2))
return true;
}
//try building dwellings
if (t->hasBuilt(BuildingID::FORT))
{
if (tryBuildAnyStructure(t, unitsSource, 8 - cb->getDate(Date::DAY_OF_WEEK)))
return true;
}
//try to upgrade dwelling
for (int i = 0; i < unitsUpgrade.size(); i++)
{
if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT))
{
if (tryBuildThisStructure(t, unitsUpgrade[i]))
return true;
}
}
//remaining tasks
if (tryBuildNextStructure(t, _spells))
return true;
if (tryBuildAnyStructure(t, extra))
return true;
//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
std::vector<BuildingID> extraBuildings;
for (auto buildingInfo : t->town->buildings)
{
if (buildingInfo.first > 43)
extraBuildings.push_back(buildingInfo.first);
}
return tryBuildAnyStructure(t, extraBuildings);
}
BuildingID BuildingManager::getMaxPossibleGoldBuilding(const CGTownInstance * t)
{
if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
return BuildingID::CAPITOL;
else if(cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
return BuildingID::CITY_HALL;
else if(cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
return BuildingID::TOWN_HALL;
else
return BuildingID::VILLAGE_HALL;
}
boost::optional<PotentialBuilding> BuildingManager::immediateBuilding() const
{
if (immediateBuildings.size())
return boost::optional<PotentialBuilding>(immediateBuildings.front()); //back? whatever
else
return boost::optional<PotentialBuilding>();
}
boost::optional<PotentialBuilding> BuildingManager::expensiveBuilding() const
{
if (expensiveBuildings.size())
return boost::optional<PotentialBuilding>(expensiveBuildings.front());
else
return boost::optional<PotentialBuilding>();
}

View File

@@ -1,75 +0,0 @@
/*
* BuildingManager.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "AIUtility.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/CTownHandler.h"
#include "../../lib/CBuildingHandler.h"
#include "VCAI.h"
struct DLL_EXPORT PotentialBuilding
{
BuildingID bid;
TResources price;
//days to build?
};
class DLL_EXPORT IBuildingManager //: public: IAbstractManager
{ //info about town development
public:
virtual ~IBuildingManager() = default;
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
virtual void setAI(VCAI * AI) = 0;
virtual bool getBuildingOptions(const CGTownInstance * t) = 0;
virtual boost::optional<PotentialBuilding> immediateBuilding() const = 0;
virtual boost::optional<PotentialBuilding> expensiveBuilding() const = 0;
virtual boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const = 0;
};
class DLL_EXPORT BuildingManager : public IBuildingManager
{
friend class VCAI;
friend class AIhelper;
friend struct SetGlobalState;
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
VCAI * ai;
public:
//try build anything in given town, and execute resulting Goal if any
bool getBuildingOptions(const CGTownInstance * t) override;
BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
boost::optional<PotentialBuilding> immediateBuilding() const override;
boost::optional<PotentialBuilding> expensiveBuilding() const override;
boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
protected:
bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
//try build first unbuilt structure
bool tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7);
//try build ANY unbuilt structure
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
private:
//TODO: remember current town?
std::vector<PotentialBuilding> immediateBuildings; //what we can build right now in current town
std::vector<PotentialBuilding> expensiveBuildings; //what we coudl build but can't afford
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
};

View File

@@ -17,19 +17,12 @@ set(VCAI_SRCS
AIhelper.cpp AIhelper.cpp
ArmyManager.cpp ArmyManager.cpp
HeroManager.cpp HeroManager.cpp
ResourceManager.cpp
BuildingManager.cpp
SectorMap.cpp
BuildingManager.cpp
MapObjectsEvaluator.cpp MapObjectsEvaluator.cpp
FuzzyEngines.cpp FuzzyEngines.cpp
FuzzyHelper.cpp FuzzyHelper.cpp
Goals/AbstractGoal.cpp Goals/AbstractGoal.cpp
Goals/BuildBoat.cpp Goals/BuildBoat.cpp
Goals/Build.cpp
Goals/BuildThis.cpp Goals/BuildThis.cpp
Goals/Explore.cpp
Goals/GatherArmy.cpp
Goals/DismissHero.cpp Goals/DismissHero.cpp
Goals/GatherTroops.cpp Goals/GatherTroops.cpp
Goals/BuyArmy.cpp Goals/BuyArmy.cpp
@@ -41,8 +34,6 @@ set(VCAI_SRCS
Goals/CollectRes.cpp Goals/CollectRes.cpp
Goals/Trade.cpp Goals/Trade.cpp
Goals/RecruitHero.cpp Goals/RecruitHero.cpp
Goals/Conquer.cpp
Goals/ClearWayTo.cpp
Goals/DigAtTile.cpp Goals/DigAtTile.cpp
Goals/GetArtOfType.cpp Goals/GetArtOfType.cpp
Goals/FindObj.cpp Goals/FindObj.cpp
@@ -85,10 +76,6 @@ set(VCAI_HEADERS
AIhelper.h AIhelper.h
ArmyManager.h ArmyManager.h
HeroManager.h HeroManager.h
ResourceManager.h
BuildingManager.h
SectorMap.h
BuildingManager.h
MapObjectsEvaluator.h MapObjectsEvaluator.h
FuzzyEngines.h FuzzyEngines.h
FuzzyHelper.h FuzzyHelper.h
@@ -96,10 +83,7 @@ set(VCAI_HEADERS
Goals/CGoal.h Goals/CGoal.h
Goals/Invalid.h Goals/Invalid.h
Goals/BuildBoat.h Goals/BuildBoat.h
Goals/Build.h
Goals/BuildThis.h Goals/BuildThis.h
Goals/Explore.h
Goals/GatherArmy.h
Goals/DismissHero.h Goals/DismissHero.h
Goals/GatherTroops.h Goals/GatherTroops.h
Goals/BuyArmy.h Goals/BuyArmy.h
@@ -111,8 +95,6 @@ set(VCAI_HEADERS
Goals/CollectRes.h Goals/CollectRes.h
Goals/Trade.h Goals/Trade.h
Goals/RecruitHero.h Goals/RecruitHero.h
Goals/Conquer.h
Goals/ClearWayTo.h
Goals/DigAtTile.h Goals/DigAtTile.h
Goals/GetArtOfType.h Goals/GetArtOfType.h
Goals/FindObj.h Goals/FindObj.h

View File

@@ -184,7 +184,7 @@ void Nullkiller::makeTurn()
} }
catch(goalFulfilledException &) catch(goalFulfilledException &)
{ {
logAi->trace(bestTask->completeMessage()); logAi->trace("Task %s completed", bestTask->name());
} }
catch(std::exception & e) catch(std::exception & e)
{ {

View File

@@ -23,7 +23,8 @@ extern FuzzyHelper * fh;
engineBase::engineBase() engineBase::engineBase()
{ {
engine.addRuleBlock(&rules); rules = new fl::RuleBlock();
engine.addRuleBlock(rules);
} }
void engineBase::configure() void engineBase::configure()
@@ -34,7 +35,7 @@ void engineBase::configure()
void engineBase::addRule(const std::string & txt) void engineBase::addRule(const std::string & txt)
{ {
rules.addRule(fl::Rule::parse(txt, &engine)); rules->addRule(fl::Rule::parse(txt, &engine));
} }
struct armyStructure struct armyStructure

View File

@@ -17,7 +17,7 @@ class engineBase //subclasses create fuzzylite variables with "new" that are not
{ {
protected: protected:
fl::Engine engine; fl::Engine engine;
fl::RuleBlock rules; fl::RuleBlock * rules;
virtual void configure(); virtual void configure();
void addRule(const std::string & txt); void addRule(const std::string & txt);
public: public:

View File

@@ -19,44 +19,6 @@ FuzzyHelper * fh;
extern boost::thread_specific_ptr<VCAI> ai; extern boost::thread_specific_ptr<VCAI> ai;
extern boost::thread_specific_ptr<CCallback> cb; extern boost::thread_specific_ptr<CCallback> cb;
Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec)
{
if(vec.empty())
{
logAi->debug("FuzzyHelper found no goals. Returning Goals::Invalid.");
//no possibilities found
return sptr(Goals::Invalid());
}
//a trick to switch between heroes less often - calculatePaths is costly
auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
{
return lhs->hero.h < rhs->hero.h;
};
boost::sort(vec, sortByHeroes);
for(auto g : vec)
{
setPriority(g);
}
auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
{
return lhs->priority < rhs->priority;
};
for(auto goal : vec)
{
logAi->trace("FuzzyHelper evaluated goal %s, priority=%.4f", goal->name(), goal->priority);
}
Goals::TSubgoal result = *boost::max_element(vec, compareGoals);
logAi->debug("FuzzyHelper returned goal %s, priority=%.4f", result->name(), result->priority);
return result;
}
ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
{ {
@@ -77,134 +39,6 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
} }
float FuzzyHelper::evaluate(Goals::VisitTile & g)
{
if(g.parent)
{
g.parent->accept(this);
}
return visitTileEngine.evaluate(g);
}
float FuzzyHelper::evaluate(Goals::BuildBoat & g)
{
const float buildBoatPenalty = 0.25;
if(!g.parent)
{
return 0;
}
return g.parent->accept(this) - buildBoatPenalty;
}
float FuzzyHelper::evaluate(Goals::AdventureSpellCast & g)
{
if(!g.parent)
{
return 0;
}
const CSpell * spell = g.getSpell();
const float spellCastPenalty = (float)g.hero->getSpellCost(spell) / g.hero->mana;
return g.parent->accept(this) - spellCastPenalty;
}
float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
{
// TODO: How to evaluate quest complexity?
const float questPenalty = 0.2;
if(!g.parent)
{
return 0;
}
return g.parent->accept(this) * questPenalty;
}
float FuzzyHelper::evaluate(Goals::VisitObj & g)
{
if(g.parent)
{
g.parent->accept(this);
}
return visitObjEngine.evaluate(g);
}
float FuzzyHelper::evaluate(Goals::VisitHero & g)
{
auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
if(!obj)
return -100; //hero died in the meantime
else
{
g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this));
}
return g.priority;
}
float FuzzyHelper::evaluate(Goals::GatherArmy & g)
{
//the more army we need, the more important goal
//the more army we lack, the less important goal
float army = g.hero->getArmyStrength();
float ratio = g.value / std::max(g.value - army, 2000.0f); //2000 is about the value of hero recruited from tavern
return 5 * (ratio / (ratio + 2)); //so 50% army gives 2.5, asymptotic 5
}
float FuzzyHelper::evaluate(Goals::ClearWayTo & g)
{
if (!g.hero.h)
return 0; //lowest priority
return g.whatToDoToAchieve()->accept(this);
}
float FuzzyHelper::evaluate(Goals::BuildThis & g)
{
return g.priority; //TODO
}
float FuzzyHelper::evaluate(Goals::DigAtTile & g)
{
return 0;
}
float FuzzyHelper::evaluate(Goals::CollectRes & g)
{
return g.priority; //handled by ResourceManager
}
float FuzzyHelper::evaluate(Goals::Build & g)
{
return 0;
}
float FuzzyHelper::evaluate(Goals::BuyArmy & g)
{
return g.priority;
}
float FuzzyHelper::evaluate(Goals::Explore & g)
{
return 1;
}
float FuzzyHelper::evaluate(Goals::RecruitHero & g)
{
return 1;
}
float FuzzyHelper::evaluate(Goals::Invalid & g)
{
return -1e10;
}
float FuzzyHelper::evaluate(Goals::AbstractGoal & g)
{
logAi->warn("Cannot evaluate goal %s", g.name());
return g.priority;
}
void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern
{
g->setpriority(g->accept(this)); //this enforces returned value is set
}
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor) ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
{ {
return evaluateDanger(tile, visitor, ai.get()); return evaluateDanger(tile, visitor, ai.get());

View File

@@ -16,33 +16,9 @@ class DLL_EXPORT FuzzyHelper
{ {
public: public:
TacticalAdvantageEngine tacticalAdvantageEngine; TacticalAdvantageEngine tacticalAdvantageEngine;
VisitTileEngine visitTileEngine;
VisitObjEngine visitObjEngine;
float evaluate(Goals::Explore & g);
float evaluate(Goals::RecruitHero & g);
float evaluate(Goals::VisitTile & g);
float evaluate(Goals::VisitObj & g);
float evaluate(Goals::VisitHero & g);
float evaluate(Goals::BuildThis & g);
float evaluate(Goals::DigAtTile & g);
float evaluate(Goals::CollectRes & g);
float evaluate(Goals::Build & g);
float evaluate(Goals::BuyArmy & g);
float evaluate(Goals::BuildBoat & g);
float evaluate(Goals::GatherArmy & g);
float evaluate(Goals::ClearWayTo & g);
float evaluate(Goals::CompleteQuest & g);
float evaluate(Goals::AdventureSpellCast & g);
float evaluate(Goals::Invalid & g);
float evaluate(Goals::AbstractGoal & g);
void setPriority(Goals::TSubgoal & g);
ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class? ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
Goals::TSubgoal chooseSolution(Goals::TGoalVec vec);
//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai); ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true); ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true);
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor); ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);

View File

@@ -12,8 +12,6 @@
#include "../VCAI.h" #include "../VCAI.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -115,63 +113,12 @@ bool AbstractGoal::operator==(const AbstractGoal & g) const
return false; return false;
} }
bool AbstractGoal::operator<(AbstractGoal & g) //for std::unique
{
//TODO: make sure it gets goals consistent with == operator
if (goalType < g.goalType)
return true;
if (goalType > g.goalType)
return false;
if (hero < g.hero)
return true;
if (hero > g.hero)
return false;
if (tile < g.tile)
return true;
if (g.tile < tile)
return false;
if (objid < g.objid)
return true;
if (objid > g.objid)
return false;
if (town < g.town)
return true;
if (town > g.town)
return false;
if (value < g.value)
return true;
if (value > g.value)
return false;
if (priority < g.priority)
return true;
if (priority > g.priority)
return false;
if (resID < g.resID)
return true;
if (resID > g.resID)
return false;
if (bid < g.bid)
return true;
if (bid > g.bid)
return false;
if (aid < g.aid)
return true;
if (aid > g.aid)
return false;
return false;
}
//TODO: find out why the following are not generated automatically on MVS? //TODO: find out why the following are not generated automatically on MVS?
bool TSubgoal::operator==(const TSubgoal & rhs) const bool TSubgoal::operator==(const TSubgoal & rhs) const
{ {
return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
} }
bool TSubgoal::operator<(const TSubgoal & rhs) const
{
return get() < rhs.get(); //compae by value
}
bool AbstractGoal::invalid() const bool AbstractGoal::invalid() const
{ {
return goalType == EGoals::INVALID; return goalType == EGoals::INVALID;
@@ -182,11 +129,6 @@ void AbstractGoal::accept(VCAI * ai)
ai->tryRealize(*this); ai->tryRealize(*this);
} }
float AbstractGoal::accept(FuzzyHelper * f)
{
return f->evaluate(*this);
}
EvaluationContext::EvaluationContext() EvaluationContext::EvaluationContext()
: movementCost(0.0), : movementCost(0.0),
manaCost(0), manaCost(0),

View File

@@ -30,7 +30,6 @@ namespace Goals
class BuildThis; class BuildThis;
class DigAtTile; class DigAtTile;
class CollectRes; class CollectRes;
class Build;
class BuyArmy; class BuyArmy;
class BuildBoat; class BuildBoat;
class GatherArmy; class GatherArmy;
@@ -43,7 +42,8 @@ namespace Goals
enum EGoals enum EGoals
{ {
INVALID = -1, INVALID = -1,
WIN, CONQUER, BUILD, //build needs to get a real reasoning WIN, CONQUER,
BUILD,
EXPLORE, GATHER_ARMY, EXPLORE, GATHER_ARMY,
BOOST_HERO, BOOST_HERO,
RECRUIT_HERO, RECRUIT_HERO,
@@ -162,25 +162,15 @@ namespace Goals
EGoals goalType; EGoals goalType;
virtual std::string name() const; virtual std::string name() const;
virtual std::string completeMessage() const
{
return "This goal is unspecified!";
}
bool invalid() const; bool invalid() const;
///Visitor pattern ///Visitor pattern
//TODO: make accept work for std::shared_ptr... somehow //TODO: make accept work for std::shared_ptr... somehow
virtual void accept(VCAI * ai); //unhandled goal will report standard error virtual void accept(VCAI * ai); //unhandled goal will report standard error
virtual float accept(FuzzyHelper * f);
virtual bool operator==(const AbstractGoal & g) const; virtual bool operator==(const AbstractGoal & g) const;
bool operator<(AbstractGoal & g); //final
virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
{
return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
}
bool operator!=(const AbstractGoal & g) const bool operator!=(const AbstractGoal & g) const
{ {
return !(*this == g); return !(*this == g);

View File

@@ -77,8 +77,3 @@ std::string AdventureSpellCast::name() const
{ {
return "AdventureSpellCast " + spellID.toSpell()->name; return "AdventureSpellCast " + spellID.toSpell()->name;
} }
std::string AdventureSpellCast::completeMessage() const
{
return "Spell casted successfully " + spellID.toSpell()->name;
}

View File

@@ -38,7 +38,6 @@ namespace Goals
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const AdventureSpellCast & other) const override; virtual bool operator==(const AdventureSpellCast & other) const override;
}; };
} }

View File

@@ -26,23 +26,23 @@ bool BuildBoat::operator==(const BuildBoat & other) const
return shipyard->o->id == other.shipyard->o->id; return shipyard->o->id == other.shipyard->o->id;
} }
TSubgoal BuildBoat::whatToDoToAchieve() //TSubgoal BuildBoat::whatToDoToAchieve()
{ //{
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) // if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
{ // {
return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); // return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
} // }
//
if(shipyard->shipyardStatus() != IShipyard::GOOD) // if(shipyard->shipyardStatus() != IShipyard::GOOD)
{ // {
throw cannotFulfillGoalException("Shipyard is busy."); // throw cannotFulfillGoalException("Shipyard is busy.");
} // }
//
TResources boatCost; // TResources boatCost;
shipyard->getBoatCost(boatCost); // shipyard->getBoatCost(boatCost);
//
return ai->ah->whatToDo(boatCost, this->iAmElementar()); // return ai->ah->whatToDo(boatCost, this->iAmElementar());
} //}
void BuildBoat::accept(VCAI * ai) void BuildBoat::accept(VCAI * ai)
{ {
@@ -79,8 +79,3 @@ std::string BuildBoat::name() const
{ {
return "BuildBoat"; return "BuildBoat";
} }
std::string BuildBoat::completeMessage() const
{
return "Boat have been built at " + shipyard->o->visitablePos().toString();
}

View File

@@ -28,10 +28,8 @@ namespace Goals
{ {
return TGoalVec(); return TGoalVec();
} }
TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const BuildBoat & other) const override; virtual bool operator==(const BuildBoat & other) const override;
}; };
} }

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -34,46 +32,4 @@ bool BuildThis::operator==(const BuildThis & other) const
std::string BuildThis::name() const std::string BuildThis::name() const
{ {
return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name; return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
} }
TSubgoal BuildThis::whatToDoToAchieve()
{
auto b = BuildingID(bid);
// find town if not set
if(!town && hero)
town = hero->visitedTown;
if(!town)
{
for(const CGTownInstance * t : cb->getTownsInfo())
{
switch(cb->canBuildStructure(town, b))
{
case EBuildingState::ALLOWED:
town = t;
break; //TODO: look for prerequisites? this is not our reponsibility
default:
continue;
}
}
}
if(town) //we have specific town to build this
{
switch(cb->canBuildStructure(town, b))
{
case EBuildingState::ALLOWED:
case EBuildingState::NO_RESOURCES:
{
auto res = town->town->buildings.at(BuildingID(bid))->resources;
return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
}
break;
default:
throw cannotFulfillGoalException("Not possible to build");
}
}
else
throw cannotFulfillGoalException("Cannot find town to build this");
}

View File

@@ -47,12 +47,6 @@ namespace Goals
bid = Bid; bid = Bid;
priority = 1; priority = 1;
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
//bool fulfillsMe(TSubgoal goal) override;
virtual bool operator==(const BuildThis & other) const override; virtual bool operator==(const BuildThis & other) const override;
virtual std::string name() const override; virtual std::string name() const override;
}; };

View File

@@ -25,21 +25,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const
return town == other.town && objid == other.objid; return town == other.town && objid == other.objid;
} }
bool BuyArmy::fulfillsMe(TSubgoal goal)
{
//if (hero && hero != goal->hero)
// return false;
return town == goal->town && goal->value >= value; //can always buy more army
}
TSubgoal BuyArmy::whatToDoToAchieve() TSubgoal BuyArmy::whatToDoToAchieve()
{ {
//TODO: calculate the actual cost of units instead return iAmElementar();
TResources price; }
price[Res::GOLD] = value * 0.4f; //some approximate value
return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
}
std::string BuyArmy::completeMessage() const
{
return boost::format("Bought army of value %d in town of %s") % value, town->name;
}

View File

@@ -32,10 +32,8 @@ namespace Goals
value = val; //expressed in AI unit strength value = val; //expressed in AI unit strength
priority = 3;//TODO: evaluate? priority = 3;//TODO: evaluate?
} }
bool fulfillsMe(TSubgoal goal) override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override;
virtual bool operator==(const BuyArmy & other) const override; virtual bool operator==(const BuyArmy & other) const override;
}; };
} }

View File

@@ -51,11 +51,6 @@ namespace Goals
ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
} }
float accept(FuzzyHelper * f) override
{
return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
}
CGoal<T> * clone() const override CGoal<T> * clone() const override
{ {
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation return new T(static_cast<T const &>(*this)); //casting enforces template instantiation

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -35,80 +33,80 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
{ {
TGoalVec ret; TGoalVec ret;
auto givesResource = [this](const CGObjectInstance * obj) -> bool //auto givesResource = [this](const CGObjectInstance * obj) -> bool
{ //{
//TODO: move this logic to object side // //TODO: move this logic to object side
//TODO: remember mithril exists // //TODO: remember mithril exists
//TODO: water objects // //TODO: water objects
//TODO: Creature banks // //TODO: Creature banks
//return false first from once-visitable, before checking if they were even visited // //return false first from once-visitable, before checking if they were even visited
switch (obj->ID.num) // switch (obj->ID.num)
{ // {
case Obj::TREASURE_CHEST: // case Obj::TREASURE_CHEST:
return resID == Res::GOLD; // return resID == Res::GOLD;
break; // break;
case Obj::RESOURCE: // case Obj::RESOURCE:
return obj->subID == resID; // return obj->subID == resID;
break; // break;
case Obj::MINE: // case Obj::MINE:
return (obj->subID == resID && // return (obj->subID == resID &&
(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines // (cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
break; // break;
case Obj::CAMPFIRE: // case Obj::CAMPFIRE:
return true; //contains all resources // return true; //contains all resources
break; // break;
case Obj::WINDMILL: // case Obj::WINDMILL:
switch (resID) // switch (resID)
{ // {
case Res::GOLD: // case Res::GOLD:
case Res::WOOD: // case Res::WOOD:
return false; // return false;
} // }
break; // break;
case Obj::WATER_WHEEL: // case Obj::WATER_WHEEL:
if (resID != Res::GOLD) // if (resID != Res::GOLD)
return false; // return false;
break; // break;
case Obj::MYSTICAL_GARDEN: // case Obj::MYSTICAL_GARDEN:
if ((resID != Res::GOLD) && (resID != Res::GEMS)) // if ((resID != Res::GOLD) && (resID != Res::GEMS))
return false; // return false;
break; // break;
case Obj::LEAN_TO: // case Obj::LEAN_TO:
case Obj::WAGON: // case Obj::WAGON:
if (resID != Res::GOLD) // if (resID != Res::GOLD)
return false; // return false;
break; // break;
default: // default:
return false; // return false;
break; // break;
} // }
return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable // return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
}; //};
std::vector<const CGObjectInstance *> objs; //std::vector<const CGObjectInstance *> objs;
for (auto obj : ai->visitableObjs) //for (auto obj : ai->visitableObjs)
{ //{
if (givesResource(obj)) // if (givesResource(obj))
objs.push_back(obj); // objs.push_back(obj);
} //}
for (auto h : cb->getHeroesInfo()) //for (auto h : cb->getHeroesInfo())
{ //{
std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects // std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero // for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
{ // {
if (givesResource(obj)) // if (givesResource(obj))
ourObjs.push_back(obj); // ourObjs.push_back(obj);
} // }
for (auto obj : ourObjs) // for (auto obj : ourObjs)
{ // {
auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj)); // auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
vstd::concatenate(ret, waysToGo); // vstd::concatenate(ret, waysToGo);
} // }
} //}
return ret; return ret;
} }
@@ -119,90 +117,78 @@ TSubgoal CollectRes::whatToDoToAchieve()
if (!trade->invalid()) if (!trade->invalid())
goals.push_back(trade); goals.push_back(trade);
if (goals.empty()) return sptr(Invalid()); //we can always do that
return sptr(Explore()); //we can always do that
else
return fh->chooseSolution(goals); //TODO: evaluate trading
} }
TSubgoal CollectRes::whatToDoToTrade() TSubgoal CollectRes::whatToDoToTrade()
{ {
std::vector<const IMarket *> markets; //std::vector<const IMarket *> markets;
std::vector<const CGObjectInstance *> visObjs; //std::vector<const CGObjectInstance *> visObjs;
ai->retrieveVisitableObjs(visObjs, true); //ai->retrieveVisitableObjs(visObjs, true);
for (const CGObjectInstance * obj : visObjs) //for (const CGObjectInstance * obj : visObjs)
{ //{
if (const IMarket * m = IMarket::castFrom(obj, false)) // if (const IMarket * m = IMarket::castFrom(obj, false))
{ // {
if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE)) // if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
markets.push_back(m); // markets.push_back(m);
else if (obj->ID == Obj::TRADING_POST) // else if (obj->ID == Obj::TRADING_POST)
markets.push_back(m); // markets.push_back(m);
} // }
} //}
boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool //boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
{ //{
return m1->getMarketEfficiency() < m2->getMarketEfficiency(); // return m1->getMarketEfficiency() < m2->getMarketEfficiency();
}); //});
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool //markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
{ //{
if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID)) // if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
{ // {
if (!ai->isAccessible(market->o->visitablePos())) // if (!ai->isAccessible(market->o->visitablePos()))
return true; // return true;
} // }
return false; // return false;
}), markets.end()); //}), markets.end());
if (!markets.size()) //if (!markets.size())
{ //{
for (const CGTownInstance * t : cb->getTownsInfo()) // for (const CGTownInstance * t : cb->getTownsInfo())
{ // {
if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED) // if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2)); // return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
} // }
} //}
else //else
{ //{
const IMarket * m = markets.back(); // const IMarket * m = markets.back();
//attempt trade at back (best prices) // //attempt trade at back (best prices)
int howManyCanWeBuy = 0; // int howManyCanWeBuy = 0;
for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1)) // for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
{ // {
if (i == resID) // if (i == resID)
continue; // continue;
int toGive = -1, toReceive = -1; // int toGive = -1, toReceive = -1;
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE); // m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
assert(toGive > 0 && toReceive > 0); // assert(toGive > 0 && toReceive > 0);
howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive); // howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
} // }
if (howManyCanWeBuy >= value) // if (howManyCanWeBuy >= value)
{ // {
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace // auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
assert(backObj); // assert(backObj);
auto objid = m->o->id.getNum(); // auto objid = m->o->id.getNum();
if (backObj->tempOwner != ai->playerID) //top object not owned // if (backObj->tempOwner != ai->playerID) //top object not owned
{ // {
return sptr(VisitObj(objid)); //just go there // return sptr(VisitObj(objid)); //just go there
} // }
else //either it's our town, or we have hero there // else //either it's our town, or we have hero there
{ // {
return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately // return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately
} // }
} // }
} //}
return sptr(Invalid()); //cannot trade return sptr(Invalid()); //cannot trade
} }
bool CollectRes::fulfillsMe(TSubgoal goal)
{
if (goal->resID == resID)
if (goal->value >= value)
return true;
return false;
}

View File

@@ -34,7 +34,6 @@ namespace Goals
TGoalVec getAllPossibleSubgoals() override; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
TSubgoal whatToDoToTrade(); TSubgoal whatToDoToTrade();
bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
virtual bool operator==(const CollectRes & other) const override; virtual bool operator==(const CollectRes & other) const override;
}; };
} }

View File

@@ -82,9 +82,8 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
TGoalVec solutions = getAllPossibleSubgoals(); TGoalVec solutions = getAllPossibleSubgoals();
if(solutions.empty()) throw cannotFulfillGoalException("Can not complete quest " + questToString());
throw cannotFulfillGoalException("Can not complete quest " + questToString()); /*
TSubgoal result = fh->chooseSolution(solutions); TSubgoal result = fh->chooseSolution(solutions);
logAi->trace( logAi->trace(
@@ -94,7 +93,7 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
result->objid, result->objid,
result->hero.validAndSet() ? result->hero->name : "not specified"); result->hero.validAndSet() ? result->hero->name : "not specified");
return result; return result;*/
} }
std::string CompleteQuest::name() const std::string CompleteQuest::name() const
@@ -102,11 +101,6 @@ std::string CompleteQuest::name() const
return "CompleteQuest"; return "CompleteQuest";
} }
std::string CompleteQuest::completeMessage() const
{
return "Completed quest " + questToString();
}
std::string CompleteQuest::questToString() const std::string CompleteQuest::questToString() const
{ {
if(q.quest->missionType == CQuest::MISSION_NONE) if(q.quest->missionType == CQuest::MISSION_NONE)
@@ -128,7 +122,7 @@ TGoalVec CompleteQuest::tryCompleteQuest() const
{ {
if(q.quest->checkQuest(hero)) if(q.quest->checkQuest(hero))
{ {
vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id))); //vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.obj->id)));
} }
} }
@@ -228,7 +222,7 @@ TGoalVec CompleteQuest::missionResources() const
{ {
if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is if(q.quest->checkQuest(heroes.front())) //it doesn't matter which hero it is
{ {
return ai->ah->howToVisitObj(q.obj); return solutions;// ai->ah->howToVisitObj(q.obj);
} }
else else
{ {
@@ -254,7 +248,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
if(!obj) if(!obj)
return ai->ah->howToVisitObj(q.obj); return solutions;// ai->ah->howToVisitObj(q.obj);
if(obj->ID == Obj::HERO) if(obj->ID == Obj::HERO)
{ {
@@ -264,11 +258,11 @@ TGoalVec CompleteQuest::missionDestroyObj() const
{ {
auto heroToProtect = cb->getHero(obj->id); auto heroToProtect = cb->getHero(obj->id);
solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); //solutions.push_back(sptr(GatherArmy().sethero(heroToProtect)));
} }
else if(relations == PlayerRelations::ENEMIES) else if(relations == PlayerRelations::ENEMIES)
{ {
solutions = ai->ah->howToVisitObj(obj); //solutions = ai->ah->howToVisitObj(obj);
} }
} }

View File

@@ -28,7 +28,6 @@ namespace Goals
TGoalVec getAllPossibleSubgoals() override; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
std::string name() const override; std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const CompleteQuest & other) const override; virtual bool operator==(const CompleteQuest & other) const override;
private: private:

View File

@@ -48,8 +48,3 @@ std::string DismissHero::name() const
{ {
return "DismissHero " + hero.name; return "DismissHero " + hero.name;
} }
std::string DismissHero::completeMessage() const
{
return "Hero dismissed successfully " + hero.name;
}

View File

@@ -30,7 +30,6 @@ namespace Goals
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const DismissHero & other) const override; virtual bool operator==(const DismissHero & other) const override;
}; };
} }

View File

@@ -36,11 +36,6 @@ std::string ExchangeSwapTownHeroes::name() const
return "Exchange and swap heroes of " + town->name; return "Exchange and swap heroes of " + town->name;
} }
std::string ExchangeSwapTownHeroes::completeMessage() const
{
return "Exchange and swap heroes of " + town->name + " compelete";
}
bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
{ {
return town == other.town; return town == other.town;

View File

@@ -35,7 +35,6 @@ namespace Goals
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const ExchangeSwapTownHeroes & other) const override; virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
}; };
} }

View File

@@ -72,27 +72,29 @@ void ExecuteHeroChain::accept(VCAI * ai)
{ {
auto & node = chainPath.nodes[i]; auto & node = chainPath.nodes[i];
HeroPtr hero = node.targetHero; const CGHeroInstance * hero = node.targetHero;
HeroPtr heroPtr = hero;
if(vstd::contains(blockedIndexes, i)) if(vstd::contains(blockedIndexes, i))
{ {
blockedIndexes.insert(node.parentIndex); blockedIndexes.insert(node.parentIndex);
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN); ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
continue; continue;
} }
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero.name, node.coord.toString()); logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->name, node.coord.toString());
try try
{ {
if(hero->movement) if(hero->movement)
{ {
ai->nullkiller->setActive(hero.get(), node.coord);
ai->nullkiller->setActive(hero, node.coord);
if(node.specialAction) if(node.specialAction)
{ {
if(node.specialAction->canAct(hero.get())) if(node.specialAction->canAct(hero))
{ {
auto specialGoal = node.specialAction->whatToDo(hero); auto specialGoal = node.specialAction->whatToDo(hero);
@@ -100,12 +102,12 @@ void ExecuteHeroChain::accept(VCAI * ai)
} }
else else
{ {
//TODO: decompose throw cannotFulfillGoalException("Path is nondeterministic.");
} }
if(!hero.validAndSet()) if(!heroPtr.validAndSet())
{ {
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", hero.name); logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name);
return; return;
} }
@@ -113,7 +115,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
if(node.turns == 0 && node.coord != hero->visitablePos()) if(node.turns == 0 && node.coord != hero->visitablePos())
{ {
auto targetNode = cb->getPathsInfo(hero.get())->getPathInfo(node.coord); auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node.coord);
if(targetNode->accessible == CGPathNode::EAccessibility::NOT_SET if(targetNode->accessible == CGPathNode::EAccessibility::NOT_SET
|| targetNode->accessible == CGPathNode::EAccessibility::BLOCKED || targetNode->accessible == CGPathNode::EAccessibility::BLOCKED
@@ -122,7 +124,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
{ {
logAi->error( logAi->error(
"Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this", "Enable to complete chain. Expected hero %s to arive to %s in 0 turns but he can not do this",
hero.name, hero->name,
node.coord.toString()); node.coord.toString());
return; return;
@@ -137,9 +139,9 @@ void ExecuteHeroChain::accept(VCAI * ai)
} }
catch(cannotFulfillGoalException) catch(cannotFulfillGoalException)
{ {
if(!hero.validAndSet()) if(!heroPtr.validAndSet())
{ {
logAi->error("Hero %s was lost. Exit hero chain.", hero.name); logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
return; return;
} }
@@ -147,13 +149,13 @@ void ExecuteHeroChain::accept(VCAI * ai)
if(hero->movement > 0) if(hero->movement > 0)
{ {
CGPath path; CGPath path;
bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord); bool isOk = cb->getPathsInfo(hero)->getPath(path, node.coord);
if(isOk && path.nodes.back().turns > 0) if(isOk && path.nodes.back().turns > 0)
{ {
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString()); logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->name, hero->movement, node.coord.toString());
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN); ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
return; return;
} }
} }
@@ -170,7 +172,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
{ {
logAi->error( logAi->error(
"Enable to complete chain. Expected hero %s to arive to %s but he is at %s", "Enable to complete chain. Expected hero %s to arive to %s but he is at %s",
hero.name, hero->name,
node.coord.toString(), node.coord.toString(),
hero->visitablePos().toString()); hero->visitablePos().toString());
@@ -178,14 +180,14 @@ void ExecuteHeroChain::accept(VCAI * ai)
} }
// no exception means we were not able to rich the tile // no exception means we were not able to rich the tile
ai->nullkiller->lockHero(hero.get(), HeroLockedReason::HERO_CHAIN); ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
blockedIndexes.insert(node.parentIndex); blockedIndexes.insert(node.parentIndex);
} }
catch(goalFulfilledException) catch(goalFulfilledException)
{ {
if(!hero) if(!heroPtr.validAndSet())
{ {
logAi->debug("Hero %s was killed while attempting to rich %s", hero.name, node.coord.toString()); logAi->debug("Hero %s was killed while attempting to rich %s", heroPtr.name, node.coord.toString());
return; return;
} }
@@ -196,9 +198,4 @@ void ExecuteHeroChain::accept(VCAI * ai)
std::string ExecuteHeroChain::name() const std::string ExecuteHeroChain::name() const
{ {
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name; return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name;
}
std::string ExecuteHeroChain::completeMessage() const
{
return "Hero chain completed";
} }

View File

@@ -30,7 +30,6 @@ namespace Goals
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
void accept(VCAI * ai) override; void accept(VCAI * ai) override;
std::string name() const override; std::string name() const override;
std::string completeMessage() const override;
virtual bool operator==(const ExecuteHeroChain & other) const override; virtual bool operator==(const ExecuteHeroChain & other) const override;
const AIPath & getPath() const { return chainPath; } const AIPath & getPath() const { return chainPath; }
}; };

View File

@@ -233,14 +233,9 @@ bool Explore::operator==(const Explore & other) const
return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy; return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy;
} }
std::string Explore::completeMessage() const
{
return "Hero " + hero.get()->name + " completed exploration";
}
TSubgoal Explore::whatToDoToAchieve() TSubgoal Explore::whatToDoToAchieve()
{ {
return fh->chooseSolution(getAllPossibleSubgoals()); return sptr(Goals::Invalid());
} }
TGoalVec Explore::getAllPossibleSubgoals() TGoalVec Explore::getAllPossibleSubgoals()
@@ -361,18 +356,6 @@ TGoalVec Explore::getAllPossibleSubgoals()
return ret; return ret;
} }
bool Explore::fulfillsMe(TSubgoal goal)
{
if(goal->goalType == EXPLORE)
{
if(goal->hero)
return hero == goal->hero;
else
return true; //cancel ALL exploration
}
return false;
}
TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const
{ {
ExplorationHelper scanResult(h, allowGatherArmy); ExplorationHelper scanResult(h, allowGatherArmy);

View File

@@ -1,66 +1,64 @@
/* ///*
* Explore.h, part of VCMI engine //* Explore.h, part of VCMI engine
* //*
* Authors: listed in file AUTHORS in main folder //* Authors: listed in file AUTHORS in main folder
* //*
* License: GNU General Public License v2.0 or later //* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder //* Full text of license available in license.txt file, in main folder
* //*
*/ //*/
#pragma once //#pragma once
//
#include "CGoal.h" //#include "CGoal.h"
//
struct HeroPtr; //struct HeroPtr;
class VCAI; //class VCAI;
class FuzzyHelper; //class FuzzyHelper;
//
namespace Goals //namespace Goals
{ //{
struct ExplorationHelper; // struct ExplorationHelper;
//
class DLL_EXPORT Explore : public CGoal<Explore> // class DLL_EXPORT Explore : public CGoal<Explore>
{ // {
private: // private:
bool allowGatherArmy; // bool allowGatherArmy;
//
public: // public:
Explore(bool allowGatherArmy) // Explore(bool allowGatherArmy)
: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy) // : CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
{ // {
priority = 1; // priority = 1;
} // }
//
Explore() // Explore()
: Explore(true) // : Explore(true)
{ // {
} // }
//
Explore(HeroPtr h) // Explore(HeroPtr h)
: CGoal(Goals::EXPLORE) // : CGoal(Goals::EXPLORE)
{ // {
hero = h; // hero = h;
priority = 1; // priority = 1;
} // }
TGoalVec getAllPossibleSubgoals() override; // TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; // TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override; // virtual bool operator==(const Explore & other) const override;
bool fulfillsMe(TSubgoal goal) override; //
virtual bool operator==(const Explore & other) const override; // private:
// TSubgoal exploreNearestNeighbour(HeroPtr h) const;
private: // TSubgoal explorationNewPoint(HeroPtr h) const;
TSubgoal exploreNearestNeighbour(HeroPtr h) const; // TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
TSubgoal explorationNewPoint(HeroPtr h) const; // void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const;
TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const; // bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const;
void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const; //
bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; // void getVisibleNeighbours(
// const std::vector<int3> & tiles,
void getVisibleNeighbours( // std::vector<int3> & out,
const std::vector<int3> & tiles, // CCallback * cbp,
std::vector<int3> & out, // const TeamState * ts) const;
CCallback * cbp, //
const TeamState * ts) const; // int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
// };
int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const; //}
};
}

View File

@@ -10,7 +10,6 @@
#include "StdInc.h" #include "StdInc.h"
#include "FindObj.h" #include "FindObj.h"
#include "VisitObj.h" #include "VisitObj.h"
#include "Explore.h"
#include "../VCAI.h" #include "../VCAI.h"
#include "../AIUtility.h" #include "../AIUtility.h"
@@ -52,19 +51,4 @@ TSubgoal FindObj::whatToDoToAchieve()
} }
if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
return sptr(VisitObj(o->id.getNum())); return sptr(VisitObj(o->id.getNum()));
else }
return sptr(Explore());
}
bool FindObj::fulfillsMe(TSubgoal goal)
{
if (goal->goalType == VISIT_TILE) //visiting tile visits object at same time
{
if (!hero || hero == goal->hero)
for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria
if (obj->visitablePos() == goal->tile) //object could be removed
if (obj->ID == objid && obj->subID == resID) //same type and subtype
return true;
}
return false;
}

View File

@@ -41,7 +41,6 @@ namespace Goals
return TGoalVec(); return TGoalVec();
} }
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
virtual bool operator==(const FindObj & other) const override; virtual bool operator==(const FindObj & other) const override;
}; };
} }

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -62,12 +60,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
} }
} }
TGoalVec solutions = getAllPossibleSubgoals(); return sptr(Invalid());
if(solutions.empty())
return sptr(Explore());
return fh->chooseSolution(solutions);
} }
@@ -75,75 +68,74 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
{ {
TGoalVec solutions; TGoalVec solutions;
for(const CGTownInstance * t : cb->getTownsInfo()) //for(const CGTownInstance * t : cb->getTownsInfo())
{ //{
int count = getCreaturesCount(t->getUpperArmy()); // int count = getCreaturesCount(t->getUpperArmy());
if(count >= this->value) // if(count >= this->value)
{ // {
if(t->visitingHero) // if(t->visitingHero)
{ // {
solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get()))); // solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get())));
} // }
else // else
{ // {
vstd::concatenate(solutions, ai->ah->howToVisitObj(t)); // vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
} // }
continue; // continue;
} // }
auto creature = VLC->creh->creatures[objid]; // auto creature = VLC->creh->creatures[objid];
if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O // if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
{ // {
auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1); // auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
if(!creatures) // if(!creatures)
continue; // continue;
int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber); // int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
if(upgradeNumber < 0) // if(upgradeNumber < 0)
continue; // continue;
BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN); // BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction // if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->cost)) //this assumes only creatures with dwellings are assigned to faction
{ // {
solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid))); // solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
} // }
/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm // /*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
{ // {
return sptr(BuildThis(bid, t).setpriority(priority)); // return sptr(BuildThis(bid, t).setpriority(priority));
}*/ // }*/
} // }
} //}
for(auto obj : ai->visitableObjs)
{
auto d = dynamic_cast<const CGDwelling *>(obj);
if(!d || obj->ID == Obj::TOWN) //for(auto obj : ai->visitableObjs)
continue; //{
// auto d = dynamic_cast<const CGDwelling *>(obj);
for(auto creature : d->creatures) // if(!d || obj->ID == Obj::TOWN)
{ // continue;
if(creature.first) //there are more than 0 creatures avaliabe
{ // for(auto creature : d->creatures)
for(auto type : creature.second) // {
{ // if(creature.first) //there are more than 0 creatures avaliabe
if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost)) // {
vstd::concatenate(solutions, ai->ah->howToVisitObj(obj)); // for(auto type : creature.second)
} // {
} // if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
} // vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
} // }
// }
// }
//}
//CreatureID creID = CreatureID(objid);
//vstd::erase_if(solutions, [&](TSubgoal goal)->bool
//{
// return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot();
//});
return solutions; return solutions;
//TODO: exchange troops between heroes //TODO: exchange troops between heroes
} }
bool GatherTroops::fulfillsMe(TSubgoal goal)
{
if (!hero || hero == goal->hero) //we got army for desired hero or any hero
if (goal->objid == objid) //same creature type //TODO: consider upgrades?
if (goal->value >= value) //notify every time we get resources?
return true;
return false;
}

View File

@@ -34,7 +34,6 @@ namespace Goals
} }
TGoalVec getAllPossibleSubgoals() override; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
virtual bool operator==(const GatherTroops & other) const override; virtual bool operator==(const GatherTroops & other) const override;
private: private:

View File

@@ -12,22 +12,17 @@
#include "CGoal.h" #include "CGoal.h"
#include "Invalid.h" #include "Invalid.h"
#include "BuildBoat.h" #include "BuildBoat.h"
#include "Build.h"
#include "BuildThis.h" #include "BuildThis.h"
#include "Conquer.h"
#include "GatherArmy.h"
#include "Win.h" #include "Win.h"
#include "VisitObj.h" #include "VisitObj.h"
#include "VisitTile.h" #include "VisitTile.h"
#include "VisitHero.h" #include "VisitHero.h"
#include "Explore.h"
#include "BuyArmy.h" #include "BuyArmy.h"
#include "GatherTroops.h" #include "GatherTroops.h"
#include "Trade.h" #include "Trade.h"
#include "CollectRes.h" #include "CollectRes.h"
#include "RecruitHero.h" #include "RecruitHero.h"
#include "GetArtOfType.h" #include "GetArtOfType.h"
#include "ClearWayTo.h"
#include "DigAtTile.h" #include "DigAtTile.h"
#include "FindObj.h" #include "FindObj.h"
#include "CompleteQuest.h" #include "CompleteQuest.h"

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -25,14 +23,3 @@ extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh; extern FuzzyHelper * fh;
using namespace Goals; using namespace Goals;
TSubgoal RecruitHero::whatToDoToAchieve()
{
const CGTownInstance * t = ai->findTownWithTavern();
if(!t)
return sptr(BuildThis(BuildingID::TAVERN).setpriority(2));
TResources res;
res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
}

View File

@@ -26,13 +26,6 @@ namespace Goals
priority = 1; priority = 1;
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
virtual bool operator==(const RecruitHero & other) const override virtual bool operator==(const RecruitHero & other) const override
{ {
return true; return true;

View File

@@ -9,14 +9,11 @@
*/ */
#include "StdInc.h" #include "StdInc.h"
#include "VisitHero.h" #include "VisitHero.h"
#include "Explore.h"
#include "Invalid.h" #include "Invalid.h"
#include "../VCAI.h" #include "../VCAI.h"
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
@@ -29,46 +26,4 @@ using namespace Goals;
bool VisitHero::operator==(const VisitHero & other) const bool VisitHero::operator==(const VisitHero & other) const
{ {
return other.hero.h == hero.h && other.objid == objid; return other.hero.h == hero.h && other.objid == objid;
} }
std::string VisitHero::completeMessage() const
{
return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
}
TSubgoal VisitHero::whatToDoToAchieve()
{
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
if(!obj)
return sptr(Explore());
int3 pos = obj->visitablePos();
if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
{
if(hero->visitablePos() == pos)
logAi->error("Hero %s tries to visit himself.", hero.name);
else
{
//can't use VISIT_TILE here as tile appears blocked by target hero
//FIXME: elementar goal should not be abstract
return sptr(VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
}
}
return sptr(Invalid());
}
bool VisitHero::fulfillsMe(TSubgoal goal)
{
//TODO: VisitObj shoudl not be used for heroes, but...
if(goal->goalType == VISIT_TILE)
{
auto obj = cb->getObj(ObjectInstanceID(objid));
if (!obj)
{
logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
return false;
}
return obj->visitablePos() == goal->tile;
}
return false;
}

View File

@@ -30,13 +30,6 @@ namespace Goals
objid = hid; objid = hid;
priority = 4; priority = 4;
} }
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
std::string completeMessage() const override;
virtual bool operator==(const VisitHero & other) const override; virtual bool operator==(const VisitHero & other) const override;
}; };
} }

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -31,65 +29,6 @@ bool VisitObj::operator==(const VisitObj & other) const
return other.hero.h == hero.h && other.objid == objid; return other.hero.h == hero.h && other.objid == objid;
} }
std::string VisitObj::completeMessage() const
{
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
}
TGoalVec VisitObj::getAllPossibleSubgoals()
{
TGoalVec goalList;
const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid));
if(!obj)
{
throw cannotFulfillGoalException("Object is missing - goal is invalid now!");
}
int3 pos = obj->visitablePos();
if(hero)
{
if(ai->isAccessibleForHero(pos, hero))
{
if(isSafeToVisit(hero, pos))
goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero)));
else
goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
return goalList;
}
}
else
{
for(auto potentialVisitor : cb->getHeroesInfo())
{
if(ai->isAccessibleForHero(pos, potentialVisitor))
{
if(isSafeToVisit(potentialVisitor, pos))
goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor)));
else
goalList.push_back(sptr(GatherArmy(fh->evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true)));
}
}
if(!goalList.empty())
{
return goalList;
}
}
goalList.push_back(sptr(ClearWayTo(pos)));
return goalList;
}
TSubgoal VisitObj::whatToDoToAchieve()
{
auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals());
if(bestGoal->goalType == VISIT_OBJ && bestGoal->hero)
bestGoal->setisElementar(true);
return bestGoal;
}
VisitObj::VisitObj(int Objid) VisitObj::VisitObj(int Objid)
: CGoal(VISIT_OBJ) : CGoal(VISIT_OBJ)
{ {
@@ -101,18 +40,4 @@ VisitObj::VisitObj(int Objid)
logAi->error("VisitObj constructed with invalid object instance %d", Objid); logAi->error("VisitObj constructed with invalid object instance %d", Objid);
priority = 3; priority = 3;
} }
bool VisitObj::fulfillsMe(TSubgoal goal)
{
if(goal->goalType == VISIT_TILE)
{
if (!hero || hero == goal->hero)
{
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
if (obj && obj->visitablePos() == goal->tile) //object could be removed
return true;
}
}
return false;
}

View File

@@ -23,10 +23,6 @@ namespace Goals
VisitObj() = delete; // empty constructor not allowed VisitObj() = delete; // empty constructor not allowed
VisitObj(int Objid); VisitObj(int Objid);
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
std::string completeMessage() const override;
virtual bool operator==(const VisitObj & other) const override; virtual bool operator==(const VisitObj & other) const override;
}; };
} }

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -30,69 +28,3 @@ bool VisitTile::operator==(const VisitTile & other) const
{ {
return other.hero.h == hero.h && other.tile == tile; return other.hero.h == hero.h && other.tile == tile;
} }
std::string VisitTile::completeMessage() const
{
return "Hero " + hero.get()->name + " visited tile " + tile.toString();
}
TSubgoal VisitTile::whatToDoToAchieve()
{
auto ret = fh->chooseSolution(getAllPossibleSubgoals());
if(ret->hero)
{
if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero))
{
ret->setisElementar(true);
return ret;
}
else
{
return sptr(GatherArmy(fh->evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT)
.sethero(ret->hero).setisAbstract(true));
}
}
return ret;
}
TGoalVec VisitTile::getAllPossibleSubgoals()
{
assert(cb->isInTheMap(tile));
TGoalVec ret;
if(!cb->isVisible(tile))
ret.push_back(sptr(Explore())); //what sense does it make?
else
{
std::vector<const CGHeroInstance *> heroes;
if(hero)
heroes.push_back(hero.h); //use assigned hero if any
else
heroes = cb->getHeroesInfo(); //use most convenient hero
for(auto h : heroes)
{
if(ai->isAccessibleForHero(tile, h))
ret.push_back(sptr(VisitTile(tile).sethero(h)));
}
if(ai->canRecruitAnyHero())
ret.push_back(sptr(RecruitHero()));
}
if(ret.empty())
{
auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
{
if(hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
ret.push_back(sptr(VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
else
throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
}
else
ret.push_back(sptr(ClearWayTo(tile)));
}
//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
return ret;
}

View File

@@ -29,9 +29,6 @@ namespace Goals
tile = Tile; tile = Tile;
priority = 5; priority = 5;
} }
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override;
virtual bool operator==(const VisitTile & other) const override; virtual bool operator==(const VisitTile & other) const override;
}; };
} }

View File

@@ -13,8 +13,6 @@
#include "../AIUtility.h" #include "../AIUtility.h"
#include "../AIhelper.h" #include "../AIhelper.h"
#include "../FuzzyHelper.h" #include "../FuzzyHelper.h"
#include "../ResourceManager.h"
#include "../BuildingManager.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions #include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h" #include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h" #include "../../../lib/StringConstants.h"
@@ -62,9 +60,9 @@ TSubgoal Win::whatToDoToAchieve()
if(goal.object) if(goal.object)
{ {
auto obj = cb->getObj(goal.object->id); auto obj = cb->getObj(goal.object->id);
if(obj) /*if(obj)
if(obj->getOwner() == ai->playerID) //we can't capture our own object if(obj->getOwner() == ai->playerID) //we can't capture our own object
return sptr(Conquer()); return sptr(Conquer());*/
return sptr(VisitObj(goal.object->id.getNum())); return sptr(VisitObj(goal.object->id.getNum()));
@@ -122,8 +120,8 @@ TSubgoal Win::whatToDoToAchieve()
} //TODO: use FIND_OBJ } //TODO: use FIND_OBJ
else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
return sptr(VisitObj(obj->id.getNum())); return sptr(VisitObj(obj->id.getNum()));
else /*else
return sptr(Explore()); return sptr(Explore());*/
} }
break; break;
} }
@@ -166,7 +164,7 @@ TSubgoal Win::whatToDoToAchieve()
break; break;
} }
case EventCondition::STANDARD_WIN: case EventCondition::STANDARD_WIN:
return sptr(Conquer()); return sptr(Invalid());
// Conditions that likely don't need any implementation // Conditions that likely don't need any implementation
case EventCondition::DAYS_PASSED: case EventCondition::DAYS_PASSED:
@@ -182,7 +180,7 @@ TSubgoal Win::whatToDoToAchieve()
case EventCondition::HAVE_BUILDING_0: case EventCondition::HAVE_BUILDING_0:
case EventCondition::DESTROY_0: case EventCondition::DESTROY_0:
//TODO: support new condition format //TODO: support new condition format
return sptr(Conquer()); return sptr(Invalid());
default: default:
assert(0); assert(0);
} }

View File

@@ -64,7 +64,12 @@ boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance
auto hero = dynamic_cast<const CGHeroInstance*>(obj); auto hero = dynamic_cast<const CGHeroInstance*>(obj);
return getObjectValue(obj->ID, hero->type->heroClass->id); return getObjectValue(obj->ID, hero->type->heroClass->id);
} }
if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) else if(obj->ID == Obj::PRISON)
{
//special case: in-game prison subID is captured hero ID, but config has one subID with index 0 for normal prison - use that one
return getObjectValue(obj->ID, 0);
}
else if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)
{ {
auto dwelling = dynamic_cast<const CGDwelling *>(obj); auto dwelling = dynamic_cast<const CGDwelling *>(obj);
int aiValue = 0; int aiValue = 0;

View File

@@ -14,7 +14,7 @@
namespace AIPathfinding namespace AIPathfinding
{ {
Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const Goals::TSubgoal BattleAction::whatToDo(const CGHeroInstance * hero) const
{ {
return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero)); return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
} }

View File

@@ -25,6 +25,6 @@ namespace AIPathfinding
{ {
} }
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
}; };
} }

View File

@@ -17,7 +17,7 @@
namespace AIPathfinding namespace AIPathfinding
{ {
Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const Goals::TSubgoal BuildBoatAction::whatToDo(const CGHeroInstance * hero) const
{ {
return Goals::sptr(Goals::BuildBoat(shipyard)); return Goals::sptr(Goals::BuildBoat(shipyard));
} }
@@ -27,7 +27,7 @@ namespace AIPathfinding
return sourceActor->resourceActor; return sourceActor->resourceActor;
} }
Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const Goals::TSubgoal SummonBoatAction::whatToDo(const CGHeroInstance * hero) const
{ {
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT)); return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
} }

View File

@@ -25,7 +25,7 @@ namespace AIPathfinding
class SummonBoatAction : public VirtualBoatAction class SummonBoatAction : public VirtualBoatAction
{ {
public: public:
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
virtual void applyOnDestination( virtual void applyOnDestination(
const CGHeroInstance * hero, const CGHeroInstance * hero,
@@ -53,7 +53,7 @@ namespace AIPathfinding
{ {
} }
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override; virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
}; };

View File

@@ -23,7 +23,7 @@ public:
return true; return true;
} }
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0; virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const = 0;
virtual void applyOnDestination( virtual void applyOnDestination(
const CGHeroInstance * hero, const CGHeroInstance * hero,

View File

@@ -16,7 +16,7 @@
using namespace AIPathfinding; using namespace AIPathfinding;
Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const Goals::TSubgoal TownPortalAction::whatToDo(const CGHeroInstance * hero) const
{ {
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown const CGTownInstance * targetTown = target; // const pointer is not allowed in settown

View File

@@ -28,6 +28,6 @@ namespace AIPathfinding
{ {
} }
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override; virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
}; };
} }

View File

@@ -32,76 +32,6 @@ void PathfindingManager::setAI(VCAI * AI)
ai = AI; ai = AI;
} }
Goals::TGoalVec PathfindingManager::howToVisitTile(const int3 & tile, bool allowGatherArmy) const
{
Goals::TGoalVec result;
auto heroes = cb->getHeroesInfo();
result.reserve(heroes.size());
for(auto hero : heroes)
{
vstd::concatenate(result, howToVisitTile(hero, tile, allowGatherArmy));
}
return result;
}
Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj, bool allowGatherArmy) const
{
Goals::TGoalVec result;
auto heroes = cb->getHeroesInfo();
result.reserve(heroes.size());
for(auto hero : heroes)
{
vstd::concatenate(result, howToVisitObj(hero, obj, allowGatherArmy));
}
return result;
}
Goals::TGoalVec PathfindingManager::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
{
auto result = findPaths(tile, allowGatherArmy, hero, [&](int3 firstTileToGet) -> Goals::TSubgoal
{
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
});
for(Goals::TSubgoal solution : result)
{
solution->setparent(sptr(Goals::VisitTile(tile).sethero(hero).setevaluationContext(solution->evaluationContext)));
}
return result;
}
Goals::TGoalVec PathfindingManager::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
{
if(!obj)
{
return Goals::TGoalVec();
}
int3 dest = obj->visitablePos();
auto result = findPaths(dest, allowGatherArmy, hero, [&](int3 firstTileToGet) -> Goals::TSubgoal
{
if(obj->ID.num == Obj::HERO && obj->getOwner() == hero->getOwner())
return sptr(Goals::VisitHero(obj->id.getNum()).sethero(hero).setisAbstract(true));
else
return sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setisAbstract(true));
});
for(Goals::TSubgoal solution : result)
{
solution->setparent(sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setevaluationContext(solution->evaluationContext)));
}
return result;
}
std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
{ {
auto paths = pathfinder->getPathInfo(tile); auto paths = pathfinder->getPathInfo(tile);
@@ -118,152 +48,6 @@ std::vector<AIPath> PathfindingManager::getPathsToTile(const int3 & tile) const
return pathfinder->getPathInfo(tile); return pathfinder->getPathInfo(tile);
} }
Goals::TGoalVec PathfindingManager::findPaths(
crint3 dest,
bool allowGatherArmy,
HeroPtr hero,
const std::function<Goals::TSubgoal(int3)> doVisitTile) const
{
Goals::TGoalVec result;
boost::optional<uint64_t> armyValueRequired;
uint64_t danger;
std::vector<AIPath> chainInfo = pathfinder->getPathInfo(dest);
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Trying to find a way for %s to visit tile %s", hero->name, dest.toString());
#endif
for(auto path : chainInfo)
{
if((hero && hero.get() != path.targetHero) || path.nodes.empty())
continue;
const AIPathNodeInfo & firstNode = path.firstNode();
int3 firstTileToGet = firstNode.coord;
#ifdef VCMI_TRACE_PATHFINDER
std::stringstream str;
str << "Path found ";
for(auto node : path.nodes)
str << node.targetHero->name << "->" << node.coord.toString() << "; ";
logAi->trace(str.str());
#endif
if(ai->isTileNotReserved(hero.get(), firstTileToGet))
{
danger = path.getTotalDanger();
if(isSafeToVisit(hero, path.heroArmy, danger))
{
Goals::TSubgoal solution;
if(path.specialAction)
{
solution = path.specialAction->whatToDo(hero);
}
else
{
solution = dest == firstTileToGet
? doVisitTile(firstTileToGet)
: clearWayTo(firstNode.targetHero, firstTileToGet);
}
if(solution->invalid())
continue;
if(solution->evaluationContext.danger < danger)
solution->evaluationContext.danger = danger;
solution->evaluationContext.movementCost += path.movementCost();
solution->evaluationContext.armyLoss += path.getTotalArmyLoss();
solution->evaluationContext.heroStrength = path.getHeroStrength();
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s",
hero->name,
dest.toString(),
std::to_string(danger),
std::to_string(path.armyLoss),
std::to_string(path.heroArmy->getArmyStrength()),
solution->name());
#endif
result.push_back(solution);
continue;
}
if(!armyValueRequired || armyValueRequired > danger)
{
armyValueRequired = boost::make_optional(danger);
}
}
}
danger = armyValueRequired.get_value_or(0);
if(allowGatherArmy && danger > 0)
{
//we need to get army in order to conquer that place
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Gather army for %s, value=%s", hero->name, std::to_string(danger));
#endif
result.push_back(sptr(Goals::GatherArmy(danger * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
}
return result;
}
Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet) const
{
if(isBlockedBorderGate(firstTileToGet))
{
//FIXME: this way we'll not visit gate and activate quest :?
return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->subID));
}
auto topObj = cb->getTopObj(firstTileToGet);
if(topObj)
{
if(vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[hero], topObj))
{
return sptr(Goals::Invalid());
}
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(hero->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
{
if(topObj != hero.get(true)) //the hero we want to free
{
logAi->warn("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
}
}
if(topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
{
if(shouldVisit(hero, topObj))
{
//do NOT use VISIT_TILE, as tile with quets guard can't be visited
return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
}
auto questObj = dynamic_cast<const IQuestObject*>(topObj);
if(questObj)
{
auto questInfo = QuestInfo(questObj->quest, topObj, topObj->visitablePos());
return sptr(Goals::CompleteQuest(questInfo));
}
return sptr(Goals::Invalid());
}
}
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
}
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain) void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
{ {
logAi->debug("AIPathfinder has been reseted."); logAi->debug("AIPathfinder has been reseted.");

View File

@@ -21,10 +21,6 @@ public:
virtual void setAI(VCAI * AI) = 0; virtual void setAI(VCAI * AI) = 0;
virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0; virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0;
virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0;
virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
virtual Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const = 0;
virtual Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0; virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0;
virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0; virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0;
}; };
@@ -42,10 +38,6 @@ public:
PathfindingManager() = default; PathfindingManager() = default;
PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override; std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
std::vector<AIPath> getPathsToTile(const int3 & tile) const override; std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override; void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;

View File

@@ -26,7 +26,7 @@ namespace AIPathfinding
return false; return false;
} }
virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override
{ {
return Goals::sptr(Goals::Invalid()); return Goals::sptr(Goals::Invalid());
} }

View File

@@ -1,364 +0,0 @@
/*
* ResourceManager.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ResourceManager.h"
#include "Goals/Goals.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/MapObjects.h"
#define GOLD_RESERVE (10000); //at least we'll be able to reach capitol
ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal)
: resources(Res), goal(Goal)
{
}
bool ResourceObjective::operator<(const ResourceObjective & ro) const
{
return goal->priority < ro.goal->priority;
}
ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
: ai(AI), cb(CB)
{
}
void ResourceManager::init(CPlayerSpecificInfoCallback * CB)
{
cb = CB;
}
void ResourceManager::setAI(VCAI * AI)
{
ai = AI;
}
bool ResourceManager::canAfford(const TResources & cost) const
{
return freeResources().canAfford(cost);
}
TResources ResourceManager::estimateIncome() const
{
TResources ret;
for (const CGTownInstance * t : cb->getTownsInfo())
{
ret += t->dailyIncome();
}
for (const CGObjectInstance * obj : ai->getFlaggedObjects())
{
if (obj->ID == Obj::MINE)
{
switch (obj->subID)
{
case Res::WOOD:
case Res::ORE:
ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION;
break;
case Res::GOLD:
case 7: //abandoned mine -> also gold
ret[Res::GOLD] += GOLD_MINE_PRODUCTION;
break;
default:
ret[obj->subID] += RESOURCE_MINE_PRODUCTION;
break;
}
}
}
return ret;
}
void ResourceManager::reserveResoures(const TResources & res, Goals::TSubgoal goal)
{
if (!goal->invalid())
tryPush(ResourceObjective(res, goal));
else
logAi->warn("Attempt to reserve resources for Invalid goal");
}
Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o) const
{
auto allResources = cb->getResourceAmount();
auto income = estimateIncome();
Res::ERes resourceType = Res::INVALID;
TResource amountToCollect = 0;
typedef std::pair<Res::ERes, TResource> resPair;
std::map<Res::ERes, TResource> missingResources;
//TODO: unit test for complex resource sets
//sum missing resources of given type for ALL reserved objectives
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
{
//choose specific resources we need for this goal (not 0)
for (auto r = Res::ResourceSet::nziterator(o.resources); r.valid(); r++)
missingResources[r->resType] += it->resources[r->resType]; //goal it costs r units of resType
}
for (auto it = Res::ResourceSet::nziterator(o.resources); it.valid(); it++)
{
missingResources[it->resType] -= allResources[it->resType]; //missing = (what we need) - (what we have)
vstd::amax(missingResources[it->resType], 0); // if we have more resources than reserved, we don't need them
}
vstd::erase_if(missingResources, [=](const resPair & p) -> bool
{
return !(p.second); //in case evaluated to 0 or less
});
if (missingResources.empty()) //FIXME: should be unit-tested out
{
logAi->error("We don't need to collect resources %s for goal %s", o.resources.toString(), o.goal->name());
return o.goal;
}
float goalPriority = 10; //arbitrary, will be divided
for (const resPair & p : missingResources)
{
if (!income[p.first]) //prioritize resources with 0 income
{
resourceType = p.first;
amountToCollect = p.second;
goalPriority /= amountToCollect; //need more resources -> lower priority
break;
}
}
if (resourceType == Res::INVALID) //no needed resources has 0 income,
{
//find the one which takes longest to collect
typedef std::pair<Res::ERes, float> timePair;
std::map<Res::ERes, float> daysToEarn;
for (auto it : missingResources)
daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first];
auto incomeComparer = [&income](const timePair & lhs, const timePair & rhs) -> bool
{
//theoretically income can be negative, but that falls into this comparison
return lhs.second < rhs.second;
};
resourceType = boost::max_element(daysToEarn, incomeComparer)->first;
amountToCollect = missingResources[resourceType];
goalPriority /= daysToEarn[resourceType]; //more days - lower priority
}
if (resourceType == Res::GOLD)
goalPriority *= 1000;
//this is abstract goal and might take soem time to complete
return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true));
}
Goals::TSubgoal ResourceManager::whatToDo() const //suggest any goal
{
if (queue.size())
{
auto o = queue.top();
auto allResources = cb->getResourceAmount(); //we don't consider savings, it's out top-priority goal
if (allResources.canAfford(o.resources))
return o.goal;
else //we can't afford even top-priority goal, need to collect resources
return collectResourcesForOurGoal(o);
}
else
return Goals::sptr(Goals::Invalid()); //nothing else to do
}
Goals::TSubgoal ResourceManager::whatToDo(TResources &res, Goals::TSubgoal goal)
{
logAi->trace("ResourceManager: checking goal %s which requires resources %s", goal->name(), res.toString());
TResources accumulatedResources;
auto allResources = cb->getResourceAmount();
ResourceObjective ro(res, goal);
tryPush(ro);
//check if we can afford all the objectives with higher priority first
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
{
accumulatedResources += it->resources;
logAi->trace(
"ResourceManager: checking goal %s, accumulatedResources=%s, available=%s",
it->goal->name(),
accumulatedResources.toString(),
allResources.toString());
if(!accumulatedResources.canBeAfforded(allResources))
{
//can't afford
break;
}
else //can afford all goals up to this point
{
if(it->goal == goal)
{
logAi->debug("ResourceManager: can afford goal %s", goal->name());
return goal; //can afford immediately
}
}
}
logAi->debug("ResourceManager: can not afford goal %s", goal->name());
return collectResourcesForOurGoal(ro);
}
bool ResourceManager::containsObjective(Goals::TSubgoal goal) const
{
logAi->trace("Entering ResourceManager.containsObjective goal=%s", goal->name());
dumpToLog();
//TODO: unit tests for once
for (auto objective : queue)
{
if (objective.goal == goal)
return true;
}
return false;
}
bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal)
{
logAi->trace("Entering ResourceManager.notifyGoalCompleted goal=%s", goal->name());
if (goal->invalid())
logAi->warn("Attempt to complete Invalid goal");
std::function<bool(const Goals::TSubgoal &)> equivalentGoalsCheck = [goal](const Goals::TSubgoal & x) -> bool
{
return x == goal || x->fulfillsMe(goal);
};
bool removedGoal = removeOutdatedObjectives(equivalentGoalsCheck);
dumpToLog();
return removedGoal;
}
bool ResourceManager::updateGoal(Goals::TSubgoal goal)
{
//we update priority of goal if it is easier or more difficult to complete
if (goal->invalid())
logAi->warn("Attempt to update Invalid goal");
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
{
return ro.goal == goal;
});
if (it != queue.end())
{
it->goal->setpriority(goal->priority);
auto handle = queue.s_handle_from_iterator(it);
queue.update(handle); //restore order
return true;
}
else
return false;
}
void ResourceManager::dumpToLog() const
{
for(auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
{
logAi->trace("ResourceManager contains goal %s which requires resources %s", it->goal->name(), it->resources.toString());
}
}
bool ResourceManager::tryPush(const ResourceObjective & o)
{
auto goal = o.goal;
logAi->trace("ResourceManager: Trying to add goal %s which requires resources %s", goal->name(), o.resources.toString());
dumpToLog();
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
{
return ro.goal == goal;
});
if (it != queue.end())
{
auto handle = queue.s_handle_from_iterator(it);
vstd::amax(goal->priority, it->goal->priority); //increase priority if case
//update resources with new value
queue.update(handle, ResourceObjective(o.resources, goal)); //restore order
return false;
}
else
{
queue.push(o); //add new objective
logAi->debug("Reserved resources (%s) for %s", o.resources.toString(), goal->name());
return true;
}
}
bool ResourceManager::hasTasksLeft() const
{
return !queue.empty();
}
bool ResourceManager::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate)
{
bool removedAnything = false;
while(true)
{ //unfortunately we can't use remove_if on heap
auto it = boost::find_if(queue, [&](const ResourceObjective & ro) -> bool
{
return predicate(ro.goal);
});
if(it != queue.end()) //removed at least one
{
logAi->debug("Removing goal %s from ResourceManager.", it->goal->name());
queue.erase(queue.s_handle_from_iterator(it));
removedAnything = true;
}
else
{ //found nothing more to remove
break;
}
}
return removedAnything;
}
TResources ResourceManager::reservedResources() const
{
TResources res;
for (auto it : queue) //substract the value of reserved goals
res += it.resources;
return res;
}
TResources ResourceManager::freeResources() const
{
TResources myRes = cb->getResourceAmount();
myRes -= reservedResources(); //substract the value of reserved goals
for (auto & val : myRes)
vstd::amax(val, 0); //never negative
return myRes;
}
TResource ResourceManager::freeGold() const
{
return freeResources()[Res::GOLD];
}
TResources ResourceManager::allResources() const
{
return cb->getResourceAmount();
}
TResource ResourceManager::allGold() const
{
return cb->getResourceAmount()[Res::GOLD];
}

View File

@@ -1,113 +0,0 @@
/*
* ResourceManager.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "AIUtility.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h"
#include "VCAI.h"
#include <boost/heap/binomial_heap.hpp>
class AIhelper;
class IResourceManager;
struct DLL_EXPORT ResourceObjective
{
ResourceObjective() = default;
ResourceObjective(const TResources &res, Goals::TSubgoal goal);
bool operator < (const ResourceObjective &ro) const;
TResources resources; //how many resoures do we need
Goals::TSubgoal goal; //what for (build, gather army etc...)
//TODO: register?
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & resources;
//h & goal; //FIXME: goal serialization is broken
}
};
class DLL_EXPORT IResourceManager //: public: IAbstractManager
{
public:
virtual ~IResourceManager() = default;
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
virtual void setAI(VCAI * AI) = 0;
virtual TResources reservedResources() const = 0;
virtual TResources freeResources() const = 0;
virtual TResource freeGold() const = 0;
virtual TResources allResources() const = 0;
virtual TResource allGold() const = 0;
virtual Goals::TSubgoal whatToDo() const = 0;//get highest-priority goal
virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0;
virtual bool containsObjective(Goals::TSubgoal goal) const = 0;
virtual bool hasTasksLeft() const = 0;
virtual bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) = 0; //remove ResourceObjectives from queue if ResourceObjective->goal meets specific criteria
virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0;
};
class DLL_EXPORT ResourceManager : public IResourceManager
{
/*Resource Manager is a smart shopping list for AI to help
Uses priority queue based on CGoal.priority */
friend class VCAI;
friend class AIhelper;
friend struct SetGlobalState;
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
VCAI * ai;
public:
ResourceManager() = default;
ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
bool canAfford(const TResources & cost) const;
TResources reservedResources() const override;
TResources freeResources() const override;
TResource freeGold() const override;
TResources allResources() const override;
TResource allGold() const override;
Goals::TSubgoal whatToDo() const override; //peek highest-priority goal
Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal) override; //can we afford this goal or need to CollectRes?
bool containsObjective(Goals::TSubgoal goal) const override;
bool hasTasksLeft() const override;
bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
protected: //not-const actions only for AI
virtual void reserveResoures(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal());
virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority
virtual bool tryPush(const ResourceObjective &o);
//inner processing
virtual TResources estimateIncome() const;
virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const;
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
private:
TResources saving;
boost::heap::binomial_heap<ResourceObjective> queue;
void dumpToLog() const;
//TODO: register?
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & saving;
h & queue;
}
};

View File

@@ -76,7 +76,7 @@
<Add option="-Wno-sign-compare" /> <Add option="-Wno-sign-compare" />
<Add option="-Wno-unused-parameter" /> <Add option="-Wno-unused-parameter" />
<Add option="-Wno-overloaded-virtual" /> <Add option="-Wno-overloaded-virtual" />
<Add option="-DBOOST_THREAD_USE_LIB" /> <Add option="-DBOOST_ALL_DYN_LINK" />
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" /> <Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
<Add option="-D_WIN32_WINNT=0x0501" /> <Add option="-D_WIN32_WINNT=0x0501" />
<Add option="-D_WIN32" /> <Add option="-D_WIN32" />
@@ -93,6 +93,8 @@
<Unit filename="AIUtility.h" /> <Unit filename="AIUtility.h" />
<Unit filename="AIhelper.cpp" /> <Unit filename="AIhelper.cpp" />
<Unit filename="AIhelper.h" /> <Unit filename="AIhelper.h" />
<Unit filename="ArmyManager.cpp" />
<Unit filename="ArmyManager.h" />
<Unit filename="BuildingManager.cpp" /> <Unit filename="BuildingManager.cpp" />
<Unit filename="BuildingManager.h" /> <Unit filename="BuildingManager.h" />
<Unit filename="FuzzyEngines.cpp" /> <Unit filename="FuzzyEngines.cpp" />

View File

@@ -10,8 +10,6 @@
#include "StdInc.h" #include "StdInc.h"
#include "VCAI.h" #include "VCAI.h"
#include "FuzzyHelper.h" #include "FuzzyHelper.h"
#include "ResourceManager.h"
#include "BuildingManager.h"
#include "Goals/Goals.h" #include "Goals/Goals.h"
#include "../../lib/UnlockGuard.h" #include "../../lib/UnlockGuard.h"
@@ -119,9 +117,6 @@ void VCAI::heroMoved(const TryMoveHero & details)
} }
} }
} }
//FIXME: teleports are not correctly visited
unreserveObject(hero, t1);
unreserveObject(hero, t2);
} }
else if(details.result == TryMoveHero::EMBARK && hero) else if(details.result == TryMoveHero::EMBARK && hero)
{ {
@@ -246,8 +241,6 @@ void VCAI::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * vi
if(start && visitedObj) //we can end visit with null object, anyway if(start && visitedObj) //we can end visit with null object, anyway
{ {
markObjectVisited(visitedObj); markObjectVisited(visitedObj);
unreserveObject(visitor, visitedObj);
completeGoal(sptr(Goals::VisitObj(visitedObj->id.getNum()).sethero(visitor))); //we don't need to visit it anymore
//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..) //TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..)
if (visitedObj->ID == Obj::HERO) if (visitedObj->ID == Obj::HERO)
{ {
@@ -350,9 +343,6 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
transferFrom2to1(secondHero, firstHero); transferFrom2to1(secondHero, firstHero);
} }
completeGoal(sptr(Goals::VisitHero(firstHero->id.getNum()))); //TODO: what if we were visited by other hero in the meantime?
completeGoal(sptr(Goals::VisitHero(secondHero->id.getNum())));
answerQuery(query, 0); answerQuery(query, 0);
}); });
} }
@@ -399,9 +389,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
vstd::erase_if_present(visitableObjs, obj); vstd::erase_if_present(visitableObjs, obj);
vstd::erase_if_present(alreadyVisited, obj); vstd::erase_if_present(alreadyVisited, obj);
for(auto h : cb->getHeroesInfo())
unreserveObject(h, obj);
std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool
{ {
if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum())) if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum()))
@@ -412,27 +399,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
return false; return false;
}; };
//clear VCAI / main loop caches
vstd::erase_if(lockedHeroes, [&](const std::pair<HeroPtr, Goals::TSubgoal> & x) -> bool
{
return checkRemovalValidity(x.second);
});
vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool
{
return checkRemovalValidity(x.first);
});
vstd::erase_if(basicGoals, checkRemovalValidity);
vstd::erase_if(goalsToAdd, checkRemovalValidity);
vstd::erase_if(goalsToRemove, checkRemovalValidity);
for(auto goal : ultimateGoalsFromBasic)
vstd::erase_if(goal.second, checkRemovalValidity);
//clear resource manager goal cache
ah->removeOutdatedObjectives(checkRemovalValidity);
//TODO: Find better way to handle hero boat removal //TODO: Find better way to handle hero boat removal
if(auto hero = dynamic_cast<const CGHeroInstance *>(obj)) if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
{ {
@@ -440,9 +406,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
{ {
vstd::erase_if_present(visitableObjs, hero->boat); vstd::erase_if_present(visitableObjs, hero->boat);
vstd::erase_if_present(alreadyVisited, hero->boat); vstd::erase_if_present(alreadyVisited, hero->boat);
for(auto h : cb->getHeroesInfo())
unreserveObject(h, hero->boat);
} }
} }
@@ -569,9 +532,6 @@ void VCAI::buildChanged(const CGTownInstance * town, BuildingID buildingID, int
{ {
LOG_TRACE_PARAMS(logAi, "what '%i'", what); LOG_TRACE_PARAMS(logAi, "what '%i'", what);
NET_EVENT_HANDLER; NET_EVENT_HANDLER;
if(town->getOwner() == playerID && what == 1) //built
completeGoal(sptr(Goals::BuildThis(buildingID, town)));
} }
void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain)
@@ -855,22 +815,7 @@ void VCAI::makeTurn()
try try
{ {
if(nullkiller) nullkiller->makeTurn();
{
nullkiller->makeTurn();
}
else
{
//it looks messy here, but it's better to have armed heroes before attempting realizing goals
for(const CGTownInstance * t : cb->getTownsInfo())
moveCreaturesToHero(t);
mainLoop();
/*Below function is also responsible for hero movement via internal wander function. By design it is separate logic for heroes that have nothing to do.
Heroes that were not picked by striveToGoal(sptr(Goals::Win())); recently (so they do not have new goals and cannot continue/reevaluate previously locked goals) will do logic in wander().*/
performTypicalActions();
}
//for debug purpose //for debug purpose
for (auto h : cb->getHeroesInfo()) for (auto h : cb->getHeroesInfo())
@@ -904,194 +849,6 @@ std::vector<HeroPtr> VCAI::getMyHeroes() const
return ret; return ret;
} }
void VCAI::mainLoop()
{
std::vector<Goals::TSubgoal> elementarGoals; //no duplicates allowed (operator ==)
basicGoals.clear();
validateVisitableObjs();
//get all potential and saved goals
//TODO: not lose
basicGoals.push_back(sptr(Goals::Win()));
for (auto goalPair : lockedHeroes)
{
fh->setPriority(goalPair.second); //re-evaluate, as heroes moved in the meantime
basicGoals.push_back(goalPair.second);
}
if (ah->hasTasksLeft())
basicGoals.push_back(ah->whatToDo());
for (auto quest : myCb->getMyQuests())
{
basicGoals.push_back(sptr(Goals::CompleteQuest(quest)));
}
basicGoals.push_back(sptr(Goals::Build()));
invalidPathHeroes.clear();
while (basicGoals.size())
{
vstd::removeDuplicates(basicGoals); //TODO: container which does this automagically without has would be nice
goalsToAdd.clear();
goalsToRemove.clear();
elementarGoals.clear();
ultimateGoalsFromBasic.clear();
ah->updatePaths(getMyHeroes());
logAi->debug("Main loop: decomposing %i basic goals", basicGoals.size());
for (auto basicGoal : basicGoals)
{
logAi->debug("Main loop: decomposing basic goal %s", basicGoal->name());
auto goalToDecompose = basicGoal;
Goals::TSubgoal elementarGoal = sptr(Goals::Invalid());
int maxAbstractGoals = 10;
while (!elementarGoal->isElementar && maxAbstractGoals)
{
try
{
elementarGoal = decomposeGoal(goalToDecompose);
}
catch (goalFulfilledException & e)
{
//it is impossible to continue some goals (like exploration, for example)
//complete abstract goal for now, but maybe main goal finds another path
logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name());
completeGoal(e.goal); //put in goalsToRemove
break;
}
catch(cannotFulfillGoalException & e)
{
//it is impossible to continue some goals (like exploration, for example)
//complete abstract goal for now, but maybe main goal finds another path
goalsToRemove.push_back(basicGoal);
logAi->debug("Goal %s decomposition failed: %s", goalToDecompose->name(), e.what());
break;
}
catch (std::exception & e) //decomposition failed, which means we can't decompose entire tree
{
goalsToRemove.push_back(basicGoal);
logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what());
break;
}
if (elementarGoal->isAbstract) //we can decompose it further
{
goalsToAdd.push_back(elementarGoal);
//decompose further now - this is necesssary if we can't add over 10 goals in the pool
goalToDecompose = elementarGoal;
//there is a risk of infinite abstract goal loop, though it indicates failed logic
maxAbstractGoals--;
}
else if (elementarGoal->isElementar) //should be
{
logAi->debug("Found elementar goal %s", elementarGoal->name());
elementarGoals.push_back(elementarGoal);
ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal?
break;
}
else //should never be here
throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name());
}
}
logAi->trace("Main loop: selecting best elementar goal");
//now choose one elementar goal to realize
Goals::TGoalVec possibleGoals(elementarGoals.begin(), elementarGoals.end()); //copy to vector
Goals::TSubgoal goalToRealize = sptr(Goals::Invalid());
while (possibleGoals.size())
{
//allow assign goals to heroes with 0 movement, but don't realize them
//maybe there are beter ones left
auto bestGoal = fh->chooseSolution(possibleGoals);
if (bestGoal->hero) //lock this hero to fulfill goal
{
setGoal(bestGoal->hero, bestGoal);
if (!bestGoal->hero->movement || vstd::contains(invalidPathHeroes, bestGoal->hero))
{
if (!vstd::erase_if_present(possibleGoals, bestGoal))
{
logAi->error("erase_if_preset failed? Something very wrong!");
break;
}
continue; //chose next from the list
}
}
goalToRealize = bestGoal; //we found our goal to execute
break;
}
//realize best goal
if (!goalToRealize->invalid())
{
logAi->debug("Trying to realize %s (value %2.3f)", goalToRealize->name(), goalToRealize->priority);
try
{
boost::this_thread::interruption_point();
goalToRealize->accept(this); //visitor pattern
boost::this_thread::interruption_point();
}
catch (boost::thread_interrupted & e)
{
logAi->debug("Player %d: Making turn thread received an interruption!", playerID);
throw; //rethrow, we want to truly end this thread
}
catch (goalFulfilledException & e)
{
//the sub-goal was completed successfully
completeGoal(e.goal);
//local goal was also completed?
completeGoal(goalToRealize);
// remove abstract visit tile if we completed the elementar one
vstd::erase_if_present(goalsToAdd, goalToRealize);
}
catch (std::exception & e)
{
logAi->debug("Failed to realize subgoal of type %s, I will stop.", goalToRealize->name());
logAi->debug("The error message was: %s", e.what());
//erase base goal if we failed to execute decomposed goal
for (auto basicGoal : ultimateGoalsFromBasic[goalToRealize])
goalsToRemove.push_back(basicGoal);
// sometimes resource manager contains an elementar goal which is not able to execute anymore and just fails each turn.
ai->ah->notifyGoalCompleted(goalToRealize);
//we failed to realize best goal, but maybe others are still possible?
}
//remove goals we couldn't decompose
for (auto goal : goalsToRemove)
vstd::erase_if_present(basicGoals, goal);
//add abstract goals
boost::sort(goalsToAdd, [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
{
return lhs->priority > rhs->priority; //highest priority at the beginning
});
//max number of goals = 10
int i = 0;
while (basicGoals.size() < 10 && goalsToAdd.size() > i)
{
if (!vstd::contains(basicGoals, goalsToAdd[i])) //don't add duplicates
basicGoals.push_back(goalsToAdd[i]);
i++;
}
}
else //no elementar goals possible
{
logAi->debug("Goal decomposition exhausted");
break;
}
}
}
void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{ {
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString()); LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString());
@@ -1099,7 +856,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{ {
case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR1:
recruitCreatures(dynamic_cast<const CGDwelling *>(obj), h.get()); recruitCreatures(dynamic_cast<const CGDwelling *>(obj), h.get());
checkHeroArmy(h);
break; break;
case Obj::TOWN: case Obj::TOWN:
if(h->visitedTown) //we are inside, not just attacking if(h->visitedTown) //we are inside, not just attacking
@@ -1116,7 +872,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
ah->update(); ah->update();
} }
if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST) if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST)
{ {
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1)) if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK); cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
@@ -1127,7 +883,6 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
makePossibleUpgrades(h.get()); makePossibleUpgrades(h.get());
break; break;
} }
completeGoal(sptr(Goals::VisitObj(obj->id.getNum()).sethero(h)));
} }
void VCAI::moveCreaturesToHero(const CGTownInstance * t) void VCAI::moveCreaturesToHero(const CGTownInstance * t)
@@ -1197,10 +952,6 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme
} }
//TODO - having now strongest possible army, we may want to think about arranging stacks //TODO - having now strongest possible army, we may want to think about arranging stacks
auto hero = dynamic_cast<const CGHeroInstance *>(destinationArmy);
if(hero)
checkHeroArmy(hero);
} }
void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other) void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
@@ -1313,7 +1064,7 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
int count = d->creatures[i].first; int count = d->creatures[i].first;
CreatureID creID = d->creatures[i].second.back(); CreatureID creID = d->creatures[i].second.back();
vstd::amin(count, ah->freeResources() / VLC->creh->creatures[creID]->cost); vstd::amin(count, cb->getResourceAmount() / VLC->creh->creatures[creID]->cost);
if(count > 0) if(count > 0)
cb->recruitCreatures(d, recruiter, creID, count, i); cb->recruitCreatures(d, recruiter, creID, count, i);
} }
@@ -1402,250 +1153,6 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
return true; return true;
} }
void VCAI::wander(HeroPtr h)
{
auto visitTownIfAny = [this](HeroPtr h) -> bool
{
if (h->visitedTown)
{
townVisitsThisWeek[h].insert(h->visitedTown);
buildArmyIn(h->visitedTown);
return true;
}
return false;
};
//unclaim objects that are now dangerous for us
auto reservedObjsSetCopy = reservedHeroesMap[h];
for(auto obj : reservedObjsSetCopy)
{
if(!isSafeToVisit(h, obj->visitablePos()))
unreserveObject(h, obj);
}
TimeCheck tc("looking for wander destination");
while(h->movement)
{
validateVisitableObjs();
ah->updatePaths(getMyHeroes());
std::vector<ObjectIdRef> dests;
//also visit our reserved objects - but they are not prioritized to avoid running back and forth
vstd::copy_if(reservedHeroesMap[h], std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
{
return ah->isTileAccessible(h, obj->visitablePos());
});
int pass = 0;
std::vector<boost::optional<float>> distanceLimits =
{
1.0,
2.0,
boost::none
};
while(!dests.size() && pass < distanceLimits.size())
{
auto & distanceLimit = distanceLimits[pass];
logAi->debug("Looking for wander destination pass=%i, cost limit=%f", pass, distanceLimit.get_value_or(-1.0));
vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
{
return isGoodForVisit(obj, h, distanceLimit);
});
pass++;
}
if(!dests.size())
{
logAi->debug("Looking for town destination");
if(cb->getVisitableObjs(h->visitablePos()).size() > 1)
moveHeroToTile(h->visitablePos(), h); //just in case we're standing on blocked subterranean gate
auto compareReinforcements = [&](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool
{
const CGHeroInstance * hptr = h.get();
auto r1 = ah->howManyReinforcementsCanGet(hptr, lhs),
r2 = ah->howManyReinforcementsCanGet(hptr, rhs);
if (r1 != r2)
return r1 < r2;
else
return ah->howManyReinforcementsCanBuy(hptr, lhs) < ah->howManyReinforcementsCanBuy(hptr, rhs);
};
std::vector<const CGTownInstance *> townsReachable;
std::vector<const CGTownInstance *> townsNotReachable;
for(const CGTownInstance * t : cb->getTownsInfo())
{
if(!t->visitingHero && !vstd::contains(townVisitsThisWeek[h], t))
{
if(isAccessibleForHero(t->visitablePos(), h))
townsReachable.push_back(t);
else
townsNotReachable.push_back(t);
}
}
if(townsReachable.size()) //travel to town with largest garrison, or empty - better than nothing
{
dests.push_back(*boost::max_element(townsReachable, compareReinforcements));
}
else if(townsNotReachable.size())
{
//TODO pick the truly best
const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements);
logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->name, t->name, t->visitablePos().toString());
int3 pos1 = h->pos;
striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop
//if out hero is stuck, we may need to request another hero to clear the way we see
if(pos1 == h->pos && h == primaryHero()) //hero can't move
{
if(canRecruitAnyHero(t))
recruitHero(t);
}
break;
}
else if(cb->getResourceAmount(Res::GOLD) >= GameConstants::HERO_GOLD_COST)
{
std::vector<const CGTownInstance *> towns = cb->getTownsInfo();
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
{
for(const CGHeroInstance * h : cb->getHeroesInfo())
{
if(!t->getArmyStrength() || ah->howManyReinforcementsCanGet(h, t))
return true;
}
return false;
});
if (towns.size())
{
recruitHero(*boost::max_element(towns, compareArmyStrength));
}
break;
}
else
{
logAi->debug("Nowhere more to go...");
break;
}
}
//end of objs empty
if(dests.size()) //performance improvement
{
Goals::TGoalVec targetObjectGoals;
for(auto destination : dests)
{
vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false));
}
if(targetObjectGoals.size())
{
auto bestObjectGoal = fh->chooseSolution(targetObjectGoals);
//wander should not cause heroes to be reserved - they are always considered free
if(bestObjectGoal->goalType == Goals::VISIT_OBJ)
{
auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid));
if(chosenObject != nullptr)
logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString());
}
else
logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name());
try
{
decomposeGoal(bestObjectGoal)->accept(this);
}
catch(const goalFulfilledException & e)
{
if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ)
continue;
throw e;
}
}
else
{
logAi->debug("Nowhere more to go...");
break;
}
visitTownIfAny(h);
}
}
visitTownIfAny(h); //in case hero is just sitting in town
}
void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
{
if(goal->invalid())
{
vstd::erase_if_present(lockedHeroes, h);
}
else
{
lockedHeroes[h] = goal;
goal->setisElementar(false); //Force always evaluate goals before realizing
}
}
void VCAI::evaluateGoal(HeroPtr h)
{
if(vstd::contains(lockedHeroes, h))
fh->setPriority(lockedHeroes[h]);
}
void VCAI::completeGoal(Goals::TSubgoal goal)
{
if (goal->goalType == Goals::WIN) //we can never complete this goal - unless we already won
return;
logAi->debug("Completing goal: %s", goal->name());
//notify Managers
ah->notifyGoalCompleted(goal);
//notify mainLoop()
goalsToRemove.push_back(goal); //will be removed from mainLoop() goals
for (auto basicGoal : basicGoals) //we could luckily fulfill any of our goals
{
if (basicGoal->fulfillsMe(goal))
goalsToRemove.push_back(basicGoal);
}
//unreserve heroes
if(const CGHeroInstance * h = goal->hero.get(true))
{
auto it = lockedHeroes.find(h);
if(it != lockedHeroes.end())
{
if(it->second == goal || it->second->fulfillsMe(goal)) //FIXME this is overspecified, fulfillsMe shoudl be complete
{
logAi->debug(goal->completeMessage());
lockedHeroes.erase(it); //goal fulfilled, free hero
}
}
}
else //complete goal for all heroes maybe?
{
vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p)
{
if(p.second == goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance
{
logAi->debug(p.second->completeMessage());
return true;
}
return false;
});
}
}
void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
{ {
NET_EVENT_HANDLER; NET_EVENT_HANDLER;
@@ -1686,19 +1193,6 @@ void VCAI::markObjectVisited(const CGObjectInstance * obj)
alreadyVisited.insert(obj); alreadyVisited.insert(obj);
} }
void VCAI::reserveObject(HeroPtr h, const CGObjectInstance * obj)
{
reservedObjs.insert(obj);
reservedHeroesMap[h].insert(obj);
logAi->debug("reserved object id=%d; address=%p; name=%s", obj->id, obj, obj->getObjectName());
}
void VCAI::unreserveObject(HeroPtr h, const CGObjectInstance * obj)
{
vstd::erase_if_present(reservedObjs, obj); //unreserve objects
vstd::erase_if_present(reservedHeroesMap[h], obj);
}
void VCAI::markHeroUnableToExplore(HeroPtr h) void VCAI::markHeroUnableToExplore(HeroPtr h)
{ {
heroesUnableToExplore.insert(h); heroesUnableToExplore.insert(h);
@@ -2053,25 +1547,15 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
} }
if(h) //we could have lost hero after last move if(h) //we could have lost hero after last move
{ {
completeGoal(sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway
completeGoal(sptr(Goals::ClearWayTo(dst).sethero(h)));
ret = ret || (dst == h->visitablePos()); ret = ret || (dst == h->visitablePos());
if(!ret) //reserve object we are heading towards
{
auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst));
if(obj && obj != *h)
reserveObject(h, obj);
}
if(startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target if(startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target
{ {
vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused or has only 95mp which is not enough to move vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused or has only 95mp which is not enough to move
invalidPathHeroes.insert(h); invalidPathHeroes.insert(h);
throw cannotFulfillGoalException("Invalid path found!"); throw cannotFulfillGoalException("Invalid path found!");
} }
evaluateGoal(h); //new hero position means new game situation
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret); logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret);
} }
return ret; return ret;
@@ -2181,7 +1665,6 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG) if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG)
{ {
cb->dig(g.hero.get()); cb->dig(g.hero.get());
completeGoal(sptr(g)); // finished digging
} }
else else
{ {
@@ -2192,7 +1675,7 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
void VCAI::tryRealize(Goals::Trade & g) //trade void VCAI::tryRealize(Goals::Trade & g) //trade
{ {
if(ah->freeResources()[g.resID] >= g.value) //goal is already fulfilled. Why we need this check, anyway? if(cb->getResourceAmount((Res::ERes)g.resID) >= g.value) //goal is already fulfilled. Why we need this check, anyway?
throw goalFulfilledException(sptr(g)); throw goalFulfilledException(sptr(g));
int accquiredResources = 0; int accquiredResources = 0;
@@ -2200,7 +1683,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
{ {
if(const IMarket * m = IMarket::castFrom(obj, false)) if(const IMarket * m = IMarket::castFrom(obj, false))
{ {
auto freeRes = ah->freeResources(); //trade only resources which are not reserved auto freeRes = cb->getResourceAmount(); //trade only resources which are not reserved
for(auto it = Res::ResourceSet::nziterator(freeRes); it.valid(); it++) for(auto it = Res::ResourceSet::nziterator(freeRes); it.valid(); it++)
{ {
auto res = it->resType; auto res = it->resType;
@@ -2217,7 +1700,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
accquiredResources = toGet * (it->resVal / toGive); accquiredResources = toGet * (it->resVal / toGive);
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName()); logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, accquiredResources, g.resID, obj->getObjectName());
} }
if (ah->freeResources()[g.resID] >= g.value) if (cb->getResourceAmount((Res::ERes)g.resID) >= g.value)
throw goalFulfilledException(sptr(g)); //we traded all we needed throw goalFulfilledException(sptr(g)); //we traded all we needed
} }
@@ -2255,7 +1738,7 @@ void VCAI::tryRealize(Goals::BuyArmy & g)
for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++) for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
{ {
auto res = ah->allResources(); auto res = cb->getResourceAmount();
auto & ci = armyToBuy[i]; auto & ci = armyToBuy[i];
if(g.objid != -1 && ci.creID != g.objid) if(g.objid != -1 && ci.creID != g.objid)
@@ -2365,140 +1848,6 @@ void VCAI::endTurn()
logGlobal->info("Player %d (%s) ended turn", playerID, playerID.getStr()); logGlobal->info("Player %d (%s) ended turn", playerID, playerID.getStr());
} }
void VCAI::striveToGoal(Goals::TSubgoal basicGoal)
{
//TODO: this function is deprecated and should be dropped altogether
auto goalToDecompose = basicGoal;
Goals::TSubgoal elementarGoal = sptr(Goals::Invalid());
int maxAbstractGoals = 10;
while (!elementarGoal->isElementar && maxAbstractGoals)
{
try
{
elementarGoal = decomposeGoal(goalToDecompose);
}
catch (goalFulfilledException & e)
{
//it is impossible to continue some goals (like exploration, for example)
completeGoal(e.goal); //put in goalsToRemove
logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name());
return;
}
catch (std::exception & e)
{
goalsToRemove.push_back(basicGoal);
logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what());
return;
}
if (elementarGoal->isAbstract) //we can decompose it further
{
goalsToAdd.push_back(elementarGoal);
//decompose further now - this is necesssary if we can't add over 10 goals in the pool
goalToDecompose = elementarGoal;
//there is a risk of infinite abstract goal loop, though it indicates failed logic
maxAbstractGoals--;
}
else if (elementarGoal->isElementar) //should be
{
logAi->debug("Found elementar goal %s", elementarGoal->name());
ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal?
break;
}
else //should never be here
throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name());
}
//realize best goal
if (!elementarGoal->invalid())
{
logAi->debug("Trying to realize %s (value %2.3f)", elementarGoal->name(), elementarGoal->priority);
try
{
boost::this_thread::interruption_point();
elementarGoal->accept(this); //visitor pattern
boost::this_thread::interruption_point();
}
catch (boost::thread_interrupted & e)
{
logAi->debug("Player %d: Making turn thread received an interruption!", playerID);
throw; //rethrow, we want to truly end this thread
}
catch (goalFulfilledException & e)
{
//the sub-goal was completed successfully
completeGoal(e.goal);
//local goal was also completed
completeGoal(elementarGoal);
}
catch (std::exception & e)
{
logAi->debug("Failed to realize subgoal of type %s, I will stop.", elementarGoal->name());
logAi->debug("The error message was: %s", e.what());
//erase base goal if we failed to execute decomposed goal
for (auto basicGoalToRemove : ultimateGoalsFromBasic[elementarGoal])
goalsToRemove.push_back(basicGoalToRemove);
}
}
}
Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal)
{
if(ultimateGoal->isElementar)
{
logAi->warn("Trying to decompose elementar goal %s", ultimateGoal->name());
return ultimateGoal;
}
const int searchDepth = 30;
Goals::TSubgoal goal = ultimateGoal;
logAi->debug("Decomposing goal %s", ultimateGoal->name());
int maxGoals = searchDepth; //preventing deadlock for mutually dependent goals
while (maxGoals)
{
boost::this_thread::interruption_point();
goal = goal->whatToDoToAchieve(); //may throw if decomposition fails
--maxGoals;
if (goal == ultimateGoal) //compare objects by value
if (goal->isElementar == ultimateGoal->isElementar)
throw cannotFulfillGoalException((boost::format("Goal dependency loop detected for %s!")
% ultimateGoal->name()).str());
if (goal->isAbstract || goal->isElementar)
return goal;
else
logAi->debug("Considering: %s", goal->name());
}
throw cannotFulfillGoalException("Too many subgoals, don't know what to do");
}
void VCAI::performTypicalActions()
{
for(auto h : getUnblockedHeroes())
{
if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
continue;
logAi->debug("Hero %s started wandering, MP=%d", h->name.c_str(), h->movement);
makePossibleUpgrades(*h);
pickBestArtifacts(*h);
try
{
wander(h);
}
catch(std::exception & e)
{
logAi->debug("Cannot use this hero anymore, received exception: %s", e.what());
continue;
}
}
}
void VCAI::buildArmyIn(const CGTownInstance * t) void VCAI::buildArmyIn(const CGTownInstance * t)
{ {
makePossibleUpgrades(t->visitingHero); makePossibleUpgrades(t->visitingHero);
@@ -2507,16 +1856,6 @@ void VCAI::buildArmyIn(const CGTownInstance * t)
moveCreaturesToHero(t); moveCreaturesToHero(t);
} }
void VCAI::checkHeroArmy(HeroPtr h)
{
auto it = lockedHeroes.find(h);
if(it != lockedHeroes.end())
{
if(it->second->goalType == Goals::GATHER_ARMY && it->second->value <= h->getArmyStrength())
completeGoal(sptr(Goals::GatherArmy(it->second->value).sethero(h)));
}
}
void VCAI::recruitHero(const CGTownInstance * t, bool throwing) void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
{ {
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString()); logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
@@ -2547,6 +1886,8 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
void VCAI::finish() void VCAI::finish()
{ {
//we want to lock to avoid multiple threads from calling makingTurn->join() at same time
boost::lock_guard<boost::mutex> multipleCleanupGuard(turnInterruptionMutex);
if(makingTurn) if(makingTurn)
{ {
makingTurn->interrupt(); makingTurn->interrupt();
@@ -2570,41 +1911,11 @@ void VCAI::lostHero(HeroPtr h)
{ {
logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name); logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name);
vstd::erase_if_present(lockedHeroes, h);
for(auto obj : reservedHeroesMap[h])
{
vstd::erase_if_present(reservedObjs, obj); //unreserve all objects for that hero
}
vstd::erase_if_present(reservedHeroesMap, h);
vstd::erase_if_present(visitedHeroes, h); vstd::erase_if_present(visitedHeroes, h);
for (auto heroVec : visitedHeroes) for (auto heroVec : visitedHeroes)
{ {
vstd::erase_if_present(heroVec.second, h); vstd::erase_if_present(heroVec.second, h);
} }
//remove goals with removed hero assigned from main loop
vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool
{
if(x.first->hero == h)
return true;
else
return false;
});
auto removedHeroGoalPredicate = [&](const Goals::TSubgoal & x) ->bool
{
if(x->hero == h)
return true;
else
return false;
};
vstd::erase_if(basicGoals, removedHeroGoalPredicate);
vstd::erase_if(goalsToAdd, removedHeroGoalPredicate);
vstd::erase_if(goalsToRemove, removedHeroGoalPredicate);
for(auto goal : ultimateGoalsFromBasic)
vstd::erase_if(goal.second, removedHeroGoalPredicate);
} }
void VCAI::answerQuery(QueryID queryID, int selection) void VCAI::answerQuery(QueryID queryID, int selection)
@@ -2914,7 +2225,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
case Obj::SCHOOL_OF_MAGIC: case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR: case Obj::SCHOOL_OF_WAR:
{ {
if (ai->ah->freeGold() < 1000) if (cb->getResourceAmount(Res::GOLD) < 1000)
return false; return false;
break; break;
} }
@@ -2924,7 +2235,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
break; break;
case Obj::TREE_OF_KNOWLEDGE: case Obj::TREE_OF_KNOWLEDGE:
{ {
TResources myRes = ai->ah->freeResources(); TResources myRes = cb->getResourceAmount();
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10) if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
return false; return false;
break; break;
@@ -2939,7 +2250,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
//TODO: only on request //TODO: only on request
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER) if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
return false; return false;
else if(ai->ah->freeGold() < GameConstants::HERO_GOLD_COST) else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
return false; return false;
break; break;
} }

View File

@@ -13,9 +13,7 @@
#include "Goals/AbstractGoal.h" #include "Goals/AbstractGoal.h"
#include "../../lib/AI_Base.h" #include "../../lib/AI_Base.h"
#include "../../CCallback.h" #include "../../CCallback.h"
#include "../../lib/CThreadHelper.h" #include "../../lib/CThreadHelper.h"
#include "../../lib/GameConstants.h" #include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h" #include "../../lib/VCMI_Lib.h"
#include "../../lib/CBuildingHandler.h" #include "../../lib/CBuildingHandler.h"
@@ -90,12 +88,6 @@ public:
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs //std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek; std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
//part of mainLoop, but accessible from outisde
std::vector<Goals::TSubgoal> basicGoals;
Goals::TGoalVec goalsToRemove;
Goals::TGoalVec goalsToAdd;
std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals
std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
@@ -113,6 +105,9 @@ public:
std::shared_ptr<CCallback> myCb; std::shared_ptr<CCallback> myCb;
std::unique_ptr<boost::thread> makingTurn; std::unique_ptr<boost::thread> makingTurn;
private:
boost::mutex turnInterruptionMutex;
public:
ObjectInstanceID selectedObject; ObjectInstanceID selectedObject;
AIhelper * ah; AIhelper * ah;
@@ -199,17 +194,9 @@ public:
void battleEnd(const BattleResult * br) override; void battleEnd(const BattleResult * br) override;
void makeTurn(); void makeTurn();
void mainLoop();
void performTypicalActions();
void buildArmyIn(const CGTownInstance * t); void buildArmyIn(const CGTownInstance * t);
void striveToGoal(Goals::TSubgoal ultimateGoal);
Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
void endTurn(); void endTurn();
void wander(HeroPtr h);
void setGoal(HeroPtr h, Goals::TSubgoal goal);
void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
void recruitHero(const CGTownInstance * t, bool throwing = false); void recruitHero(const CGTownInstance * t, bool throwing = false);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none); bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
@@ -230,8 +217,6 @@ public:
void addVisitableObj(const CGObjectInstance * obj); void addVisitableObj(const CGObjectInstance * obj);
void markObjectVisited(const CGObjectInstance * obj); void markObjectVisited(const CGObjectInstance * obj);
void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit
void unreserveObject(HeroPtr h, const CGObjectInstance * obj);
void markHeroUnableToExplore(HeroPtr h); void markHeroUnableToExplore(HeroPtr h);
void markHeroAbleToExplore(HeroPtr h); void markHeroAbleToExplore(HeroPtr h);
@@ -261,7 +246,6 @@ public:
std::vector<HeroPtr> getUnblockedHeroes() const; std::vector<HeroPtr> getUnblockedHeroes() const;
std::vector<HeroPtr> getMyHeroes() const; std::vector<HeroPtr> getMyHeroes() const;
HeroPtr primaryHero() const; HeroPtr primaryHero() const;
void checkHeroArmy(HeroPtr h);
void requestSent(const CPackForServer * pack, int requestID) override; void requestSent(const CPackForServer * pack, int requestID) override;
void answerQuery(QueryID queryID, int selection); void answerQuery(QueryID queryID, int selection);

View File

@@ -21,7 +21,7 @@
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid> <ProjectGuid>{276C3DB0-7A6B-4417-8E5C-322B08633AAC}</ProjectGuid>
<RootNamespace>StupidAI</RootNamespace> <RootNamespace>StupidAI</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@@ -41,7 +41,7 @@
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v141</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RD|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
@@ -117,7 +117,7 @@
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<Optimization>MaxSpeed</Optimization> <Optimization>Disabled</Optimization>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>VCMI_lib.lib;FuzzyLite.lib;%(AdditionalDependencies)</AdditionalDependencies>