mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
NKAI: speedup exploration a bit
This commit is contained in:
parent
820f0e0c1a
commit
02ea497951
@ -15,233 +15,15 @@
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Markers/ExplorationPoint.h"
|
||||
#include "CaptureObjectsBehavior.h"
|
||||
#include "../Goals/CaptureObject.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
#include "../Goals/ExploreNeighbourTile.h"
|
||||
#include "../Helpers/ExplorationHelper.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
struct ExplorationHelper
|
||||
{
|
||||
const CGHeroInstance * hero;
|
||||
int sightRadius;
|
||||
float bestValue;
|
||||
TSubgoal bestGoal;
|
||||
int3 bestTile;
|
||||
int bestTilesDiscovered;
|
||||
const Nullkiller * ai;
|
||||
CCallback * cbp;
|
||||
const TeamState * ts;
|
||||
int3 ourPos;
|
||||
bool allowDeadEndCancellation;
|
||||
|
||||
ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai)
|
||||
:ai(ai), cbp(ai->cb.get()), hero(hero)
|
||||
{
|
||||
ts = cbp->getPlayerTeam(ai->playerID);
|
||||
sightRadius = hero->getSightRadius();
|
||||
bestGoal = sptr(Goals::Invalid());
|
||||
bestValue = 0;
|
||||
bestTilesDiscovered = 0;
|
||||
ourPos = hero->visitablePos();
|
||||
allowDeadEndCancellation = true;
|
||||
}
|
||||
|
||||
TSubgoal makeComposition() const
|
||||
{
|
||||
Composition c;
|
||||
c.addNext(ExplorationPoint(bestTile, bestTilesDiscovered));
|
||||
c.addNext(bestGoal);
|
||||
return sptr(c);
|
||||
}
|
||||
|
||||
void scanSector(int scanRadius)
|
||||
{
|
||||
int3 tile = int3(0, 0, ourPos.z);
|
||||
|
||||
const auto & slice = (*(ts->fogOfWarMap))[ourPos.z];
|
||||
|
||||
for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
|
||||
{
|
||||
for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++)
|
||||
{
|
||||
|
||||
if(cbp->isInTheMap(tile) && slice[tile.x][tile.y])
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scanMap()
|
||||
{
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[pos.z][pos.x][pos.y])
|
||||
{
|
||||
bool hasInvisibleNeighbor = false;
|
||||
|
||||
foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(!(*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
hasInvisibleNeighbor = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
if(!bestGoal->invalid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
void scanTile(const int3 & tile)
|
||||
{
|
||||
if(tile == ourPos
|
||||
|| !ai->pathfinder->isTileAccessible(hero, tile)) //shouldn't happen, but it does
|
||||
return;
|
||||
|
||||
int tilesDiscovered = howManyTilesWillBeDiscovered(tile);
|
||||
if(!tilesDiscovered)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(tile);
|
||||
auto waysToVisit = CaptureObjectsBehavior::getVisitGoals(paths, ai, ai->cb->getTopObj(tile));
|
||||
|
||||
for(int i = 0; i != paths.size(); i++)
|
||||
{
|
||||
auto & path = paths[i];
|
||||
auto goal = waysToVisit[i];
|
||||
|
||||
if(path.exchangeCount > 1 || path.targetHero != hero || path.movementCost() <= 0.0 || goal->invalid())
|
||||
continue;
|
||||
|
||||
float ourValue = (float)tilesDiscovered * tilesDiscovered / path.movementCost();
|
||||
|
||||
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||
{
|
||||
auto obj = cb->getTopObj(tile);
|
||||
|
||||
// picking up resources does not yield any exploration at all.
|
||||
// if it blocks the way to some explorable tile AIPathfinder will take care of it
|
||||
if(obj && obj->isBlockedVisitable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, path.getTotalDanger()))
|
||||
{
|
||||
bestGoal = goal;
|
||||
bestValue = ourValue;
|
||||
bestTile = tile;
|
||||
bestTilesDiscovered = tilesDiscovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
int3 npos = int3(0, 0, pos.z);
|
||||
|
||||
const auto & slice = (*(ts->fogOfWarMap))[pos.z];
|
||||
|
||||
for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
|
||||
{
|
||||
for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++)
|
||||
{
|
||||
if(cbp->isInTheMap(npos)
|
||||
&& pos.dist2d(npos) - 0.5 < sightRadius
|
||||
&& !slice[npos.x][npos.y])
|
||||
{
|
||||
if(allowDeadEndCancellation
|
||||
&& !hasReachableNeighbor(npos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool hasReachableNeighbor(const int3 & pos) const
|
||||
{
|
||||
for(const int3 & dir : int3::getDirs())
|
||||
{
|
||||
int3 tile = pos + dir;
|
||||
if(cbp->isInTheMap(tile))
|
||||
{
|
||||
auto isAccessible = ai->pathfinder->isTileAccessible(hero, tile);
|
||||
|
||||
if(isAccessible)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
std::string ExplorationBehavior::toString() const
|
||||
{
|
||||
return "Explore";
|
||||
@ -301,17 +83,13 @@ Goals::TGoalVec ExplorationBehavior::decompose(const Nullkiller * ai) const
|
||||
{
|
||||
ExplorationHelper scanResult(hero, ai);
|
||||
|
||||
scanResult.scanSector(1);
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
if(scanResult.scanSector(1))
|
||||
{
|
||||
tasks.push_back(scanResult.makeComposition());
|
||||
continue;
|
||||
}
|
||||
|
||||
scanResult.scanSector(15);
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
if(scanResult.scanSector(15))
|
||||
{
|
||||
tasks.push_back(scanResult.makeComposition());
|
||||
continue;
|
||||
@ -319,9 +97,7 @@ Goals::TGoalVec ExplorationBehavior::decompose(const Nullkiller * ai) const
|
||||
|
||||
if(ai->getScanDepth() == ScanDepth::ALL_FULL)
|
||||
{
|
||||
scanResult.scanMap();
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
if(scanResult.scanMap())
|
||||
{
|
||||
tasks.push_back(scanResult.makeComposition());
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ set(Nullkiller_SRCS
|
||||
Goals/ExchangeSwapTownHeroes.cpp
|
||||
Goals/CompleteQuest.cpp
|
||||
Goals/StayAtTown.cpp
|
||||
Goals/ExploreNeighbourTile.cpp
|
||||
Markers/ArmyUpgrade.cpp
|
||||
Markers/HeroExchange.cpp
|
||||
Markers/UnlockCluster.cpp
|
||||
@ -62,6 +63,7 @@ set(Nullkiller_SRCS
|
||||
Behaviors/StayAtTownBehavior.cpp
|
||||
Behaviors/ExplorationBehavior.cpp
|
||||
Helpers/ArmyFormation.cpp
|
||||
Helpers/ExplorationHelper.cpp
|
||||
AIGateway.cpp
|
||||
)
|
||||
|
||||
@ -113,6 +115,7 @@ set(Nullkiller_HEADERS
|
||||
Goals/CompleteQuest.h
|
||||
Goals/Goals.h
|
||||
Goals/StayAtTown.h
|
||||
Goals/ExploreNeighbourTile.h
|
||||
Markers/ArmyUpgrade.h
|
||||
Markers/HeroExchange.h
|
||||
Markers/UnlockCluster.h
|
||||
@ -135,6 +138,7 @@ set(Nullkiller_HEADERS
|
||||
Behaviors/StayAtTownBehavior.h
|
||||
Behaviors/ExplorationBehavior.h
|
||||
Helpers/ArmyFormation.h
|
||||
Helpers/ExplorationHelper.h
|
||||
AIGateway.h
|
||||
)
|
||||
|
||||
|
@ -75,7 +75,8 @@ namespace Goals
|
||||
STAY_AT_TOWN_BEHAVIOR,
|
||||
STAY_AT_TOWN,
|
||||
EXPLORATION_BEHAVIOR,
|
||||
EXPLORATION_POINT
|
||||
EXPLORATION_POINT,
|
||||
EXPLORE_NEIGHBOUR_TILE
|
||||
};
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||
|
69
AI/Nullkiller/Goals/ExploreNeighbourTile.cpp
Normal file
69
AI/Nullkiller/Goals/ExploreNeighbourTile.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* ExploreNeighbourTile.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 "ExploreNeighbourTile.h"
|
||||
#include "../AIGateway.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Helpers/ExplorationHelper.h"
|
||||
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool ExploreNeighbourTile::operator==(const ExploreNeighbourTile & other) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExploreNeighbourTile::accept(AIGateway * ai)
|
||||
{
|
||||
ExplorationHelper h(hero, ai->nullkiller.get());
|
||||
|
||||
for(int i = 0; i < tilesToExplore && hero->movementPointsRemaining() > 0; i++)
|
||||
{
|
||||
int3 pos = hero->visitablePos();
|
||||
float value = 0;
|
||||
int3 target = int3(-1);
|
||||
foreach_neighbour(pos, [&](int3 tile)
|
||||
{
|
||||
auto pathInfo = ai->myCb->getPathsInfo(hero)->getPathInfo(tile);
|
||||
|
||||
if(pathInfo->turns > 0)
|
||||
return;
|
||||
|
||||
if(pathInfo->accessible == EPathAccessibility::ACCESSIBLE)
|
||||
{
|
||||
float newValue = h.howManyTilesWillBeDiscovered(tile);
|
||||
|
||||
newValue /= std::min(0.1f, pathInfo->getCost());
|
||||
|
||||
if(newValue > value)
|
||||
{
|
||||
value = newValue;
|
||||
target = tile;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(!target.valid() || !ai->moveHeroToTile(target, hero))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ExploreNeighbourTile::toString() const
|
||||
{
|
||||
return "Explore neighbour tiles by " + hero->getNameTranslated();
|
||||
}
|
||||
|
||||
}
|
45
AI/Nullkiller/Goals/ExploreNeighbourTile.h
Normal file
45
AI/Nullkiller/Goals/ExploreNeighbourTile.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* ExploreNeighbourTile.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 NKAI
|
||||
{
|
||||
|
||||
class AIGateway;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT ExploreNeighbourTile : public ElementarGoal<ExploreNeighbourTile>
|
||||
{
|
||||
private:
|
||||
int tilesToExplore;
|
||||
|
||||
public:
|
||||
ExploreNeighbourTile(const CGHeroInstance * hero, int amount)
|
||||
: ElementarGoal(Goals::EXPLORE_NEIGHBOUR_TILE)
|
||||
{
|
||||
tilesToExplore = amount;
|
||||
sethero(hero);
|
||||
}
|
||||
|
||||
bool operator==(const ExploreNeighbourTile & other) const override;
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
|
||||
private:
|
||||
//TSubgoal decomposeSingle() const override;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
235
AI/Nullkiller/Helpers/ExplorationHelper.cpp
Normal file
235
AI/Nullkiller/Helpers/ExplorationHelper.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* ExplorationHelper.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 "ExplorationHelper.h"
|
||||
#include "../../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../Goals/Invalid.h"
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Markers/ExplorationPoint.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
#include "../Behaviors/CaptureObjectsBehavior.h"
|
||||
#include "../Goals/ExploreNeighbourTile.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
ExplorationHelper::ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai)
|
||||
:ai(ai), cbp(ai->cb.get()), hero(hero)
|
||||
{
|
||||
ts = cbp->getPlayerTeam(ai->playerID);
|
||||
sightRadius = hero->getSightRadius();
|
||||
bestGoal = sptr(Goals::Invalid());
|
||||
bestValue = 0;
|
||||
bestTilesDiscovered = 0;
|
||||
ourPos = hero->visitablePos();
|
||||
allowDeadEndCancellation = true;
|
||||
}
|
||||
|
||||
TSubgoal ExplorationHelper::makeComposition() const
|
||||
{
|
||||
Composition c;
|
||||
c.addNext(ExplorationPoint(bestTile, bestTilesDiscovered));
|
||||
c.addNextSequence({bestGoal, sptr(ExploreNeighbourTile(hero, 5))});
|
||||
return sptr(c);
|
||||
}
|
||||
|
||||
|
||||
bool ExplorationHelper::scanSector(int scanRadius)
|
||||
{
|
||||
int3 tile = int3(0, 0, ourPos.z);
|
||||
|
||||
const auto & slice = (*(ts->fogOfWarMap))[ourPos.z];
|
||||
|
||||
for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
|
||||
{
|
||||
for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++)
|
||||
{
|
||||
if(cbp->isInTheMap(tile) && slice[tile.x][tile.y])
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !bestGoal->invalid();
|
||||
}
|
||||
|
||||
bool ExplorationHelper::scanMap()
|
||||
{
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[pos.z][pos.x][pos.y])
|
||||
{
|
||||
bool hasInvisibleNeighbor = false;
|
||||
|
||||
foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(!(*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
hasInvisibleNeighbor = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
if(!bestGoal->invalid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
return !bestGoal->invalid();
|
||||
}
|
||||
|
||||
void ExplorationHelper::scanTile(const int3 & tile)
|
||||
{
|
||||
if(tile == ourPos
|
||||
|| !ai->cb->getTile(tile, false)
|
||||
|| !ai->pathfinder->isTileAccessible(hero, tile)) //shouldn't happen, but it does
|
||||
return;
|
||||
|
||||
int tilesDiscovered = howManyTilesWillBeDiscovered(tile);
|
||||
if(!tilesDiscovered)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(tile);
|
||||
auto waysToVisit = CaptureObjectsBehavior::getVisitGoals(paths, ai, ai->cb->getTopObj(tile));
|
||||
|
||||
for(int i = 0; i != paths.size(); i++)
|
||||
{
|
||||
auto & path = paths[i];
|
||||
auto goal = waysToVisit[i];
|
||||
|
||||
if(path.exchangeCount > 1 || path.targetHero != hero || path.movementCost() <= 0.0 || goal->invalid())
|
||||
continue;
|
||||
|
||||
float ourValue = (float)tilesDiscovered * tilesDiscovered / path.movementCost();
|
||||
|
||||
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||
{
|
||||
auto obj = cb->getTopObj(tile);
|
||||
|
||||
// picking up resources does not yield any exploration at all.
|
||||
// if it blocks the way to some explorable tile AIPathfinder will take care of it
|
||||
if(obj && obj->isBlockedVisitable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, path.getTotalDanger()))
|
||||
{
|
||||
bestGoal = goal;
|
||||
bestValue = ourValue;
|
||||
bestTile = tile;
|
||||
bestTilesDiscovered = tilesDiscovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExplorationHelper::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int ExplorationHelper::howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
int3 npos = int3(0, 0, pos.z);
|
||||
|
||||
const auto & slice = (*(ts->fogOfWarMap))[pos.z];
|
||||
|
||||
for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
|
||||
{
|
||||
for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++)
|
||||
{
|
||||
if(cbp->isInTheMap(npos)
|
||||
&& pos.dist2d(npos) - 0.5 < sightRadius
|
||||
&& !slice[npos.x][npos.y])
|
||||
{
|
||||
if(allowDeadEndCancellation
|
||||
&& !hasReachableNeighbor(npos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ExplorationHelper::hasReachableNeighbor(const int3 & pos) const
|
||||
{
|
||||
for(const int3 & dir : int3::getDirs())
|
||||
{
|
||||
int3 tile = pos + dir;
|
||||
if(cbp->isInTheMap(tile))
|
||||
{
|
||||
auto isAccessible = ai->pathfinder->isTileAccessible(hero, tile);
|
||||
|
||||
if(isAccessible)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
51
AI/Nullkiller/Helpers/ExplorationHelper.h
Normal file
51
AI/Nullkiller/Helpers/ExplorationHelper.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* ExplorationHelper.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 "../AIUtility.h"
|
||||
|
||||
#include "../../../lib/GameConstants.h"
|
||||
#include "../../../lib/VCMI_Lib.h"
|
||||
#include "../../../lib/CTownHandler.h"
|
||||
#include "../../../lib/CBuildingHandler.h"
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
class ExplorationHelper
|
||||
{
|
||||
private:
|
||||
const CGHeroInstance * hero;
|
||||
int sightRadius;
|
||||
float bestValue;
|
||||
Goals::TSubgoal bestGoal;
|
||||
int3 bestTile;
|
||||
int bestTilesDiscovered;
|
||||
const Nullkiller * ai;
|
||||
CCallback * cbp;
|
||||
const TeamState * ts;
|
||||
int3 ourPos;
|
||||
bool allowDeadEndCancellation;
|
||||
|
||||
public:
|
||||
ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai);
|
||||
Goals::TSubgoal makeComposition() const;
|
||||
bool scanSector(int scanRadius);
|
||||
bool scanMap();
|
||||
int howManyTilesWillBeDiscovered(const int3 & pos) const;
|
||||
|
||||
private:
|
||||
void scanTile(const int3 & tile);
|
||||
bool hasReachableNeighbor(const int3 & pos) const;
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user