// 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::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(); } }
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 UnitData::addCompletedAction(const ActionType & action, bool wasBuilt) { const static ActionType Lair = ActionTypes::GetActionType("Zerg_Lair"); const static ActionType Hive = ActionTypes::GetActionType("Zerg_Hive"); _numUnits[action.ID()] += action.numProduced(); if (wasBuilt) { // a lair or hive from a hatchery don't produce additional supply if (action != Lair && action != Hive) { _maxSupply += action.supplyProvided(); } } else { _maxSupply += action.supplyProvided(); } if (action.isWorker()) { _mineralWorkers++; } // 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()) { if (!action.isMorphed()) { _buildings.addBuilding(action, ActionTypes::None); } } // special case for hatcheries if (action.isBuilding() && (action.getUnitType() == BWAPI::UnitTypes::Zerg_Hatchery)) { _hatcheryData.addHatchery(wasBuilt ? 1 : 3); } }
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; } } }
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); } }
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; }
// 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; }
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; }
void BuildingData::addBuilding(const ActionType & action, const FrameCountType timeUntilFree, const ActionType & constructing, const ActionType & addon) { BOSS_ASSERT(action.isBuilding(), "Trying to add a non-building to the building data"); _buildings.push_back(BuildingStatus(action, timeUntilFree, constructing, addon)); }
void BuildingData::addBuilding(const ActionType & action, const ActionType & addon) { BOSS_ASSERT(action.isBuilding(), "Trying to add a non-building to the building data"); _buildings.push_back(BuildingStatus(action, addon)); }
void BOSSExperiments::runBuildOrderVisualizationExperiment(const rapidjson::Value & val) { BOSS_ASSERT(val.HasMember("scenarios") && val["scenarios"].IsArray(), "BOVis has no scenarios"); std::vector< GameState > states; std::vector< std::vector<ActionType> > buildOrders; std::vector< std::vector<FrameCountType> > startTimes(val["scenarios"].Size(), std::vector<FrameCountType>()); std::vector< std::vector<FrameCountType> > finishTimes(val["scenarios"].Size(), std::vector<FrameCountType>());; std::vector< size_t > nextActionIndexes(val["scenarios"].Size(), 0); double frameDelayMS = 0; if (val.HasMember("fps") && val["fps"].IsInt()) { frameDelayMS = 1000.0 / val["fps"].GetInt(); } const rapidjson::Value & scenarios = val["scenarios"]; for (size_t i(0); i < scenarios.Size(); ++i) { const rapidjson::Value & scenario = scenarios[i]; BOSS_ASSERT(scenario.HasMember("state") && scenario["state"].IsString(), "Scenario has no 'state' string"); BOSS_ASSERT(scenario.HasMember("buildOrder") && scenario["buildOrder"].IsString(), "Scenario has no 'buildOrder' string"); states.push_back(getState(scenario["state"].GetString())); buildOrders.push_back(getBuildOrder(scenario["buildOrder"].GetString())); } BOSS::GUI::Instance().OnStart(); while (true) { Timer t; t.start(); for (size_t s(0); s < states.size(); ++s) { bool didAction = false; if (nextActionIndexes[s] < buildOrders[s].size()) { FrameCountType nextActionFrame = states[s].whenCanPerform(buildOrders[s][nextActionIndexes[s]]); if (nextActionFrame == states[s].getCurrentFrame()) { ActionType type = buildOrders[s][nextActionIndexes[s]]; FrameCountType finish = states[s].getCurrentFrame() + buildOrders[s][nextActionIndexes[s]].buildTime(); if (type.isBuilding() && !type.isAddon() && !type.isMorphed()) { finish += Constants::BUILDING_PLACEMENT; } startTimes[s].push_back(states[s].getCurrentFrame()); finishTimes[s].push_back(finish); states[s].doAction(buildOrders[s][nextActionIndexes[s]]); didAction = true; //std::cout << states[s].getCurrentFrame() << " Action Performed: " << buildOrder[nextActionIndex].getName() << std::endl; nextActionIndexes[s]++; } } if (!didAction) { states[s].fastForward(states[s].getCurrentFrame() + 1); } BOSS::GUI::Instance().AddState(states[s]); BOSS::GUI::Instance().AddActionTimes(startTimes[s], finishTimes[s]); BOSS::GUI::Instance().AddBuildOrder(buildOrders[s], nextActionIndexes[s]); } BOSS::GUI::Instance().OnFrame(); while (t.getElapsedTimeInMilliSec() < frameDelayMS) {} bool anyLess = false; for (size_t i(0); i < states.size(); ++i) { if (nextActionIndexes[i] < buildOrders[i].size() || states[i].getCurrentFrame() < states[i].getLastActionFinishTime()) { anyLess = true; break; } } if (!anyLess) { break; } } }