mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-17 01:32:21 +02:00
Nullkiller: defence behavior
This commit is contained in:
committed by
Andrii Danylchenko
parent
f44c3f2aaf
commit
a2ac19e4ec
177
AI/Nullkiller/Behaviors/DefenceBehavior.cpp
Normal file
177
AI/Nullkiller/Behaviors/DefenceBehavior.cpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* DefenceBehavior.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 "DefenceBehavior.h"
|
||||||
|
#include "../VCAI.h"
|
||||||
|
#include "../Engine/Nullkiller.h"
|
||||||
|
#include "../AIhelper.h"
|
||||||
|
#include "../AIUtility.h"
|
||||||
|
#include "../Goals/BuyArmy.h"
|
||||||
|
#include "../Goals/VisitTile.h"
|
||||||
|
#include "../Goals/ExecuteHeroChain.h"
|
||||||
|
#include "../Goals/ExchangeSwapTownHeroes.h"
|
||||||
|
#include "lib/mapping/CMap.h" //for victory conditions
|
||||||
|
#include "lib/CPathfinder.h"
|
||||||
|
|
||||||
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
|
extern boost::thread_specific_ptr<VCAI> ai;
|
||||||
|
extern FuzzyHelper * fh;
|
||||||
|
|
||||||
|
using namespace Goals;
|
||||||
|
|
||||||
|
std::string DefenceBehavior::toString() const
|
||||||
|
{
|
||||||
|
return "Defend towns";
|
||||||
|
}
|
||||||
|
|
||||||
|
Goals::TGoalVec DefenceBehavior::getTasks()
|
||||||
|
{
|
||||||
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
|
auto heroes = cb->getHeroesInfo();
|
||||||
|
|
||||||
|
if(heroes.size())
|
||||||
|
{
|
||||||
|
auto mainArmy = *vstd::maxElementByFun(heroes, [](const CGHeroInstance * hero) -> uint64_t
|
||||||
|
{
|
||||||
|
return hero->getTotalStrength();
|
||||||
|
});
|
||||||
|
|
||||||
|
for(auto town : cb->getTownsInfo())
|
||||||
|
{
|
||||||
|
evaluateDefence(tasks, town);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town)
|
||||||
|
{
|
||||||
|
logAi->debug("Evaluating defence for %s", town->name);
|
||||||
|
|
||||||
|
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||||
|
auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
|
||||||
|
|
||||||
|
if(town->garrisonHero)
|
||||||
|
{
|
||||||
|
if(ai->nullkiller->isActive(town->garrisonHero.get()))
|
||||||
|
{
|
||||||
|
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logAi->debug(
|
||||||
|
"Hero %s in garrison of town %s is suposed to defend the town",
|
||||||
|
town->name,
|
||||||
|
town->garrisonHero->name);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(town->visitingHero && isSafeToVisit(town->visitingHero.get(), treatNode.maximumDanger.danger))
|
||||||
|
{
|
||||||
|
logAi->debug(
|
||||||
|
"Town %s has visiting hero %s who is strong enough to defend the town",
|
||||||
|
town->name,
|
||||||
|
town->visitingHero->name);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
||||||
|
|
||||||
|
if(reinforcement)
|
||||||
|
{
|
||||||
|
logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement);
|
||||||
|
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto paths = ai->ah->getPathsToTile(town->visitablePos());
|
||||||
|
|
||||||
|
if(paths.empty())
|
||||||
|
{
|
||||||
|
logAi->debug("No ways to defend town %s", town->name);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(AIPath & path : paths)
|
||||||
|
{
|
||||||
|
for(auto & treat : treats)
|
||||||
|
{
|
||||||
|
if(path.turn() < treat.turn && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
|
||||||
|
{
|
||||||
|
logAi->debug("Town %s is not in danger.", town->name);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto & treat : treats)
|
||||||
|
{
|
||||||
|
logAi->debug(
|
||||||
|
"Town %s has treat %lld in %s turns, hero: %s",
|
||||||
|
town->name,
|
||||||
|
treat.danger,
|
||||||
|
std::to_string(treat.turn),
|
||||||
|
treat.hero->name);
|
||||||
|
|
||||||
|
for(AIPath & path : paths)
|
||||||
|
{
|
||||||
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace(
|
||||||
|
"Hero %s can defend town with force %lld in %s turns, path: %s",
|
||||||
|
path.targetHero->name,
|
||||||
|
path.getHeroStrength(),
|
||||||
|
std::to_string(path.turn()),
|
||||||
|
path.toString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float priority = 0.6f + (float)path.getHeroStrength() / treat.danger / (treat.turn + 1);
|
||||||
|
|
||||||
|
if(path.targetHero == town->visitingHero && path.exchangeCount == 1)
|
||||||
|
{
|
||||||
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace("Put %s to garrison of town %s with priority %f",
|
||||||
|
path.targetHero->name,
|
||||||
|
town->name,
|
||||||
|
priority);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, town->visitingHero.get()).setpriority(priority)));
|
||||||
|
}
|
||||||
|
else if(path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
|
||||||
|
{
|
||||||
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace("Move %s to defend town %s with priority %f",
|
||||||
|
path.targetHero->name,
|
||||||
|
town->name,
|
||||||
|
priority);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, town).setpriority(priority)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logAi->debug("Found %d tasks", tasks.size());
|
||||||
|
|
||||||
|
/*for(auto & treat : treats)
|
||||||
|
{
|
||||||
|
auto paths = ai->ah->getPathsToTile(treat.hero->visitablePos());
|
||||||
|
|
||||||
|
for(AIPath & path : paths)
|
||||||
|
{
|
||||||
|
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path)));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
29
AI/Nullkiller/Behaviors/DefenceBehavior.h
Normal file
29
AI/Nullkiller/Behaviors/DefenceBehavior.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* BuyArmyBehavior.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 "lib/VCMI_Lib.h"
|
||||||
|
#include "Behavior.h"
|
||||||
|
#include "../AIUtility.h"
|
||||||
|
|
||||||
|
class DefenceBehavior : public Behavior
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DefenceBehavior()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Goals::TGoalVec getTasks() override;
|
||||||
|
virtual std::string toString() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town);
|
||||||
|
};
|
||||||
|
|
@ -55,6 +55,7 @@ set(VCAI_SRCS
|
|||||||
Behaviors/CaptureObjectsBehavior.cpp
|
Behaviors/CaptureObjectsBehavior.cpp
|
||||||
Behaviors/RecruitHeroBehavior.cpp
|
Behaviors/RecruitHeroBehavior.cpp
|
||||||
Behaviors/BuyArmyBehavior.cpp
|
Behaviors/BuyArmyBehavior.cpp
|
||||||
|
Behaviors/DefenceBehavior.cpp
|
||||||
Behaviors/StartupBehavior.cpp
|
Behaviors/StartupBehavior.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
VCAI.cpp
|
VCAI.cpp
|
||||||
@ -121,6 +122,7 @@ set(VCAI_HEADERS
|
|||||||
Behaviors/CaptureObjectsBehavior.h
|
Behaviors/CaptureObjectsBehavior.h
|
||||||
Behaviors/RecruitHeroBehavior.h
|
Behaviors/RecruitHeroBehavior.h
|
||||||
Behaviors/BuyArmyBehavior.h
|
Behaviors/BuyArmyBehavior.h
|
||||||
|
Behaviors/DefenceBehavior.h
|
||||||
Behaviors/StartupBehavior.h
|
Behaviors/StartupBehavior.h
|
||||||
VCAI.h
|
VCAI.h
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "DangerHitMapAnalyzer.h"
|
#include "DangerHitMapAnalyzer.h"
|
||||||
|
#include "lib/mapping/CMap.h" //for victory conditions
|
||||||
|
|
||||||
extern boost::thread_specific_ptr<CCallback> cb;
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
extern boost::thread_specific_ptr<VCAI> ai;
|
extern boost::thread_specific_ptr<VCAI> ai;
|
||||||
@ -38,7 +39,8 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
{
|
{
|
||||||
ai->ah->updatePaths(pair.second, false);
|
ai->ah->updatePaths(pair.second, false);
|
||||||
|
|
||||||
foreach_tile_pos([&](const int3 & pos){
|
foreach_tile_pos([&](const int3 & pos)
|
||||||
|
{
|
||||||
for(AIPath & path : ai->ah->getPathsToTile(pos))
|
for(AIPath & path : ai->ah->getPathsToTile(pos))
|
||||||
{
|
{
|
||||||
auto tileDanger = path.getHeroStrength();
|
auto tileDanger = path.getHeroStrength();
|
||||||
@ -50,6 +52,7 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
{
|
{
|
||||||
node.maximumDanger.danger = tileDanger;
|
node.maximumDanger.danger = tileDanger;
|
||||||
node.maximumDanger.turn = turn;
|
node.maximumDanger.turn = turn;
|
||||||
|
node.maximumDanger.hero = path.targetHero;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(turn < node.fastestDanger.turn
|
if(turn < node.fastestDanger.turn
|
||||||
@ -57,6 +60,7 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
{
|
{
|
||||||
node.fastestDanger.danger = tileDanger;
|
node.fastestDanger.danger = tileDanger;
|
||||||
node.fastestDanger.turn = turn;
|
node.fastestDanger.turn = turn;
|
||||||
|
node.fastestDanger.hero = path.targetHero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -72,3 +76,11 @@ uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath &
|
|||||||
return info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger)
|
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);
|
|| info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HitMapNode & DangerHitMapAnalyzer::getObjectTreat(const CGObjectInstance * town) const
|
||||||
|
{
|
||||||
|
auto tile = town->visitablePos();
|
||||||
|
const HitMapNode & info = hitMap[tile.x][tile.y][tile.z];
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
@ -16,11 +16,13 @@ struct HitMapInfo
|
|||||||
{
|
{
|
||||||
uint64_t danger;
|
uint64_t danger;
|
||||||
uint8_t turn;
|
uint8_t turn;
|
||||||
|
const CGHeroInstance * hero;
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
{
|
{
|
||||||
danger = 0;
|
danger = 0;
|
||||||
turn = 255;
|
turn = 255;
|
||||||
|
hero = nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,8 +42,10 @@ class DangerHitMapAnalyzer
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
boost::multi_array<HitMapNode, 3> hitMap;
|
boost::multi_array<HitMapNode, 3> hitMap;
|
||||||
|
std::map<const CGHeroInstance *, int> enemyHeroTreatMAp;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void updateHitMap();
|
void updateHitMap();
|
||||||
uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
|
uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
|
||||||
|
const HitMapNode & getObjectTreat(const CGObjectInstance * town) const;
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "../Behaviors/RecruitHeroBehavior.h"
|
#include "../Behaviors/RecruitHeroBehavior.h"
|
||||||
#include "../Behaviors/BuyArmyBehavior.h"
|
#include "../Behaviors/BuyArmyBehavior.h"
|
||||||
#include "../Behaviors/StartupBehavior.h"
|
#include "../Behaviors/StartupBehavior.h"
|
||||||
|
#include "../Behaviors/DefenceBehavior.h"
|
||||||
#include "../Goals/Invalid.h"
|
#include "../Goals/Invalid.h"
|
||||||
|
|
||||||
extern boost::thread_specific_ptr<CCallback> cb;
|
extern boost::thread_specific_ptr<CCallback> cb;
|
||||||
@ -95,7 +96,8 @@ void Nullkiller::makeTurn()
|
|||||||
Goals::TGoalVec bestTasks = {
|
Goals::TGoalVec bestTasks = {
|
||||||
choseBestTask(std::make_shared<BuyArmyBehavior>()),
|
choseBestTask(std::make_shared<BuyArmyBehavior>()),
|
||||||
choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
|
choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
|
||||||
choseBestTask(std::make_shared<RecruitHeroBehavior>())
|
choseBestTask(std::make_shared<RecruitHeroBehavior>()),
|
||||||
|
choseBestTask(std::make_shared<DefenceBehavior>())
|
||||||
};
|
};
|
||||||
|
|
||||||
if(cb->getDate(Date::DAY) == 1)
|
if(cb->getDate(Date::DAY) == 1)
|
||||||
|
@ -484,9 +484,17 @@ void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
|
|||||||
for(auto & hero : heroes)
|
for(auto & hero : heroes)
|
||||||
{
|
{
|
||||||
uint64_t mask = 1 << actors.size();
|
uint64_t mask = 1 << actors.size();
|
||||||
|
auto actor = std::make_shared<HeroActor>(hero.get(), mask, ai);
|
||||||
|
|
||||||
|
if(hero->tempOwner != ai->playerID)
|
||||||
|
{
|
||||||
|
bool onLand = !actor->hero->boat;
|
||||||
|
actor->initialMovement = actor->hero->maxMovePoints(onLand);
|
||||||
|
}
|
||||||
|
|
||||||
playerID = hero->tempOwner;
|
playerID = hero->tempOwner;
|
||||||
actors.push_back(std::make_shared<HeroActor>(hero.get(), mask, ai));
|
|
||||||
|
actors.push_back(actor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VCMI_TRACE_PATHFINDER 1
|
#define VCMI_TRACE_PATHFINDER 1
|
||||||
|
#define AI_TRACE_LEVEL 1
|
||||||
|
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||||
|
Reference in New Issue
Block a user