Skip to content

Commit

Permalink
Correct AI hero task management (#3794)
Browse files Browse the repository at this point in the history
  • Loading branch information
ihhub authored Jul 4, 2021
1 parent 9d977c8 commit 3626550
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 13 deletions.
5 changes: 4 additions & 1 deletion src/fheroes2/ai/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ namespace AI
virtual void CastleTurn( Castle & castle, bool defensive );
virtual void BattleTurn( Battle::Arena & arena, const Battle::Unit & unit, Battle::Actions & actions );
virtual void HeroTurn( Heroes & hero );
virtual void HeroesTurn( VecHeroes & ) {}
virtual bool HeroesTurn( VecHeroes & )
{
return true;
}

virtual void revealFog( const Maps::Tiles & tile );

Expand Down
2 changes: 1 addition & 1 deletion src/fheroes2/ai/normal/ai_normal.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ namespace AI
void KingdomTurn( Kingdom & kingdom ) override;
void CastleTurn( Castle & castle, bool defensive ) override;
void BattleTurn( Battle::Arena & arena, const Battle::Unit & currentUnit, Battle::Actions & actions ) override;
void HeroesTurn( VecHeroes & heroes ) override;
bool HeroesTurn( VecHeroes & heroes ) override;

void revealFog( const Maps::Tiles & tile ) override;

Expand Down
9 changes: 9 additions & 0 deletions src/fheroes2/ai/normal/ai_normal_castle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ namespace AI
{DWELLING_MONSTER2, 3}, {DWELLING_MONSTER1, 4}, {BUILD_MAGEGUILD1, 2}, {BUILD_WEL2, 10}, {BUILD_TAVERN, 5}, {BUILD_THIEVESGUILD, 10},
{BUILD_MAGEGUILD2, 3}, {BUILD_MAGEGUILD3, 4}, {BUILD_MAGEGUILD4, 5}, {BUILD_MAGEGUILD5, 5}, {BUILD_SHIPYARD, 4}, {BUILD_MARKETPLACE, 10}};

static const std::vector<BuildOrder> barbarianBuildOrder
= { { BUILD_CASTLE, 2 }, { BUILD_STATUE, 1 }, { DWELLING_MONSTER6, 1 }, { DWELLING_UPGRADE5, 1 }, { DWELLING_MONSTER5, 1 },
{ DWELLING_UPGRADE4, 1 }, { DWELLING_MONSTER4, 1 }, { DWELLING_MONSTER3, 1 }, { DWELLING_UPGRADE2, 2 }, { DWELLING_MONSTER2, 2 },
{ DWELLING_MONSTER1, 4 }, { BUILD_MAGEGUILD1, 3 }, { BUILD_WEL2, 10 }, { BUILD_TAVERN, 5 }, { BUILD_THIEVESGUILD, 10 },
{ BUILD_MAGEGUILD2, 4 }, { BUILD_MAGEGUILD3, 5 }, { BUILD_MAGEGUILD4, 6 }, { BUILD_MAGEGUILD5, 7 }, { BUILD_SHIPYARD, 4 },
{ BUILD_MARKETPLACE, 10 } };

static const std::vector<BuildOrder> sorceressBuildOrder
= { { BUILD_CASTLE, 2 }, { BUILD_STATUE, 1 }, { DWELLING_MONSTER6, 1 }, { DWELLING_MONSTER5, 1 }, { DWELLING_MONSTER4, 1 },
{ BUILD_MAGEGUILD1, 1 }, { DWELLING_MONSTER3, 1 }, { DWELLING_UPGRADE4, 1 }, { DWELLING_UPGRADE3, 2 }, { DWELLING_UPGRADE2, 5 },
Expand Down Expand Up @@ -101,6 +108,8 @@ namespace AI
return wizardBuildOrder;
case Race::SORC:
return sorceressBuildOrder;
case Race::BARB:
return barbarianBuildOrder;
default:
break;
}
Expand Down
51 changes: 43 additions & 8 deletions src/fheroes2/ai/normal/ai_normal_hero.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ namespace AI
}
}

void Normal::HeroesTurn( VecHeroes & heroes )
bool Normal::HeroesTurn( VecHeroes & heroes )
{
std::vector<HeroToMove> availableHeroes;

Expand Down Expand Up @@ -843,18 +843,44 @@ namespace AI
}
}

const double originalMonsterStrengthMultipler = _pathfinder.getCurrentArmyStrengthMultiplier();

const int monsterStrengthMultiplierCount = 2;
const double monsterStrengthMultipliers[monsterStrengthMultiplierCount] = { ARMY_STRENGTH_ADVANTAGE_MEDUIM, ARMY_STRENGTH_ADVANTAGE_SMALL };

