mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
AI: danger hitmap using visible enemy heroes
This commit is contained in:
committed by
Andrii Danylchenko
parent
04bf6f536d
commit
4bf653f596
@@ -101,16 +101,16 @@ const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
|
|||||||
if(h)
|
if(h)
|
||||||
{
|
{
|
||||||
auto obj = cb->getObj(hid);
|
auto obj = cb->getObj(hid);
|
||||||
const bool owned = obj && obj->tempOwner == ai->playerID;
|
//const bool owned = obj && obj->tempOwner == ai->playerID;
|
||||||
|
|
||||||
if(doWeExpectNull && !owned)
|
if(doWeExpectNull && !obj)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(obj);
|
assert(obj);
|
||||||
assert(owned);
|
//assert(owned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "../VCAI.h"
|
#include "../VCAI.h"
|
||||||
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../AIhelper.h"
|
#include "../AIhelper.h"
|
||||||
#include "../Goals/ExecuteHeroChain.h"
|
#include "../Goals/ExecuteHeroChain.h"
|
||||||
#include "CaptureObjectsBehavior.h"
|
#include "CaptureObjectsBehavior.h"
|
||||||
@@ -47,6 +48,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const int3 pos = objToVisit->visitablePos();
|
const int3 pos = objToVisit->visitablePos();
|
||||||
|
|
||||||
auto paths = ai->ah->getPathsToTile(pos);
|
auto paths = ai->ah->getPathsToTile(pos);
|
||||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
std::shared_ptr<ExecuteHeroChain> closestWay;
|
||||||
@@ -61,6 +63,14 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
|||||||
logAi->trace("Path found %s", path.toString());
|
logAi->trace("Path found %s", path.toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||||
|
{
|
||||||
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
|
logAi->trace("Ignore path. Target hero can be killed by enemy");
|
||||||
|
#endif
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(!shouldVisit(path.targetHero, objToVisit))
|
if(!shouldVisit(path.targetHero, objToVisit))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@@ -55,6 +55,7 @@ set(VCAI_SRCS
|
|||||||
Goals/ExecuteHeroChain.cpp
|
Goals/ExecuteHeroChain.cpp
|
||||||
Engine/Nullkiller.cpp
|
Engine/Nullkiller.cpp
|
||||||
Engine/PriorityEvaluator.cpp
|
Engine/PriorityEvaluator.cpp
|
||||||
|
Engine/DangerHitMapAnalyzer.cpp
|
||||||
Behaviors/Behavior.cpp
|
Behaviors/Behavior.cpp
|
||||||
Behaviors/CaptureObjectsBehavior.cpp
|
Behaviors/CaptureObjectsBehavior.cpp
|
||||||
Behaviors/RecruitHeroBehavior.cpp
|
Behaviors/RecruitHeroBehavior.cpp
|
||||||
@@ -117,6 +118,7 @@ set(VCAI_HEADERS
|
|||||||
Goals/Goals.h
|
Goals/Goals.h
|
||||||
Engine/Nullkiller.h
|
Engine/Nullkiller.h
|
||||||
Engine/PriorityEvaluator.h
|
Engine/PriorityEvaluator.h
|
||||||
|
Engine/DangerHitMapAnalyzer.h
|
||||||
Behaviors/Behavior.h
|
Behaviors/Behavior.h
|
||||||
Behaviors/CaptureObjectsBehavior.h
|
Behaviors/CaptureObjectsBehavior.h
|
||||||
Behaviors/RecruitHeroBehavior.h
|
Behaviors/RecruitHeroBehavior.h
|
||||||
|
65
AI/Nullkiller/Engine/DangerHitMapAnalyzer.cpp
Normal file
65
AI/Nullkiller/Engine/DangerHitMapAnalyzer.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include "StdInc.h"
|
||||||
|
#include "DangerHitMapAnalyzer.h"
|
||||||
|
|
||||||
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
|
extern boost::thread_specific_ptr<VCAI> ai;
|
||||||
|
|
||||||
|
void DangerHitMapAnalyzer::updateHitMap()
|
||||||
|
{
|
||||||
|
auto mapSize = cb->getMapSize();
|
||||||
|
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||||
|
|
||||||
|
std::map<PlayerColor, std::vector<HeroPtr>> heroes;
|
||||||
|
|
||||||
|
for(const CGObjectInstance * obj : ai->visitableObjs)
|
||||||
|
{
|
||||||
|
if(obj->ID == Obj::HERO)
|
||||||
|
{
|
||||||
|
HeroPtr hero = dynamic_cast<const CGHeroInstance *>(obj);
|
||||||
|
|
||||||
|
heroes[hero->tempOwner].push_back(hero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach_tile_pos([&](const int3 & pos){
|
||||||
|
hitMap[pos.x][pos.y][pos.z].reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
for(auto pair : heroes)
|
||||||
|
{
|
||||||
|
ai->ah->updatePaths(pair.second, false);
|
||||||
|
|
||||||
|
foreach_tile_pos([&](const int3 & pos){
|
||||||
|
for(AIPath & path : ai->ah->getPathsToTile(pos))
|
||||||
|
{
|
||||||
|
auto tileDanger = path.getHeroStrength();
|
||||||
|
auto turn = path.turn();
|
||||||
|
auto & node = hitMap[pos.x][pos.y][pos.z];
|
||||||
|
|
||||||
|
if(tileDanger > node.maximumDanger.danger
|
||||||
|
|| tileDanger == node.maximumDanger.danger && node.maximumDanger.turn > turn)
|
||||||
|
{
|
||||||
|
node.maximumDanger.danger = tileDanger;
|
||||||
|
node.maximumDanger.turn = turn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(turn < node.fastestDanger.turn
|
||||||
|
|| turn == node.fastestDanger.turn && node.fastestDanger.danger < tileDanger)
|
||||||
|
{
|
||||||
|
node.fastestDanger.danger = tileDanger;
|
||||||
|
node.fastestDanger.turn = turn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath & path) const
|
||||||
|
{
|
||||||
|
int3 tile = path.targetTile();
|
||||||
|
int turn = path.turn();
|
||||||
|
const HitMapNode & info = hitMap[tile.x][tile.y][tile.z];
|
||||||
|
|
||||||
|
return info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger)
|
||||||
|
|| info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger);
|
||||||
|
}
|
38
AI/Nullkiller/Engine/DangerHitMapAnalyzer.h
Normal file
38
AI/Nullkiller/Engine/DangerHitMapAnalyzer.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../VCAI.h"
|
||||||
|
#include "../AIHelper.h"
|
||||||
|
|
||||||
|
struct HitMapInfo
|
||||||
|
{
|
||||||
|
uint64_t danger;
|
||||||
|
uint8_t turn;
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
danger = 0;
|
||||||
|
turn = 255;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HitMapNode
|
||||||
|
{
|
||||||
|
HitMapInfo maximumDanger;
|
||||||
|
HitMapInfo fastestDanger;
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
maximumDanger.reset();
|
||||||
|
fastestDanger.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DangerHitMapAnalyzer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
boost::multi_array<HitMapNode, 3> hitMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void updateHitMap();
|
||||||
|
uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
|
||||||
|
};
|
@@ -13,12 +13,12 @@ extern boost::thread_specific_ptr<VCAI> ai;
|
|||||||
Nullkiller::Nullkiller()
|
Nullkiller::Nullkiller()
|
||||||
{
|
{
|
||||||
priorityEvaluator.reset(new PriorityEvaluator());
|
priorityEvaluator.reset(new PriorityEvaluator());
|
||||||
|
dangerHitMap.reset(new DangerHitMapAnalyzer());
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TSubgoal Nullkiller::choseBestTask(Goals::TGoalVec tasks)
|
Goals::TSubgoal Nullkiller::choseBestTask(Goals::TGoalVec tasks)
|
||||||
{
|
{
|
||||||
Goals::TSubgoal bestTask = *vstd::maxElementByFun(tasks, [](Goals::TSubgoal goal) -> float
|
Goals::TSubgoal bestTask = *vstd::maxElementByFun(tasks, [](Goals::TSubgoal goal) -> float{
|
||||||
{
|
|
||||||
return goal->priority;
|
return goal->priority;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,6 +53,8 @@ Goals::TSubgoal Nullkiller::choseBestTask(Behavior & behavior)
|
|||||||
void Nullkiller::resetAiState()
|
void Nullkiller::resetAiState()
|
||||||
{
|
{
|
||||||
lockedHeroes.clear();
|
lockedHeroes.clear();
|
||||||
|
|
||||||
|
dangerHitMap->updateHitMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nullkiller::updateAiState()
|
void Nullkiller::updateAiState()
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PriorityEvaluator.h"
|
#include "PriorityEvaluator.h"
|
||||||
|
#include "DangerHitMapAnalyzer.h"
|
||||||
#include "../Goals/AbstractGoal.h"
|
#include "../Goals/AbstractGoal.h"
|
||||||
#include "../Behaviors/Behavior.h"
|
#include "../Behaviors/Behavior.h"
|
||||||
|
|
||||||
@@ -12,6 +13,8 @@ private:
|
|||||||
std::set<HeroPtr> lockedHeroes;
|
std::set<HeroPtr> lockedHeroes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
||||||
|
|
||||||
Nullkiller();
|
Nullkiller();
|
||||||
void makeTurn();
|
void makeTurn();
|
||||||
bool isActive(const CGHeroInstance * hero) const { return activeHero.h == hero; }
|
bool isActive(const CGHeroInstance * hero) const { return activeHero.h == hero; }
|
||||||
|
@@ -33,9 +33,10 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
|
|||||||
|
|
||||||
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
|
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
|
||||||
int3 pos;
|
int3 pos;
|
||||||
const PlayerColor player = ai->playerID;
|
const PlayerColor player = playerID;
|
||||||
|
const PlayerColor fowPlayer = ai->playerID;
|
||||||
const int3 sizes = gs->getMapSize();
|
const int3 sizes = gs->getMapSize();
|
||||||
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(player)->fogOfWarMap;
|
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
|
||||||
|
|
||||||
//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
|
//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
|
||||||
const bool useFlying = options.useFlying;
|
const bool useFlying = options.useFlying;
|
||||||
@@ -478,10 +479,13 @@ void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
|
|||||||
cb = _ai->myCb.get();
|
cb = _ai->myCb.get();
|
||||||
ai = _ai;
|
ai = _ai;
|
||||||
|
|
||||||
|
playerID = ai->playerID;
|
||||||
|
|
||||||
for(auto & hero : heroes)
|
for(auto & hero : heroes)
|
||||||
{
|
{
|
||||||
uint64_t mask = 1 << actors.size();
|
uint64_t mask = 1 << actors.size();
|
||||||
|
|
||||||
|
playerID = hero->tempOwner;
|
||||||
actors.push_back(std::make_shared<HeroActor>(hero.get(), mask, ai));
|
actors.push_back(std::make_shared<HeroActor>(hero.get(), mask, ai));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -824,6 +828,17 @@ float AIPath::movementCost() const
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t AIPath::turn() const
|
||||||
|
{
|
||||||
|
if(nodes.size())
|
||||||
|
{
|
||||||
|
return nodes.front().turns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: boost:optional?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t AIPath::getHeroStrength() const
|
uint64_t AIPath::getHeroStrength() const
|
||||||
{
|
{
|
||||||
return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
|
return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
|
||||||
|
@@ -34,7 +34,7 @@ struct AIPathNode : public CGPathNode
|
|||||||
struct AIPathNodeInfo
|
struct AIPathNodeInfo
|
||||||
{
|
{
|
||||||
float cost;
|
float cost;
|
||||||
int turns;
|
uint8_t turns;
|
||||||
int3 coord;
|
int3 coord;
|
||||||
uint64_t danger;
|
uint64_t danger;
|
||||||
const CGHeroInstance * targetHero;
|
const CGHeroInstance * targetHero;
|
||||||
@@ -66,6 +66,8 @@ struct AIPath
|
|||||||
|
|
||||||
float movementCost() const;
|
float movementCost() const;
|
||||||
|
|
||||||
|
uint8_t turn() const;
|
||||||
|
|
||||||
uint64_t getHeroStrength() const;
|
uint64_t getHeroStrength() const;
|
||||||
|
|
||||||
std::string toString();
|
std::string toString();
|
||||||
@@ -91,6 +93,7 @@ private:
|
|||||||
std::vector<CGPathNode *> heroChain;
|
std::vector<CGPathNode *> heroChain;
|
||||||
bool heroChainPass; // true if we need to calculate hero chain
|
bool heroChainPass; // true if we need to calculate hero chain
|
||||||
int heroChainTurn;
|
int heroChainTurn;
|
||||||
|
PlayerColor playerID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
|
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
|
||||||
|
Reference in New Issue
Block a user