1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Nullkiller: stabilisation fixes

This commit is contained in:
Andrii Danylchenko 2021-05-16 14:19:07 +03:00 committed by Andrii Danylchenko
parent 17a960e850
commit b7b615ec70
8 changed files with 234 additions and 21 deletions

View File

@ -0,0 +1,162 @@
/*
* GatherArmyBehavior.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 "../VCAI.h"
#include "../Engine/Nullkiller.h"
#include "../AIhelper.h"
#include "../Goals/ExecuteHeroChain.h"
#include "GatherArmyBehavior.h"
#include "../AIUtility.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;
#define AI_TRACE_LEVEL 2
std::string GatherArmyBehavior::toString() const
{
return "Gather army";
}
Goals::TGoalVec GatherArmyBehavior::getTasks()
{
Goals::TGoalVec tasks;
auto heroes = cb->getHeroesInfo();
if(heroes.empty())
{
return tasks;
}
for(const CGHeroInstance * hero : heroes)
{
if(ai->ah->getHeroRole(hero) != HeroRole::MAIN
|| hero->getArmyStrength() < 300)
{
#ifdef AI_TRACE_LEVEL >= 1
logAi->trace("Skipping hero %s", hero->name);
#endif
continue;
}
const int3 pos = hero->visitablePos();
#ifdef AI_TRACE_LEVEL >= 1
logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
#endif
if(ai->nullkiller->isHeroLocked(hero))
{
#ifdef AI_TRACE_LEVEL >= 1
logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString());
#endif
continue;
}
auto paths = ai->ah->getPathsToTile(pos);
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
#ifdef AI_TRACE_LEVEL >= 1
logAi->trace("Found %d paths", paths.size());
#endif
for(auto & path : paths)
{
#ifdef AI_TRACE_LEVEL >= 2
logAi->trace("Path found %s", path.toString());
#endif
bool skip = path.targetHero == hero;
for(auto node : path.nodes)
{
skip |= (node.targetHero == hero);
}
if(skip) continue;
#ifdef AI_TRACE_LEVEL >= 2
logAi->trace("Path found %s", path.toString());
#endif
if(path.getFirstBlockedAction())
{
#ifdef AI_TRACE_LEVEL >= 2
// TODO: decomposition?
logAi->trace("Ignore path. Action is blocked.");
#endif
continue;
}
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
{
#ifdef AI_TRACE_LEVEL >= 2
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
#endif
continue;
}
float armyValue = (float)ai->ah->howManyReinforcementsCanGet(hero, path.heroArmy) / hero->getArmyStrength();
// avoid transferring very small amount of army
if(armyValue < 0.1f)
continue;
// avoid trying to move bigger army to the weaker one.
if(armyValue > 0.5f)
continue;
auto danger = path.getTotalDanger();
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
#ifdef AI_TRACE_LEVEL >= 2
logAi->trace(
"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
isSafe ? "safe" : "not safe",
hero->name,
path.targetHero->name,
path.getHeroStrength(),
danger,
path.getTotalArmyLoss());
#endif
if(isSafe)
{
auto newWay = std::make_shared<ExecuteHeroChain>(path, hero);
newWay->evaluationContext.strategicalValue = armyValue;
waysToVisitObj.push_back(newWay);
}
}
if(waysToVisitObj.empty())
continue;
for(auto way : waysToVisitObj)
{
if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
continue;
if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP)
continue;
way->evaluationContext.closestWayRatio = 1;
tasks.push_back(sptr(*way));
}
}
return tasks;
}

View File

@ -0,0 +1,32 @@
/*
* GatherArmyBehavior.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 GatherArmyBehavior : public Behavior {
private:
std::vector<int> objectTypes;
std::vector<int> objectSubTypes;
std::vector<const CGObjectInstance *> objectsToCapture;
bool specificObjects;
public:
GatherArmyBehavior()
{
objectTypes = std::vector<int>();
specificObjects = false;
}
virtual Goals::TGoalVec getTasks() override;
virtual std::string toString() const override;
};

View File

@ -60,6 +60,7 @@ set(VCAI_SRCS
Behaviors/DefenceBehavior.cpp
Behaviors/StartupBehavior.cpp
Behaviors/BuildingBehavior.cpp
Behaviors/GatherArmyBehavior.cpp
main.cpp
VCAI.cpp
)
@ -130,6 +131,7 @@ set(VCAI_HEADERS
Behaviors/DefenceBehavior.h
Behaviors/StartupBehavior.h
Behaviors/BuildingBehavior.h
Behaviors/GatherArmyBehavior.h
VCAI.h
)

View File

@ -17,6 +17,7 @@
#include "../Behaviors/StartupBehavior.h"
#include "../Behaviors/DefenceBehavior.h"
#include "../Behaviors/BuildingBehavior.h"
#include "../Behaviors/GatherArmyBehavior.h"
#include "../Goals/Invalid.h"
extern boost::thread_specific_ptr<CCallback> cb;
@ -117,7 +118,8 @@ void Nullkiller::makeTurn()
choseBestTask(std::make_shared<CaptureObjectsBehavior>()),
choseBestTask(std::make_shared<RecruitHeroBehavior>()),
choseBestTask(std::make_shared<DefenceBehavior>()),
choseBestTask(std::make_shared<BuildingBehavior>())
choseBestTask(std::make_shared<BuildingBehavior>()),
choseBestTask(std::make_shared<GatherArmyBehavior>())
};
if(cb->getDate(Date::DAY) == 1)

View File

@ -447,7 +447,7 @@ public:
evaluationContext.goldReward = getGoldReward(target, hero);
evaluationContext.armyReward = getArmyReward(target, hero, army, checkGold);
evaluationContext.skillReward = getSkillReward(target, hero, evaluationContext.heroRole);
evaluationContext.strategicalValue = getStrategicalValue(target);
evaluationContext.strategicalValue += getStrategicalValue(target);
evaluationContext.goldCost = getGoldCost(target, hero, army);
evaluationContext.turn = chain.getPath().turn();

View File

@ -186,3 +186,22 @@ float AbstractGoal::accept(FuzzyHelper * f)
{
return f->evaluate(*this);
}
EvaluationContext::EvaluationContext()
: movementCost(0.0),
manaCost(0),
danger(0),
closestWayRatio(1),
armyLoss(0),
heroStrength(0),
movementCostByRole(),
skillReward(0),
goldReward(0),
goldCost(0),
armyReward(0),
armyLossPersentage(0),
heroRole(HeroRole::SCOUT),
turn(0),
strategicalValue(0)
{
}

View File

@ -110,23 +110,7 @@ namespace Goals
HeroRole heroRole;
uint8_t turn;
EvaluationContext()
: movementCost(0.0),
manaCost(0),
danger(0),
closestWayRatio(1),
armyLoss(0),
heroStrength(0),
movementCostByRole(),
skillReward(0),
goldReward(0),
goldCost(0),
armyReward(0),
armyLossPersentage(0),
heroRole(HeroRole::SCOUT),
turn(0)
{
}
EvaluationContext();
};
class DLL_EXPORT AbstractGoal

View File

@ -96,7 +96,16 @@ void ExecuteHeroChain::accept(VCAI * ai)
{
auto specialGoal = node.specialAction->whatToDo(hero);
specialGoal->accept(ai);
try
{
specialGoal->accept(ai);
}
catch(cannotFulfillGoalException e)
{
logAi->warn("Can not complete %s because of an exception: %s", specialGoal->name(), e.what());
throw;
}
}
else
{
@ -104,7 +113,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
}
}
if(node.turns == 0)
if(node.turns == 0 && node.coord != hero->visitablePos())
{
auto targetNode = cb->getPathsInfo(hero.get())->getPathInfo(node.coord);
@ -146,6 +155,9 @@ void ExecuteHeroChain::accept(VCAI * ai)
}
}
if(node.coord == hero->visitablePos())
continue;
if(node.turns == 0)
{
logAi->error(