| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 | 
							- /*
 
- * BuildAnalyzer.cpp, part of VCMI engine
 
- *
 
- * Authors: listed in file AUTHORS in main folder
 
- *
 
- * License: GNU General Public License v2.0 or later
 
- * Full text of license available in license.txt file, in main folder
 
- *
 
- */
 
- #include "../StdInc.h"
 
- #include "../Engine/Nullkiller.h"
 
- #include "../Engine/Nullkiller.h"
 
- namespace NKAI
 
- {
 
- void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
 
- {
 
- 	auto townInfo = developmentInfo.town->town;
 
- 	auto creatures = townInfo->creatures;
 
- 	auto buildings = townInfo->getAllBuildings();
 
- 	std::map<BuildingID, BuildingID> parentMap;
 
- 	for(auto &pair : townInfo->buildings)
 
- 	{
 
- 		if(pair.second->upgrade != -1)
 
- 		{
 
- 			parentMap[pair.second->upgrade] = pair.first;
 
- 		}
 
- 	}
 
- 	BuildingID prefixes[] = {BuildingID::DWELL_UP_FIRST, BuildingID::DWELL_FIRST};
 
- 	for(int level = 0; level < GameConstants::CREATURES_PER_TOWN; level++)
 
- 	{
 
- 		logAi->trace("Checking dwelling level %d", level);
 
- 		BuildingInfo nextToBuild = BuildingInfo();
 
- 		for(BuildingID prefix : prefixes)
 
- 		{
 
- 			BuildingID building = BuildingID(prefix + level);
 
- 			if(!vstd::contains(buildings, building))
 
- 				continue; // no such building in town
 
- 			auto info = getBuildingOrPrerequisite(developmentInfo.town, building);
 
- 			if(info.exists)
 
- 			{
 
- 				developmentInfo.addExistingDwelling(info);
 
- 				break;
 
- 			}
 
- 			nextToBuild = info;
 
- 		}
 
- 		if(nextToBuild.id != BuildingID::NONE)
 
- 		{
 
- 			developmentInfo.addBuildingToBuild(nextToBuild);
 
- 		}
 
- 	}
 
- }
 
- void BuildAnalyzer::updateOtherBuildings(TownDevelopmentInfo & developmentInfo)
 
- {
 
- 	logAi->trace("Checking other buildings");
 
- 	std::vector<std::vector<BuildingID>> otherBuildings = {
 
- 		{BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL},
 
- 		{BuildingID::MAGES_GUILD_3, BuildingID::MAGES_GUILD_5}
 
- 	};
 
- 	if(developmentInfo.existingDwellings.size() >= 2 && ai->cb->getDate(Date::DAY_OF_WEEK) > boost::date_time::Friday)
 
- 	{
 
- 		otherBuildings.push_back({BuildingID::CITADEL, BuildingID::CASTLE});
 
- 		otherBuildings.push_back({BuildingID::HORDE_1});
 
- 		otherBuildings.push_back({BuildingID::HORDE_2});
 
- 	}
 
- 	for(auto & buildingSet : otherBuildings)
 
- 	{
 
- 		for(auto & buildingID : buildingSet)
 
- 		{
 
- 			if(!developmentInfo.town->hasBuilt(buildingID) && developmentInfo.town->town->buildings.count(buildingID))
 
- 			{
 
- 				developmentInfo.addBuildingToBuild(getBuildingOrPrerequisite(developmentInfo.town, buildingID));
 
- 				break;
 
- 			}
 
- 		}
 
- 	}
 
- }
 
- int32_t convertToGold(const TResources & res)
 
- {
 
- 	return res[EGameResID::GOLD] 
 
- 		+ 75 * (res[EGameResID::WOOD] + res[EGameResID::ORE]) 
 
- 		+ 125 * (res[EGameResID::GEMS] + res[EGameResID::CRYSTAL] + res[EGameResID::MERCURY] + res[EGameResID::SULFUR]);
 
- }
 
- TResources BuildAnalyzer::getResourcesRequiredNow() const
 
- {
 
- 	auto resourcesAvailable = ai->getFreeResources();
 
- 	auto result = requiredResources - resourcesAvailable;
 
- 	result.positive();
 
- 	return result;
 
- }
 
- TResources BuildAnalyzer::getTotalResourcesRequired() const
 
- {
 
- 	auto resourcesAvailable = ai->getFreeResources();
 
- 	auto result = totalDevelopmentCost - resourcesAvailable;
 
- 	result.positive();
 
- 	return result;
 
- }
 
- void BuildAnalyzer::update()
 
- {
 
- 	logAi->trace("Start analysing build");
 
- 	BuildingInfo bi;
 
- 	reset();
 
- 	auto towns = ai->cb->getTownsInfo();
 
- 	for(const CGTownInstance* town : towns)
 
- 	{
 
- 		logAi->trace("Checking town %s", town->getNameTranslated());
 
- 		developmentInfos.push_back(TownDevelopmentInfo(town));
 
- 		TownDevelopmentInfo & developmentInfo = developmentInfos.back();
 
- 		updateTownDwellings(developmentInfo);
 
- 		updateOtherBuildings(developmentInfo);
 
- 		requiredResources += developmentInfo.requiredResources;
 
- 		totalDevelopmentCost += developmentInfo.townDevelopmentCost;
 
- 		armyCost += developmentInfo.armyCost;
 
- 		for(auto bi : developmentInfo.toBuild)
 
- 		{
 
- 			logAi->trace("Building preferences %s", bi.toString());
 
- 		}
 
- 	}
 
- 	std::sort(developmentInfos.begin(), developmentInfos.end(), [](const TownDevelopmentInfo & t1, const TownDevelopmentInfo & t2) -> bool
 
- 	{
 
- 		auto val1 = convertToGold(t1.armyCost) - convertToGold(t1.townDevelopmentCost);
 
- 		auto val2 = convertToGold(t2.armyCost) - convertToGold(t2.townDevelopmentCost);
 
- 		return val1 > val2;
 
- 	});
 
- 	updateDailyIncome();
 
- 	if(ai->cb->getDate(Date::DAY) == 1)
 
- 	{
 
- 		goldPreasure = 1;
 
- 	}
 
- 	else
 
- 	{
 
- 		goldPreasure = ai->getLockedResources()[EGameResID::GOLD] / 5000.0f
 
- 			+ (float)armyCost[EGameResID::GOLD] / (1 + 2 * ai->getFreeGold() + (float)dailyIncome[EGameResID::GOLD] * 7.0f);
 
- 	}
 
- 	logAi->trace("Gold preasure: %f", goldPreasure);
 
- }
 
- void BuildAnalyzer::reset()
 
- {
 
- 	requiredResources = TResources();
 
- 	totalDevelopmentCost = TResources();
 
- 	armyCost = TResources();
 
- 	developmentInfos.clear();
 
- }
 
- BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
 
- 	const CGTownInstance * town,
 
- 	BuildingID toBuild,
 
- 	bool excludeDwellingDependencies) const
 
