1
0
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:
Andrii Danylchenko 2021-05-15 21:56:31 +03:00 committed by Andrii Danylchenko
parent 37434dc4cf
commit ffa626dc2f
15 changed files with 229 additions and 34 deletions

View File

@ -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);

View File

@ -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

View File

@ -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));
}
}
};

View File

@ -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

View File

@ -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);

View File

@ -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>

View 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";
}

View 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;
};
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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);

View File

@ -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();

View File

@ -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());

View File

@ -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

View File

@ -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())