void DFBB_BuildOrderSearchParameters::setRepetitionThreshold(const ActionType & a, const UnitCountType & thresh) { BOSS_ASSERT(a.ID() >= 0 && a.ID() < repetitionThresholds.size(), "Action type not valid"); BOSS_ASSERT(a.getRace() == race, "Action type race doesn't match this parameter object"); repetitionThresholds[a.ID()] = thresh; }
const bool BuildingStatus::canBuildNow(const ActionType & action) const { if (_timeRemaining > 0) { return false; } if (!_type.canBuild(action)) { return false; } if (action.isAddon() && (_addon != ActionTypes::None)) { return false; } if (action.requiresAddon() && (_addon != action.requiredAddonType())) { return false; } return true; }
ActionType UnitData::finishNextActionInProgress() { // get the actionUnit from the progress data ActionType action = _progress.nextAction(); // add the unit to the unit counter addCompletedAction(action); // pop it from the progress vector _progress.popNextAction(); if (getRace() == Races::Terran) { // if it's a building, release the worker back if (action.isBuilding() && !action.isAddon()) { releaseBuildingWorker(); } } else if (getRace() == Races::Zerg) { const static ActionType hatchery = ActionTypes::GetActionType("Zerg_Hatchery"); } return action; }
void runActionState(ActionType& action) { action.setState(ActionType::State::WALK); runAllAction(action); action.setState(ActionType::State::RUN); runAllAction(action); action.setState(ActionType::State::SLEEP); runAllAction(action); }
void UnitData::removeCompletedAction(const ActionType & action) { //Logger::LogAppendToFile(BOSS_LOGFILE, "Unit removed " + action.getName()); const static ActionType Lair = ActionTypes::GetActionType("Zerg_Lair"); const static ActionType Hive = ActionTypes::GetActionType("Zerg_Hive"); _numUnits[action.ID()] -= action.numProduced(); // a lair or hive from a hatchery don't produce additional supply if (action != Lair && action != Hive) { _maxSupply -= action.supplyProvided(); } if (action.isWorker()) { if (_mineralWorkers > 0) { _mineralWorkers--; } else if (_gasWorkers > 0) { _gasWorkers--; } } // if it's an extractor if (action.isRefinery()) { // take those workers from minerals and put them into it _mineralWorkers += 3; _gasWorkers -= 3; } BOSS_ASSERT(_mineralWorkers >= 0, "Can't have negative mineral workers"); BOSS_ASSERT(_gasWorkers >= 0, "Can't have negative gas workers"); // if it's a building that can produce units, add it to the building data if (action.isBuilding() && !action.isSupplyProvider()) { if (!action.isMorphed()) { _buildings.removeBuilding(action, ActionTypes::None); } } // special case for hatcheries if (action.isBuilding() && (action.getUnitType() == BWAPI::UnitTypes::Zerg_Hatchery)) { _hatcheryData.removeHatchery(); } }
// only used for adding existing buildings from a BWAPI Game * object void UnitData::addCompletedBuilding(const ActionType & action, const FrameCountType timeUntilFree, const ActionType & constructing, const ActionType & addon) { _numUnits[action.ID()] += action.numProduced(); _maxSupply += action.supplyProvided(); // if it's an extractor if (action.isRefinery()) { // take those workers from minerals and put them into it _mineralWorkers -= 3; _gasWorkers += 3; } // if it's a building that can produce units, add it to the building data if (action.isBuilding() && !action.isSupplyProvider()) { _buildings.addBuilding(action, timeUntilFree, constructing, addon); } // special case for hatcheries if (action.isBuilding() && (action.getUnitType() == BWAPI::UnitTypes::Zerg_Hatchery)) { _hatcheryData.addHatchery(1); } }
void UnitData::morphUnit(const ActionType & from, const ActionType & to, const FrameCountType & completionFrame) { BOSS_ASSERT(getNumCompleted(from) > 0, "Must have the unit type to morph it"); _numUnits[from.ID()]--; _currentSupply -= from.supplyRequired(); if (from.isWorker()) { BOSS_ASSERT(_mineralWorkers > 0, "Need mineral worker"); _mineralWorkers--; } addActionInProgress(to, completionFrame); }
const PrerequisiteSet UnitData::getPrerequistesInProgress(const ActionType & action) const { PrerequisiteSet inProgress; for (size_t a(0); a<action.getPrerequisites().size(); ++a) { const ActionType & actionType = action.getPrerequisites().getActionType(a); if (getNumInProgress(actionType) > 0 && getNumCompleted(actionType) == 0) { inProgress.add(actionType); } } return inProgress; }
const FrameCountType GameState::whenSupplyReady(const ActionType & action) const { int supplyNeeded = action.supplyRequired() + _units.getCurrentSupply() - _units.getMaxSupply(); if (supplyNeeded <= 0) { return getCurrentFrame(); } FrameCountType whenSupplyReady = _currentFrame; if (supplyNeeded > 0) { FrameCountType min = 99999; // if we don't have the resources, this action would only be legal if there is an // overlord in progress, so check to see when the first overlord will finish for (int i(0); i<_units.getNumActionsInProgress(); ++i) { // so, if the unit provides the supply we need if (_units.getActionInProgressByIndex(i).supplyProvided() > supplyNeeded) { // set 'min' to the min of these times min = (_units.getFinishTimeByIndex(i) < min) ? _units.getFinishTimeByIndex(i) : min; } // then set supply time to min whenSupplyReady = min; } } return whenSupplyReady; }
const FrameCountType GameState::whenPrerequisitesReady(const ActionType & action) const { if (action == ActionTypes::GetActionType("Protoss_Dark_Templar")) { int a = 6; } FrameCountType preReqReadyTime = _currentFrame; // if a building builds this action if (action.whatBuildsIsBuilding()) { // get when the building / prereqs will be ready preReqReadyTime = whenBuildingPrereqReady(action); } // otherwise something else builds this action so we don't worry about buildings else { // if requirement in progress (and not already made), set when it will be finished PrerequisiteSet reqInProgress = _units.getPrerequistesInProgress(action); // if it's not empty, check when they will be done if (!reqInProgress.isEmpty()) { preReqReadyTime = _units.getFinishTime(reqInProgress); } } return preReqReadyTime; }
void ActionSet::addAction(const ActionType& action) { if (action == ActionType::anyAction) { addAllActions(); return; } _actions.set(action.getIdentifier(), true); }
void GameState::removeCompletedAction(const ActionType & action, const size_t num) { for (size_t i(0); i < num; ++i) { _units.setCurrentSupply(_units.getCurrentSupply() - action.supplyRequired()); _units.removeCompletedAction(action); } }
const FrameCountType GameState::whenBuildingPrereqReady(const ActionType & action) const { FrameCountType buildingAvailableTime(0); const ActionType & builder = action.whatBuildsActionType(); BOSS_ASSERT(builder.isBuilding(), "The thing that builds this is not a building"); bool buildingIsConstructed = _units.getBuildingData().canBuildEventually(action);//getNumCompleted(builder) > 0; bool buildingInProgress = _units.getNumInProgress(builder) > 0; FrameCountType constructedBuildingFreeTime = std::numeric_limits<int>::max()-10; FrameCountType buildingInProgressFinishTime = std::numeric_limits<int>::max()-10; BOSS_ASSERT(buildingIsConstructed || (!action.requiresAddon() && buildingInProgress), "We will never be able to build action: %s", action.getName().c_str()); if (buildingIsConstructed) { constructedBuildingFreeTime = _currentFrame + _units.getBuildingData().getTimeUntilCanBuild(action); } if (!action.requiresAddon() && buildingInProgress) { buildingInProgressFinishTime = _units.getFinishTime(builder); } // this will give us when the building will be free to build this action buildingAvailableTime = std::min(constructedBuildingFreeTime, buildingInProgressFinishTime); // get all prerequisites currently in progress but do not have any completed PrerequisiteSet prereqInProgress = _units.getPrerequistesInProgress(action); // remove the specific builder from this list since we calculated that earlier prereqInProgress.remove(builder); //// if we actually have some prerequisites in progress other than the building if (!prereqInProgress.isEmpty()) { // get the max time the earliest of each type will be finished in FrameCountType C = _units.getFinishTime(prereqInProgress); // take the maximum of this value and when the building was available buildingAvailableTime = (C > buildingAvailableTime) ? C : buildingAvailableTime; } return buildingAvailableTime; }
const bool BuildingStatus::canBuildEventually(const ActionType & action) const { if (!_type.canBuild(action)) { return false; } // if the type is an addon if (action.isAddon()) { // if we already have an addon we can't build it if (_addon != ActionTypes::None) { return false; } // if we are building an addon we can't ever build it if (_timeRemaining > 0 && _isConstructing.isAddon()) { return false; } } if (action.requiresAddon() && (_addon != action.requiredAddonType())) { if (_isConstructing != action.requiredAddonType()) { return false; } } // if the built type is morphed and we are morphing something, we won't be able to build it if (action.isMorphed() && (_timeRemaining > 0) && (_isConstructing.isMorphed())) { return false; } return true; }
void BuildingData::removeBuilding(const ActionType & action, const ActionType & addon) { BOSS_ASSERT(action.isBuilding(), "Trying to remove a non-building from the building data"); for (size_t i = 0; i < _buildings.size(); i++) { if (_buildings[i]._type == action) { _buildings.remove(i); break; } } }
const std::string GUI::getTextureFileName(const ActionType & type) const { std::string filename = "units/" + type.getName() + ".png"; if (type.isTech()) { filename = "command_icons/" + type.getName() + ".png"; } else if (type.isUpgrade()) { filename = "command_icons/" + type.getName() + ".png"; } for (size_t i(0); i<filename.size(); ++i) { if (filename[i] == ' ') { filename[i] = '_'; } } return filename; }
void BuildingData::queueAction(const ActionType & action) { for (size_t i=0; i<_buildings.size(); ++i) { if (_buildings[i].canBuildNow(action)) { _buildings[i].queueActionType(action); return; } } // this method should always work since we have fast forwarded to the correct point in time BOSS_ASSERT(false, "Didn't find a building to queue this type of unit in: %s", action.getName().c_str()); }
int Normalize::normActionsV1orV2(const ActionRange &actions, UInt8 ipProto) { using namespace deprecated; // Return change in length of action list. int lengthChange = 0; ActionIterator iter = actions.begin(); ActionIterator iterEnd = actions.end(); while (iter < iterEnd) { ActionType actType = iter->type(); UInt16 type = actType.enumType(); if (type <= UInt16_cast(v1::OFPAT_ENQUEUE) || type == UInt16_cast(v2::OFPAT_SET_MPLS_LABEL) || type == UInt16_cast(v2::OFPAT_SET_MPLS_TC)) { lengthChange += normActionV1orV2(type, &iter, &iterEnd, ipProto); } ++iter; } return lengthChange; }
const FrameCountType GameState::whenWorkerReady(const ActionType & action) const { if (!action.whatBuildsActionType().isWorker()) { return _currentFrame; } int refineriesInProgress = _units.getNumInProgress(ActionTypes::GetRefinery(getRace())); // protoss doesn't tie up a worker to build, so they can build whenever a mineral worker is free if (getRace() == Races::Protoss && getNumMineralWorkers() > 0) { return _currentFrame; } // if we have a mineral worker, then it is ready right now if (getNumMineralWorkers() > 3*refineriesInProgress) { return _currentFrame; } // at this point we need to wait for the next worker to become free since existing workers // are either all used, or they are reserved to be put into refineries // so we must have either a worker in progress, or a building in progress const ActionType & Worker = ActionTypes::GetWorker(getRace()); BOSS_ASSERT(_units.getNumInProgress(Worker) > 0 || getNumBuildingWorkers() > 0, "No worker will ever be free"); FrameCountType workerReadyTime = _currentFrame; // if we have a worker in progress, when will it be ready? FrameCountType whenWorkerInProgressFinished = std::numeric_limits<FrameCountType>::max(); if (_units.getNumInProgress(Worker)) { whenWorkerInProgressFinished = _units.getFinishTime(Worker); } // if we have a worker currently building, when will it be free? FrameCountType whenBuildingWorkerFree = std::numeric_limits<FrameCountType>::max(); if (getNumBuildingWorkers() > 0) { whenBuildingWorkerFree = _units.getNextBuildingFinishTime(); } return std::min(whenWorkerInProgressFinished, whenBuildingWorkerFree); }
// Takes an ActionType and returns the string representation std::string ActionType::actionToString(const ActionType& action) { switch (action.getIdentifier()) { case READ_VALUE: return "r"; case READ_WRITE_VALUE: return "w"; case USER_ADMIN_VALUE: return "u"; case DB_ADMIN_VALUE: return "d"; case SERVER_ADMIN_VALUE: return "s"; case CLUSTER_ADMIN_VALUE: return "c"; default: return ""; } }
// recursively checks the tech tree of Action and sets each to have goalMax of 1 void DFBB_BuildOrderSmartSearch::recurseOverStrictDependencies(const ActionType & actionType) { if (actionType.isResourceDepot() || actionType.isWorker() || actionType.isSupplyProvider() || actionType.isRefinery()) { return; } PrerequisiteSet recursivePrerequisites = actionType.getRecursivePrerequisites(); for (size_t a(0); a < recursivePrerequisites.size(); ++a) { const ActionType & actionType = recursivePrerequisites.getActionType(a); if (actionType.isResourceDepot() ||actionType.isWorker() || actionType.isSupplyProvider() || actionType.isRefinery()) { continue; } _goal.setGoalMax(actionType, std::max((UnitCountType)1, _goal.getGoalMax(actionType))); } }
void UnitData::addActionInProgress(const ActionType & action, const FrameCountType & completionFrame, bool queueAction) { FrameCountType finishTime = (action.isBuilding() && !action.isMorphed()) ? completionFrame + Constants::BUILDING_PLACEMENT : completionFrame; // add it to the actions in progress _progress.addAction(action, finishTime); if (!action.isMorphed()) { _currentSupply += action.supplyRequired() * action.numProduced(); } if (queueAction && action.whatBuildsIsBuilding()) { // add it to a free building, which MUST be free since it's called from doAction // which must be already fastForwarded to the correct time _buildings.queueAction(action); } }
// do an action, action must be legal for this not to break std::vector<ActionType> GameState::doAction(const ActionType & action) { BOSS_ASSERT(action.getRace() == _race, "Race of action does not match race of the state"); _actionsPerformed.push_back(ActionPerformed()); _actionsPerformed[_actionsPerformed.size()-1].actionType = action; BOSS_ASSERT(isLegal(action), "Trying to perform an illegal action: %s %s", action.getName().c_str(), getActionsPerformedString().c_str()); // set the actionPerformed _actionPerformed = action; _actionPerformedK = 1; FrameCountType workerReadyTime = whenWorkerReady(action); FrameCountType ffTime = whenCanPerform(action); BOSS_ASSERT(ffTime >= 0 && ffTime < 1000000, "FFTime is very strange: %d", ffTime); auto actionsFinished=fastForward(ffTime); _actionsPerformed[_actionsPerformed.size()-1].actionQueuedFrame = _currentFrame; _actionsPerformed[_actionsPerformed.size()-1].gasWhenQueued = _gas; _actionsPerformed[_actionsPerformed.size()-1].mineralsWhenQueued = _minerals; // how much time has elapsed since the last action was queued? FrameCountType elapsed(_currentFrame - _lastActionFrame); _lastActionFrame = _currentFrame; BOSS_ASSERT(canAffordMinerals(action), "Minerals less than price: %ld < %d, ffTime=%d %s", _minerals, action.mineralPrice(), (int)elapsed, action.getName().c_str()); BOSS_ASSERT(canAffordGas(action), "Gas less than price: %ld < %d, ffTime=%d %s", _gas, action.gasPrice(), (int)elapsed, action.getName().c_str()); // modify our resources _minerals -= action.mineralPrice(); _gas -= action.gasPrice(); // do race specific things here if (getRace() == Races::Protoss) { _units.addActionInProgress(action, _currentFrame + action.buildTime()); } else if (getRace() == Races::Terran) { if (action.isBuilding() && !action.isAddon()) { if (getNumMineralWorkers() == 0) { std::cout << toString() << std::endl; } BOSS_ASSERT(getNumMineralWorkers() > 0, "Don't have any mineral workers to assign"); _units.setBuildingWorker(); } _units.addActionInProgress(action, _currentFrame + action.buildTime()); } else if (getRace() == Races::Zerg) { // zerg must subtract a larva if the action was unit creation if (action.isUnit() && !action.isBuilding()) { if (action.isMorphed()) { _units.morphUnit(action.whatBuildsActionType(), action, _currentFrame + action.buildTime()); } else { BOSS_ASSERT(getHatcheryData().numLarva() > 0, "We should have a larva to use"); _units.getHatcheryData().useLarva(); _units.addActionInProgress(action, _currentFrame + action.buildTime()); } } else if (action.isBuilding()) { _units.morphUnit(action.whatBuildsActionType(), action, _currentFrame + action.buildTime()); } else { // if it's not a unit or a building it's a tech so we queue it normally _units.addActionInProgress(action, _currentFrame + action.buildTime()); } } return actionsFinished; }
GameState::GameState(BWAPI::GameWrapper & game, BWAPI::PlayerInterface * self) : _race (Races::GetRaceID(self->getRace())) , _currentFrame (game->getFrameCount()) , _lastActionFrame (0) , _units (Races::GetRaceID(self->getRace())) , _minerals (self->minerals() * Constants::RESOURCE_SCALE) , _gas (self->gas() * Constants::RESOURCE_SCALE) { // we will count the worker jobs as we add units UnitCountType mineralWorkerCount = 0; UnitCountType gasWorkerCount = 0; UnitCountType buildingWorkerCount = 0; UnitCountType larvaCount = 0; _units.setMineralWorkers(mineralWorkerCount); _units.setGasWorkers(gasWorkerCount); _units.setBuildingWorkers(buildingWorkerCount); // add each unit we have to the current state for (BWAPI::UnitInterface * unit : self->getUnits()) { if (unit->getType() == BWAPI::UnitTypes::Zerg_Larva) { ++larvaCount; continue; } if (!ActionTypes::TypeExists(unit->getType())) { continue; } const ActionType actionType(unit->getType()); // if the unit is completed if (unit->isCompleted()) { // if it is a building if (unit->getType().isBuilding() && !unit->getType().isAddon()) { // add the building data accordingly FrameCountType trainTime = unit->getRemainingTrainTime() + unit->getRemainingResearchTime() + unit->getRemainingUpgradeTime(); ActionType constructing; ActionType addon; if (unit->getRemainingTrainTime() > 0) { constructing = ActionType(*unit->getTrainingQueue().begin()); } else if (unit->getRemainingResearchTime() > 0) { constructing = ActionType(unit->getTech()); _units.addActionInProgress(constructing, game->getFrameCount() + unit->getRemainingResearchTime(), false); } else if (unit->getRemainingUpgradeTime() > 0) { constructing = ActionType(unit->getUpgrade()); _units.addActionInProgress(constructing, game->getFrameCount() + unit->getRemainingUpgradeTime(), false); } // TODO: special case for Zerg_Hatchery if (unit->getAddon() != NULL) { if (unit->getAddon()->isConstructing()) { constructing = ActionType(unit->getAddon()->getType()); } else { addon = ActionType(unit->getAddon()->getType()); } } _units.addCompletedBuilding(actionType, trainTime, constructing, addon); } else { // add the unit to the state _units.addCompletedAction(actionType); _units.setCurrentSupply(_units.getCurrentSupply() + actionType.supplyRequired()); } } else if (unit->isBeingConstructed() && !unit->getType().isAddon()) { _units.addActionInProgress(ActionType(unit->getType()), game->getFrameCount() + unit->getRemainingBuildTime(), false); } } // TODO: set correct number of larva from state for (const BWAPI::UpgradeType & type : BWAPI::UpgradeTypes::allUpgradeTypes()) { if (!ActionTypes::TypeExists(type)) { continue; } if (self->getUpgradeLevel(type) > 0) { _units.addCompletedAction(ActionType(type)); } } for (const BWAPI::TechType & type : BWAPI::TechTypes::allTechTypes()) { if (!ActionTypes::TypeExists(type)) { continue; } if (self->hasResearched(type)) { _units.addCompletedAction(ActionType(type)); } } }
void runAllAction(ActionType& action) { action.doAction(); action.doPreAction(); action.doPostAction(); }
bool GameState::isLegal(const ActionType & action) const { const size_t numRefineries = _units.getNumTotal(ActionTypes::GetRefinery(getRace())); const size_t numDepots = _units.getNumTotal(ActionTypes::GetResourceDepot(getRace())); const size_t refineriesInProgress = _units.getNumInProgress(ActionTypes::GetRefinery(getRace())); // we can never build a larva static const ActionType & Zerg_Larva = ActionTypes::GetActionType("Zerg_Larva"); if (action == Zerg_Larva) { return false; } // check if the tech requirements are met if (!_units.hasPrerequisites(action.getPrerequisites())) { return false; } // if it's a unit and we are out of supply and aren't making an overlord, it's not legal if (!action.isMorphed() && !action.isSupplyProvider() && ((_units.getCurrentSupply() + action.supplyRequired()) > (_units.getMaxSupply() + _units.getSupplyInProgress()))) { return false; } // TODO: require an extra for refineries byt not buildings // rules for buildings which are built by workers if (action.isBuilding() && !action.isMorphed() && !action.isAddon()) { // be very strict about when we can make refineries to ensure we have enough workers to go in gas if (action.isRefinery() && (getNumMineralWorkers() <= (4 + 3*refineriesInProgress))) { return false; } int workersPerRefinery = 3; int workersRequiredToBuild = getRace() == Races::Protoss ? 0 : 1; int buildingIsRefinery = action.isRefinery() ? 1 : 0; int candidateWorkers = getNumMineralWorkers() + _units.getNumInProgress(ActionTypes::GetWorker(getRace())) + getNumBuildingWorkers(); int workersToBeUsed = workersRequiredToBuild + workersPerRefinery*(refineriesInProgress); if (candidateWorkers < workersToBeUsed) { return false; } } // if we have no gas income we can't make a gas unit if (!canAffordGas(action) && !_units.hasGasIncome()) { return false; } // if we have no mineral income we'll never have a minerla unit if (!canAffordMinerals(action) && !_units.hasMineralIncome()) { return false; } // don't build more refineries than resource depots if (action.isRefinery() && (numRefineries >= numDepots)) { return false; } // we don't need to go over the maximum supply limit with supply providers if (action.isSupplyProvider() && (_units.getMaxSupply() + _units.getSupplyInProgress() > 400)) { return false; } // can only build one of a tech type if (action.isTech() && getUnitData().getNumTotal(action) > 0) { return false; } // check to see if an addon can ever be built if (action.isAddon() && !_units.getBuildingData().canBuildEventually(action) && (_units.getNumInProgress(action.whatBuildsActionType()) == 0)) { return false; } return true; }
bool GameState::whyIsNotLegal(const ActionType & action) const { const size_t numRefineries = _units.getNumTotal(ActionTypes::GetRefinery(getRace())); const size_t numDepots = _units.getNumTotal(ActionTypes::GetResourceDepot(getRace())); const size_t refineriesInProgress = _units.getNumInProgress(ActionTypes::GetRefinery(getRace())); // we can never build a larva static const ActionType & Zerg_Larva = ActionTypes::GetActionType("Zerg_Larva"); if (action == Zerg_Larva) { std::cout << "WhyNotLegal: " << action.getName() << " - Cannot build a Larva" << std::endl; return false; } // check if the tech requirements are met if (!_units.hasPrerequisites(action.getPrerequisites())) { std::cout << "WhyNotLegal: " << action.getName() << " - Tech requirements not met" << std::endl; return false; } // if it's a unit and we are out of supply and aren't making an overlord, it's not legal if (!action.isMorphed() && ((_units.getCurrentSupply() + action.supplyRequired()) > (_units.getMaxSupply() + _units.getSupplyInProgress()))) { std::cout << "WhyNotLegal: " << action.getName() << " - Not enough supply to construct" << std::endl; return false; } // specific rule for never leaving 0 workers on minerals if (action.isRefinery() && (getNumMineralWorkers() <= 4)) { std::cout << "WhyNotLegal: " << action.getName() << " - Cannot leave 0 workers on minerals" << std::endl; return false; } // if it's a new building and no drones are available, it's not legal if (!action.isMorphed() && action.isBuilding() && (getNumMineralWorkers() <= 1) && (getNumBuildingWorkers() == 0)) { std::cout << "WhyNotLegal: " << action.getName() << " - No building worker available" << std::endl; return false; } // we can't build a building with our last worker if (!action.isMorphed() && action.isBuilding() && (getNumMineralWorkers() <= 1 + 3*refineriesInProgress) && (getNumBuildingWorkers() == 0)) { std::cout << "WhyNotLegal: " << action.getName() << " - Can't build with last worker" << std::endl; return false; } // if we have no gas income we can't make a gas unit if (!canAffordGas(action) && !_units.hasGasIncome()) { std::cout << "WhyNotLegal: " << action.getName() << " - No gas income for gas unit" << std::endl; return false; } // if we have no mineral income we'll never have a minerla unit if (!canAffordMinerals(action) && !_units.hasMineralIncome()) { std::cout << "WhyNotLegal: " << action.getName() << " - No mineral income" << std::endl; return false; } // don't build more refineries than resource depots if (action.isRefinery() && (numRefineries >= numDepots)) { std::cout << "WhyNotLegal: " << action.getName() << " - Can't have more refineries than depots" << std::endl; return false; } // we don't need to go over the maximum supply limit with supply providers if (action.isSupplyProvider() && (_units.getMaxSupply() + _units.getSupplyInProgress() >= 400)) { std::cout << "WhyNotLegal: " << action.getName() << " - Can't go over max supply bound with providers" << std::endl; return false; } if (action.isTech() && getUnitData().getNumTotal(action) > 0) { std::cout << "WhyNotLegal: " << action.getName() << " - Can't produce additional copy of tech" << std::endl; return false; } return true; }
const FrameCountType GameState::whenGasReady(const ActionType & action) const { if (_gas >= action.gasPrice()) { return getCurrentFrame(); } UnitCountType currentMineralWorkers = _units.getNumMineralWorkers(); UnitCountType currentGasWorkers = _units.getNumGasWorkers(); FrameCountType lastActionFinishFrame = _currentFrame; FrameCountType addedTime = 0; ResourceCountType addedGas = 0; ResourceCountType difference = action.gasPrice() - _gas; // loop through each action in progress, adding the minerals we would gather from each interval for (size_t i(0); i<_units.getNumActionsInProgress(); ++i) { // the vector is sorted in descending order size_t progressIndex = _units.getNumActionsInProgress() - i - 1; // the time elapsed and the current minerals per frame FrameCountType elapsed = _units.getFinishTimeByIndex(progressIndex) - lastActionFinishFrame; ResourceCountType gasPerFrame = (currentGasWorkers * Constants::GPWPF); // the amount of minerals that would be added this time step ResourceCountType tempAdd = elapsed * gasPerFrame; // if this amount isn't enough, update the amount added for this interval if (addedGas + tempAdd < difference) { addedGas += tempAdd; addedTime += elapsed; } else { // otherwise we can just break out and update at the end break; } // if it was a drone or extractor update the temp variables const ActionType & actionPerformed = _units.getActionInProgressByIndex(progressIndex); // finishing a building as terran gives you a mineral worker back if (actionPerformed.isBuilding() && !actionPerformed.isAddon() && (getRace() == Races::Terran)) { currentMineralWorkers++; } if (actionPerformed.isWorker()) { currentMineralWorkers++; } else if (actionPerformed.isRefinery()) { BOSS_ASSERT(currentMineralWorkers > 3, "Not enough mineral workers"); currentMineralWorkers -= 3; currentGasWorkers += 3; } // update the last action lastActionFinishFrame = _units.getFinishTimeByIndex(progressIndex); } // if we still haven't added enough minerals, add more time if (addedGas < difference) { BOSS_ASSERT(currentGasWorkers > 0, "Shouldn't have 0 gas workers"); FrameCountType finalTimeToAdd; if (currentGasWorkers != 0) { finalTimeToAdd = (difference - addedGas) / (currentGasWorkers * Constants::GPWPF); } else { finalTimeToAdd = 1000000; } addedGas += finalTimeToAdd * currentGasWorkers * Constants::GPWPF; addedTime += finalTimeToAdd; // the last operation could have added one frame too little due to integer division so we need to check if (addedGas < difference) { addedTime += 1; addedGas += currentGasWorkers * Constants::GPWPF; } } BOSS_ASSERT(addedGas >= difference, "Gas prediction error"); // for some reason if i don't return +1, i mine 1 less mineral in the interval return _currentFrame + addedTime; }
bool GameState::canAffordMinerals(const ActionType & action) const { return _minerals >= action.mineralPrice(); }
bool GameState::canAffordGas(const ActionType & action) const { return _gas >= action.gasPrice(); }