- {
 
- 	BuildingID building = toBuild;
 
- 	auto townInfo = town->town;
 
- 	const CBuilding * buildPtr = townInfo->buildings.at(building);
 
- 	const CCreature * creature = nullptr;
 
- 	CreatureID baseCreatureID;
 
- 	int creatureLevel = -1;
 
- 	int creatureUpgrade = 0;
 
- 	if(BuildingID::DWELL_FIRST <= toBuild && toBuild <= BuildingID::DWELL_UP_LAST)
 
- 	{
 
- 		creatureLevel = (toBuild - BuildingID::DWELL_FIRST) % GameConstants::CREATURES_PER_TOWN;
 
- 		creatureUpgrade = (toBuild - BuildingID::DWELL_FIRST) / GameConstants::CREATURES_PER_TOWN;
 
- 	}
 
- 	else if(toBuild == BuildingID::HORDE_1 || toBuild == BuildingID::HORDE_1_UPGR)
 
- 	{
 
- 		creatureLevel = townInfo->hordeLvl.at(0);
 
- 	}
 
- 	else if(toBuild == BuildingID::HORDE_2 || toBuild == BuildingID::HORDE_2_UPGR)
 
- 	{
 
- 		creatureLevel = townInfo->hordeLvl.at(1);
 
- 	}
 
- 	if(creatureLevel >=  0)
 
- 	{
 
- 		auto creatures = townInfo->creatures.at(creatureLevel);
 
- 		auto creatureID = creatures.size() > creatureUpgrade
 
- 			? creatures.at(creatureUpgrade)
 
- 			: creatures.front();
 
- 		baseCreatureID = creatures.front();
 
- 		creature = creatureID.toCreature();
 
- 	}
 
- 	auto info = BuildingInfo(buildPtr, creature, baseCreatureID, town, ai);
 
- 	logAi->trace("checking %s", info.name);
 
- 	logAi->trace("buildInfo %s", info.toString());
 
- 	if(!town->hasBuilt(building))
 
- 	{
 
- 		auto canBuild = ai->cb->canBuildStructure(town, building);
 
- 		if(canBuild == EBuildingState::ALLOWED)
 
- 		{
 
- 			info.canBuild = true;
 
- 		}
 
- 		else if(canBuild == EBuildingState::NO_RESOURCES)
 
- 		{
 
- 			logAi->trace("cant build. Not enough resources. Need %s", info.buildCost.toString());
 
- 			info.notEnoughRes = true;
 
- 		}
 
- 		else if(canBuild == EBuildingState::PREREQUIRES)
 
- 		{
 
- 			auto buildExpression = town->genBuildingRequirements(building, false);
 
- 			auto missingBuildings = buildExpression.getFulfillmentCandidates([&](const BuildingID & id) -> bool
 
- 			{
 
- 				return town->hasBuilt(id);
 
- 			});
 
- 			auto otherDwelling = [](const BuildingID & id) -> bool
 
- 			{
 
- 				return BuildingID::DWELL_FIRST <= id && id <= BuildingID::DWELL_UP_LAST;
 
- 			};
 
- 			if(vstd::contains_if(missingBuildings, otherDwelling))
 
- 			{
 
- 				logAi->trace("cant build. Need other dwelling");
 
- 			}
 
- 			else
 
- 			{
 
- 				logAi->trace("cant build. Need %d", missingBuildings[0].num);
 
- 				BuildingInfo prerequisite = getBuildingOrPrerequisite(town, missingBuildings[0], excludeDwellingDependencies);
 
- 				prerequisite.buildCostWithPrerequisits += info.buildCost;
 
- 				prerequisite.creatureCost = info.creatureCost;
 
- 				prerequisite.creatureGrows = info.creatureGrows;
 
- 				prerequisite.creatureLevel = info.creatureLevel;
 
- 				prerequisite.creatureID = info.creatureID;
 
- 				prerequisite.baseCreatureID = info.baseCreatureID;
 
- 				prerequisite.prerequisitesCount++;
 
- 				prerequisite.armyCost = info.armyCost;
 
- 				prerequisite.dailyIncome = info.dailyIncome;
 
- 				return prerequisite;
 
- 			}
 
- 		}
 
- 	}
 
- 	else
 
- 	{
 
- 		logAi->trace("exists");
 
- 		info.exists = true;
 
- 	}
 
- 	return info;
 
- }
 
