Browse Source

Trading logic

Added trading-logic to Nullkiller-AI.
The AI can now identify which resources it is lacking the most and buy them to fix softlocks in their build-order. It can also sell excess resources that it doesn't have a need for.
Xilmi 1 year ago
parent
commit
2d715f4d7e
2 changed files with 110 additions and 14 deletions
  1. 109 14
      AI/Nullkiller/Engine/Nullkiller.cpp
  2. 1 0
      AI/Nullkiller/Engine/Nullkiller.h

+ 109 - 14
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -352,16 +352,18 @@ void Nullkiller::makeTurn()
 	const int MAX_DEPTH = 10;
 	const float FAST_TASK_MINIMAL_PRIORITY = 0.7f;
 
-	//float totalHeroStrength = 0;
-	//int totalTownLevel = 0;
-	//for (auto heroInfo : cb->getHeroesInfo())
-	//{
-	//	totalHeroStrength += heroInfo->getTotalStrength();
-	//}
-	//for (auto townInfo : cb->getTownsInfo())
-	//{
-	//	totalTownLevel += townInfo->getTownLevel();
-	//}
+	float totalHeroStrength = 0;
+	int totalTownLevel = 0;
+	for (auto heroInfo : cb->getHeroesInfo())
+	{
+		totalHeroStrength += heroInfo->getTotalStrength();
+	}
+	for (auto townInfo : cb->getTownsInfo())
+	{
+		totalTownLevel += townInfo->getTownLevel();
+	}
+	logAi->info("Resources: %s Strength: %f Townlevel: %d", cb->getResourceAmount().toString(), totalHeroStrength, totalTownLevel);
+
 
 	resetAiState();
 
@@ -371,7 +373,6 @@ void Nullkiller::makeTurn()
 	{
 		auto start = std::chrono::high_resolution_clock::now();
 		updateAiState(i);
-		//logAi->info("Gold: %d", cb->getResourceAmount(EGameResID::GOLD));
 
 		Goals::TTask bestTask = taskptr(Goals::Invalid());
 
@@ -386,7 +387,7 @@ void Nullkiller::makeTurn()
 
 			if(bestTask->priority >= FAST_TASK_MINIMAL_PRIORITY)
 			{
-				//logAi->info("Performing task %s with prio: %d", bestTask->toString(), bestTask->priority);
+				logAi->info("Performing task %s with prio: %d", bestTask->toString(), bestTask->priority);
 				if(!executeTask(bestTask))
 					return;
 
@@ -483,7 +484,7 @@ void Nullkiller::makeTurn()
 
 				continue;
 			}
-			//logAi->info("Performing task %s with prio: %d", bestTask->toString(), bestTask->priority);
+			logAi->info("Performing task %s with prio: %d", bestTask->toString(), bestTask->priority);
 			if(!executeTask(bestTask))
 			{
 				if(hasAnySuccess)
@@ -491,10 +492,11 @@ void Nullkiller::makeTurn()
 				else
 					return;
 			}
-
 			hasAnySuccess = true;
 		}
 
+		hasAnySuccess |= handleTrading();
+
 		if(!hasAnySuccess)
 		{
 			logAi->trace("Nothing was done this turn. Ending turn.");
@@ -574,4 +576,97 @@ void Nullkiller::lockResources(const TResources & res)
 	lockedResources += res;
 }
 
+bool Nullkiller::handleTrading()
+{
+	bool haveTraded = false;
+	bool shouldTryToTrade = true;
+	int marketId = -1;
+	for (auto town : cb->getTownsInfo())
+	{
+		if (town->hasBuiltSomeTradeBuilding())
+		{
+			marketId = town->id;
+		}
+	}
+	if (marketId == -1)
+		return false;
+	if (const CGObjectInstance* obj = cb->getObj(ObjectInstanceID(marketId), false))
+	{
+		if (const auto* m = dynamic_cast<const IMarket*>(obj))
+		{
+			while (shouldTryToTrade)
+			{
+				shouldTryToTrade = false;
+				buildAnalyzer->update();
+				TResources required = buildAnalyzer->getTotalResourcesRequired();
+				TResources income = buildAnalyzer->getDailyIncome();
+				TResources available = getFreeResources();
+
+				int mostWanted = -1;
+				int mostExpendable = -1;
+				float minRatio = std::numeric_limits<float>::max();
+				float maxRatio = std::numeric_limits<float>::min();
+
+				for (int i = 0; i < required.size(); ++i)
+				{
+					if (required[i] == 0)
+						continue;
+					float ratio = static_cast<float>(available[i]) / required[i];
+
+					if (ratio < minRatio) {
+						minRatio = ratio;
+						mostWanted = i;
+					}
+				}
+
+				for (int i = 0; i < required.size(); ++i)
+				{
+					float ratio = available[i];
+					if (required[i] > 0)
+						ratio = static_cast<float>(available[i]) / required[i];
+
+					bool okToSell = false;
+
+					if (i == 6)
+					{
+						if (income[i] > 0)
+							okToSell = true;
+					}
+					else
+					{
+						if (available[i] > required[i])
+							okToSell = true;
+					}
+
+					if (ratio > maxRatio && okToSell) {
+						maxRatio = ratio;
+						mostExpendable = i;
+					}
+				}
+
+				//logAi->info("mostExpendable: %d mostWanted: %d", mostExpendable, mostWanted);
+
+				if (mostExpendable == mostWanted || mostWanted == -1 || mostExpendable == -1)
+					return false;
+
+				int acquiredResources = 0;
+
+				int toGive;
+				int toGet;
+				m->getOffer(mostExpendable, mostWanted, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
+				//logAi->info("Offer is: I get %d of %s for %d of %s at %s", toGet, mostWanted, toGive, mostExpendable, obj->getObjectName());
+				//TODO trade only as much as needed
+				if (toGive && toGive <= available[mostExpendable]) //don't try to sell 0 resources
+				{
+					cb->trade(m, EMarketMode::RESOURCE_RESOURCE, GameResID(mostExpendable), GameResID(mostWanted), toGive);
+					logAi->info("Traded %d of %s for %d of %s at %s", toGive, mostExpendable, toGet, mostWanted, obj->getObjectName());
+					haveTraded = true;
+					shouldTryToTrade = true;
+				}
+			}
+		}
+	}
+	return haveTraded;
+}
+
 }

+ 1 - 0
AI/Nullkiller/Engine/Nullkiller.h

@@ -120,6 +120,7 @@ public:
 	ScanDepth getScanDepth() const { return scanDepth; }
 	bool isOpenMap() const { return openMap; }
 	bool isObjectGraphAllowed() const { return useObjectGraph; }
+	bool handleTrading();
 
 private:
 	void resetAiState();