mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
Nullkiller: initial decomposition
This commit is contained in:
parent
223a52b3d1
commit
8f8c5ca255
@ -36,9 +36,9 @@ extern const int GOLD_RESERVE;
|
||||
|
||||
enum HeroRole
|
||||
{
|
||||
MAIN,
|
||||
SCOUT,
|
||||
|
||||
SCOUT
|
||||
MAIN
|
||||
};
|
||||
|
||||
//provisional class for AI to store a reference to an owned hero object
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "../AIhelper.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Goals/BuyArmy.h"
|
||||
#include "../Goals/VisitTile.h"
|
||||
#include "lib/mapping/CMap.h" //for victory conditions
|
||||
#include "lib/CPathfinder.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
|
@ -19,6 +19,7 @@ namespace Goals
|
||||
{
|
||||
public:
|
||||
BuildingBehavior()
|
||||
:CGoal(Goals::BUILD)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "../AIhelper.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Goals/BuyArmy.h"
|
||||
#include "../Goals/VisitTile.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "lib/mapping/CMap.h" //for victory conditions
|
||||
#include "lib/CPathfinder.h"
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "../VCAI.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "CaptureObjectsBehavior.h"
|
||||
#include "../AIUtility.h"
|
||||
@ -23,11 +24,32 @@ extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
template <typename T>
|
||||
bool vectorEquals(const std::vector<T> & v1, const std::vector<T> & v2)
|
||||
{
|
||||
return vstd::contains_if(v1, [&](T o) -> bool
|
||||
{
|
||||
return vstd::contains(v2, o);
|
||||
});
|
||||
}
|
||||
|
||||
std::string CaptureObjectsBehavior::toString() const
|
||||
{
|
||||
return "Capture objects";
|
||||
}
|
||||
|
||||
bool CaptureObjectsBehavior::operator==(const CaptureObjectsBehavior & other) const
|
||||
{
|
||||
if(specificObjects != other.specificObjects)
|
||||
return false;
|
||||
|
||||
if(specificObjects)
|
||||
return vectorEquals(objectsToCapture, other.objectsToCapture);
|
||||
|
||||
return vectorEquals(objectTypes, other.objectTypes)
|
||||
&& vectorEquals(objectSubTypes, other.objectSubTypes);
|
||||
}
|
||||
|
||||
Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
{
|
||||
Goals::TGoalVec tasks;
|
||||
@ -65,15 +87,6 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
logAi->trace("Path found %s", path.toString());
|
||||
#endif
|
||||
|
||||
if(path.getFirstBlockedAction())
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 2
|
||||
// TODO: decomposition?
|
||||
logAi->trace("Ignore path. Action is blocked.");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 2
|
||||
@ -91,9 +104,26 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
|
||||
continue;
|
||||
|
||||
if(path.specialAction && !path.specialAction->canAct(path.targetHero))
|
||||
auto firstBlockedAction = path.getFirstBlockedAction();
|
||||
if(firstBlockedAction)
|
||||
{
|
||||
auto subGoal = path.specialAction->whatToDo(path.targetHero);
|
||||
auto subGoal = firstBlockedAction->decompose(path.targetHero);
|
||||
|
||||
#if AI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
|
||||
#endif
|
||||
|
||||
if(!subGoal->invalid())
|
||||
{
|
||||
Composition composition;
|
||||
|
||||
composition.addNext(ExecuteHeroChain(path, objToVisit));
|
||||
composition.addNext(subGoal);
|
||||
|
||||
tasks.push_back(sptr(composition));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||
@ -115,7 +145,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
|
||||
waysToVisitObj.push_back(newWay);
|
||||
|
||||
if(!closestWay || closestWay->evaluationContext.movementCost > newWay->evaluationContext.movementCost)
|
||||
if(!closestWay || closestWay->getPath().movementCost() > newWay->getPath().movementCost())
|
||||
closestWay = newWay;
|
||||
}
|
||||
}
|
||||
@ -128,8 +158,8 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
||||
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
||||
continue;
|
||||
|
||||
way->evaluationContext.closestWayRatio
|
||||
= closestWay->evaluationContext.movementCost / way->evaluationContext.movementCost;
|
||||
way->closestWayRatio
|
||||
= closestWay->getPath().movementCost() / way->getPath().movementCost();
|
||||
|
||||
tasks.push_back(sptr(*way));
|
||||
}
|
||||
|
@ -24,18 +24,21 @@ namespace Goals
|
||||
bool specificObjects;
|
||||
public:
|
||||
CaptureObjectsBehavior()
|
||||
:CGoal(CAPTURE_OBJECTS)
|
||||
{
|
||||
objectTypes = std::vector<int>();
|
||||
specificObjects = false;
|
||||
}
|
||||
|
||||
CaptureObjectsBehavior(std::vector<const CGObjectInstance *> objectsToCapture)
|
||||
:CGoal(CAPTURE_OBJECTS)
|
||||
{
|
||||
this->objectsToCapture = objectsToCapture;
|
||||
specificObjects = true;
|
||||
}
|
||||
|
||||
CaptureObjectsBehavior(const CGObjectInstance * objectToCapture)
|
||||
:CGoal(CAPTURE_OBJECTS)
|
||||
{
|
||||
objectsToCapture = std::vector<const CGObjectInstance *>();
|
||||
objectsToCapture.push_back(objectToCapture);
|
||||
@ -59,10 +62,7 @@ namespace Goals
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual bool operator==(const CaptureObjectsBehavior & other) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool operator==(const CaptureObjectsBehavior & other) const override;
|
||||
|
||||
private:
|
||||
bool shouldVisitObject(ObjectIdRef obj) const;
|
||||
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "lib/VCMI_Lib.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../../../lib/VCMI_Lib.h"
|
||||
#include "../../../CCallback.h"
|
||||
@ -21,6 +20,7 @@ namespace Goals
|
||||
{
|
||||
public:
|
||||
CompleteQuestBehavior()
|
||||
:CGoal(COMPLETE_QUEST)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace Goals
|
||||
{
|
||||
public:
|
||||
DefenceBehavior()
|
||||
:CGoal(DEFENCE)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
||||
{
|
||||
auto newWay = std::make_shared<ExecuteHeroChain>(path, hero);
|
||||
|
||||
newWay->evaluationContext.strategicalValue = armyValue;
|
||||
newWay->strategicalValue = armyValue;
|
||||
waysToVisitObj.push_back(newWay);
|
||||
}
|
||||
}
|
||||
@ -148,7 +148,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
||||
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
||||
continue;
|
||||
|
||||
way->evaluationContext.closestWayRatio = 1;
|
||||
way->closestWayRatio = 1;
|
||||
|
||||
tasks.push_back(sptr(*way));
|
||||
}
|
||||
@ -228,8 +228,8 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
{
|
||||
auto newWay = std::make_shared<ExecuteHeroChain>(path, upgrader);
|
||||
|
||||
newWay->evaluationContext.strategicalValue = armyValue;
|
||||
newWay->evaluationContext.goldCost = upgrade.upgradeCost[Res::GOLD];
|
||||
newWay->strategicalValue = armyValue;
|
||||
newWay->goldCost = upgrade.upgradeCost[Res::GOLD];
|
||||
|
||||
waysToVisitObj.push_back(newWay);
|
||||
}
|
||||
@ -243,7 +243,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
|
||||
continue;
|
||||
|
||||
way->evaluationContext.closestWayRatio = 1;
|
||||
way->closestWayRatio = 1;
|
||||
|
||||
tasks.push_back(sptr(*way));
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ namespace Goals
|
||||
{
|
||||
public:
|
||||
GatherArmyBehavior()
|
||||
:CGoal(Goals::GATHER_ARMY)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace Goals
|
||||
{
|
||||
public:
|
||||
RecruitHeroBehavior()
|
||||
:CGoal(RECRUIT_HERO_BEHAVIOR)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace Goals
|
||||
{
|
||||
public:
|
||||
StartupBehavior()
|
||||
:CGoal(STARTUP)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ set(VCAI_SRCS
|
||||
Pathfinding/AINodeStorage.cpp
|
||||
Pathfinding/PathfindingManager.cpp
|
||||
Pathfinding/Actors.cpp
|
||||
Pathfinding/Actions/SpecialAction.cpp
|
||||
Pathfinding/Actions/BattleAction.cpp
|
||||
Pathfinding/Actions/BoatActions.cpp
|
||||
Pathfinding/Actions/TownPortalAction.cpp
|
||||
@ -21,6 +22,7 @@ set(VCAI_SRCS
|
||||
FuzzyEngines.cpp
|
||||
FuzzyHelper.cpp
|
||||
Goals/AbstractGoal.cpp
|
||||
Goals/Composition.cpp
|
||||
Goals/BuildBoat.cpp
|
||||
Goals/BuildThis.cpp
|
||||
Goals/DismissHero.cpp
|
||||
@ -32,7 +34,6 @@ set(VCAI_SRCS
|
||||
Goals/RecruitHero.cpp
|
||||
Goals/DigAtTile.cpp
|
||||
Goals/GetArtOfType.cpp
|
||||
Goals/FindObj.cpp
|
||||
Goals/ExecuteHeroChain.cpp
|
||||
Goals/ExchangeSwapTownHeroes.cpp
|
||||
Engine/Nullkiller.cpp
|
||||
@ -59,7 +60,7 @@ set(VCAI_HEADERS
|
||||
Pathfinding/AINodeStorage.h
|
||||
Pathfinding/PathfindingManager.h
|
||||
Pathfinding/Actors.h
|
||||
Pathfinding/Actions/ISpecialAction.h
|
||||
Pathfinding/Actions/SpecialAction.h
|
||||
Pathfinding/Actions/BattleAction.h
|
||||
Pathfinding/Actions/BoatActions.h
|
||||
Pathfinding/Actions/TownPortalAction.h
|
||||
@ -76,6 +77,7 @@ set(VCAI_HEADERS
|
||||
FuzzyHelper.h
|
||||
Goals/AbstractGoal.h
|
||||
Goals/CGoal.h
|
||||
Goals/Composition.h
|
||||
Goals/Invalid.h
|
||||
Goals/BuildBoat.h
|
||||
Goals/BuildThis.h
|
||||
@ -88,7 +90,6 @@ set(VCAI_HEADERS
|
||||
Goals/RecruitHero.h
|
||||
Goals/DigAtTile.h
|
||||
Goals/GetArtOfType.h
|
||||
Goals/FindObj.h
|
||||
Goals/ExecuteHeroChain.h
|
||||
Goals/ExchangeSwapTownHeroes.h
|
||||
Goals/Goals.h
|
||||
|
@ -51,29 +51,33 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
|
||||
|
||||
goals[0] = {behavior};
|
||||
|
||||
if(tasks.empty())
|
||||
{
|
||||
logAi->debug("Behavior %s found no tasks", behavior->toString());
|
||||
|
||||
return Goals::taskptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
logAi->trace("Evaluating priorities, tasks count %d", tasks.size());
|
||||
|
||||
int depth = 0;
|
||||
while(goals[0].size())
|
||||
{
|
||||
TSubgoal current = goals[depth].back();
|
||||
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Decomposing %s, level: %d", current->toString(), depth);
|
||||
#endif
|
||||
|
||||
TGoalVec subgoals = current->decompose();
|
||||
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Found %d goals", subgoals.size());
|
||||
#endif
|
||||
|
||||
goals[depth + 1].clear();
|
||||
|
||||
for(auto subgoal : subgoals)
|
||||
{
|
||||
if(subgoal->isElementar)
|
||||
if(subgoal->isElementar())
|
||||
{
|
||||
auto task = taskptr(*subgoal);
|
||||
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Found task %s", task->toString());
|
||||
#endif
|
||||
|
||||
if(task->priority <= 0)
|
||||
task->priority = priorityEvaluator->evaluate(subgoal);
|
||||
|
||||
@ -81,6 +85,9 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
|
||||
}
|
||||
else
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Found abstract goal %s", subgoal->toString());
|
||||
#endif
|
||||
goals[depth + 1].push_back(subgoal);
|
||||
}
|
||||
}
|
||||
@ -91,13 +98,23 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
|
||||
}
|
||||
else
|
||||
{
|
||||
goals[depth].pop_back();
|
||||
|
||||
while(depth > 0 && goals[depth].empty())
|
||||
{
|
||||
depth--;
|
||||
goals[depth].pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(tasks.empty())
|
||||
{
|
||||
logAi->debug("Behavior %s found no tasks", behavior->toString());
|
||||
|
||||
return Goals::taskptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
auto task = choseBestTask(tasks);
|
||||
|
||||
logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority);
|
||||
|
@ -36,6 +36,23 @@ class CGTownInstance;
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
EvaluationContext::EvaluationContext()
|
||||
: movementCost(0.0),
|
||||
manaCost(0),
|
||||
danger(0),
|
||||
closestWayRatio(1),
|
||||
movementCostByRole(),
|
||||
skillReward(0),
|
||||
goldReward(0),
|
||||
goldCost(0),
|
||||
armyReward(0),
|
||||
armyLossPersentage(0),
|
||||
heroRole(HeroRole::SCOUT),
|
||||
turn(0),
|
||||
strategicalValue(0)
|
||||
{
|
||||
}
|
||||
|
||||
PriorityEvaluator::~PriorityEvaluator()
|
||||
{
|
||||
delete engine;
|
||||
@ -430,46 +447,59 @@ int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * he
|
||||
class ExecuteHeroChainEvaluationContextBuilder : public IEvaluationContextBuilder
|
||||
{
|
||||
public:
|
||||
virtual Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal task) const override
|
||||
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
|
||||
{
|
||||
if(task->goalType != Goals::EXECUTE_HERO_CHAIN)
|
||||
return;
|
||||
|
||||
Goals::ExecuteHeroChain & chain = dynamic_cast<Goals::ExecuteHeroChain &>(*task);
|
||||
auto evaluationContext = task->evaluationContext;
|
||||
const AIPath & path = chain.getPath();
|
||||
|
||||
vstd::amax(evaluationContext.danger, path.getTotalDanger());
|
||||
evaluationContext.movementCost += path.movementCost();
|
||||
evaluationContext.closestWayRatio = chain.closestWayRatio;
|
||||
|
||||
for(auto & node : path.nodes)
|
||||
{
|
||||
auto role = ai->ah->getHeroRole(node.targetHero);
|
||||
|
||||
evaluationContext.movementCostByRole[role] += node.cost;
|
||||
}
|
||||
|
||||
auto heroPtr = task->hero;
|
||||
const CGObjectInstance * target = cb->getObj((ObjectInstanceID)task->objid, false);
|
||||
auto day = cb->getDate(Date::DAY);
|
||||
auto hero = heroPtr.get();
|
||||
bool checkGold = evaluationContext.danger == 0;
|
||||
auto army = chain.getPath().heroArmy;
|
||||
auto army = path.heroArmy;
|
||||
|
||||
evaluationContext.armyLossPersentage = task->evaluationContext.armyLoss / (double)task->evaluationContext.heroStrength;
|
||||
evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
|
||||
evaluationContext.goldReward = getGoldReward(target, hero);
|
||||
evaluationContext.armyReward = getArmyReward(target, hero, army, checkGold);
|
||||
evaluationContext.skillReward = getSkillReward(target, hero, evaluationContext.heroRole);
|
||||
vstd::amax(evaluationContext.armyLossPersentage, path.getTotalArmyLoss() / (double)path.getHeroStrength());
|
||||
vstd::amax(evaluationContext.heroRole, ai->ah->getHeroRole(heroPtr));
|
||||
evaluationContext.goldReward += getGoldReward(target, hero);
|
||||
evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold);
|
||||
evaluationContext.skillReward += getSkillReward(target, hero, evaluationContext.heroRole);
|
||||
evaluationContext.strategicalValue += getStrategicalValue(target);
|
||||
evaluationContext.goldCost = getGoldCost(target, hero, army);
|
||||
evaluationContext.turn = chain.getPath().turn();
|
||||
|
||||
return evaluationContext;
|
||||
evaluationContext.goldCost += getGoldCost(target, hero, army);
|
||||
vstd::amax(evaluationContext.turn, path.turn());
|
||||
}
|
||||
};
|
||||
|
||||
class BuildThisEvaluationContextBuilder : public IEvaluationContextBuilder
|
||||
{
|
||||
public:
|
||||
virtual Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal task) const override
|
||||
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
|
||||
{
|
||||
Goals::EvaluationContext evaluationContext;
|
||||
if(task->goalType != Goals::BUILD_STRUCTURE)
|
||||
return;
|
||||
|
||||
Goals::BuildThis & buildThis = dynamic_cast<Goals::BuildThis &>(*task);
|
||||
auto & bi = buildThis.buildingInfo;
|
||||
|
||||
evaluationContext.goldReward = 7 * bi.dailyIncome[Res::GOLD] / 2; // 7 day income but half we already have
|
||||
evaluationContext.goldReward += 7 * bi.dailyIncome[Res::GOLD] / 2; // 7 day income but half we already have
|
||||
evaluationContext.heroRole = HeroRole::MAIN;
|
||||
evaluationContext.movementCostByRole[evaluationContext.heroRole] = bi.prerequisitesCount;
|
||||
evaluationContext.armyReward = 0;
|
||||
evaluationContext.strategicalValue = buildThis.townInfo.armyScore / 50000.0;
|
||||
evaluationContext.goldCost = bi.buildCostWithPrerequisits[Res::GOLD];
|
||||
evaluationContext.movementCostByRole[evaluationContext.heroRole] += bi.prerequisitesCount;
|
||||
evaluationContext.strategicalValue += buildThis.townInfo.armyScore / 50000.0;
|
||||
evaluationContext.goldCost += bi.buildCostWithPrerequisits[Res::GOLD];
|
||||
|
||||
if(bi.creatureID != CreatureID::NONE)
|
||||
{
|
||||
@ -477,38 +507,56 @@ public:
|
||||
|
||||
if(bi.baseCreatureID == bi.creatureID)
|
||||
{
|
||||
evaluationContext.armyReward = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
|
||||
evaluationContext.armyReward += ai->ah->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
|
||||
}
|
||||
|
||||
auto creaturesToUpgrade = ai->ah->getTotalCreaturesAvailable(bi.baseCreatureID);
|
||||
auto upgradedPower = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
|
||||
else
|
||||
{
|
||||
auto creaturesToUpgrade = ai->ah->getTotalCreaturesAvailable(bi.baseCreatureID);
|
||||
auto upgradedPower = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
|
||||
|
||||
evaluationContext.armyReward = upgradedPower - creaturesToUpgrade.power;
|
||||
evaluationContext.armyReward += upgradedPower - creaturesToUpgrade.power;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
evaluationContext.strategicalValue = ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
|
||||
evaluationContext.strategicalValue += ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
|
||||
}
|
||||
|
||||
return evaluationContext;
|
||||
}
|
||||
};
|
||||
|
||||
PriorityEvaluator::PriorityEvaluator()
|
||||
{
|
||||
initVisitTile();
|
||||
evaluationContextBuilders[Goals::EXECUTE_HERO_CHAIN] = std::make_shared<ExecuteHeroChainEvaluationContextBuilder>();
|
||||
evaluationContextBuilders[Goals::BUILD_STRUCTURE] = std::make_shared<BuildThisEvaluationContextBuilder>();
|
||||
evaluationContextBuilders.push_back(std::make_shared<ExecuteHeroChainEvaluationContextBuilder>());
|
||||
evaluationContextBuilders.push_back(std::make_shared<BuildThisEvaluationContextBuilder>());
|
||||
}
|
||||
|
||||
Goals::EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
|
||||
EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
|
||||
{
|
||||
auto builder = evaluationContextBuilders.find(goal->goalType);
|
||||
Goals::TGoalVec parts;
|
||||
EvaluationContext context;
|
||||
|
||||
if(builder == evaluationContextBuilders.end())
|
||||
return goal->evaluationContext;
|
||||
if(goal->goalType == Goals::COMPOSITION)
|
||||
{
|
||||
parts = goal->decompose();
|
||||
}
|
||||
else
|
||||
{
|
||||
parts.push_back(goal);
|
||||
}
|
||||
|
||||
return builder->second->buildEvaluationContext(goal);
|
||||
for(auto goal : parts)
|
||||
{
|
||||
context.strategicalValue += goal->strategicalValue;
|
||||
context.goldCost += goal->goldCost;
|
||||
|
||||
for(auto builder : evaluationContextBuilders)
|
||||
{
|
||||
builder->buildEvaluationContext(context, goal);
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/// distance
|
||||
|
@ -11,10 +11,29 @@
|
||||
#include "fl/Headers.h"
|
||||
#include "../Goals/Goals.h"
|
||||
|
||||
struct DLL_EXPORT EvaluationContext
|
||||
{
|
||||
float movementCost;
|
||||
std::map<HeroRole, float> movementCostByRole;
|
||||
int manaCost;
|
||||
uint64_t danger;
|
||||
float closestWayRatio;
|
||||
float armyLossPersentage;
|
||||
float armyReward;
|
||||
int32_t goldReward;
|
||||
int32_t goldCost;
|
||||
float skillReward;
|
||||
float strategicalValue;
|
||||
HeroRole heroRole;
|
||||
uint8_t turn;
|
||||
|
||||
EvaluationContext();
|
||||
};
|
||||
|
||||
class IEvaluationContextBuilder
|
||||
{
|
||||
public:
|
||||
virtual Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const = 0;
|
||||
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal goal) const = 0;
|
||||
};
|
||||
|
||||
class PriorityEvaluator
|
||||
@ -43,7 +62,7 @@ private:
|
||||
fl::InputVariable * goldPreasureVariable;
|
||||
fl::InputVariable * goldCostVariable;
|
||||
fl::OutputVariable * value;
|
||||
std::map<Goals::EGoals, std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
|
||||
std::vector<std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
|
||||
|
||||
Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const;
|
||||
EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const;
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ TTask Goals::taskptr(const AbstractGoal & tmp)
|
||||
{
|
||||
TTask ptr;
|
||||
|
||||
if(!tmp.isElementar)
|
||||
if(!tmp.isElementar())
|
||||
throw cannotFulfillGoalException(tmp.toString() + " is not elementar");
|
||||
|
||||
ptr.reset(dynamic_cast<ITask *>(tmp.clone()));
|
||||
@ -46,30 +46,6 @@ std::string AbstractGoal::toString() const //TODO: virtualize
|
||||
std::string desc;
|
||||
switch(goalType)
|
||||
{
|
||||
case INVALID:
|
||||
return "INVALID";
|
||||
case WIN:
|
||||
return "WIN";
|
||||
case CONQUER:
|
||||
return "CONQUER";
|
||||
case BUILD:
|
||||
return "BUILD";
|
||||
case EXPLORE:
|
||||
desc = "EXPLORE";
|
||||
break;
|
||||
case GATHER_ARMY:
|
||||
desc = "GATHER ARMY";
|
||||
break;
|
||||
case BUY_ARMY:
|
||||
return "BUY ARMY";
|
||||
break;
|
||||
case BOOST_HERO:
|
||||
desc = "BOOST_HERO (unsupported)";
|
||||
break;
|
||||
case RECRUIT_HERO:
|
||||
return "RECRUIT HERO";
|
||||
case BUILD_STRUCTURE:
|
||||
return "BUILD STRUCTURE";
|
||||
case COLLECT_RES:
|
||||
desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
|
||||
break;
|
||||
@ -83,32 +59,9 @@ std::string AbstractGoal::toString() const //TODO: virtualize
|
||||
case GATHER_TROOPS:
|
||||
desc = "GATHER TROOPS";
|
||||
break;
|
||||
case VISIT_OBJ:
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(obj)
|
||||
desc = "VISIT OBJ " + obj->getObjectName();
|
||||
}
|
||||
break;
|
||||
case FIND_OBJ:
|
||||
desc = "FIND OBJ " + boost::lexical_cast<std::string>(objid);
|
||||
break;
|
||||
case VISIT_HERO:
|
||||
{
|
||||
auto obj = cb->getObjInstance(ObjectInstanceID(objid));
|
||||
if(obj)
|
||||
desc = "VISIT HERO " + obj->getObjectName();
|
||||
}
|
||||
break;
|
||||
case GET_ART_TYPE:
|
||||
desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
|
||||
break;
|
||||
case VISIT_TILE:
|
||||
desc = "VISIT TILE " + tile.toString();
|
||||
break;
|
||||
case CLEAR_WAY_TO:
|
||||
desc = "CLEAR WAY TO " + tile.toString();
|
||||
break;
|
||||
case DIG_AT_TILE:
|
||||
desc = "DIG AT TILE " + tile.toString();
|
||||
break;
|
||||
@ -134,23 +87,4 @@ bool TSubgoal::operator==(const TSubgoal & rhs) const
|
||||
bool AbstractGoal::invalid() const
|
||||
{
|
||||
return goalType == EGoals::INVALID;
|
||||
}
|
||||
|
||||
EvaluationContext::EvaluationContext()
|
||||
: movementCost(0.0),
|
||||
manaCost(0),
|
||||
danger(0),
|
||||
closestWayRatio(1),
|
||||
armyLoss(0),
|
||||
heroStrength(0),
|
||||
movementCostByRole(),
|
||||
skillReward(0),
|
||||
goldReward(0),
|
||||
goldCost(0),
|
||||
armyReward(0),
|
||||
armyLossPersentage(0),
|
||||
heroRole(HeroRole::SCOUT),
|
||||
turn(0),
|
||||
strategicalValue(0)
|
||||
{
|
||||
}
|
@ -29,7 +29,6 @@ namespace Goals
|
||||
class CollectRes;
|
||||
class BuyArmy;
|
||||
class BuildBoat;
|
||||
class ClearWayTo;
|
||||
class Invalid;
|
||||
class Trade;
|
||||
class AdventureSpellCast;
|
||||
@ -42,18 +41,17 @@ namespace Goals
|
||||
EXPLORE, GATHER_ARMY,
|
||||
BOOST_HERO,
|
||||
RECRUIT_HERO,
|
||||
RECRUIT_HERO_BEHAVIOR,
|
||||
BUILD_STRUCTURE, //if hero set, then in visited town
|
||||
COLLECT_RES,
|
||||
GATHER_TROOPS, // val of creatures with objid
|
||||
|
||||
VISIT_OBJ, //visit or defeat or collect the object
|
||||
FIND_OBJ, //find and visit any obj with objid + resid //TODO: consider universal subid for various types (aid, bid)
|
||||
VISIT_HERO, //heroes can move around - set goal abstract and track hero every turn
|
||||
CAPTURE_OBJECTS,
|
||||
|
||||
GET_ART_TYPE,
|
||||
|
||||
VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
|
||||
CLEAR_WAY_TO,
|
||||
DEFENCE,
|
||||
STARTUP,
|
||||
DIG_AT_TILE,//elementar with hero on tile
|
||||
BUY_ARMY, //at specific town
|
||||
TRADE, //val resID at object objid
|
||||
@ -62,7 +60,8 @@ namespace Goals
|
||||
ADVENTURE_SPELL_CAST,
|
||||
EXECUTE_HERO_CHAIN,
|
||||
EXCHANGE_SWAP_TOWN_HEROES,
|
||||
DISMISS_HERO
|
||||
DISMISS_HERO,
|
||||
COMPOSITION
|
||||
};
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||
@ -89,34 +88,14 @@ namespace Goals
|
||||
|
||||
DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
|
||||
DLL_EXPORT TTask taskptr(const AbstractGoal & tmp);
|
||||
|
||||
struct DLL_EXPORT EvaluationContext
|
||||
{
|
||||
float movementCost;
|
||||
std::map<HeroRole, float> movementCostByRole;
|
||||
int manaCost;
|
||||
uint64_t danger;
|
||||
float closestWayRatio;
|
||||
uint64_t armyLoss;
|
||||
uint64_t heroStrength;
|
||||
float armyLossPersentage;
|
||||
float armyReward;
|
||||
int32_t goldReward;
|
||||
int32_t goldCost;
|
||||
float skillReward;
|
||||
float strategicalValue;
|
||||
HeroRole heroRole;
|
||||
uint8_t turn;
|
||||
|
||||
EvaluationContext();
|
||||
};
|
||||
|
||||
|
||||
class DLL_EXPORT AbstractGoal
|
||||
{
|
||||
public:
|
||||
bool isElementar; VSETTER(bool, isElementar)
|
||||
bool isAbstract; VSETTER(bool, isAbstract)
|
||||
int value; VSETTER(int, value)
|
||||
float strategicalValue; VSETTER(float, strategicalValue)
|
||||
ui64 goldCost; VSETTER(ui64, goldCost)
|
||||
int resID; VSETTER(int, resID)
|
||||
int objid; VSETTER(int, objid)
|
||||
int aid; VSETTER(int, aid)
|
||||
@ -125,12 +104,11 @@ namespace Goals
|
||||
const CGTownInstance *town; VSETTER(CGTownInstance *, town)
|
||||
int bid; VSETTER(int, bid)
|
||||
TSubgoal parent; VSETTER(TSubgoal, parent)
|
||||
EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
|
||||
//EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
|
||||
|
||||
AbstractGoal(EGoals goal = EGoals::INVALID)
|
||||
: goalType(goal), evaluationContext()
|
||||
: goalType(goal), hero()
|
||||
{
|
||||
isElementar = false;
|
||||
isAbstract = false;
|
||||
value = 0;
|
||||
aid = -1;
|
||||
@ -139,6 +117,8 @@ namespace Goals
|
||||
tile = int3(-1, -1, -1);
|
||||
town = nullptr;
|
||||
bid = -1;
|
||||
strategicalValue = 0;
|
||||
goldCost = 0;
|
||||
}
|
||||
virtual ~AbstractGoal() {}
|
||||
//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
|
||||
@ -159,6 +139,8 @@ namespace Goals
|
||||
bool invalid() const;
|
||||
|
||||
virtual bool operator==(const AbstractGoal & g) const;
|
||||
|
||||
virtual bool isElementar() const { return false; }
|
||||
|
||||
bool operator!=(const AbstractGoal & g) const
|
||||
{
|
||||
@ -167,6 +149,9 @@ namespace Goals
|
||||
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
float priority;
|
||||
bool isElementar;
|
||||
|
||||
h & goalType;
|
||||
h & isElementar;
|
||||
h & isAbstract;
|
||||
@ -187,9 +172,12 @@ namespace Goals
|
||||
public:
|
||||
float priority;
|
||||
|
||||
ITask() : priority(0) {}
|
||||
|
||||
///Visitor pattern
|
||||
//TODO: make accept work for std::shared_ptr... somehow
|
||||
virtual void accept(VCAI * ai) = 0; //unhandled goal will report standard error
|
||||
virtual std::string toString() const = 0;
|
||||
virtual ~ITask() {}
|
||||
};
|
||||
}
|
||||
|
@ -26,24 +26,24 @@ bool BuildBoat::operator==(const BuildBoat & other) const
|
||||
{
|
||||
return shipyard->o->id == other.shipyard->o->id;
|
||||
}
|
||||
|
||||
TSubgoal BuildBoat::decomposeSingle() const
|
||||
{
|
||||
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
return sptr(CaptureObjectsBehavior(shipyard->o));
|
||||
}
|
||||
|
||||
if(shipyard->shipyardStatus() != IShipyard::GOOD)
|
||||
{
|
||||
throw cannotFulfillGoalException("Shipyard is busy.");
|
||||
}
|
||||
|
||||
TResources boatCost;
|
||||
shipyard->getBoatCost(boatCost);
|
||||
|
||||
return iAmElementar();
|
||||
}
|
||||
//
|
||||
//TSubgoal BuildBoat::decomposeSingle() const
|
||||
//{
|
||||
// if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||
// {
|
||||
// return sptr(CaptureObjectsBehavior(shipyard->o));
|
||||
// }
|
||||
//
|
||||
// if(shipyard->shipyardStatus() != IShipyard::GOOD)
|
||||
// {
|
||||
// throw cannotFulfillGoalException("Shipyard is busy.");
|
||||
// }
|
||||
//
|
||||
// TResources boatCost;
|
||||
// shipyard->getBoatCost(boatCost);
|
||||
//
|
||||
// return iAmElementar();
|
||||
//}
|
||||
|
||||
void BuildBoat::accept(VCAI * ai)
|
||||
{
|
||||
|
@ -17,7 +17,6 @@ namespace Goals
|
||||
{
|
||||
private:
|
||||
const IShipyard * shipyard;
|
||||
TSubgoal decomposeSingle() const override;
|
||||
|
||||
public:
|
||||
BuildBoat(const IShipyard * shipyard)
|
||||
|
@ -32,4 +32,23 @@ bool BuildThis::operator==(const BuildThis & other) const
|
||||
std::string BuildThis::toString() const
|
||||
{
|
||||
return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
|
||||
}
|
||||
|
||||
void BuildThis::accept(VCAI * ai)
|
||||
{
|
||||
auto b = BuildingID(bid);
|
||||
|
||||
if(town)
|
||||
{
|
||||
if(cb->canBuildStructure(town, b) == EBuildingState::ALLOWED)
|
||||
{
|
||||
logAi->debug("Player %d will build %s in town of %s at %s",
|
||||
ai->playerID, town->town->buildings.at(b)->Name(), town->name, town->pos.toString());
|
||||
cb->buildBuilding(town, b);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw cannotFulfillGoalException("Cannot build a given structure!");
|
||||
}
|
@ -49,5 +49,6 @@ namespace Goals
|
||||
}
|
||||
virtual bool operator==(const BuildThis & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
void accept(VCAI * ai) override;
|
||||
};
|
||||
}
|
||||
|
@ -28,4 +28,49 @@ bool BuyArmy::operator==(const BuyArmy & other) const
|
||||
std::string BuyArmy::toString() const
|
||||
{
|
||||
return "Buy army at " + town->name;
|
||||
}
|
||||
|
||||
void BuyArmy::accept(VCAI * ai)
|
||||
{
|
||||
ui64 valueBought = 0;
|
||||
//buy the stacks with largest AI value
|
||||
|
||||
auto upgradeSuccessfull = ai->makePossibleUpgrades(town);
|
||||
|
||||
auto armyToBuy = ai->ah->getArmyAvailableToBuy(town->getUpperArmy(), town);
|
||||
|
||||
if(armyToBuy.empty())
|
||||
{
|
||||
if(upgradeSuccessfull)
|
||||
return;
|
||||
|
||||
throw cannotFulfillGoalException("No creatures to buy.");
|
||||
}
|
||||
|
||||
for(int i = 0; valueBought < value && i < armyToBuy.size(); i++)
|
||||
{
|
||||
auto res = cb->getResourceAmount();
|
||||
auto & ci = armyToBuy[i];
|
||||
|
||||
if(objid != -1 && ci.creID != objid)
|
||||
continue;
|
||||
|
||||
vstd::amin(ci.count, res / ci.cre->cost);
|
||||
|
||||
if(ci.count)
|
||||
{
|
||||
cb->recruitCreatures(town, town->getUpperArmy(), ci.creID, ci.count, ci.level);
|
||||
valueBought += ci.count * ci.cre->AIValue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!valueBought)
|
||||
{
|
||||
throw cannotFulfillGoalException("No creatures to buy.");
|
||||
}
|
||||
|
||||
if(town->visitingHero)
|
||||
{
|
||||
ai->moveHeroToTile(town->visitablePos(), town->visitingHero.get());
|
||||
}
|
||||
}
|
@ -36,5 +36,7 @@ namespace Goals
|
||||
virtual bool operator==(const BuyArmy & other) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual void accept(VCAI * ai) override;
|
||||
};
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ namespace Goals
|
||||
public:
|
||||
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
{
|
||||
isElementar = false;
|
||||
isAbstract = true;
|
||||
value = 0;
|
||||
aid = -1;
|
||||
@ -33,7 +32,6 @@ namespace Goals
|
||||
town = nullptr;
|
||||
}
|
||||
|
||||
OSETTER(bool, isElementar)
|
||||
OSETTER(bool, isAbstract)
|
||||
OSETTER(int, value)
|
||||
OSETTER(int, resID)
|
||||
@ -48,15 +46,6 @@ namespace Goals
|
||||
{
|
||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
TSubgoal iAmElementar() const
|
||||
{
|
||||
TSubgoal ptr;
|
||||
|
||||
ptr.reset(clone());
|
||||
ptr->setisElementar(true);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
template<typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & static_cast<AbstractGoal &>(*this);
|
||||
@ -76,7 +65,12 @@ namespace Goals
|
||||
|
||||
virtual TGoalVec decompose() const override
|
||||
{
|
||||
return {decomposeSingle()};
|
||||
TSubgoal single = decomposeSingle();
|
||||
|
||||
if(single->invalid())
|
||||
return {};
|
||||
|
||||
return {single};
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -89,18 +83,20 @@ namespace Goals
|
||||
template<typename T> class DLL_EXPORT ElementarGoal : public CGoal<T>, public ITask
|
||||
{
|
||||
public:
|
||||
ElementarGoal<T>(EGoals goal = INVALID) : CGoal(goal)
|
||||
ElementarGoal<T>(EGoals goal = INVALID) : CGoal(goal), ITask()
|
||||
{
|
||||
priority = 0;
|
||||
isElementar = true;
|
||||
isAbstract = false;
|
||||
}
|
||||
|
||||
ElementarGoal<T>(const ElementarGoal<T> & other) : CGoal(other), ITask(other)
|
||||
{
|
||||
}
|
||||
|
||||
///Visitor pattern
|
||||
//TODO: make accept work for std::shared_ptr... somehow
|
||||
virtual void accept(VCAI * ai) override //unhandled goal will report standard error
|
||||
{
|
||||
ai->tryRealize(*this);
|
||||
ai->tryRealize(*((T *)this));
|
||||
}
|
||||
|
||||
T & setpriority(float p)
|
||||
@ -109,6 +105,11 @@ namespace Goals
|
||||
|
||||
return *((T *)this);
|
||||
}
|
||||
|
||||
virtual bool isElementar() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT Invalid : public ElementarGoal<Invalid>
|
||||
|
114
AI/Nullkiller/Goals/Composition.cpp
Normal file
114
AI/Nullkiller/Goals/Composition.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* BuildThis.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 "Composition.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/StringConstants.h"
|
||||
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool Composition::operator==(const Composition & other) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Composition::toString() const
|
||||
{
|
||||
std::string result = "Composition";
|
||||
|
||||
for(auto goal : subtasks)
|
||||
{
|
||||
result += " " + goal->toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Composition::accept(VCAI * ai)
|
||||
{
|
||||
taskptr(*subtasks.back())->accept(ai);
|
||||
}
|
||||
|
||||
TGoalVec Composition::decompose() const
|
||||
{
|
||||
if(isElementar())
|
||||
return subtasks;
|
||||
|
||||
auto tasks = subtasks;
|
||||
tasks.pop_back();
|
||||
|
||||
TSubgoal last = subtasks.back();
|
||||
auto decomposed = last->decompose();
|
||||
TGoalVec result;
|
||||
|
||||
for(TSubgoal goal : decomposed)
|
||||
{
|
||||
if(goal->invalid() || goal == last || vstd::contains(tasks, goal))
|
||||
continue;
|
||||
|
||||
auto newComposition = Composition(tasks);
|
||||
|
||||
if(goal->goalType == COMPOSITION)
|
||||
{
|
||||
Composition & other = dynamic_cast<Composition &>(*goal);
|
||||
bool cancel = false;
|
||||
|
||||
for(auto goal : other.subtasks)
|
||||
{
|
||||
if(goal == last || vstd::contains(tasks, goal))
|
||||
{
|
||||
cancel = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
newComposition.addNext(goal);
|
||||
}
|
||||
|
||||
if(cancel)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
newComposition.addNext(goal);
|
||||
}
|
||||
|
||||
result.push_back(sptr(newComposition));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Composition & Composition::addNext(AbstractGoal & goal)
|
||||
{
|
||||
return addNext(sptr(goal));
|
||||
}
|
||||
|
||||
Composition & Composition::addNext(TSubgoal goal)
|
||||
{
|
||||
subtasks.push_back(goal);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Composition::isElementar() const
|
||||
{
|
||||
return subtasks.back()->isElementar();
|
||||
}
|
40
AI/Nullkiller/Goals/Composition.h
Normal file
40
AI/Nullkiller/Goals/Composition.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* BuildThis.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 "CGoal.h"
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT Composition : public ElementarGoal<Composition>
|
||||
{
|
||||
private:
|
||||
TGoalVec subtasks;
|
||||
|
||||
public:
|
||||
Composition()
|
||||
: ElementarGoal(Goals::COMPOSITION), subtasks()
|
||||
{
|
||||
}
|
||||
|
||||
Composition(TGoalVec subtasks)
|
||||
: ElementarGoal(Goals::COMPOSITION), subtasks(subtasks)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator==(const Composition & other) const override;
|
||||
virtual std::string toString() const override;
|
||||
void accept(VCAI * ai) override;
|
||||
Composition & addNext(AbstractGoal & goal);
|
||||
Composition & addNext(TSubgoal goal);
|
||||
virtual TGoalVec decompose() const override;
|
||||
virtual bool isElementar() const override;
|
||||
};
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "DigAtTile.h"
|
||||
#include "VisitTile.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "ExecuteHeroChain.h"
|
||||
#include "VisitTile.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
@ -26,7 +25,6 @@ using namespace Goals;
|
||||
ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)
|
||||
:ElementarGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
|
||||
{
|
||||
|
||||
hero = path.targetHero;
|
||||
tile = path.targetTile();
|
||||
|
||||
@ -43,7 +41,10 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance *
|
||||
|
||||
bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const
|
||||
{
|
||||
return false;
|
||||
return tile == other.tile
|
||||
&& chainPath.targetHero == other.chainPath.targetHero
|
||||
&& chainPath.nodes.size() == other.chainPath.nodes.size()
|
||||
&& chainPath.chainMask == other.chainPath.chainMask;
|
||||
}
|
||||
|
||||
void ExecuteHeroChain::accept(VCAI * ai)
|
||||
@ -79,19 +80,13 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
|
||||
if(node.specialAction)
|
||||
{
|
||||
if(node.specialAction->canAct(hero))
|
||||
{
|
||||
auto specialGoal = node.specialAction->whatToDo(hero);
|
||||
|
||||
if(!specialGoal->isElementar)
|
||||
|
||||
specialGoal->accept(ai);
|
||||
}
|
||||
else
|
||||
if(node.actionIsBlocked)
|
||||
{
|
||||
throw cannotFulfillGoalException("Path is nondeterministic.");
|
||||
}
|
||||
|
||||
node.specialAction->execute(hero);
|
||||
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name);
|
||||
@ -192,9 +187,9 @@ std::string ExecuteHeroChain::toString() const
|
||||
|
||||
bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile)
|
||||
{
|
||||
if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
|
||||
if(tile == hero->visitablePos() && cb->getVisitableObjs(hero->visitablePos()).size() < 2)
|
||||
{
|
||||
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
|
||||
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", hero->name, tile.toString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ namespace Goals
|
||||
std::string targetName;
|
||||
|
||||
public:
|
||||
float closestWayRatio;
|
||||
|
||||
ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "FindObj.h"
|
||||
#include "VisitObj.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
|
@ -11,32 +11,4 @@
|
||||
|
||||
#include "CGoal.h"
|
||||
|
||||
struct HeroPtr;
|
||||
class VCAI;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT FindObj : public CGoal<FindObj>
|
||||
{
|
||||
public:
|
||||
FindObj() {} // empty constructor not allowed
|
||||
|
||||
FindObj(int ID)
|
||||
: CGoal(Goals::FIND_OBJ)
|
||||
{
|
||||
objid = ID;
|
||||
resID = -1; //subid unspecified
|
||||
}
|
||||
FindObj(int ID, int subID)
|
||||
: CGoal(Goals::FIND_OBJ)
|
||||
{
|
||||
objid = ID;
|
||||
resID = subID;
|
||||
}
|
||||
virtual bool operator==(const FindObj & other) const override;
|
||||
|
||||
private:
|
||||
//TSubgoal decomposeSingle() const override;
|
||||
};
|
||||
}
|
||||
#error not supported
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "GetArtOfType.h"
|
||||
#include "FindObj.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
|
||||
|
@ -20,5 +20,4 @@
|
||||
#include "RecruitHero.h"
|
||||
#include "GetArtOfType.h"
|
||||
#include "DigAtTile.h"
|
||||
#include "FindObj.h"
|
||||
#include "AdventureSpellCast.h"
|
@ -27,4 +27,22 @@ using namespace Goals;
|
||||
std::string RecruitHero::toString() const
|
||||
{
|
||||
return "Recruit hero at " + town->name;
|
||||
}
|
||||
|
||||
void RecruitHero::accept(VCAI * ai)
|
||||
{
|
||||
auto t = town;
|
||||
|
||||
if(!t) t = ai->findTownWithTavern();
|
||||
|
||||
if(t)
|
||||
{
|
||||
ai->recruitHero(t, true);
|
||||
//TODO try to free way to blocked town
|
||||
//TODO: adventure map tavern or prison?
|
||||
}
|
||||
else
|
||||
{
|
||||
throw cannotFulfillGoalException("No town to recruit hero!");
|
||||
}
|
||||
}
|
@ -39,5 +39,6 @@ namespace Goals
|
||||
}
|
||||
|
||||
virtual std::string toString() const override;
|
||||
void accept(VCAI * ai) override;
|
||||
};
|
||||
}
|
||||
|
@ -1033,13 +1033,16 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa
|
||||
pathNode.coord = node->coord;
|
||||
pathNode.parentIndex = parentIndex;
|
||||
|
||||
if(pathNode.specialAction)
|
||||
{
|
||||
pathNode.actionIsBlocked = !pathNode.specialAction->canAct(node);
|
||||
}
|
||||
|
||||
parentIndex = path.nodes.size();
|
||||
|
||||
path.nodes.push_back(pathNode);
|
||||
}
|
||||
|
||||
path.specialAction = node->specialAction;
|
||||
|
||||
|
||||
node = getAINode(node->theNodeBefore);
|
||||
}
|
||||
}
|
||||
@ -1049,15 +1052,15 @@ AIPath::AIPath()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const ISpecialAction> AIPath::getFirstBlockedAction() const
|
||||
std::shared_ptr<const SpecialAction> AIPath::getFirstBlockedAction() const
|
||||
{
|
||||
for(auto node : nodes)
|
||||
{
|
||||
if(node.specialAction && !node.specialAction->canAct(node.targetHero))
|
||||
if(node.specialAction && node.actionIsBlocked)
|
||||
return node.specialAction;
|
||||
}
|
||||
|
||||
return std::shared_ptr<const ISpecialAction>();
|
||||
return std::shared_ptr<const SpecialAction>();
|
||||
}
|
||||
|
||||
int3 AIPath::firstTileToGet() const
|
||||
|
@ -10,15 +10,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define VCMI_TRACE_PATHFINDER 1
|
||||
#define AI_TRACE_LEVEL 1
|
||||
#define VCMI_TRACE_PATHFINDER 2
|
||||
#define AI_TRACE_LEVEL 2
|
||||
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
#include "Actions/ISpecialAction.h"
|
||||
#include "Actions/SpecialAction.h"
|
||||
#include "Actors.h"
|
||||
|
||||
struct AIPathNode : public CGPathNode
|
||||
@ -27,7 +27,7 @@ struct AIPathNode : public CGPathNode
|
||||
uint64_t armyLoss;
|
||||
uint32_t manaCost;
|
||||
const AIPathNode * chainOther;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
std::shared_ptr<const SpecialAction> specialAction;
|
||||
const ChainActor * actor;
|
||||
};
|
||||
|
||||
@ -40,13 +40,13 @@ struct AIPathNodeInfo
|
||||
const CGHeroInstance * targetHero;
|
||||
int parentIndex;
|
||||
uint64_t chainMask;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
std::shared_ptr<const SpecialAction> specialAction;
|
||||
bool actionIsBlocked;
|
||||
};
|
||||
|
||||
struct AIPath
|
||||
{
|
||||
std::vector<AIPathNodeInfo> nodes;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
uint64_t targetObjectDanger;
|
||||
uint64_t armyLoss;
|
||||
uint64_t targetObjectArmyLoss;
|
||||
@ -81,7 +81,7 @@ struct AIPath
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::shared_ptr<const ISpecialAction> getFirstBlockedAction() const;
|
||||
std::shared_ptr<const SpecialAction> getFirstBlockedAction() const;
|
||||
|
||||
bool containsHero(const CGHeroInstance * hero) const;
|
||||
};
|
||||
|
@ -9,13 +9,44 @@
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../../Goals/VisitTile.h"
|
||||
#include "BattleAction.h"
|
||||
#include "../../VCAI.h"
|
||||
#include "../../Behaviors/CompleteQuestBehavior.h"
|
||||
#include "../../../../lib/mapping/CMap.h" //for victory conditions
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
Goals::TSubgoal BattleAction::whatToDo(const CGHeroInstance * hero) const
|
||||
void BattleAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
|
||||
ai->moveHeroToTile(targetTile, hero);
|
||||
}
|
||||
|
||||
std::string BattleAction::toString() const
|
||||
{
|
||||
return "Battle at " + targetTile.toString();
|
||||
}
|
||||
|
||||
bool QuestAction::canAct(const AIPathNode * node) const
|
||||
{
|
||||
QuestInfo q = questInfo;
|
||||
return q.quest->checkQuest(node->actor->hero);
|
||||
}
|
||||
|
||||
Goals::TSubgoal QuestAction::decompose(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
void QuestAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
ai->moveHeroToTile(questInfo.obj->visitablePos(), hero);
|
||||
}
|
||||
|
||||
std::string QuestAction::toString() const
|
||||
{
|
||||
return "Complete Quest";
|
||||
}
|
||||
}
|
@ -10,11 +10,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISpecialAction.h"
|
||||
#include "SpecialAction.h"
|
||||
#include "../../../../lib/CGameState.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class BattleAction : public ISpecialAction
|
||||
class BattleAction : public SpecialAction
|
||||
{
|
||||
private:
|
||||
const int3 targetTile;
|
||||
@ -25,6 +26,28 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
|
||||
class QuestAction : public SpecialAction
|
||||
{
|
||||
private:
|
||||
QuestInfo questInfo;
|
||||
|
||||
public:
|
||||
QuestAction(QuestInfo questInfo)
|
||||
:questInfo(questInfo)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool canAct(const AIPathNode * node) const override;
|
||||
|
||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
}
|
@ -10,16 +10,30 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../../Goals/AdventureSpellCast.h"
|
||||
#include "../../Behaviors/CaptureObjectsBehavior.h"
|
||||
#include "../../Goals/BuildBoat.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "BoatActions.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
Goals::TSubgoal BuildBoatAction::whatToDo(const CGHeroInstance * hero) const
|
||||
void BuildBoatAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::BuildBoat(shipyard));
|
||||
return Goals::BuildBoat(shipyard).accept(ai.get());
|
||||
}
|
||||
|
||||
Goals::TSubgoal BuildBoatAction::decompose(const CGHeroInstance * hero) const
|
||||
{
|
||||
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
return Goals::sptr(Goals::CaptureObjectsBehavior(shipyard->o));
|
||||
}
|
||||
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const
|
||||
@ -27,9 +41,9 @@ namespace AIPathfinding
|
||||
return sourceActor->resourceActor;
|
||||
}
|
||||
|
||||
Goals::TSubgoal SummonBoatAction::whatToDo(const CGHeroInstance * hero) const
|
||||
void SummonBoatAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
|
||||
Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai.get());
|
||||
}
|
||||
|
||||
const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const
|
||||
@ -48,8 +62,43 @@ namespace AIPathfinding
|
||||
dstMode->theNodeBefore = source.node;
|
||||
}
|
||||
|
||||
bool SummonBoatAction::isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const
|
||||
bool BuildBoatAction::canAct(const AIPathNode * source) const
|
||||
{
|
||||
auto hero = source->actor->hero;
|
||||
|
||||
if(cb->getPlayerRelations(hero->tempOwner, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
#if AI_TRACE_LEVEL > 1
|
||||
logAi->trace("Can not build a boat. Shipyard is enemy.");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
TResources boatCost;
|
||||
|
||||
shipyard->getBoatCost(boatCost);
|
||||
|
||||
if(!cb->getResourceAmount().canAfford(source->actor->armyCost + boatCost))
|
||||
{
|
||||
#if AI_TRACE_LEVEL > 1
|
||||
logAi->trace("Can not build a boat. Not enough resources.");
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string BuildBoatAction::toString() const
|
||||
{
|
||||
return "Build Boat at " + shipyard->o->getObjectName();
|
||||
}
|
||||
|
||||
bool SummonBoatAction::canAct(const AIPathNode * source) const
|
||||
{
|
||||
auto hero = source->actor->hero;
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Hero %s has %d mana and needed %d and already spent %d",
|
||||
@ -62,6 +111,11 @@ namespace AIPathfinding
|
||||
return hero->mana >= source->manaCost + getManaCost(hero);
|
||||
}
|
||||
|
||||
std::string SummonBoatAction::toString() const
|
||||
{
|
||||
return "Summon Boat";
|
||||
}
|
||||
|
||||
uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
|
||||
{
|
||||
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
||||
|
@ -10,13 +10,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISpecialAction.h"
|
||||
#include "SpecialAction.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class VirtualBoatAction : public ISpecialAction
|
||||
class VirtualBoatAction : public SpecialAction
|
||||
{
|
||||
public:
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const = 0;
|
||||
@ -24,8 +24,11 @@ namespace AIPathfinding
|
||||
|
||||
class SummonBoatAction : public VirtualBoatAction
|
||||
{
|
||||
private:
|
||||
const CGHeroInstance * hero;
|
||||
|
||||
public:
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
@ -34,10 +37,12 @@ namespace AIPathfinding
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const override;
|
||||
|
||||
bool isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const;
|
||||
virtual bool canAct(const AIPathNode * source) const;
|
||||
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
|
||||
private:
|
||||
uint32_t getManaCost(const CGHeroInstance * hero) const;
|
||||
};
|
||||
@ -53,8 +58,14 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
virtual bool canAct(const AIPathNode * source) const;
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
}
|
25
AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp
Normal file
25
AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SpecialAction.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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SpecialAction.h"
|
||||
#include "../../VCAI.h"
|
||||
#include "../../Goals/CGoal.h"
|
||||
|
||||
Goals::TSubgoal SpecialAction::decompose(const CGHeroInstance * hero) const
|
||||
{
|
||||
return Goals::sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
void SpecialAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
throw cannotFulfillGoalException("Can not execute " + toString());
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* ISpecialAction.h, part of VCMI engine
|
||||
* SpecialAction.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
@ -15,15 +15,17 @@
|
||||
|
||||
struct AIPathNode;
|
||||
|
||||
class ISpecialAction
|
||||
class SpecialAction
|
||||
{
|
||||
public:
|
||||
virtual bool canAct(const CGHeroInstance * hero) const
|
||||
virtual bool canAct(const AIPathNode * source) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const = 0;
|
||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const;
|
||||
|
||||
virtual void execute(const CGHeroInstance * hero) const;
|
||||
|
||||
virtual void applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
@ -33,4 +35,6 @@ public:
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
};
|
@ -16,9 +16,41 @@
|
||||
|
||||
using namespace AIPathfinding;
|
||||
|
||||
Goals::TSubgoal TownPortalAction::whatToDo(const CGHeroInstance * hero) const
|
||||
{
|
||||
const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
|
||||
}
|
||||
void TownPortalAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
auto goal = Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL);
|
||||
|
||||
goal.town = target;
|
||||
goal.tile = target->visitablePos();
|
||||
|
||||
goal.accept(ai.get());
|
||||
}
|
||||
|
||||
std::string TownPortalAction::toString() const
|
||||
{
|
||||
return "Town Portal to " + target->name;
|
||||
}
|
||||
/*
|
||||
bool TownPortalAction::canAct(const CGHeroInstance * hero, const AIPathNode * source) const
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Hero %s has %d mana and needed %d and already spent %d",
|
||||
hero->name,
|
||||
hero->mana,
|
||||
getManaCost(hero),
|
||||
source->manaCost);
|
||||
#endif
|
||||
|
||||
return hero->mana >= source->manaCost + getManaCost(hero);
|
||||
}
|
||||
|
||||
uint32_t TownPortalAction::getManaCost(const CGHeroInstance * hero) const
|
||||
{
|
||||
SpellID summonBoat = SpellID::TOWN_PORTAL;
|
||||
|
||||
return hero->getSpellCost(summonBoat.toSpell());
|
||||
}*/
|
@ -10,14 +10,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISpecialAction.h"
|
||||
#include "SpecialAction.h"
|
||||
#include "../../../../lib/mapping/CMap.h"
|
||||
#include "../../../../lib/mapObjects/MapObjects.h"
|
||||
#include "../../Goals/AdventureSpellCast.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class TownPortalAction : public ISpecialAction
|
||||
class TownPortalAction : public SpecialAction
|
||||
{
|
||||
private:
|
||||
const CGTownInstance * target;
|
||||
@ -28,6 +28,8 @@ namespace AIPathfinding
|
||||
{
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
|
||||
virtual void execute(const CGHeroInstance * hero) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
};
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "Actors.h"
|
||||
#include "../Goals/VisitHero.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../../../CCallback.h"
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "Actions/ISpecialAction.h"
|
||||
#include "Actions/SpecialAction.h"
|
||||
|
||||
class HeroActor;
|
||||
class VCAI;
|
||||
@ -102,7 +102,7 @@ private:
|
||||
void setupSpecialActors();
|
||||
|
||||
public:
|
||||
std::shared_ptr<ISpecialAction> exchangeAction;
|
||||
std::shared_ptr<SpecialAction> exchangeAction;
|
||||
// chain flags, can be combined meaning hero exchange and so on
|
||||
|
||||
HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai);
|
||||
|
@ -101,7 +101,7 @@ namespace AIPathfinding
|
||||
const CGHeroInstance * hero = nodeStorage->getHero(source.node);
|
||||
|
||||
if(vstd::contains(summonableVirtualBoats, hero)
|
||||
&& summonableVirtualBoats.at(hero)->isAffordableBy(hero, nodeStorage->getAINode(source.node)))
|
||||
&& summonableVirtualBoats.at(hero)->canAct(nodeStorage->getAINode(source.node)))
|
||||
{
|
||||
virtualBoat = summonableVirtualBoats.at(hero);
|
||||
}
|
||||
|
@ -14,24 +14,6 @@
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
class QuestAction : public ISpecialAction
|
||||
{
|
||||
public:
|
||||
QuestAction(QuestInfo questInfo)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool canAct(const CGHeroInstance * hero) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override
|
||||
{
|
||||
return Goals::sptr(Goals::Invalid());
|
||||
}
|
||||
};
|
||||
|
||||
AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
|
||||
CPlayerSpecificInfoCallback * cb,
|
||||
std::shared_ptr<AINodeStorage> nodeStorage)
|
||||
@ -218,9 +200,9 @@ namespace AIPathfinding
|
||||
}
|
||||
|
||||
auto hero = nodeStorage->getHero(source.node);
|
||||
auto danger = nodeStorage->evaluateDanger(destination.coord, hero, true);
|
||||
double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
|
||||
double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
|
||||
uint64_t danger = nodeStorage->evaluateDanger(destination.coord, hero, true);
|
||||
uint64_t actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
|
||||
uint64_t loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
|
||||
|
||||
if(loss < actualArmyValue)
|
||||
{
|
||||
|
@ -36,8 +36,6 @@ const float SAFE_ATTACK_CONSTANT = 1.5;
|
||||
boost::thread_specific_ptr<CCallback> cb;
|
||||
boost::thread_specific_ptr<VCAI> ai;
|
||||
|
||||
//std::map<int, std::map<int, int> > HeroView::infosCount;
|
||||
|
||||
//helper RAII to manage global ai/cb ptrs
|
||||
struct SetGlobalState
|
||||
{
|
||||
@ -365,16 +363,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
|
||||
vstd::erase_if_present(visitableObjs, obj);
|
||||
vstd::erase_if_present(alreadyVisited, obj);
|
||||
|
||||
std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool
|
||||
{
|
||||
if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum()))
|
||||
return true;
|
||||
else if(x->parent && checkRemovalValidity(x->parent)) //repeat this lambda check recursively on parent goal
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
};
|
||||
|
||||
//TODO: Find better way to handle hero boat removal
|
||||
if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
|
||||
{
|
||||
@ -1544,42 +1532,6 @@ void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
|
||||
cb->buildBuilding(t, building); //just do this;
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::RecruitHero & g)
|
||||
{
|
||||
const CGTownInstance * t = g.town;
|
||||
|
||||
if(!t) t = findTownWithTavern();
|
||||
|
||||
if(t)
|
||||
{
|
||||
recruitHero(t, true);
|
||||
//TODO try to free way to blocked town
|
||||
//TODO: adventure map tavern or prison?
|
||||
}
|
||||
else
|
||||
{
|
||||
throw cannotFulfillGoalException("No town to recruit hero!");
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::BuildThis & g)
|
||||
{
|
||||
auto b = BuildingID(g.bid);
|
||||
auto t = g.town;
|
||||
|
||||
if (t)
|
||||
{
|
||||
if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
|
||||
{
|
||||
logAi->debug("Player %d will build %s in town of %s at %s",
|
||||
playerID, t->town->buildings.at(b)->Name(), t->name, t->pos.toString());
|
||||
cb->buildBuilding(t, b);
|
||||
throw goalFulfilledException(sptr(g));
|
||||
}
|
||||
}
|
||||
throw cannotFulfillGoalException("Cannot build a given structure!");
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::DigAtTile & g)
|
||||
{
|
||||
assert(g.hero->visitablePos() == g.tile); //surely we want to crash here?
|
||||
@ -1638,55 +1590,6 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
||||
}
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::BuyArmy & g)
|
||||
{
|
||||
auto t = g.town;
|
||||
|
||||
ui64 valueBought = 0;
|
||||
//buy the stacks with largest AI value
|
||||
|
||||
auto upgradeSuccessfull = makePossibleUpgrades(t);
|
||||
|
||||
auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
|
||||
|
||||
if(armyToBuy.empty())
|
||||
{
|
||||
if(upgradeSuccessfull)
|
||||
throw goalFulfilledException(sptr(g));
|
||||
|
||||
throw cannotFulfillGoalException("No creatures to buy.");
|
||||
}
|
||||
|
||||
for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
|
||||
{
|
||||
auto res = cb->getResourceAmount();
|
||||
auto & ci = armyToBuy[i];
|
||||
|
||||
if(g.objid != -1 && ci.creID != g.objid)
|
||||
continue;
|
||||
|
||||
vstd::amin(ci.count, res / ci.cre->cost);
|
||||
|
||||
if(ci.count)
|
||||
{
|
||||
cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
|
||||
valueBought += ci.count * ci.cre->AIValue;
|
||||
}
|
||||
}
|
||||
|
||||
if(!valueBought)
|
||||
{
|
||||
throw cannotFulfillGoalException("No creatures to buy.");
|
||||
}
|
||||
|
||||
if(t->visitingHero)
|
||||
{
|
||||
moveHeroToTile(t->visitablePos(), t->visitingHero.get());
|
||||
}
|
||||
|
||||
throw goalFulfilledException(sptr(g)); //we bought as many creatures as we wanted
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::Invalid & g)
|
||||
{
|
||||
throw cannotFulfillGoalException("I don't know how to fulfill this!");
|
||||
|
@ -117,11 +117,8 @@ public:
|
||||
virtual ~VCAI();
|
||||
|
||||
//TODO: use only smart pointers?
|
||||
void tryRealize(Goals::RecruitHero & g);
|
||||
void tryRealize(Goals::BuildThis & g);
|
||||
void tryRealize(Goals::DigAtTile & g);
|
||||
void tryRealize(Goals::Trade & g);
|
||||
void tryRealize(Goals::BuyArmy & g);
|
||||
void tryRealize(Goals::Invalid & g);
|
||||
void tryRealize(Goals::AbstractGoal & g);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user