- void BuildAnalyzer::updateDailyIncome()
 
- {
 
- 	auto objects = ai->cb->getMyObjects();
 
- 	auto towns = ai->cb->getTownsInfo();
 
- 	
 
- 	dailyIncome = TResources();
 
- 	for(const CGObjectInstance* obj : objects)
 
- 	{
 
- 		const CGMine* mine = dynamic_cast<const CGMine*>(obj);
 
- 		if(mine)
 
- 		{
 
- 			dailyIncome[mine->producedResource.getNum()] += mine->producedQuantity;
 
- 		}
 
- 	}
 
- 	for(const CGTownInstance* town : towns)
 
- 	{
 
- 		dailyIncome += town->dailyIncome();
 
- 	}
 
- }
 
- bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const
 
- {
 
- 	for(auto tdi : developmentInfos)
 
- 	{
 
- 		if(tdi.town->subID == alignment && tdi.town->hasBuilt(bid))
 
- 			return true;
 
- 	}
 
- 	return false;
 
- }
 
- void TownDevelopmentInfo::addExistingDwelling(const BuildingInfo & existingDwelling)
 
- {
 
- 	existingDwellings.push_back(existingDwelling);
 
- 	armyCost += existingDwelling.armyCost;
 
- 	armyStrength += existingDwelling.armyStrength;
 
- }
 