while ( !availableHeroes.empty() ) {
Heroes * bestHero = availableHeroes.front().hero;
double maxPriority = 0;
int bestTargetIndex = -1;

for ( HeroToMove & heroInfo : availableHeroes ) {
double priority = -1;
const int targetIndex = getPriorityTarget( *heroInfo.hero, priority, heroInfo.patrolCenter, heroInfo.patrolDistance );
if ( targetIndex != -1 && ( priority > maxPriority || bestTargetIndex == -1 ) ) {
maxPriority = priority;
bestTargetIndex = targetIndex;
bestHero = heroInfo.hero;
while ( true ) {
for ( const HeroToMove & heroInfo : availableHeroes ) {
double priority = -1;
const int targetIndex = getPriorityTarget( *heroInfo.hero, priority, heroInfo.patrolCenter, heroInfo.patrolDistance );
if ( targetIndex != -1 && ( priority > maxPriority || bestTargetIndex == -1 ) ) {
maxPriority = priority;
bestTargetIndex = targetIndex;
bestHero = heroInfo.hero;
}
}

if ( bestTargetIndex != -1 ) {
break;
}

// If nowhere to move perhaps it's because of high monster estimation. Let's reduce it.
const double currentMonsterStrengthMultipler = _pathfinder.getCurrentArmyStrengthMultiplier();
bool setNewMultipler = false;
for ( int i = 0; i < monsterStrengthMultiplierCount; ++i ) {
if ( currentMonsterStrengthMultipler > monsterStrengthMultipliers[i] ) {
_pathfinder.setArmyStrengthMultplier( monsterStrengthMultipliers[i] );
setNewMultipler = true;
break;
}
}

if ( !setNewMultipler ) {
break;
}
}

Expand Down Expand Up @@ -882,6 +908,7 @@ namespace AI

if ( bestTargetIndex == -1 ) {
// Nothing to do. Stop everything
_pathfinder.setArmyStrengthMultplier( originalMonsterStrengthMultipler );
break;
}
}
Expand All @@ -908,12 +935,20 @@ namespace AI

++i;
}

_pathfinder.setArmyStrengthMultplier( originalMonsterStrengthMultipler );
}

const bool allHeroesMoved = availableHeroes.empty();

for ( HeroToMove & heroInfo : availableHeroes ) {
if ( !heroInfo.hero->MayStillMove() ) {
heroInfo.hero->SetModes( Heroes::MOVED );
}
}

_pathfinder.setArmyStrengthMultplier( originalMonsterStrengthMultipler );

return allHeroesMoved;
}
}
4 changes: 2 additions & 2 deletions src/fheroes2/ai/normal/ai_normal_kingdom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ namespace AI
heroLimit = 2;

// Step 3. Do some hero stuff.
HeroesTurn( heroes );
const bool moreTasksForHeroes = HeroesTurn( heroes );

status.RedrawTurnProgress( 6 );

Expand All @@ -183,7 +183,7 @@ namespace AI
VecCastles sortedCastleList( castles );
sortedCastleList.SortByBuildingValue();

if ( heroes.size() < static_cast<size_t>( heroLimit ) ) { // safe to cast as heroLimit is > 0
if ( ( moreTasksForHeroes && heroes.size() < static_cast<size_t>( heroLimit ) ) || heroes.empty() ) { // safe to cast as heroLimit is > 0
Recruits & rec = kingdom.GetRecruits();
Castle * recruitmentCastle = nullptr;
int lowestHeroCount = heroLimit;
Expand Down
10 changes: 9 additions & 1 deletion src/fheroes2/world/world_pathfinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ int AIWorldPathfinder::getNeareastTileToMove( const Heroes & hero )

for ( size_t i = 0; i < directions.size(); ++i ) {
if ( Maps::isValidDirection( start, directions[i] ) ) {
const int newIndex = start + _mapOffset[i];
const int newIndex = Maps::GetDirectionIndex( start, directions[i] );
if ( newIndex == start )
continue;

Expand Down Expand Up @@ -576,3 +576,11 @@ uint32_t AIWorldPathfinder::getDistance( int start, int targetIndex, int color,
reEvaluateIfNeeded( start, color, armyStrength, skill );
return _cache[targetIndex]._cost;
}

void AIWorldPathfinder::setArmyStrengthMultplier( const double multiplier )
{
if ( multiplier > 0 && std::fabs( _advantage - multiplier ) > 0.001 ) {
_advantage = multiplier;
reset();
}
}
7 changes: 7 additions & 0 deletions src/fheroes2/world/world_pathfinding.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ class AIWorldPathfinder : public WorldPathfinder
// Faster, but does not re-evaluate the map (expose base class method)
using Pathfinder::getDistance;

double getCurrentArmyStrengthMultiplier() const
{
return _advantage;
}

void setArmyStrengthMultplier( const double multiplier );

private:
void processCurrentNode( std::vector<int> & nodesToExplore, int pathStart, int currentNodeIdx, bool fromWater ) override;

Expand Down

0 comments on commit 3626550

Please sign in to comment.