浏览代码

Merge pull request #494 from dydzio0614/AiTownDevelopmentFixes

AI town development fixes
Alexander Shishkin 7 年之前
父节点
当前提交
0f1e853b4a
共有 5 个文件被更改,包括 85 次插入44 次删除
  1. 5 0
      AI/VCAI/AIhelper.cpp
  2. 1 0
      AI/VCAI/AIhelper.h
  3. 43 32
      AI/VCAI/BuildingManager.cpp
  4. 1 0
      AI/VCAI/BuildingManager.h
  5. 35 12
      AI/VCAI/Goals.cpp

+ 5 - 0
AI/VCAI/AIhelper.cpp

@@ -48,6 +48,11 @@ bool AIhelper::getBuildingOptions(const CGTownInstance * t)
 	return buildingManager->getBuildingOptions(t);
 }
 
+BuildingID AIhelper::getMaxPossibleGoldBuilding(const CGTownInstance * t)
+{
+	return buildingManager->getMaxPossibleGoldBuilding(t);
+}
+
 boost::optional<PotentialBuilding> AIhelper::immediateBuilding() const
 {
 	return buildingManager->immediateBuilding();

+ 1 - 0
AI/VCAI/AIhelper.h

@@ -49,6 +49,7 @@ public:
 	bool hasTasksLeft() const override;
 
 	bool getBuildingOptions(const CGTownInstance * t) override;
+	BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
 	boost::optional<PotentialBuilding> immediateBuilding() const override;
 	boost::optional<PotentialBuilding> expensiveBuilding() const override;
 	boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;

+ 43 - 32
AI/VCAI/BuildingManager.cpp

@@ -137,18 +137,18 @@ void BuildingManager::setAI(VCAI * AI)
 	ai = AI;
 }
 //Set of buildings for different goals. Does not include any prerequisites.
-static const BuildingID essential[] = { BuildingID::TAVERN, BuildingID::TOWN_HALL };
-static const BuildingID goldSource[] = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL };
-static const BuildingID capitolRequirements[] = { BuildingID::FORT, BuildingID::CITADEL };
-static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
+static const std::vector<BuildingID> essential = { BuildingID::TAVERN, BuildingID::TOWN_HALL };
+static const std::vector<BuildingID> basicGoldSource = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL };
+static const std::vector<BuildingID> capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL };
+static const std::vector<BuildingID> unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
 BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7 };
-static const BuildingID unitsUpgrade[] = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
+static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
 BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP };
-static const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
+static const std::vector<BuildingID> unitGrowth = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
 BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR };
-static const BuildingID _spells[] = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
+static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
 BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 };
-static const BuildingID extra[] = { BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
+static const std::vector<BuildingID> extra = { BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
 BuildingID::SPECIAL_4, BuildingID::SHIPYARD }; // all remaining buildings
 
 bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
@@ -157,56 +157,55 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
 	//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
 	//TODO: build resource silo, defences when needed
 	//Possible - allow "locking" on specific building (build prerequisites and then building itself)
+	
+	//TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal.
 
 	immediateBuildings.clear();
 	expensiveBuildings.clear();
 
-	//below algorithm focuses on economy growth at start of the game.
+	//below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal
+	//changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings
 
 	TResources currentRes = cb->getResourceAmount();
 	TResources currentIncome = t->dailyIncome();
 
-	if (tryBuildAnyStructure(t, std::vector<BuildingID>(essential, essential + ARRAY_COUNT(essential))))
+	if(tryBuildAnyStructure(t, essential))
 		return true;
 
-	//the more gold the better and less problems later
-	if (tryBuildNextStructure(t, std::vector<BuildingID>(goldSource, goldSource + ARRAY_COUNT(goldSource))))
+	//the more gold the better and less problems later //TODO: what about building mage guild / marketplace etc. with city hall disabled in editor?
+	if(tryBuildNextStructure(t, basicGoldSource))
 		return true;
 
-	//workaround for mantis #2696 - build fort and citadel - building castle will be handled without bug
-	if (vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) &&
-		cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
+	//workaround for mantis #2696 - build capitol with separate algorithm if it is available
+	if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
 	{
-		if (cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
-		{
-			if (tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements,
-				capitolRequirements + ARRAY_COUNT(capitolRequirements))))
-				return true;
-		}
+		if(tryBuildNextStructure(t, capitolAndRequirements))
+			return true;
 	}
 