- void TownDevelopmentInfo::addBuildingToBuild(const BuildingInfo & nextToBuild)
 
- {
 
- 	townDevelopmentCost += nextToBuild.buildCostWithPrerequisits;
 
- 	if(nextToBuild.canBuild)
 
- 	{
 
- 		hasSomethingToBuild = true;
 
- 		toBuild.push_back(nextToBuild);
 
- 	}
 
- 	else if(nextToBuild.notEnoughRes)
 
- 	{
 
- 		requiredResources += nextToBuild.buildCost;
 
- 		hasSomethingToBuild = true;
 
- 		toBuild.push_back(nextToBuild);
 
- 	}
 
- }
 
- BuildingInfo::BuildingInfo()
 
- {
 
- 	id = BuildingID::NONE;
 
- 	creatureGrows = 0;
 
- 	creatureID = CreatureID::NONE;
 
- 	buildCost = 0;
 
- 	buildCostWithPrerequisits = 0;
 
- 	prerequisitesCount = 0;
 
- 	name.clear();
 
- 	armyStrength = 0;
 
- }
 
- BuildingInfo::BuildingInfo(
 
- 	const CBuilding * building,
 
- 	const CCreature * creature,
 
- 	CreatureID baseCreature,
 
- 	const CGTownInstance * town,
 
- 	Nullkiller * ai)
 
- {
 
- 	id = building->bid;
 
- 	buildCost = building->resources;
 
- 	buildCostWithPrerequisits = building->resources;
 
- 	dailyIncome = building->produce;
 
- 	exists = town->hasBuilt(id);
 
- 	prerequisitesCount = 1;
 
- 	name = building->getNameTranslated();
 
- 	if(creature)
 
- 	{
 
- 		creatureGrows = creature->getGrowth();
 
- 		creatureID = creature->getId();
 
- 		creatureCost = creature->getFullRecruitCost();
 
- 		creatureLevel = creature->getLevel();
 
- 		baseCreatureID = baseCreature;
 
- 		if(exists)
 
- 		{
 
- 			creatureGrows = town->creatureGrowth(creatureLevel - 1);
 
- 		}
 
- 		else
 
- 		{
 
- 			if(BuildingID::DWELL_FIRST <= id && id <= BuildingID::DWELL_UP_LAST)
 
- 			{
 
- 				creatureGrows = creature->getGrowth();
 
- 				if(town->hasBuilt(BuildingID::CASTLE))
 
- 					creatureGrows *= 2;
 
- 				else if(town->hasBuilt(BuildingID::CITADEL))
 
- 					creatureGrows += creatureGrows / 2;
 
- 			}
 
- 			else
 
- 			{
 
- 				creatureGrows = creature->getHorde();
 
- 			}
 
- 		}
 
- 		armyStrength = ai->armyManager->evaluateStackPower(creature, creatureGrows);
 
- 		armyCost = creatureCost * creatureGrows;
 
- 	}
 
- 	else
 
- 	{
 
- 		creatureGrows = 0;
 
- 		creatureID = CreatureID::NONE;
 
- 		baseCreatureID = CreatureID::NONE;
 
- 		creatureCost = TResources();
 
- 		armyCost = TResources();
 
- 		creatureLevel = 0;
 
- 		armyStrength = 0;
 
- 	}
 
- }
 
- std::string BuildingInfo::toString() const
 
- {
 
- 	return name + ", cost: " + buildCost.toString()
 
- 		+ ", creature: " + std::to_string(creatureGrows) + " x " + std::to_string(creatureLevel)
 
- 		+ " x " + creatureCost.toString()
 
- 		+ ", daily: " + dailyIncome.toString();
 
- }
 
- }
 
 
  |