Skip to content

Commit

Permalink
Update to SteamHammer 2.3.5
Browse files Browse the repository at this point in the history
  • Loading branch information
kant2002 committed Sep 4, 2020
1 parent 5fa01b3 commit dc90e59
Show file tree
Hide file tree
Showing 72 changed files with 3,173 additions and 1,362 deletions.
165 changes: 165 additions & 0 deletions Steamhammer/Source/BOSimulator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include "BOSimulator.h"

using namespace UAlbertaBot;

// TODO unfinished! this is work in progress

// Simulate a given build order to estimate its duration and its final gas and minerals.
// This is meant to be a low-level tool to compare build orders, to help in making
// dynamic decisions during games.

// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

// Minerals mined over the given (short) duration. The worker count is constant.
// NOTE This is a simplified estimate. The same is used in WorkerManager.
int BOSimulator::mineralsMined(int duration) const
{
return int(std::round(duration * nWorkers * mineralRate));
}

// TODO unimplemented
int BOSimulator::gasMined(int duration) const
{
return 0;
}

// When will we have enough resources to produce the item?
// NOTE This doesn't check supply or prerequisites, only resources.
int BOSimulator::findItemFrame(const MacroAct & act) const
{
if (act.isUnit())
{
int mineralsNeeded = act.getUnitType().mineralPrice() - minerals;
int gasNeeded = act.getUnitType().gasPrice() - gas;

if (mineralsNeeded > 0)
{
return frame + int(std::round(mineralsNeeded / (nWorkers * mineralRate)));
}
// Otherwise we already have enough and can fall through.
}

return frame;
}

// The next in-progress item is now completing.
void BOSimulator::doInProgressItem()
{
int nextFrame = inProgress.top().first;
const MacroAct * act = inProgress.top().second; // do not alter inProgress until the end

int duration = nextFrame - frame;
minerals += mineralsMined(duration);
gas += gasMined(duration);

if (act->isUnit())
{
BWAPI::UnitType type = act->getUnitType();
completedUnits[type] += 1;
supply += type.supplyProvided();
if (type.isWorker())
{
++nWorkers;
}
}

frame = nextFrame;
inProgress.pop();
}

void BOSimulator::doBuildItem(int nextFrame)
{
int duration = nextFrame - frame;
minerals += mineralsMined(duration);
gas += gasMined(duration);

const MacroAct & act = buildOrder[boindex];
if (act.isUnit())
{
BWAPI::UnitType type = act.getUnitType();
minerals -= type.mineralPrice();
gas -= type.gasPrice();
inProgress.push(std::pair<int, const MacroAct *>(nextFrame + type.buildTime(), &act));
}

UAB_ASSERT(minerals >= 0 && gas >= 0, "resources out of bounds");

++boindex;
}

// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

BOSimulator::BOSimulator(const std::vector<MacroAct> & bo)
: buildOrder(bo)
, boindex(0)
, frame(BWAPI::Broodwar->getFrameCount())
, startFrame(BWAPI::Broodwar->getFrameCount())
, nWorkers(4) // start of game
, minerals(BWAPI::Broodwar->self()->minerals())
, gas(BWAPI::Broodwar->self()->gas())
, supply(BWAPI::Broodwar->self()->supplyTotal())
{
// TODO find pending research, etc.

run();
}

bool BOSimulator::done() const
{
return
deadlock ||
boindex >= buildOrder.size() && inProgress.empty();
}

bool BOSimulator::deadlocked() const
{
return deadlock;
}

void BOSimulator::step()
{
UAB_ASSERT(!done(), "simulation over");

// 1. Is the next item a build order item, or the completion of an in-progress item?
bool nextIsInProgress;
int boFrame = -1;

if (boindex >= buildOrder.size())
{
nextIsInProgress = true;
}
else
{
const MacroAct & act = buildOrder.at(boindex);
boFrame = findItemFrame(act);
if (inProgress.empty())
{
nextIsInProgress = false;
}
else
{
// Within a frame, do in progress items before build items.
int inProgressFrame = inProgress.top().first;
nextIsInProgress = inProgressFrame <= boFrame;
}
}

// 2. Execute the next item.
if (nextIsInProgress)
{
doInProgressItem();
}
else
{
doBuildItem(boFrame);
}
}


void BOSimulator::run()
{
while (!done())
{
step();
}
}
62 changes: 62 additions & 0 deletions Steamhammer/Source/BOSimulator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#pragma once

#include <queue>
#include "MacroAct.h"

