mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-25 21:38:59 +02:00
AI: danger hitmap using visible enemy heroes
This commit is contained in:
parent
04bf6f536d
commit
4bf653f596
@ -101,16 +101,16 @@ const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
|
||||
if(h)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(obj);
|
||||
assert(owned);
|
||||
//assert(owned);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../AIhelper.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "CaptureObjectsBehavior.h"
|
||||
@ -47,6 +48,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
continue;
|
||||
|
||||
const int3 pos = objToVisit->visitablePos();
|
||||
|
||||
auto paths = ai->ah->getPathsToTile(pos);
|
||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
||||
@ -61,6 +63,14 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
|
||||
logAi->trace("Path found %s", path.toString());
|
||||
#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))
|
||||
continue;
|
||||
|
||||
|
@ -55,6 +55,7 @@ set(VCAI_SRCS
|
||||
Goals/ExecuteHeroChain.cpp
|
||||
Engine/Nullkiller.cpp
|
||||
Engine/PriorityEvaluator.cpp
|
||||
Engine/DangerHitMapAnalyzer.cpp
|
||||
Behaviors/Behavior.cpp
|
||||
Behaviors/CaptureObjectsBehavior.cpp
|
||||
Behaviors/RecruitHeroBehavior.cpp
|
||||
@ -117,6 +118,7 @@ set(VCAI_HEADERS
|
||||
Goals/Goals.h
|
||||
Engine/Nullkiller.h
|
||||
Engine/PriorityEvaluator.h
|
||||
Engine/DangerHitMapAnalyzer.h
|
||||
Behaviors/Behavior.h
|
||||
Behaviors/CaptureObjectsBehavior.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()
|
||||
{
|
||||
priorityEvaluator.reset(new PriorityEvaluator());
|
||||
dangerHitMap.reset(new DangerHitMapAnalyzer());
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
@ -53,6 +53,8 @@ Goals::TSubgoal Nullkiller::choseBestTask(Behavior & behavior)
|
||||
void Nullkiller::resetAiState()
|
||||
{
|
||||
lockedHeroes.clear();
|
||||
|
||||
dangerHitMap->updateHitMap();
|
||||
}
|
||||
|
||||
void Nullkiller::updateAiState()
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "PriorityEvaluator.h"
|
||||
#include "DangerHitMapAnalyzer.h"
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
#include "../Behaviors/Behavior.h"
|
||||
|
||||
@ -12,6 +13,8 @@ private:
|
||||
std::set<HeroPtr> lockedHeroes;
|
||||
|
||||
public:
|
||||
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
||||
|
||||
Nullkiller();
|
||||
void makeTurn();
|
||||
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
|
||||
int3 pos;
|
||||
const PlayerColor player = ai->playerID;
|
||||
const PlayerColor player = playerID;
|
||||
const PlayerColor fowPlayer = ai->playerID;
|
||||
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)
|
||||
const bool useFlying = options.useFlying;
|
||||
@ -478,10 +479,13 @@ void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
|
||||
cb = _ai->myCb.get();
|
||||
ai = _ai;
|
||||
|
||||
playerID = ai->playerID;
|
||||
|
||||
for(auto & hero : heroes)
|
||||
{
|
||||
uint64_t mask = 1 << actors.size();
|
||||
|
||||
playerID = hero->tempOwner;
|
||||
actors.push_back(std::make_shared<HeroActor>(hero.get(), mask, ai));
|
||||
}
|
||||
}
|
||||
@ -824,6 +828,17 @@ float AIPath::movementCost() const
|
||||
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
|
||||
{
|
||||
return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
|
||||
|
@ -34,7 +34,7 @@ struct AIPathNode : public CGPathNode
|
||||
struct AIPathNodeInfo
|
||||
{
|
||||
float cost;
|
||||
int turns;
|
||||
uint8_t turns;
|
||||
int3 coord;
|
||||
uint64_t danger;
|
||||
const CGHeroInstance * targetHero;
|
||||
@ -66,6 +66,8 @@ struct AIPath
|
||||
|
||||
float movementCost() const;
|
||||
|
||||
uint8_t turn() const;
|
||||
|
||||
uint64_t getHeroStrength() const;
|
||||
|
||||
std::string toString();
|
||||
@ -91,6 +93,7 @@ private:
|
||||
std::vector<CGPathNode *> heroChain;
|
||||
bool heroChainPass; // true if we need to calculate hero chain
|
||||
int heroChainTurn;
|
||||
PlayerColor playerID;
|
||||
|
||||
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user