From 37dc2a38e8df63887d7a5128f7e744f0eb9fdd40 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Thu, 18 Jul 2024 13:37:18 +0300 Subject: [PATCH] NKAI: reduce double army loss cases --- AI/Nullkiller/Goals/ExecuteHeroChain.cpp | 9 ++++ AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 22 +++++++++ AI/Nullkiller/Pathfinding/GraphPaths.cpp | 52 +++++++++++++++------ AI/Nullkiller/Pathfinding/GraphPaths.h | 2 +- client/ClientCommandManager.cpp | 4 +- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp index d69b529c7..95b5ca725 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp @@ -26,7 +26,12 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * if(obj) { objid = obj->id.getNum(); + +#if NKAI_TRACE_LEVEL >= 1 + targetName = obj->getObjectName() + tile.toString(); +#else targetName = obj->typeName + tile.toString(); +#endif } else { @@ -260,7 +265,11 @@ void ExecuteHeroChain::accept(AIGateway * ai) std::string ExecuteHeroChain::toString() const { +#if NKAI_TRACE_LEVEL >= 1 + return "ExecuteHeroChain " + targetName + " by " + chainPath.toString(); +#else return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->getNameTranslated(); +#endif } bool ExecuteHeroChain::moveHeroToTile(AIGateway * ai, const CGHeroInstance * hero, const int3 & tile) diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index da7abceba..b4fb42cf2 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -1385,6 +1385,28 @@ void AINodeStorage::calculateChainInfo(std::vector & paths, const int3 & path.armyLoss = node.armyLoss; path.targetObjectDanger = evaluateDanger(pos, path.targetHero, !node.actor->allowBattle); + if(path.targetObjectDanger > 0) + { + if(node.theNodeBefore) + { + auto prevNode = getAINode(node.theNodeBefore); + + if(node.coord == prevNode->coord && node.actor->hero == prevNode->actor->hero) + { + paths.pop_back(); + continue; + } + else + { + path.armyLoss = prevNode->armyLoss; + } + } + else + { + path.armyLoss = 0; + } + } + path.targetObjectArmyLoss = evaluateArmyLoss( path.targetHero, getHeroArmyStrengthWithCommander(path.targetHero, path.heroArmy), diff --git a/AI/Nullkiller/Pathfinding/GraphPaths.cpp b/AI/Nullkiller/Pathfinding/GraphPaths.cpp index 150d37c1a..a3dba0696 100644 --- a/AI/Nullkiller/Pathfinding/GraphPaths.cpp +++ b/AI/Nullkiller/Pathfinding/GraphPaths.cpp @@ -160,7 +160,7 @@ void GraphPaths::dumpToLog() const node.previous.coord.toString(), tile.first.toString(), node.cost, - node.danger); + node.linkDanger); } logBuilder.addLine(node.previous.coord, tile.first); @@ -169,14 +169,17 @@ void GraphPaths::dumpToLog() const }); } -bool GraphPathNode::tryUpdate(const GraphPathNodePointer & pos, const GraphPathNode & prev, const ObjectLink & link) +bool GraphPathNode::tryUpdate( + const GraphPathNodePointer & pos, + const GraphPathNode & prev, + const ObjectLink & link) { auto newCost = prev.cost + link.cost; if(newCost < cost) { previous = pos; - danger = prev.danger + link.danger; + linkDanger = link.danger; cost = newCost; return true; @@ -199,7 +202,7 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe std::vector tilesToPass; - uint64_t danger = node.danger; + uint64_t danger = node.linkDanger; float cost = node.cost; bool allowBattle = false; @@ -212,13 +215,13 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe if(currentTile == pathNodes.end()) break; - auto currentNode = currentTile->second[current.nodeType]; + auto & currentNode = currentTile->second[current.nodeType]; if(!currentNode.previous.valid()) break; allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE; - vstd::amax(danger, currentNode.danger); + vstd::amax(danger, currentNode.linkDanger); vstd::amax(cost, currentNode.cost); tilesToPass.push_back(current); @@ -239,9 +242,13 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe if(path.targetHero != hero) continue; - for(auto graphTile = tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++) + uint64_t loss = 0; + uint64_t strength = getHeroArmyStrengthWithCommander(path.targetHero, path.heroArmy); + + for(auto graphTile = ++tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++) { AIPathNodeInfo n; + auto & node = getNode(*graphTile); n.coord = graphTile->coord; n.cost = cost; @@ -249,7 +256,21 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe n.danger = danger; n.targetHero = hero; n.parentIndex = -1; - n.specialAction = getNode(*graphTile).specialAction; + n.specialAction = node.specialAction; + + if(node.linkDanger > 0) + { + auto additionalLoss = ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, strength, node.linkDanger); + loss += additionalLoss; + + if(strength > additionalLoss) + strength -= additionalLoss; + else + { + strength = 0; + break; + } + } if(n.specialAction) { @@ -264,7 +285,12 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe path.nodes.insert(path.nodes.begin(), n); } - path.armyLoss += ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), danger); + if(strength == 0) + { + continue; + } + + path.armyLoss += loss; path.targetObjectDanger = ai->pathfinder->getStorage()->evaluateDanger(tile, path.targetHero, !allowBattle); path.targetObjectArmyLoss = ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger); @@ -287,7 +313,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 std::vector tilesToPass; - uint64_t danger = targetNode.danger; + uint64_t danger = targetNode.linkDanger; float cost = targetNode.cost; bool allowBattle = false; @@ -303,7 +329,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 auto currentNode = currentTile->second[current.nodeType]; allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE; - vstd::amax(danger, currentNode.danger); + vstd::amax(danger, currentNode.linkDanger); vstd::amax(cost, currentNode.cost); tilesToPass.push_back(current); @@ -341,7 +367,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 // final node n.coord = tile; n.cost = targetNode.cost; - n.danger = targetNode.danger; + n.danger = danger; n.parentIndex = path.nodes.size(); path.nodes.push_back(n); @@ -368,7 +394,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 n.coord = graphTile->coord; n.cost = node.cost; n.turns = static_cast(node.cost); - n.danger = node.danger; + n.danger = danger; n.specialAction = node.specialAction; n.parentIndex = path.nodes.size(); diff --git a/AI/Nullkiller/Pathfinding/GraphPaths.h b/AI/Nullkiller/Pathfinding/GraphPaths.h index 83167eabe..a41781f68 100644 --- a/AI/Nullkiller/Pathfinding/GraphPaths.h +++ b/AI/Nullkiller/Pathfinding/GraphPaths.h @@ -67,7 +67,7 @@ struct GraphPathNode GrapthPathNodeType nodeType = GrapthPathNodeType::NORMAL; GraphPathNodePointer previous; float cost = BAD_COST; - uint64_t danger = 0; + uint64_t linkDanger = 0; const CGObjectInstance * obj = nullptr; std::shared_ptr specialAction; diff --git a/client/ClientCommandManager.cpp b/client/ClientCommandManager.cpp index 3119c7f68..b260ff4fb 100644 --- a/client/ClientCommandManager.cpp +++ b/client/ClientCommandManager.cpp @@ -89,7 +89,7 @@ void ClientCommandManager::handleGoSoloCommand() // unlikely it will work but just in case to be consistent for(auto & color : CSH->getAllClientPlayers(CSH->logicConnection->connectionID)) { - if(CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) + if(color.isValidPlayer() && CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) { CSH->client->installNewPlayerInterface(std::make_shared(color), color); } @@ -102,7 +102,7 @@ void ClientCommandManager::handleGoSoloCommand() for(auto & color : CSH->getAllClientPlayers(CSH->logicConnection->connectionID)) { - if(CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) + if(color.isValidPlayer() && CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) { auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(color), false, false); printCommandMessage("Player " + color.toString() + " will be lead by " + AiToGive, ELogLevel::INFO);