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::canAffordGas(const ActionType & action) const { return _gas >= action.gasPrice(); }
// 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; }