mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
2184 - fix battlefield corners unreachable for 2 hex units
This commit is contained in:
parent
58b8fe3d26
commit
ea073c81d3
@ -189,13 +189,17 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
if(stack->waited())
|
||||
{
|
||||
//ThreatMap threatsToUs(stack); // These lines may be usefull but they are't used in the code.
|
||||
auto dists = getCbc()->battleGetDistances(stack, stack->getPosition());
|
||||
auto dists = getCbc()->getReachability(stack);
|
||||
if(!targets.unreachableEnemies.empty())
|
||||
{
|
||||
const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, std::bind(isCloser, _1, _2, std::ref(dists)));
|
||||
if(distToNearestNeighbour(ei.s->getPosition(), dists) < GameConstants::BFIELD_SIZE)
|
||||
auto closestEnemy = vstd::minElementByFun(targets.unreachableEnemies, [&](const battle::Unit * enemy) -> int
|
||||
{
|
||||
return goTowards(stack, ei.s->getPosition());
|
||||
return dists.distToNearestNeighbour(stack, enemy);
|
||||
});
|
||||
|
||||
if(dists.distToNearestNeighbour(stack, *closestEnemy) < GameConstants::BFIELD_SIZE)
|
||||
{
|
||||
return goTowards(stack, *closestEnemy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,19 +220,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
||||
BattleAction CBattleAI::goTowards(const CStack * stack, const battle::Unit * enemy) const
|
||||
{
|
||||
if(!destination.isValid())
|
||||
{
|
||||
logAi->error("CBattleAI::goTowards: invalid destination");
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
auto reachability = cb->getReachability(stack);
|
||||
auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
|
||||
auto destination = enemy->getPosition();
|
||||
|
||||
if(vstd::contains(avHexes, destination))
|
||||
return BattleAction::makeMove(stack, destination);
|
||||
|
||||
auto destNeighbours = destination.neighbouringTiles();
|
||||
if(vstd::contains_if(destNeighbours, [&](BattleHex n) { return stack->coversPos(destination); }))
|
||||
{
|
||||
@ -236,31 +236,31 @@ BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
||||
//We shouldn't even be here...
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
vstd::erase_if(destNeighbours, [&](BattleHex hex){ return !reachability.accessibility.accessible(hex, stack); });
|
||||
if(!avHexes.size() || !destNeighbours.size()) //we are blocked or dest is blocked
|
||||
|
||||
if(!avHexes.size()) //we are blocked or dest is blocked
|
||||
{
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
BattleHex bestNeighbor = destination;
|
||||
if(reachability.distToNearestNeighbour(stack, enemy, &bestNeighbor) > GameConstants::BFIELD_SIZE)
|
||||
{
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
if(stack->hasBonusOfType(Bonus::FLYING))
|
||||
{
|
||||
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
|
||||
// We just check all available hexes and pick the one closest to the target.
|
||||
auto distToDestNeighbour = [&](BattleHex hex) -> int
|
||||
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
|
||||
{
|
||||
auto nearestNeighbourToHex = vstd::minElementByFun(destNeighbours, [&](BattleHex a)
|
||||
{return BattleHex::getDistance(a, hex);});
|
||||
return BattleHex::getDistance(*nearestNeighbourToHex, hex);
|
||||
};
|
||||
auto nearestAvailableHex = vstd::minElementByFun(avHexes, distToDestNeighbour);
|
||||
return BattleHex::getDistance(bestNeighbor, hex);
|
||||
});
|
||||
|
||||
return BattleAction::makeMove(stack, *nearestAvailableHex);
|
||||
}
|
||||
else
|
||||
{
|
||||
BattleHex bestNeighbor = destination;
|
||||
if(distToNearestNeighbour(destination, reachability.distances, &bestNeighbor) > GameConstants::BFIELD_SIZE)
|
||||
{
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
BattleHex currentDest = bestNeighbor;
|
||||
while(1)
|
||||
{
|
||||
@ -272,6 +272,7 @@ BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
||||
|
||||
if(vstd::contains(avHexes, currentDest))
|
||||
return BattleAction::makeMove(stack, currentDest);
|
||||
|
||||
currentDest = reachability.predecessors[currentDest];
|
||||
}
|
||||
}
|
||||
@ -624,32 +625,12 @@ void CBattleAI::evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcas
|
||||
ps.value = totalGain;
|
||||
};
|
||||
|
||||
int CBattleAI::distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances &dists, BattleHex *chosenHex)
|
||||
{
|
||||
int ret = 1000000;
|
||||
for(BattleHex n : hex.neighbouringTiles())
|
||||
{
|
||||
if(dists[n] >= 0 && dists[n] < ret)
|
||||
{
|
||||
ret = dists[n];
|
||||
if(chosenHex)
|
||||
*chosenHex = n;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
side = Side;
|
||||
}
|
||||
|
||||
bool CBattleAI::isCloser(const EnemyInfo &ei1, const EnemyInfo &ei2, const ReachabilityInfo::TDistances &dists)
|
||||
{
|
||||
return distToNearestNeighbour(ei1.s->getPosition(), dists) < distToNearestNeighbour(ei2.s->getPosition(), dists);
|
||||
}
|
||||
|
||||
void CBattleAI::print(const std::string &text) const
|
||||
{
|
||||
logAi->trace("%s Battle AI[%p]: %s", playerID.getStr(), this, text);
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include "../../lib/AI_Base.h"
|
||||
#include "../../lib/battle/ReachabilityInfo.h"
|
||||
#include "PossibleSpellcast.h"
|
||||
#include "PotentialTargets.h"
|
||||
|
||||
@ -64,13 +65,9 @@ public:
|
||||
void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only
|
||||
|
||||
BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
|
||||
BattleAction goTowards(const CStack * stack, BattleHex hex );
|
||||
|
||||
boost::optional<BattleAction> considerFleeingOrSurrendering();
|
||||
|
||||
static int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr);
|
||||
static bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists);
|
||||
|
||||
void print(const std::string &text) const;
|
||||
BattleAction useCatapult(const CStack *stack);
|
||||
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool Side) override;
|
||||
@ -88,4 +85,7 @@ public:
|
||||
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
|
||||
//void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||
//void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
||||
|
||||
private:
|
||||
BattleAction goTowards(const CStack * stack, const battle::Unit * enemy) const;
|
||||
};
|
||||
|
@ -69,31 +69,6 @@ bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
|
||||
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr)
|
||||
{
|
||||
int ret = 1000000;
|
||||
for(auto & n: hex.neighbouringTiles())
|
||||
{
|
||||
if(dists[n] >= 0 && dists[n] < ret)
|
||||
{
|
||||
ret = dists[n];
|
||||
if(chosenHex)
|
||||
*chosenHex = n;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists)
|
||||
{
|
||||
return distToNearestNeighbour(ei1.s->getPosition(), dists) < distToNearestNeighbour(ei2.s->getPosition(), dists);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool willSecondHexBlockMoreEnemyShooters(const BattleHex &h1, const BattleHex &h2)
|
||||
{
|
||||
int shooters[2] = {0}; //count of shooters on hexes
|
||||
@ -111,7 +86,7 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
{
|
||||
//boost::this_thread::sleep(boost::posix_time::seconds(2));
|
||||
print("activeStack called for " + stack->nodeName());
|
||||
auto dists = cb->battleGetDistances(stack, stack->getPosition());
|
||||
ReachabilityInfo dists = cb->getReachability(stack);
|
||||
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
|
||||
|
||||
if(stack->type->idNumber == CreatureID::CATAPULT)
|
||||
@ -179,12 +154,14 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
||||
}
|
||||
else if(enemiesUnreachable.size()) //due to #955 - a buggy battle may occur when there are no enemies
|
||||
{
|
||||
assert(enemiesUnreachable.size());
|
||||
const EnemyInfo &ei= *std::min_element(enemiesUnreachable.begin(), enemiesUnreachable.end(), std::bind(isCloser, _1, _2, std::ref(dists)));
|
||||
assert(ei.s);
|
||||
if(distToNearestNeighbour(ei.s->getPosition(), dists) < GameConstants::BFIELD_SIZE)
|
||||
auto closestEnemy = vstd::minElementByFun(enemiesUnreachable, [&](const EnemyInfo & ei) -> int
|
||||
{
|
||||
return goTowards(stack, ei.s->getPosition());
|
||||
return dists.distToNearestNeighbour(stack, ei.s);
|
||||
});
|
||||
|
||||
if(dists.distToNearestNeighbour(stack, closestEnemy->s) < GameConstants::BFIELD_SIZE)
|
||||
{
|
||||
return goTowards(stack, closestEnemy->s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,11 +229,12 @@ void CStupidAI::print(const std::string &text) const
|
||||
logAi->trace("CStupidAI [%p]: %s", this, text);
|
||||
}
|
||||
|
||||
BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
|
||||
BattleAction CStupidAI::goTowards(const CStack * stack, const CStack * enemy) const
|
||||
{
|
||||
assert(destination.isValid());
|
||||
auto reachability = cb->getReachability(stack);
|
||||
auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
|
||||
auto destination = enemy->getPosition();
|
||||
|
||||
if(vstd::contains(avHexes, destination))
|
||||
return BattleAction::makeMove(stack, destination);
|
||||
@ -269,11 +247,14 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
vstd::erase_if(destNeighbours, [&](BattleHex hex){ return !reachability.accessibility.accessible(hex, stack); });
|
||||
|
||||
if(!avHexes.size() || !destNeighbours.size()) //we are blocked or dest is blocked
|
||||
if(!avHexes.size()) //we are blocked or dest is blocked
|
||||
{
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
BattleHex bestNeighbor = destination;
|
||||
if(reachability.distToNearestNeighbour(stack, enemy, &bestNeighbor) > GameConstants::BFIELD_SIZE)
|
||||
{
|
||||
print("goTowards: Stack cannot move! That's " + stack->nodeName());
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
@ -281,32 +262,24 @@ BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
|
||||
{
|
||||
// Flying stack doesn't go hex by hex, so we can't backtrack using predecessors.
|
||||
// We just check all available hexes and pick the one closest to the target.
|
||||
auto distToDestNeighbour = [&](BattleHex hex) -> int
|
||||
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
|
||||
{
|
||||
auto nearestNeighbourToHex = vstd::minElementByFun(destNeighbours, [&](BattleHex a)
|
||||
{
|
||||
return BattleHex::getDistance(a, hex);
|
||||
return BattleHex::getDistance(bestNeighbor, hex);
|
||||
});
|
||||
|
||||
return BattleHex::getDistance(*nearestNeighbourToHex, hex);
|
||||
};
|
||||
|
||||
auto nearestAvailableHex = vstd::minElementByFun(avHexes, distToDestNeighbour);
|
||||
return BattleAction::makeMove(stack, *nearestAvailableHex);
|
||||
}
|
||||
else
|
||||
{
|
||||
BattleHex bestNeighbor = destination;
|
||||
if(distToNearestNeighbour(destination, reachability.distances, &bestNeighbor) > GameConstants::BFIELD_SIZE)
|
||||
{
|
||||
print("goTowards: Cannot reach");
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
BattleHex currentDest = bestNeighbor;
|
||||
while(1)
|
||||
{
|
||||
assert(currentDest.isValid());
|
||||
if(!currentDest.isValid())
|
||||
{
|
||||
logAi->error("CBattleAI::goTowards: internal error");
|
||||
return BattleAction::makeDefend(stack);
|
||||
}
|
||||
|
||||
if(vstd::contains(avHexes, currentDest))
|
||||
return BattleAction::makeMove(stack, currentDest);
|
||||
|
||||
|
@ -10,6 +10,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/battle/BattleHex.h"
|
||||
#include "../../lib/battle/ReachabilityInfo.h"
|
||||
|
||||
class EnemyInfo;
|
||||
|
||||
class CStupidAI : public CBattleGameInterface
|
||||
{
|
||||
@ -39,10 +42,10 @@ public:
|
||||
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
||||
|
||||
BattleAction goTowards(const CStack * stack, BattleHex hex );
|
||||
|
||||
virtual void saveGame(BinarySerializer & h, const int version) override;
|
||||
virtual void loadGame(BinaryDeserializer & h, const int version) override;
|
||||
|
||||
private:
|
||||
BattleAction goTowards(const CStack * stack, const CStack * enemy) const;
|
||||
};
|
||||
|
||||
|
@ -40,3 +40,42 @@ bool ReachabilityInfo::isReachable(BattleHex hex) const
|
||||
{
|
||||
return distances[hex] < INFINITE_DIST;
|
||||
}
|
||||
|
||||
int ReachabilityInfo::distToNearestNeighbour(
|
||||
const battle::Unit * attacker,
|
||||
const battle::Unit * defender,
|
||||
BattleHex * chosenHex) const
|
||||
{
|
||||
int ret = 1000000;
|
||||
auto defenderHexes = battle::Unit::getHexes(
|
||||
defender->getPosition(),
|
||||
defender->doubleWide(),
|
||||
defender->unitSide());
|
||||
|
||||
std::vector<BattleHex> targetableHexes;
|
||||
|
||||
for(auto defenderHex : defenderHexes)
|
||||
{
|
||||
vstd::concatenate(targetableHexes, battle::Unit::getHexes(
|
||||
defenderHex,
|
||||
attacker->doubleWide(),
|
||||
defender->unitSide()));
|
||||
}
|
||||
|
||||
vstd::removeDuplicates(targetableHexes);
|
||||
|
||||
for(auto targetableHex : targetableHexes)
|
||||
{
|
||||
for(auto & n : targetableHex.neighbouringTiles())
|
||||
{
|
||||
if(distances[n] >= 0 && distances[n] < ret)
|
||||
{
|
||||
ret = distances[n];
|
||||
if(chosenHex)
|
||||
*chosenHex = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -43,6 +43,11 @@ struct DLL_LINKAGE ReachabilityInfo
|
||||
ReachabilityInfo();
|
||||
|
||||
bool isReachable(BattleHex hex) const;
|
||||
|
||||
int distToNearestNeighbour(
|
||||
const battle::Unit * attacker,
|
||||
const battle::Unit * defender,
|
||||
BattleHex * chosenHex = nullptr) const;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user