Explorar o código

Merge pull request #4292 from vcmi/nkai-reduce-double-army-losses

NKAI: reduce double army loss cases
Andrii Danylchenko hai 1 ano
pai
achega
e227fa1691

+ 9 - 0
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)

+ 22 - 0
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -1385,6 +1385,28 @@ void AINodeStorage::calculateChainInfo(std::vector<AIPath> & 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),

+ 39 - 13
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<AIPath> & paths, int3 tile, const CGHe
 
 		std::vector<GraphPathNodePointer> 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<AIPath> & 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<AIPath> & 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<AIPath> & 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<AIPath> & 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<AIPath> & paths, int3
 
 		std::vector<GraphPathNodePointer> 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<AIPath> & 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<AIPath> & 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<AIPath> & paths, int3
 				n.coord = graphTile->coord;
 				n.cost = node.cost;
 				n.turns = static_cast<ui8>(node.cost);
-				n.danger = node.danger;
+				n.danger = danger;
 				n.specialAction = node.specialAction;
 				n.parentIndex = path.nodes.size();
 

+ 1 - 1
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> specialAction;
 

+ 2 - 2
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<CPlayerInterface>(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);