From f1a9ae99ee5920a943ba8c71db069280c18f5e84 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sun, 30 Jul 2023 11:33:52 +0300 Subject: [PATCH] NKAI: various behavior fixes, undo max_gold_preasure --- AI/BattleAI/BattleAI.cpp | 2 +- AI/Nullkiller/AIGateway.cpp | 29 ++++------------ AI/Nullkiller/AIGateway.h | 1 - AI/Nullkiller/Analyzers/BuildAnalyzer.cpp | 2 +- AI/Nullkiller/Analyzers/ObjectClusterizer.cpp | 7 +++- AI/Nullkiller/Engine/FuzzyHelper.cpp | 10 +++--- AI/Nullkiller/Engine/Nullkiller.h | 2 +- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 34 +++++++++++++------ AI/Nullkiller/Pathfinding/AINodeStorage.h | 4 +-- config/ai/object-priorities.txt | 6 +++- 10 files changed, 49 insertions(+), 48 deletions(-) diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index d3c4ba13c..b3986de94 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -863,7 +863,7 @@ std::optional CBattleAI::considerFleeingOrSurrendering() bs.turnsSkippedByDefense = movesSkippedByDefense / bs.ourStacks.size(); - if(!bs.canFlee || !bs.canSurrender) + if(!bs.canFlee && !bs.canSurrender) { return std::nullopt; } diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 6301b9223..a8911d3b6 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -779,25 +779,21 @@ void AIGateway::makeTurn() boost::shared_lock gsLock(CGameState::mutex); setThreadName("AIGateway::makeTurn"); + cb->sendMessage("vcmieagles"); + + retrieveVisitableObjs(); + if(cb->getDate(Date::DAY_OF_WEEK) == 1) { - std::vector objs; - retrieveVisitableObjs(objs, true); - - for(const CGObjectInstance * obj : objs) + for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs) { if(isWeeklyRevisitable(obj)) { - addVisitableObj(obj); nullkiller->memory->markObjectUnvisited(obj); } } } - cb->sendMessage("vcmieagles"); - - retrieveVisitableObjs(); - #if NKAI_TRACE_LEVEL == 0 try { @@ -1106,26 +1102,13 @@ void AIGateway::waitTillFree() status.waitTillFree(); } -void AIGateway::retrieveVisitableObjs(std::vector & out, bool includeOwned) const -{ - foreach_tile_pos([&](const int3 & pos) - { - for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false)) - { - if(includeOwned || obj->tempOwner != playerID) - out.push_back(obj); - } - }); -} - void AIGateway::retrieveVisitableObjs() { foreach_tile_pos([&](const int3 & pos) { for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false)) { - if(obj->tempOwner != playerID) - addVisitableObj(obj); + addVisitableObj(obj); } }); } diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index cab6ce797..3a9c23b31 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -195,7 +195,6 @@ public: void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it - void retrieveVisitableObjs(std::vector & out, bool includeOwned = false) const; void retrieveVisitableObjs(); virtual std::vector getFlaggedObjects() const; diff --git a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp index 690437447..359ccc1ca 100644 --- a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp @@ -167,7 +167,7 @@ void BuildAnalyzer::update() else { goldPreasure = ai->getLockedResources()[EGameResID::GOLD] / 5000.0f - + (float)armyCost[EGameResID::GOLD] / (1 + ai->getFreeGold() + (float)dailyIncome[EGameResID::GOLD] * 7.0f); + + (float)armyCost[EGameResID::GOLD] / (1 + 2 * ai->getFreeGold() + (float)dailyIncome[EGameResID::GOLD] * 7.0f); } logAi->trace("Gold preasure: %f", goldPreasure); diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp index 3aee0dcbf..5e2b41977 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp @@ -227,7 +227,12 @@ void ObjectClusterizer::clusterize() auto obj = objs[i]; if(!shouldVisitObject(obj)) - return; + { +#if NKAI_TRACE_LEVEL >= 2 + logAi->trace("Skip object %s%s.", obj->getObjectName(), obj->visitablePos().toString()); +#endif + continue; + } #if NKAI_TRACE_LEVEL >= 2 logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString()); diff --git a/AI/Nullkiller/Engine/FuzzyHelper.cpp b/AI/Nullkiller/Engine/FuzzyHelper.cpp index f9ac898dc..2757cb35a 100644 --- a/AI/Nullkiller/Engine/FuzzyHelper.cpp +++ b/AI/Nullkiller/Engine/FuzzyHelper.cpp @@ -150,17 +150,15 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj) case Obj::MINE: case Obj::ABANDONED_MINE: case Obj::PANDORAS_BOX: - { - const CArmedInstance * a = dynamic_cast(obj); - return a->getArmyStrength(); - } case Obj::CRYPT: //crypt case Obj::CREATURE_BANK: //crebank case Obj::DRAGON_UTOPIA: case Obj::SHIPWRECK: //shipwreck case Obj::DERELICT_SHIP: //derelict ship - // case Obj::PYRAMID: - return estimateBankDanger(dynamic_cast(obj)); + { + const CArmedInstance * a = dynamic_cast(obj); + return a->getArmyStrength(); + } case Obj::PYRAMID: { if(obj->subID == 0) diff --git a/AI/Nullkiller/Engine/Nullkiller.h b/AI/Nullkiller/Engine/Nullkiller.h index 04578ebec..36f3504fd 100644 --- a/AI/Nullkiller/Engine/Nullkiller.h +++ b/AI/Nullkiller/Engine/Nullkiller.h @@ -23,7 +23,7 @@ namespace NKAI { -const float MAX_GOLD_PEASURE = 0.6f; +const float MAX_GOLD_PEASURE = 0.3f; const float MIN_PRIORITY = 0.01f; const float SMALL_SCAN_MIN_PRIORITY = 0.4f; diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index bbf931355..5d2e1685a 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -162,11 +162,11 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero { result += (c.data.type->getAIValue() * c.data.count) * c.chance; } - else + /*else { //we will need to discard the weakest stack result += (c.data.type->getAIValue() * c.data.count - weakestStackPower) * c.chance; - } + }*/ } result /= 100; //divide by total chance @@ -277,6 +277,8 @@ uint64_t RewardEvaluator::getArmyReward( { const float enemyArmyEliminationRewardRatio = 0.5f; + auto relations = ai->cb->getPlayerRelations(target->tempOwner, ai->playerID); + if(!target) return 0; @@ -301,7 +303,7 @@ uint64_t RewardEvaluator::getArmyReward( case Obj::DRAGON_UTOPIA: return 10000; case Obj::HERO: - return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES + return relations == PlayerRelations::ENEMIES ? enemyArmyEliminationRewardRatio * dynamic_cast(target)->getArmyStrength() : 0; case Obj::PANDORAS_BOX: @@ -319,6 +321,11 @@ uint64_t RewardEvaluator::getArmyGrowth( if(!target) return 0; + auto relations = ai->cb->getPlayerRelations(target->tempOwner, hero->tempOwner); + + if(relations != PlayerRelations::ENEMIES) + return 0; + switch(target->ID) { case Obj::TOWN: @@ -542,15 +549,18 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH case Obj::GARDEN_OF_REVELATION: case Obj::MARLETTO_TOWER: case Obj::MERCENARY_CAMP: - case Obj::SHRINE_OF_MAGIC_GESTURE: - case Obj::SHRINE_OF_MAGIC_INCANTATION: case Obj::TREE_OF_KNOWLEDGE: return 1; case Obj::LEARNING_STONE: return 1.0f / std::sqrt(hero->level); case Obj::ARENA: - case Obj::SHRINE_OF_MAGIC_THOUGHT: return 2; + case Obj::SHRINE_OF_MAGIC_INCANTATION: + return 0.2f; + case Obj::SHRINE_OF_MAGIC_GESTURE: + return 0.3f; + case Obj::SHRINE_OF_MAGIC_THOUGHT: + return 0.5f; case Obj::LIBRARY_OF_ENLIGHTENMENT: return 8; case Obj::WITCH_HUT: @@ -597,6 +607,8 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG if(!target) return 0; + auto relations = ai->cb->getPlayerRelations(target->tempOwner, hero->tempOwner); + const int dailyIncomeMultiplier = 5; const float enemyArmyEliminationGoldRewardRatio = 0.2f; const int32_t heroEliminationBonus = GameConstants::HERO_GOLD_COST / 2; @@ -637,7 +649,7 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG //Objectively saves us 2500 to hire hero return GameConstants::HERO_GOLD_COST; case Obj::HERO: - return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES + return relations == PlayerRelations::ENEMIES ? heroEliminationBonus + enemyArmyEliminationGoldRewardRatio * getArmyCost(dynamic_cast(target)) : 0; default: @@ -788,7 +800,7 @@ public: if(heroRole == HeroRole::MAIN) evaluationContext.heroRole = heroRole; - if (target && ai->cb->getPlayerRelations(target->tempOwner, hero->tempOwner) == PlayerRelations::ENEMIES) + if (target) { evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero); evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold); @@ -1022,7 +1034,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task) + (evaluationContext.skillReward > 0 ? 1 : 0) + (evaluationContext.strategicalValue > 0 ? 1 : 0); - auto goldRewardPerTurn = evaluationContext.goldReward / std::log2f(evaluationContext.movementCost * 10); + float goldRewardPerTurn = evaluationContext.goldReward / std::log2f(2 + evaluationContext.movementCost * 10); double result = 0; @@ -1055,13 +1067,13 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task) } #if NKAI_TRACE_LEVEL >= 2 - logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f", + logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f", task->toString(), evaluationContext.armyLossPersentage, (int)evaluationContext.turn, evaluationContext.movementCostByRole[HeroRole::MAIN], evaluationContext.movementCostByRole[HeroRole::SCOUT], - evaluationContext.goldReward, + goldRewardPerTurn, evaluationContext.goldCost, evaluationContext.armyReward, evaluationContext.danger, diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index 02364ad11..c127f294b 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -11,7 +11,7 @@ #pragma once #define NKAI_PATHFINDER_TRACE_LEVEL 0 -#define NKAI_TRACE_LEVEL 2 +#define NKAI_TRACE_LEVEL 0 #include "../../../lib/pathfinder/CGPathNode.h" #include "../../../lib/pathfinder/INodeStorage.h" @@ -258,7 +258,7 @@ public: { double ratio = (double)danger / (armyValue * hero->getFightingStrength()); - return (uint64_t)(armyValue * ratio * ratio * ratio); + return (uint64_t)(armyValue * ratio * ratio); } STRONG_INLINE diff --git a/config/ai/object-priorities.txt b/config/ai/object-priorities.txt index d5439ee18..1989bc3c7 100644 --- a/config/ai/object-priorities.txt +++ b/config/ai/object-priorities.txt @@ -170,7 +170,6 @@ RuleBlock: basic rule: if heroRole is SCOUT and turn is NOW and mainTurnDistance is MEDIUM then Value is BAD rule: if heroRole is SCOUT and turn is NEXT and mainTurnDistance is LONG then Value is BAD rule: if heroRole is SCOUT and turn is NOW and scoutTurnDistance is LONG then Value is BAD - rule: if heroRole is SCOUT and turn is NOW and scoutTurnDistance is MEDIUM then Value is BAD with 0.3 rule: if heroRole is SCOUT and fear is HIGH then Value is BAD with 0.8 rule: if heroRole is SCOUT and fear is MEDIUM then Value is BAD with 0.5 rule: if heroRole is MAIN and fear is HIGH then Value is BAD with 0.5 @@ -252,6 +251,11 @@ RuleBlock: gold rule: if goldReward is MEDIUM and goldPreasure is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is BITHIGH rule: if goldReward is SMALL and goldPreasure is HIGH and heroRole is SCOUT and danger is NONE then Value is MEDIUM rule: if goldReward is SMALL and goldPreasure is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is SMALL + rule: if goldReward is LOWEST then Value is SMALL with 0.1 + rule: if goldReward is SMALL then Value is SMALL with 0.2 + rule: if goldReward is MEDIUM then Value is SMALL with 0.5 + rule: if goldReward is BIG then Value is SMALL + rule: if goldReward is HUGE then Value is BITHIGH RuleBlock: skill reward enabled: true conjunction: AlgebraicProduct