FrameCountType Tools::GetLowerBound(const GameState & state, const BuildOrderSearchGoal & goal) { PrerequisiteSet wanted; // add everything from the goal to the needed set for (size_t a(0); a < ActionTypes::GetAllActionTypes(state.getRace()).size(); ++a) { const ActionType & actionType = ActionTypes::GetActionType(state.getRace(), a); UnitCountType numCompleted = state.getUnitData().getNumTotal(actionType); if (goal.getGoal(actionType) > numCompleted) { wanted.addUnique(actionType); } } FrameCountType lowerBound = Tools::CalculatePrerequisitesLowerBound(state, wanted, 0); return lowerBound; }
BuildOrder Tools::GetNaiveBuildOrderAddWorkers(const GameState & state, const BuildOrderSearchGoal & goal, UnitCountType maxWorkers) { PrerequisiteSet wanted; int minWorkers = 8; const ActionType & worker = ActionTypes::GetWorker(state.getRace()); std::vector<size_t> buildOrderActionTypeCount(ActionTypes::GetAllActionTypes(state.getRace()).size(), 0); // add everything from the goal to the needed set for (size_t a(0); a < ActionTypes::GetAllActionTypes(state.getRace()).size(); ++a) { const ActionType & actionType = ActionTypes::GetActionType(state.getRace(), a); UnitCountType numCompleted = state.getUnitData().getNumTotal(actionType); if (goal.getGoal(actionType) > numCompleted) { wanted.addUnique(actionType); } } if (wanted.size() == 0) { return BuildOrder(); } // Calculate which prerequisite units we need to build to achieve the units we want from the goal PrerequisiteSet requiredToBuild; CalculatePrerequisitesRequiredToBuild(state, wanted, requiredToBuild); // Add the required units to a preliminary build order BuildOrder buildOrder; for (size_t a(0); a < requiredToBuild.size(); ++a) { const ActionType & type = requiredToBuild.getActionType(a); buildOrder.add(type); buildOrderActionTypeCount[type.ID()]++; } // Add some workers to the build order if we don't have many, this usually gives a lower upper bound int requiredWorkers = minWorkers - state.getUnitData().getNumCompleted(ActionTypes::GetWorker(state.getRace())); while (requiredWorkers-- > 0) { buildOrder.add(worker); buildOrderActionTypeCount[worker.ID()]++; } // Add the goal units to the end of the build order for (size_t a(0); a < ActionTypes::GetAllActionTypes(state.getRace()).size(); ++a) { const ActionType & actionType = ActionTypes::GetActionType(state.getRace(), a); int need = (int)goal.getGoal(actionType); int have = (int)state.getUnitData().getNumTotal(actionType); int numNeeded = need - have - buildOrderActionTypeCount[actionType.ID()]; for (int i(0); i < numNeeded; ++i) { buildOrder.add(actionType); } } static const ActionType commandCenter = ActionTypes::GetActionType("Terran_Command_Center"); static const ActionType factory = ActionTypes::GetActionType("Terran_Factory"); static const ActionType starport = ActionTypes::GetActionType("Terran_Starport"); static const ActionType scienceFacility = ActionTypes::GetActionType("Terran_Science_Facility"); // Check to see if we have enough buildings for the required addons if (state.getRace() == Races::Terran) { int commandCenterAddons = 0; int factoryAddons = 0; int starportAddons = 0; int sciAddons = 0; int numCommandCenters = state.getUnitData().getNumTotal(commandCenter); int numFactories = state.getUnitData().getNumTotal(factory); int numStarports = state.getUnitData().getNumTotal(starport); int numSci = state.getUnitData().getNumTotal(scienceFacility); for (size_t a(0); a < buildOrder.size(); ++a) { const ActionType & actionType = buildOrder[a]; if (actionType.isAddon()) { if (actionType.whatBuildsActionType() == commandCenter) { ++commandCenterAddons; } else if (actionType.whatBuildsActionType() == factory) { ++factoryAddons; } else if (actionType.whatBuildsActionType() == starport) { ++starportAddons; } else if (actionType.whatBuildsActionType() == scienceFacility) { ++sciAddons; } else { BOSS_ASSERT(false, "Addon has no builder: %s %s", actionType.getName().c_str(), actionType.whatBuildsActionType().getName().c_str()); } } if (actionType == commandCenter) { ++numCommandCenters; } else if (actionType == factory) { ++numFactories; } else if (actionType == starport) { ++numStarports; } else if (actionType == scienceFacility) { ++numSci; } } // add the necessary buildings to make the addons for (int n(0); n < commandCenterAddons - numCommandCenters; ++n) { buildOrder.add(commandCenter); } for (int n(0); n < factoryAddons - numFactories; ++n) { buildOrder.add(factory); } for (int n(0); n < starportAddons - numStarports; ++n) { buildOrder.add(starport); } for (int n(0); n < sciAddons - numSci; ++n) { buildOrder.add(scienceFacility); } } // Bubble sort the build order so that prerequites always come before what requires them for (size_t i(0); i < buildOrder.size()-1; ++i) { for (size_t j(i+1); j < buildOrder.size(); ++j) { const PrerequisiteSet & recursivePre = buildOrder[i].getRecursivePrerequisites(); if (recursivePre.contains(buildOrder[j])) { std::swap(buildOrder[i], buildOrder[j]); } } } // finish the build order with workers and supply BuildOrder finalBuildOrder; GameState currentState(state); size_t i = 0; while (i < buildOrder.size()) { const ActionType & worker = ActionTypes::GetWorker(currentState.getRace()); const ActionType & supplyProvider = ActionTypes::GetSupplyProvider(currentState.getRace()); const ActionType & nextAction = buildOrder[i]; UnitCountType maxSupply = currentState.getUnitData().getMaxSupply() + currentState.getUnitData().getSupplyInProgress(); UnitCountType numWorkers = currentState.getUnitData().getNumTotal(worker); UnitCountType currentSupply = currentState.getUnitData().getCurrentSupply(); if (numWorkers < 8) { finalBuildOrder.add(worker); currentState.doAction(worker); continue; } // insert a supply provider if we are behind int surplusSupply = maxSupply - currentSupply; if (surplusSupply < nextAction.supplyRequired() + 2) { try { BOSS_ASSERT(currentState.isLegal(supplyProvider), "supplyProvider should be legal"); finalBuildOrder.add(supplyProvider); currentState.doAction(supplyProvider); continue; } catch (const Assert::BOSSException &) { break; } } FrameCountType whenWorkerReady = currentState.whenCanPerform(worker); FrameCountType whennextActionReady = currentState.whenCanPerform(nextAction); if ((numWorkers < maxWorkers) && (whenWorkerReady < whennextActionReady)) { // check to see if we should insert a worker try { BOSS_ASSERT(currentState.isLegal(worker), "Worker should be legal"); finalBuildOrder.add(worker); currentState.doAction(worker); } catch (const Assert::BOSSException &) { } continue; } else { ActionType testNextAction = buildOrder[i]; BOSS_ASSERT(currentState.isLegal(nextAction), "nextAction should be legal"); finalBuildOrder.add(nextAction); currentState.doAction(nextAction); ++i; } } return finalBuildOrder; }