namespace UAlbertaBot
{

class BOSimulator
{
private:
const std::vector<MacroAct> & buildOrder;

size_t boindex; // how far into the simulation?
int frame; // simulated time

int startFrame;
int nWorkers;
int minerals;
int gas;
int supply;

bool deadlock;

// Assumed rate at which 1 worker can mine minerals.
const double mineralRate = 0.045;

// Completed items.
std::map<BWAPI::UnitType, int> completedUnits;

// The finishing time of items that are started and take time to complete.
std::priority_queue<
std::pair<int, const MacroAct *>,
std::vector< std::pair<int, const MacroAct *> >,
std::greater< std::pair<int, const MacroAct *> >
> inProgress;

int mineralsMined(int duration) const;
int gasMined(int duration) const;

bool canBuildItem(const MacroAct & act) const;
int findItemFrame(const MacroAct & act) const;
void doInProgressItem();
void doBuildItem(int nextFrame);

public:
BOSimulator(const std::vector<MacroAct> & bo);

int getStartFrame() const { return startFrame; };
int getFrame() const { return frame; };
int getMinerals() const { return minerals; };
int getGas() const { return gas; };

int getDuration() const { return frame - startFrame; };

bool done() const; // the simulation is completed (or deadlocked)
bool deadlocked() const; // the simulation cannot continue (and is therefore done)
void step(); // execute one simulation step
void run(); // run the simulation to its end
};

}
25 changes: 25 additions & 0 deletions Steamhammer/Source/Base.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "Common.h"
#include "Base.h"

#include "WorkerManager.h"

using namespace UAlbertaBot;

// NOTE This depends on tilePosition, so the startingBase flag must be declared after tilePosition.
Expand Down Expand Up @@ -137,6 +139,29 @@ int Base::getInitialGas() const
return total;
}

// How many workers to saturate the base?
// Two per mineral patch plus three per geyser.
// NOTE This doesn't account for mineral patches mining out, decreasing the maximum.
int Base::getMaxWorkers() const
{
return 2 * minerals.size() + 3 * geysers.size();
}

// Two per mineral patch plus three per geyser.
int Base::getNumWorkers() const
{
// The number of assigned mineral workers.
int nWorkers = WorkerManager::Instance().getNumWorkers(resourceDepot);

// Add the assigned gas workers.
for (BWAPI::Unit geyser : geysers)
{
nWorkers += WorkerManager::Instance().getNumWorkers(geyser);
}

return nWorkers;
}

// The mean offset of the base's mineral patches from the center of the resource depot.
// This is used to tell what direction the minerals are in.
BWAPI::Position Base::getMineralOffset() const
Expand Down
6 changes: 5 additions & 1 deletion Steamhammer/Source/Base.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Base
BWAPI::Unitset geysers; // the base's associated geysers
BWAPI::Unitset blockers; // destructible neutral units that may be in the way
GridDistances distances; // ground distances from tilePosition
bool startingBase; // one of the map's starting bases
bool startingBase; // one of the map's starting bases?

bool reserved; // if this is a planned expansion
bool workerDanger; // for our own bases only; false for others
Expand Down Expand Up @@ -66,6 +66,10 @@ class Base
int getInitialMinerals() const;
int getInitialGas() const;

// Workers assigned to mine minerals or gas.
int getMaxWorkers() const;
int getNumWorkers() const;

BWAPI::Position getMineralOffset() const; // mean offset of minerals from base center
BWAPI::Position getFrontPoint() const; // the "front" of the base, where static defense should go

Expand Down
4 changes: 2 additions & 2 deletions Steamhammer/Source/Bases.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,8 +585,8 @@ void Bases::update()
}

// When a building is placed, we are told the desired and actual location of the building.
// Buildings are usually placed in the main base, so this can give us a hint when the main base
// is full and we need to choose a new one.
// Buildings are usually placed in the "main" base, so this can give us a hint when the main base
// is full and we need to choose a new main base.
void Bases::checkBuildingPosition(const BWAPI::TilePosition & desired, const BWAPI::TilePosition & actual)
{
UAB_ASSERT(desired.isValid(), "bad location");
Expand Down
10 changes: 6 additions & 4 deletions Steamhammer/Source/BuildingManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,6 @@ void BuildingManager::assignWorkersToUnassignedBuildings()
}
b.finalPosition = testLocation;

the.micro.Move(b.builderUnit, b.getCenter());

++b.buildersSent; // count workers ever assigned to build it

//BWAPI::Broodwar->printf("assign builder %d to %s", b.builderUnit->getID(), UnitTypeName(b.type).c_str());
Expand Down Expand Up @@ -748,13 +746,17 @@ BWAPI::TilePosition BuildingManager::getBuildingLocation(const Building & b)
if (b.type.isResourceDepot())
{
BWAPI::TilePosition front = Bases::Instance().frontPoint();
if (b.macroLocation == MacroLocation::Front && front.isValid())
if (b.macroLocation == MacroLocation::Front &&
front.isValid() &&
!the.groundAttacks.inRange(b.type, front))
{
// This means build an additional hatchery at our existing front base, wherever it is.
return front;
}
Base * natural = Bases::Instance().myNaturalBase();
if (b.macroLocation == MacroLocation::Natural && natural)
if (b.macroLocation == MacroLocation::Natural &&
natural &&
!the.groundAttacks.inRange(b.type, natural->getTilePosition()))
{
return natural->getTilePosition();
}
Expand Down
Loading

0 comments on commit dc90e59

Please sign in to comment.