mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
AI: add ExecuteChain goal
This commit is contained in:
parent
37434dc4cf
commit
ffa626dc2f
@ -149,6 +149,11 @@ std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 &
|
||||
return pathfindingManager->getPathsToTile(hero, tile);
|
||||
}
|
||||
|
||||
std::vector<AIPath> AIhelper::getPathsToTile(const int3 & tile) const
|
||||
{
|
||||
return pathfindingManager->getPathsToTile(tile);
|
||||
}
|
||||
|
||||
void AIhelper::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||
{
|
||||
pathfindingManager->updatePaths(heroes, useHeroChain);
|
||||
|
@ -62,6 +62,7 @@ public:
|
||||
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 int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||
|
||||
STRONG_INLINE
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "CaptureObjectsBehavior.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "lib/mapping/CMap.h" //for victory conditions
|
||||
@ -38,32 +39,51 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() {
|
||||
continue;
|
||||
|
||||
const int3 pos = objToVisit->visitablePos();
|
||||
Goals::TGoalVec waysToVisitObj = ai->ah->howToVisitObj(objToVisit, false);
|
||||
auto paths = ai->ah->getPathsToTile(pos);
|
||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
||||
|
||||
vstd::erase_if(waysToVisitObj, [objToVisit](Goals::TSubgoal goal) -> bool
|
||||
for(auto & path : paths)
|
||||
{
|
||||
return !goal->hero.validAndSet()
|
||||
|| !shouldVisit(goal->hero, objToVisit);
|
||||
});
|
||||
#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(!shouldVisit(path.targetHero, objToVisit))
|
||||
continue;
|
||||
|
||||
auto hero = path.targetHero;
|
||||
auto danger = path.getTotalDanger(hero);
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, danger))
|
||||
{
|
||||
auto newWay = std::make_shared<ExecuteHeroChain>(path, objToVisit);
|
||||
|
||||
waysToVisitObj.push_back(newWay);
|
||||
|
||||
if(!closestWay || closestWay->evaluationContext.movementCost > newWay->evaluationContext.movementCost)
|
||||
closestWay = newWay;
|
||||
}
|
||||
}
|
||||
|
||||
if(waysToVisitObj.empty())
|
||||
continue;
|
||||
|
||||
Goals::TSubgoal closestWay = *vstd::minElementByFun(waysToVisitObj, [](Goals::TSubgoal goal) -> float {
|
||||
return goal->evaluationContext.movementCost;
|
||||
});
|
||||
|
||||
for(Goals::TSubgoal way : waysToVisitObj)
|
||||
|
||||
for(auto way : waysToVisitObj)
|
||||
{
|
||||
if(!way->hero->movement)
|
||||
continue;
|
||||
|
||||
way->evaluationContext.closestWayRatio
|
||||
= way->evaluationContext.movementCost / closestWay->evaluationContext.movementCost;
|
||||
|
||||
logAi->trace("Behavior %s found %s(%s), danger %d", toString(), way->name(), way->tile.toString(), way->evaluationContext.danger);
|
||||
|
||||
tasks.push_back(way);
|
||||
|
||||
tasks.push_back(sptr(*way));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -52,6 +52,7 @@ set(VCAI_SRCS
|
||||
Goals/GetArtOfType.cpp
|
||||
Goals/FindObj.cpp
|
||||
Goals/CompleteQuest.cpp
|
||||
Goals/ExecuteHeroChain.cpp
|
||||
Engine/Nullkiller.cpp
|
||||
Engine/PriorityEvaluator.cpp
|
||||
Behaviors/Behavior.cpp
|
||||
@ -111,6 +112,7 @@ set(VCAI_HEADERS
|
||||
Goals/GetArtOfType.h
|
||||
Goals/FindObj.h
|
||||
Goals/CompleteQuest.h
|
||||
Goals/ExecuteHeroChain.h
|
||||
Goals/Goals.h
|
||||
Engine/Nullkiller.h
|
||||
Engine/PriorityEvaluator.h
|
||||
|
@ -14,6 +14,7 @@ public:
|
||||
Nullkiller();
|
||||
void makeTurn();
|
||||
bool isActive(const CGHeroInstance * hero) const { return activeHero.h == hero; }
|
||||
void setActive(const HeroPtr & hero) { activeHero = hero; }
|
||||
|
||||
private:
|
||||
Goals::TSubgoal choseBestTask(Behavior & behavior);
|
||||
|
@ -64,7 +64,8 @@ namespace Goals
|
||||
TRADE, //val resID at object objid
|
||||
BUILD_BOAT,
|
||||
COMPLETE_QUEST,
|
||||
ADVENTURE_SPELL_CAST
|
||||
ADVENTURE_SPELL_CAST,
|
||||
EXECUTE_HERO_CHAIN
|
||||
};
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||
|
111
AI/Nullkiller/Goals/ExecuteHeroChain.cpp
Normal file
111
AI/Nullkiller/Goals/ExecuteHeroChain.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* ExecuteHeroChain.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 "ExecuteHeroChain.h"
|
||||
#include "VisitTile.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../FuzzyHelper.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../../lib/mapping/CMap.h" //for victory conditions
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)
|
||||
:CGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
|
||||
{
|
||||
if(obj)
|
||||
objid = obj->id.getNum();
|
||||
|
||||
evaluationContext.danger = path.getTotalDanger(hero);
|
||||
evaluationContext.movementCost = path.movementCost();
|
||||
evaluationContext.armyLoss = path.armyLoss;
|
||||
evaluationContext.heroStrength = path.getHeroStrength();
|
||||
hero = path.targetHero;
|
||||
tile = path.firstTileToGet();
|
||||
}
|
||||
|
||||
bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TSubgoal ExecuteHeroChain::whatToDoToAchieve()
|
||||
{
|
||||
return iAmElementar();
|
||||
}
|
||||
|
||||
void ExecuteHeroChain::accept(VCAI * ai)
|
||||
{
|
||||
logAi->debug("Executing hero chain towards %s", tile.toString());
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
std::stringstream str;
|
||||
|
||||
str << "Path ";
|
||||
|
||||
for(auto node : chainPath.nodes)
|
||||
str << node.targetHero->name << "->" << node.coord.toString() << "; ";
|
||||
|
||||
logAi->trace(str.str());
|
||||
#endif
|
||||
|
||||
std::set<int> blockedIndexes;
|
||||
|
||||
for(int i = chainPath.nodes.size() - 1; i >= 0; i--)
|
||||
{
|
||||
auto & node = chainPath.nodes[i];
|
||||
|
||||
HeroPtr hero = node.targetHero;
|
||||
auto vt = Goals::sptr(Goals::VisitTile(node.coord).sethero(hero));
|
||||
|
||||
if(vstd::contains(blockedIndexes, i))
|
||||
{
|
||||
blockedIndexes.insert(node.parentIndex);
|
||||
ai->setGoal(hero, vt);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
logAi->debug("Moving hero %s to %s", hero.name, node.coord.toString());
|
||||
|
||||
try
|
||||
{
|
||||
ai->nullkiller->setActive(hero);
|
||||
vt->accept(ai);
|
||||
|
||||
blockedIndexes.insert(node.parentIndex);
|
||||
}
|
||||
catch(goalFulfilledException)
|
||||
{
|
||||
if(!hero)
|
||||
{
|
||||
logAi->debug("Hero %s was killed while attempting to rich %s", hero.name, node.coord.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ExecuteHeroChain::name() const
|
||||
{
|
||||
return "ExecuteHeroChain";
|
||||
}
|
||||
|
||||
std::string ExecuteHeroChain::completeMessage() const
|
||||
{
|
||||
return "Hero chain completed";
|
||||
}
|
35
AI/Nullkiller/Goals/ExecuteHeroChain.h
Normal file
35
AI/Nullkiller/Goals/ExecuteHeroChain.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* ExecuteHeroChain.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 ExecuteHeroChain : public CGoal<ExecuteHeroChain>
|
||||
{
|
||||
private:
|
||||
AIPath chainPath;
|
||||
|
||||
public:
|
||||
ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
|
||||
|
||||
TGoalVec getAllPossibleSubgoals() override
|
||||
{
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
TSubgoal whatToDoToAchieve() override;
|
||||
void accept(VCAI * ai) override;
|
||||
std::string name() const override;
|
||||
std::string completeMessage() const override;
|
||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||
};
|
||||
}
|
@ -731,7 +731,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
|
||||
path.chainMask = node.actor->chainMask;
|
||||
|
||||
fillChainInfo(&node, path);
|
||||
fillChainInfo(&node, path, -1);
|
||||
|
||||
paths.push_back(path);
|
||||
}
|
||||
@ -739,7 +739,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
||||
return paths;
|
||||
}
|
||||
|
||||
void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path) const
|
||||
void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const
|
||||
{
|
||||
while(node != nullptr)
|
||||
{
|
||||
@ -747,7 +747,7 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path) const
|
||||
return;
|
||||
|
||||
if(node->chainOther)
|
||||
fillChainInfo(node->chainOther, path);
|
||||
fillChainInfo(node->chainOther, path, parentIndex);
|
||||
|
||||
if(node->actor->hero->visitablePos() != node->coord)
|
||||
{
|
||||
@ -757,6 +757,9 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path) const
|
||||
pathNode.turns = node->turns;
|
||||
pathNode.danger = node->danger;
|
||||
pathNode.coord = node->coord;
|
||||
pathNode.parentIndex = parentIndex;
|
||||
|
||||
parentIndex = path.nodes.size();
|
||||
|
||||
path.nodes.push_back(pathNode);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ struct AIPathNodeInfo
|
||||
int3 coord;
|
||||
uint64_t danger;
|
||||
const CGHeroInstance * targetHero;
|
||||
int parentIndex;
|
||||
};
|
||||
|
||||
struct AIPath
|
||||
@ -158,7 +159,7 @@ private:
|
||||
void addHeroChain(const std::vector<ExchangeCandidate> & result);
|
||||
|
||||
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
||||
void fillChainInfo(const AIPathNode * node, AIPath & path) const;
|
||||
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
|
||||
void commit(
|
||||
AIPathNode * destination,
|
||||
const AIPathNode * source,
|
||||
|
@ -31,7 +31,7 @@ bool AIPathfinder::isTileAccessible(const HeroPtr & hero, const int3 & tile) con
|
||||
|| storage->isTileAccessible(hero, tile, EPathfindingLayer::SAIL);
|
||||
}
|
||||
|
||||
std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 & tile) const
|
||||
std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile) const
|
||||
{
|
||||
const TerrainTile * tileInfo = cb->getTile(tile, false);
|
||||
|
||||
|
@ -23,7 +23,7 @@ private:
|
||||
|
||||
public:
|
||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
|
||||
std::vector<AIPath> getPathInfo(const HeroPtr & hero, const int3 & tile) const;
|
||||
std::vector<AIPath> getPathInfo(const int3 & tile) const;
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
|
||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false);
|
||||
void init();
|
||||
|
@ -104,7 +104,18 @@ Goals::TGoalVec PathfindingManager::howToVisitObj(const HeroPtr & hero, ObjectId
|
||||
|
||||
std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
|
||||
{
|
||||
return pathfinder->getPathInfo(hero, tile);
|
||||
auto paths = pathfinder->getPathInfo(tile);
|
||||
|
||||
vstd::erase_if(paths, [&](AIPath & path) -> bool{
|
||||
return path.targetHero != hero.h;
|
||||
});
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
std::vector<AIPath> PathfindingManager::getPathsToTile(const int3 & tile) const
|
||||
{
|
||||
return pathfinder->getPathInfo(tile);
|
||||
}
|
||||
|
||||
Goals::TGoalVec PathfindingManager::findPaths(
|
||||
@ -117,7 +128,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
||||
boost::optional<uint64_t> armyValueRequired;
|
||||
uint64_t danger;
|
||||
|
||||
std::vector<AIPath> chainInfo = pathfinder->getPathInfo(hero, dest);
|
||||
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());
|
||||
|
@ -26,6 +26,7 @@ public:
|
||||
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 int3 & tile) const = 0;
|
||||
};
|
||||
|
||||
class DLL_EXPORT PathfindingManager : public IPathfindingManager
|
||||
@ -46,6 +47,7 @@ public:
|
||||
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 int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||
|
||||
STRONG_INLINE
|
||||
|
@ -603,7 +603,7 @@ void VCAI::init(std::shared_ptr<CCallback> CB)
|
||||
if(!fh)
|
||||
fh = new FuzzyHelper();
|
||||
|
||||
if(playerID.getStr(false) == "green")
|
||||
if(playerID.getStr(false) == "blue")
|
||||
{
|
||||
nullkiller.reset(new Nullkiller());
|
||||
}
|
||||
@ -815,16 +815,18 @@ void VCAI::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);
|
||||
|
||||
//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();
|
||||
|
||||
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();
|
||||
/*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 (auto h : cb->getHeroesInfo())
|
||||
|
Loading…
Reference in New Issue
Block a user