-	//TODO: save money for capitol or city hall if capitol unavailable
-	//do not build other things (unless gold source buildings are disabled in map editor)
+	if(!t->hasBuilt(BuildingID::FORT)) //in vast majority of situations fort is top priority building if we already have city hall, TODO: unite with unitGrowth building chain
+		if(tryBuildThisStructure(t, BuildingID::FORT))
+			return true;
+
 
 
 	if (cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
 	{
-		if (tryBuildNextStructure(t, std::vector<BuildingID>(unitGrowth, unitGrowth + ARRAY_COUNT(unitGrowth)), 2))
+		if (tryBuildNextStructure(t, unitGrowth, 2))
 			return true;
 	}
 
-	// first in-game week or second half of any week: try build dwellings
-	if (cb->getDate(Date::DAY) < 7 || cb->getDate(Date::DAY_OF_WEEK) > 3)
+	//try building dwellings
+	if (t->hasBuilt(BuildingID::FORT))
 	{
-		if (tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource,
-			unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
+		if (tryBuildAnyStructure(t, unitsSource, 8 - cb->getDate(Date::DAY_OF_WEEK)))
 			return true;
 	}
 
 	//try to upgrade dwelling
-	for (int i = 0; i < ARRAY_COUNT(unitsUpgrade); i++)
+	for (int i = 0; i < unitsUpgrade.size(); i++)
 	{
-		if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]))
+		if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT))
 		{
 			if (tryBuildThisStructure(t, unitsUpgrade[i]))
 				return true;
@@ -214,9 +213,9 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
 	}
 
 	//remaining tasks
-	if (tryBuildNextStructure(t, std::vector<BuildingID>(_spells, _spells + ARRAY_COUNT(_spells))))
+	if (tryBuildNextStructure(t, _spells))
 		return true;
-	if (tryBuildAnyStructure(t, std::vector<BuildingID>(extra, extra + ARRAY_COUNT(extra))))
+	if (tryBuildAnyStructure(t, extra))
 		return true;
 
 	//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
@@ -232,6 +231,18 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
 	return false;
 }
 
+BuildingID BuildingManager::getMaxPossibleGoldBuilding(const CGTownInstance * t)
+{
+	if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
+		return BuildingID::CAPITOL;
+	else if(cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
+		return BuildingID::CITY_HALL;
+	else if(cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
+		return BuildingID::TOWN_HALL;
+	else
+		return BuildingID::VILLAGE_HALL;
+}
+
 boost::optional<PotentialBuilding> BuildingManager::immediateBuilding() const
 {
 	if (immediateBuildings.size())

+ 1 - 0
AI/VCAI/BuildingManager.h

@@ -51,6 +51,7 @@ public:
 
 	//try build anything in given town, and execute resulting Goal if any
 	bool getBuildingOptions(const CGTownInstance * t) override;
+	BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
 	boost::optional<PotentialBuilding> immediateBuilding() const override;
 	boost::optional<PotentialBuilding> expensiveBuilding() const override;
 	boost::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;

+ 35 - 12
AI/VCAI/Goals.cpp

@@ -1227,10 +1227,10 @@ TSubgoal GatherTroops::whatToDoToAchieve()
 			{
 				dwellings.push_back(t);
 			}
-			else
-			{
+			/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
+			{	
 				return sptr(Goals::BuildThis(bid, t).setpriority(priority));
-			}
+			}*/
 		}
 	}
 	for(auto obj : ai->visitableObjs)
@@ -1364,18 +1364,39 @@ TGoalVec Goals::Build::getAllPossibleSubgoals()
 	{
 		//start fresh with every town
 		ai->ah->getBuildingOptions(t);
-		auto ib = ai->ah->immediateBuilding();
-		if (ib.is_initialized())
+		auto immediateBuilding = ai->ah->immediateBuilding();
+		auto expensiveBuilding = ai->ah->expensiveBuilding();
+
+		//handling for early town development to save money and focus on income
+		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.is_initialized())
+		{		
+			auto potentialBuilding = expensiveBuilding.get();
+			switch(expensiveBuilding.get().bid)
+			{
+			case BuildingID::TOWN_HALL:
+			case BuildingID::CITY_HALL:
+			case BuildingID::CAPITOL:
+			case BuildingID::FORT:
+			case BuildingID::CITADEL:
+			case BuildingID::CASTLE:
+				//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
+				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(Goals::BuildThis(potentialBuilding.bid, t).setpriority(2.25)));
+				ret.push_back(goal);
+				return ret;
+				break;
+			}
+		}
+
+		if (immediateBuilding.is_initialized())
 		{
-			ret.push_back(sptr(Goals::BuildThis(ib.get().bid, t).setpriority(2))); //prioritize buildings we can build quick
+			ret.push_back(sptr(Goals::BuildThis(immediateBuilding.get().bid, t).setpriority(2))); //prioritize buildings we can build quick
 		}
 		else //try build later
 		{
-			auto eb = ai->ah->expensiveBuilding();
-			if (eb.is_initialized())
+			if (expensiveBuilding.is_initialized())
 			{
-				auto pb = eb.get(); //gather resources for any we can't afford
-				auto goal = ai->ah->whatToDo(pb.price, sptr(Goals::BuildThis(pb.bid, t).setpriority(0.5)));
+				auto potentialBuilding = expensiveBuilding.get(); //gather resources for any we can't afford
+				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(Goals::BuildThis(potentialBuilding.bid, t).setpriority(0.5)));
 				ret.push_back(goal);
 			}
 		}
@@ -1459,7 +1480,9 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 			//build dwelling
 			//TODO: plan building over multiple turns?
 			//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
-			auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
+
+			//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
+			/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
 			if (bid.is_initialized())
 			{
 				auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
@@ -1467,7 +1490,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 					ret.push_back(goal);
 				else
 					logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
-			}
+			}*/
 		}
 	}