Skip to content
Snippets Groups Projects
Commit 7abf8c26 authored by David Bergström's avatar David Bergström
Browse files

Separate baseline agent from API

parent ec5e8f3f
Branches
Tags
No related merge requests found
......@@ -60,7 +60,6 @@ PYBIND11_MODULE(library, m)
// the name used for inheritance in Pyhon stays the same. Weird.
.def("OnGameStart", &IDABot::OnGameStart)
.def("OnStep", &IDABot::OnStep)
.def("OnStep_UpdateIDABot", &IDABot::OnStep_UpdateIDABot)
.def("get_all_units", &IDABot::GetAllUnits)
.def("get_my_units", &IDABot::GetMyUnits)
.def("get_player_race", &IDABot::GetPlayerRace)
......
......@@ -43,14 +43,6 @@ public:
OnStep
);
}
void OnStep_UpdateIDABot() override
{
PYBIND11_OVERLOAD(
void,
IDABot,
OnStep_UpdateIDABot
);
}
};
// The functions below are all defined in different .cpp files, in order
......
#include "IDABot.h"
#include "Util.h"
void IDABot::OnStep_UpdateIDABot()
{
// This is the entry point of the bot.
// This function is called every time the game loop is run.
//if (!Bases().getPlayerStartingBaseLocation(Players::Enemy)->isExplored())
//{
// assignScout();
//}
std::vector<UnitType> build_plan = CreateBuildPlan();
manageWorkers(build_plan);
manageBuilding(build_plan);
}
void IDABot::manageBuilding(std::vector<UnitType> & build_plan)
{
std::map<UnitType, int> currently_being_built = numberOfTypeBeingBuilt();
for (auto & pair : currently_being_built)
std::cout << "Building " << pair.second << " of " << pair.first.getName() << std::endl;
int minerals = GetMinerals();
int gas = GetGas();
// TODO: Supply
for (UnitType type : build_plan)
{
if (currently_being_built[type] > 0)
{
currently_being_built[type]--;
std::cout << "Already building " << type.getName() << std::endl;
continue;
}
// If we cannot afford the next thing, we don't want to build at all
if (type.mineralPrice() > minerals || type.gasPrice() > gas)
{
break;
}
std::vector<Unit> producers = getProducer(MetaType(type, *this));
// Reserve some resources
// TODO: Only reserve resources if the corresponding worker is in BuildingWalking
minerals -= type.mineralPrice();
gas -= type.gasPrice();
if (!producers.empty())
{
if (type.isBuilding())
{
for (Unit & worker : getWorkers())
{
if (isFree(worker))
{
CCTilePosition position = getBuildPosition(type);
BuildStatus status{ type, position };
currently_building[worker] = status;
// Reserve the location
m_buildingPlacer.reserveTiles(position.x, position.y, type.tileWidth(), type.tileHeight());
// Update economy book-keeping
if (type.isRefinery() || type.isResourceDepot())
{
economy_spending.minerals += type.mineralPrice();
economy_spending.gas += type.gasPrice();
}
else if (type.supplyProvided() == 0)
{
military_spending.minerals += type.mineralPrice();
military_spending.gas += type.gasPrice();
}
assignWork(worker, Assignment::BuildWalking);
return;
}
}
}
else if (type.isCombatUnit() || type.isWorker())
{
// TODO: Remove code-duplication
// Update economy book-keeping
if (type.isWorker())
{
economy_spending.minerals += type.mineralPrice();
economy_spending.gas += type.gasPrice();
}
else
{
military_spending.minerals += type.mineralPrice();
military_spending.gas += type.gasPrice();
}
std::cout << "Training unit " << type.getName() << std::endl;
producers.front().train(type);
return;
}
}
}
}
bool IDABot::isFree(Unit & worker)
{
if (workerAssignment.count(worker) > 0)
{
return workerAssignment[worker] == Assignment::Mineral;
}
else
{
return true;
}
}
std::vector<UnitType> IDABot::CreateBuildPlan()
{
// Count the total number of minerals, including all bases
size_t number_of_minerals{ 0 };
for (const BaseLocation * base : Bases().getOccupiedBaseLocations(Players::Self))
{
number_of_minerals += base->getMinerals().size();
}
size_t number_of_workers = getWorkers().size();
size_t available_food = GetMaxSupply() - GetCurrentSupply();
std::vector<UnitType> build_plan;
std::cout << "Military spending (minerals) " << military_spending.minerals << std::endl;
std::cout << "Economy spending (minerals) " << economy_spending.minerals << std::endl;
// TODO: Make a better decision here
if ((500 + military_spending.minerals) < economy_spending.minerals)
{
CreateMaximizeMilitaryBuildPlan(build_plan, available_food);
CreateMaximizeEconomyBuildPlan(number_of_minerals, number_of_workers, available_food, build_plan);
}
else
{
CreateMaximizeEconomyBuildPlan(number_of_minerals, number_of_workers, available_food, build_plan);
CreateMaximizeMilitaryBuildPlan(build_plan, available_food);
}
for (size_t i{}; i < build_plan.size() && i < 10; ++i)
{
std::cout << build_plan[i].getName() << ", ";
}
std::cout << std::endl;
return build_plan;
}
void IDABot::CreateMaximizeMilitaryBuildPlan(std::vector<UnitType> &build_plan, size_t &available_food)
{
for (auto & pair : military_goal)
{
// How many do we already have?
size_t units_of_type = GetUnits(pair.first).size();
// First, do we meet the military goal for this UnitType?
if (units_of_type >= pair.second) {
continue;
}
// Second, can we produce anything of UnitType?
std::vector<Unit> producers = getProducer(MetaType(pair.first, *this), true);
// TODO: Don't build too many Barracks if we don't need to
if (producers.empty())
{
// TODO: Calculate what we need to build using the TechTree
// For now, let's build a barrack
build_plan.push_back(UnitType(sc2::UNIT_TYPEID::TERRAN_BARRACKS, *this));
}
else
{
// TODO: This is never run, or is it??
int units_needed = pair.second - units_of_type;
int food_required = pair.first.supplyRequired();
for (int i{}; i < units_needed; ++i)
{
if (available_food >= food_required)
{
return;
}
else
{
available_food -= food_required;
build_plan.push_back(pair.first);
}
}
}
}
}
void IDABot::CreateMaximizeEconomyBuildPlan(size_t &number_of_minerals, size_t &number_of_workers, size_t &available_food, std::vector<UnitType> &build_plan)
{
const int WORKER_PER_MINERAL_DEPOSIT = 3;
const int FOOD_THRESHOLD_BUILD_DEPOT = 1;
// Plan for the two next base expansions
for (int i = 0; i < 2; i++)
{
while (WORKER_PER_MINERAL_DEPOSIT * number_of_minerals > number_of_workers)
{
if (available_food == FOOD_THRESHOLD_BUILD_DEPOT)
{
UnitType type{ sc2::UNIT_TYPEID::TERRAN_SUPPLYDEPOT, *this };
build_plan.push_back(type);
available_food += type.supplyProvided(); // TODO: Maybe 16 sometimes? (+16 with Calldown: Supplies)
}
else
{
build_plan.push_back(UnitType(sc2::UNIT_TYPEID::TERRAN_SCV, *this));
available_food--;
number_of_workers++;
}
}
UnitType type{ sc2::UNIT_TYPEID::TERRAN_COMMANDCENTER, *this };
build_plan.push_back(type);
available_food += type.supplyProvided();
const BaseLocation * next_expansion = Bases().getNextExpansion(Players::Self);
if (next_expansion)
{
number_of_minerals += next_expansion->getMinerals().size();
}
}
}
void IDABot::assignScout()
{
// Assumes that at least one worker is unassigned
for (Unit worker : getWorkers())
{
if (workerAssignment.count(worker) == 0)
{
assignWork(worker, Assignment::Scout);
std::cout << "Assigned worker to Scout" << std::endl;
break;
}
}
}
void IDABot::manageWorkers(std::vector<UnitType> & build_plan)
{
std::vector<Unit> workers = getWorkers();
for (Unit worker : workers)
{
// If the worker has does not have an assignment
if (workerAssignment.count(worker) == 0)
{
assignWork(worker, Assignment::Mineral);
}
else
{
CCTilePosition position;
switch (workerAssignment[worker]) {
case Assignment::Mineral:
case Assignment::Gas:
// Never change let the gas/mineral workers rest
break;
case Assignment::Scout:
if (Bases().getPlayerStartingBaseLocation(Players::Enemy)->isExplored()) {
assignWork(worker, Assignment::Mineral);
}
break;
case Assignment::BuildWalking:
position = currently_building[worker].position;
if (position == Util::GetTilePosition(worker.getPosition()))
{
assignWork(worker, Assignment::BuildBuilding);
}
break;
case Assignment::BuildBuilding:
// Probably done,
if (worker.isIdle())
{
BuildStatus status = currently_building[worker];
m_buildingPlacer.freeTiles(status.position.x, status.position.y, status.type.tileWidth(), status.type.tileHeight());
currently_building.erase(worker);
assignWork(worker, Assignment::Mineral);
}
break;
}
}
}
}
void IDABot::assignWork(Unit & worker, Assignment assignment)
{
// Assigns worker to assignment
workerAssignment[worker] = assignment;
Unit mineral;
const BaseLocation * enemyBaseLocation;
CCTilePosition position;
AssignmentData data;
const BaseLocation * assigned_base;
switch (assignment) {
case Assignment::Mineral:
// Select which base to mine for
assigned_base = AssignBase(worker);
mineral = getClosestMineral(assigned_base->getPosition());
worker.rightClick(mineral);
break;
case Assignment::Scout:
enemyBaseLocation = Bases().getPlayerStartingBaseLocation(Players::Enemy);
if (enemyBaseLocation)
{
std::cout << "Enemy base location known!" << std::endl;
worker.move(enemyBaseLocation->getPosition());
}
else
{
std::cout << "Enemy base location unknown!" << std::endl;
for (const BaseLocation * location : Bases().getStartingBaseLocations())
{
if (!Map().isExplored(location->getPosition()))
{
worker.move(location->getPosition());
break;
}
}
}
break;
case Assignment::BuildWalking:
worker.move(currently_building[worker].position);
break;
case Assignment::BuildBuilding:
worker.build(currently_building[worker].type, currently_building[worker].position);
break;
}
}
std::map<UnitType, int> IDABot::numberOfTypeBeingBuilt() const
{
std::map<UnitType, int> being_built;
for (auto & pair : currently_building)
{
being_built[pair.second.type]++;
}
return being_built;
}
const BaseLocation * IDABot::AssignBase(Unit & worker)
{
std::set<const BaseLocation *> base_locations = Bases().getOccupiedBaseLocations(Players::Self);
int most_needed_workers_value{std::numeric_limits<int>().min()};
const BaseLocation * most_needed_workers_pointer{};
for (const BaseLocation * base_location : base_locations)
{
int workers_needed = 3 * base_location->getMinerals().size();
int number_of_workers;
if (base_assignment.count(base_location) > 0)
{
std::vector<Unit> & workers = base_assignment[base_location];
number_of_workers = workers.size();
}
else
{
number_of_workers = 0;
std::vector<Unit> workers;
base_assignment[base_location] = workers;
}
int needed_workers = workers_needed - number_of_workers;
if (needed_workers > most_needed_workers_value)
{
most_needed_workers_value = needed_workers;
most_needed_workers_pointer = base_location;
}
}
std::vector<Unit> & workers = base_assignment[most_needed_workers_pointer];
workers.push_back(worker);
return most_needed_workers_pointer;
}
CCTilePosition IDABot::getBuildPosition(UnitType & building)
{
CCTilePosition tile_position;
if (Util::GetTownHall(GetPlayerRace((int) Players::Self), *this) == building)
{
tile_position = Bases().getNextExpansion((int)Players::Self)->getDepotPosition();
}
else
{
CCPosition position = Bases().getPlayerStartingBaseLocation(Players::Self)->getPosition();
tile_position = m_buildingPlacer.getBuildLocationNear(Util::GetTilePosition(position), building, 1);
}
return tile_position;
}
Unit IDABot::getClosestMineral(const CCPosition & position) const
{
Unit bestMineral;
double bestDist = 100000;
for (auto & mineral : GetAllUnits())
{
if (!mineral.getType().isMineral()) continue;
double dist = Util::Dist(mineral, position);
if (dist < bestDist)
{
bestMineral = mineral;
bestDist = dist;
}
}
return bestMineral;
}
std::vector<Unit> IDABot::getWorkers()
{
std::vector<Unit> workers;
for (auto & unit : GetMyUnits())
{
if (unit.getType().isWorker())
{
workers.push_back(unit);
}
}
return workers;
}
std::vector<Unit> IDABot::getProducer(const MetaType & type, bool includeBusy, bool includeIncomplete)
{
// get all the types of units that cna build this type
auto & producerTypes = Data(type).whatBuilds;
// make a set of all candidate producers
std::vector<Unit> candidateProducers;
for (auto unit : UnitInfo().getUnits(Players::Self))
{
// reasons a unit can not train the desired type
if (std::find(producerTypes.begin(), producerTypes.end(), unit.getType()) == producerTypes.end()) { continue; }
if (!includeIncomplete && !unit.isCompleted()) { continue; }
if (!includeBusy && Data(unit).isBuilding && unit.isTraining()) { continue; }
if (unit.isFlying()) { continue; }
// TODO: if unit is not powered continue
// TODO: if the type is an addon, some special cases
// TODO: if the type requires an addon and the producer doesn't have one
// if we haven't cut it, add it to the set of candidates
candidateProducers.push_back(unit);
}
return candidateProducers;
}
IDABot::IDABot()
: m_map(*this)
, m_bases(*this)
......@@ -465,7 +8,6 @@ IDABot::IDABot()
, m_techTree(*this)
, m_buildingPlacer(*this)
{
military_goal[UnitType(sc2::UNIT_TYPEID::TERRAN_MARINE, *this)] = 30;
}
void IDABot::OnGameStart()
......@@ -530,11 +72,6 @@ void IDABot::OnStep()
m_unitInfo.onFrame();
m_bases.onFrame();
// -----------------------------------------------------------------
// Run the actual bot.
// -----------------------------------------------------------------
OnStep_UpdateIDABot();
// -----------------------------------------------------------------
// Draw debug interface, and send debug interface to the Sc2 client.
// -----------------------------------------------------------------
......
......@@ -19,7 +19,6 @@ class IDABot : public sc2::Agent
BaseLocationManager m_bases;
UnitInfoManager m_unitInfo;
TechTree m_techTree;
// TODO: This should not be exported for student use
BuildingPlacer m_buildingPlacer;
std::vector<Unit> m_allUnits;
......@@ -34,75 +33,6 @@ public:
void OnGameStart() override;
void OnStep() override;
virtual void OnStep_UpdateIDABot();
/*
My stuff
*/
// Worker management
enum Assignment {
Scout,
Mineral,
Gas,
BuildWalking,
BuildBuilding
};
struct AssignmentData {
// When assigned to building, this corresponds to the location for the building
CCTilePosition buildGoal;
};
// Building management
// Worker assignment book-keeping
std::map<Unit, Assignment> workerAssignment;
// When workers are assigned to either minerals or gas, they get assigned a base
std::map<const BaseLocation *, std::vector<Unit>> base_assignment;
std::vector<UnitType> build_order; // TODO: Not used
std::vector<UnitType> CreateBuildPlan();
void CreateMaximizeMilitaryBuildPlan(std::vector<UnitType> &build_plan, size_t &available_food);
void CreateMaximizeEconomyBuildPlan(size_t &number_of_minerals, size_t &number_of_workers, size_t &available_food, std::vector<UnitType> &build_plan);
void assignScout();
void manageWorkers(std::vector<UnitType> & build_plan);
void manageBuilding(std::vector<UnitType> & build_plan);
bool isFree(Unit & worker);
void assignWork(Unit & worker, Assignment assignment);
std::map<UnitType, int> numberOfTypeBeingBuilt() const;
const BaseLocation * AssignBase(Unit & worker);
CCTilePosition getBuildPosition(UnitType & building);
Unit getClosestMineral(const CCPosition & unit) const;
struct BuildStatus
{
UnitType type;
CCTilePosition position;
int idle;
};
std::map<Unit, BuildStatus> currently_building;
// Military management
std::map<UnitType, int> military_goal;
// Economy
struct Resources
{
int minerals;
int gas;
};
Resources military_spending{};
Resources economy_spending{};
// Getters
std::vector<Unit> getWorkers();
// Maybe
std::vector<Unit> getProducer(const MetaType & type, bool includeBusy = false, bool include_incomplete = false);
/*
API for students
......
#include "MyAgent.h"
MyAgent::MyAgent()
{
military_goal[UnitType(sc2::UNIT_TYPEID::TERRAN_MARINE, *this)] = 30;
}
void MyAgent::OnGameStart()
{
IDABot::OnGameStart();
}
void MyAgent::OnStep()
{
IDABot::OnStep();
// This is the entry point of the bot.
// This function is called every time the game loop is run.
//if (!Bases().getPlayerStartingBaseLocation(Players::Enemy)->isExplored())
//{
// assignScout();
//}
std::vector<UnitType> build_plan = CreateBuildPlan();
manageWorkers(build_plan);
manageBuilding(build_plan);
}
void MyAgent::manageBuilding(std::vector<UnitType> & build_plan)
{
std::map<UnitType, int> currently_being_built = numberOfTypeBeingBuilt();
for (auto & pair : currently_being_built)
std::cout << "Building " << pair.second << " of " << pair.first.getName() << std::endl;
int minerals = GetMinerals();
int gas = GetGas();
// TODO: Supply
for (UnitType type : build_plan)
{
if (currently_being_built[type] > 0)
{
currently_being_built[type]--;
std::cout << "Already building " << type.getName() << std::endl;
continue;
}
// If we cannot afford the next thing, we don't want to build at all
if (type.mineralPrice() > minerals || type.gasPrice() > gas)
{
break;
}
std::vector<Unit> producers = getProducer(MetaType(type, *this));
// Reserve some resources
// TODO: Only reserve resources if the corresponding worker is in BuildingWalking
minerals -= type.mineralPrice();
gas -= type.gasPrice();
if (!producers.empty())
{
if (type.isBuilding())
{
for (Unit & worker : getWorkers())
{
if (isFree(worker))
{
CCTilePosition position = getBuildPosition(type);
BuildStatus status{ type, position };
currently_building[worker] = status;
// Reserve the location
BuildingPlacer().reserveTiles(position.x, position.y, type.tileWidth(), type.tileHeight());
// Update economy book-keeping
if (type.isRefinery() || type.isResourceDepot())
{
economy_spending.minerals += type.mineralPrice();
economy_spending.gas += type.gasPrice();
}
else if (type.supplyProvided() == 0)
{
military_spending.minerals += type.mineralPrice();
military_spending.gas += type.gasPrice();
}
assignWork(worker, Assignment::BuildWalking);
return;
}
}
}
else if (type.isCombatUnit() || type.isWorker())
{
// TODO: Remove code-duplication
// Update economy book-keeping
if (type.isWorker())
{
economy_spending.minerals += type.mineralPrice();
economy_spending.gas += type.gasPrice();
}
else
{
military_spending.minerals += type.mineralPrice();
military_spending.gas += type.gasPrice();
}
std::cout << "Training unit " << type.getName() << std::endl;
producers.front().train(type);
return;
}
}
}
}
bool MyAgent::isFree(Unit & worker)
{
if (workerAssignment.count(worker) > 0)
{
return workerAssignment[worker] == Assignment::Mineral;
}
else
{
return true;
}
}
std::vector<UnitType> MyAgent::CreateBuildPlan()
{
// Count the total number of minerals, including all bases
size_t number_of_minerals{ 0 };
for (const BaseLocation * base : Bases().getOccupiedBaseLocations(Players::Self))
{
number_of_minerals += base->getMinerals().size();
}
size_t number_of_workers = getWorkers().size();
size_t available_food = GetMaxSupply() - GetCurrentSupply();
std::vector<UnitType> build_plan;
std::cout << "Military spending (minerals) " << military_spending.minerals << std::endl;
std::cout << "Economy spending (minerals) " << economy_spending.minerals << std::endl;
// TODO: Make a better decision here
if ((500 + military_spending.minerals) < economy_spending.minerals)
{
CreateMaximizeMilitaryBuildPlan(build_plan, available_food);
CreateMaximizeEconomyBuildPlan(number_of_minerals, number_of_workers, available_food, build_plan);
}
else
{
CreateMaximizeEconomyBuildPlan(number_of_minerals, number_of_workers, available_food, build_plan);
CreateMaximizeMilitaryBuildPlan(build_plan, available_food);
}
for (size_t i{}; i < build_plan.size() && i < 10; ++i)
{
std::cout << build_plan[i].getName() << ", ";
}
std::cout << std::endl;
return build_plan;
}
void MyAgent::CreateMaximizeMilitaryBuildPlan(std::vector<UnitType> &build_plan, size_t &available_food)
{
for (auto & pair : military_goal)
{
// How many do we already have?
size_t units_of_type = GetUnits(pair.first).size();
// First, do we meet the military goal for this UnitType?
if (units_of_type >= pair.second) {
continue;
}
// Second, can we produce anything of UnitType?
std::vector<Unit> producers = getProducer(MetaType(pair.first, *this), true);
// TODO: Don't build too many Barracks if we don't need to
if (producers.empty())
{
// TODO: Calculate what we need to build using the TechTree
// For now, let's build a barrack
build_plan.push_back(UnitType(sc2::UNIT_TYPEID::TERRAN_BARRACKS, *this));
}
else
{
// TODO: This is never run, or is it??
int units_needed = pair.second - units_of_type;
int food_required = pair.first.supplyRequired();
for (int i{}; i < units_needed; ++i)
{
if (available_food >= food_required)
{
return;
}
else
{
available_food -= food_required;
build_plan.push_back(pair.first);
}
}
}
}
}
void MyAgent::CreateMaximizeEconomyBuildPlan(size_t &number_of_minerals, size_t &number_of_workers, size_t &available_food, std::vector<UnitType> &build_plan)
{
const int WORKER_PER_MINERAL_DEPOSIT = 3;
const int FOOD_THRESHOLD_BUILD_DEPOT = 1;
// Plan for the two next base expansions
for (int i = 0; i < 2; i++)
{
while (WORKER_PER_MINERAL_DEPOSIT * number_of_minerals > number_of_workers)
{
if (available_food == FOOD_THRESHOLD_BUILD_DEPOT)
{
UnitType type{ sc2::UNIT_TYPEID::TERRAN_SUPPLYDEPOT, *this };
build_plan.push_back(type);
available_food += type.supplyProvided(); // TODO: Maybe 16 sometimes? (+16 with Calldown: Supplies)
}
else
{
build_plan.push_back(UnitType(sc2::UNIT_TYPEID::TERRAN_SCV, *this));
available_food--;
number_of_workers++;
}
}
UnitType type{ sc2::UNIT_TYPEID::TERRAN_COMMANDCENTER, *this };
build_plan.push_back(type);
available_food += type.supplyProvided();
const BaseLocation * next_expansion = Bases().getNextExpansion(Players::Self);
if (next_expansion)
{
number_of_minerals += next_expansion->getMinerals().size();
}
}
}
void MyAgent::assignScout()
{
// Assumes that at least one worker is unassigned
for (Unit worker : getWorkers())
{
if (workerAssignment.count(worker) == 0)
{
assignWork(worker, Assignment::Scout);
std::cout << "Assigned worker to Scout" << std::endl;
break;
}
}
}
void MyAgent::manageWorkers(std::vector<UnitType> & build_plan)
{
std::vector<Unit> workers = getWorkers();
for (Unit worker : workers)
{
// If the worker has does not have an assignment
if (workerAssignment.count(worker) == 0)
{
assignWork(worker, Assignment::Mineral);
}
else
{
CCTilePosition position;
switch (workerAssignment[worker]) {
case Assignment::Mineral:
case Assignment::Gas:
// Never change let the gas/mineral workers rest
break;
case Assignment::Scout:
if (Bases().getPlayerStartingBaseLocation(Players::Enemy)->isExplored()) {
assignWork(worker, Assignment::Mineral);
}
break;
case Assignment::BuildWalking:
position = currently_building[worker].position;
if (position == Util::GetTilePosition(worker.getPosition()))
{
assignWork(worker, Assignment::BuildBuilding);
}
break;
case Assignment::BuildBuilding:
// Probably done,
if (worker.isIdle())
{
BuildStatus status = currently_building[worker];
BuildingPlacer().freeTiles(status.position.x, status.position.y, status.type.tileWidth(), status.type.tileHeight());
currently_building.erase(worker);
assignWork(worker, Assignment::Mineral);
}
break;
}
}
}
}
void MyAgent::assignWork(Unit & worker, Assignment assignment)
{
// Assigns worker to assignment
workerAssignment[worker] = assignment;
Unit mineral;
const BaseLocation * enemyBaseLocation;
CCTilePosition position;
AssignmentData data;
const BaseLocation * assigned_base;
switch (assignment) {
case Assignment::Mineral:
// Select which base to mine for
assigned_base = AssignBase(worker);
mineral = getClosestMineral(assigned_base->getPosition());
worker.rightClick(mineral);
break;
case Assignment::Scout:
enemyBaseLocation = Bases().getPlayerStartingBaseLocation(Players::Enemy);
if (enemyBaseLocation)
{
std::cout << "Enemy base location known!" << std::endl;
worker.move(enemyBaseLocation->getPosition());
}
else
{
std::cout << "Enemy base location unknown!" << std::endl;
for (const BaseLocation * location : Bases().getStartingBaseLocations())
{
if (!Map().isExplored(location->getPosition()))
{
worker.move(location->getPosition());
break;
}
}
}
break;
case Assignment::BuildWalking:
worker.move(currently_building[worker].position);
break;
case Assignment::BuildBuilding:
worker.build(currently_building[worker].type, currently_building[worker].position);
break;
}
}
std::map<UnitType, int> MyAgent::numberOfTypeBeingBuilt() const
{
std::map<UnitType, int> being_built;
for (auto & pair : currently_building)
{
being_built[pair.second.type]++;
}
return being_built;
}
const BaseLocation * MyAgent::AssignBase(Unit & worker)
{
std::set<const BaseLocation *> base_locations = Bases().getOccupiedBaseLocations(Players::Self);
int most_needed_workers_value{std::numeric_limits<int>().min()};
const BaseLocation * most_needed_workers_pointer{};
for (const BaseLocation * base_location : base_locations)
{
int workers_needed = 3 * base_location->getMinerals().size();
int number_of_workers;
if (base_assignment.count(base_location) > 0)
{
std::vector<Unit> & workers = base_assignment[base_location];
number_of_workers = workers.size();
}
else
{
number_of_workers = 0;
std::vector<Unit> workers;
base_assignment[base_location] = workers;
}
int needed_workers = workers_needed - number_of_workers;
if (needed_workers > most_needed_workers_value)
{
most_needed_workers_value = needed_workers;
most_needed_workers_pointer = base_location;
}
}
std::vector<Unit> & workers = base_assignment[most_needed_workers_pointer];
workers.push_back(worker);
return most_needed_workers_pointer;
}
CCTilePosition MyAgent::getBuildPosition(UnitType & building)
{
CCTilePosition tile_position;
if (Util::GetTownHall(GetPlayerRace((int) Players::Self), *this) == building)
{
tile_position = Bases().getNextExpansion((int)Players::Self)->getDepotPosition();
}
else
{
CCPosition position = Bases().getPlayerStartingBaseLocation(Players::Self)->getPosition();
tile_position = BuildingPlacer().getBuildLocationNear(Util::GetTilePosition(position), building, 1);
}
return tile_position;
}
Unit MyAgent::getClosestMineral(const CCPosition & position) const
{
Unit bestMineral;
double bestDist = 100000;
for (auto & mineral : GetAllUnits())
{
if (!mineral.getType().isMineral()) continue;
double dist = Util::Dist(mineral, position);
if (dist < bestDist)
{
bestMineral = mineral;
bestDist = dist;
}
}
return bestMineral;
}
std::vector<Unit> MyAgent::getWorkers()
{
std::vector<Unit> workers;
for (auto & unit : GetMyUnits())
{
if (unit.getType().isWorker())
{
workers.push_back(unit);
}
}
return workers;
}
std::vector<Unit> MyAgent::getProducer(const MetaType & type, bool includeBusy, bool includeIncomplete)
{
// get all the types of units that cna build this type
auto & producerTypes = Data(type).whatBuilds;
// make a set of all candidate producers
std::vector<Unit> candidateProducers;
for (auto unit : UnitInfo().getUnits(Players::Self))
{
// reasons a unit can not train the desired type
if (std::find(producerTypes.begin(), producerTypes.end(), unit.getType()) == producerTypes.end()) { continue; }
if (!includeIncomplete && !unit.isCompleted()) { continue; }
if (!includeBusy && Data(unit).isBuilding && unit.isTraining()) { continue; }
if (unit.isFlying()) { continue; }
// TODO: if unit is not powered continue
// TODO: if the type is an addon, some special cases
// TODO: if the type requires an addon and the producer doesn't have one
// if we haven't cut it, add it to the set of candidates
candidateProducers.push_back(unit);
}
return candidateProducers;
}
#pragma once
#include "IDABot.h"
class MyAgent : public IDABot
{
// Worker management
enum Assignment {
Scout,
Mineral,
Gas,
BuildWalking,
BuildBuilding
};
struct AssignmentData {
// When assigned to building, this corresponds to the location for the building
CCTilePosition buildGoal;
};
// Building management
// Worker assignment book-keeping
std::map<Unit, Assignment> workerAssignment;
// When workers are assigned to either minerals or gas, they get assigned a base
std::map<const BaseLocation *, std::vector<Unit>> base_assignment;
std::vector<UnitType> build_order; // TODO: Not used
std::vector<UnitType> CreateBuildPlan();
void CreateMaximizeMilitaryBuildPlan(std::vector<UnitType> &build_plan, size_t &available_food);
void CreateMaximizeEconomyBuildPlan(size_t &number_of_minerals, size_t &number_of_workers, size_t &available_food, std::vector<UnitType> &build_plan);
void assignScout();
void manageWorkers(std::vector<UnitType> & build_plan);
void manageBuilding(std::vector<UnitType> & build_plan);
bool isFree(Unit & worker);
void assignWork(Unit & worker, Assignment assignment);
std::map<UnitType, int> numberOfTypeBeingBuilt() const;
const BaseLocation * AssignBase(Unit & worker);
CCTilePosition getBuildPosition(UnitType & building);
Unit getClosestMineral(const CCPosition & unit) const;
struct BuildStatus
{
UnitType type;
CCTilePosition position;
int idle;
};
std::map<Unit, BuildStatus> currently_building;
// Military management
std::map<UnitType, int> military_goal;
// Economy
struct Resources
{
int minerals;
int gas;
};
Resources military_spending{};
Resources economy_spending{};
// Getters
std::vector<Unit> getWorkers();
// Maybe
std::vector<Unit> getProducer(const MetaType & type, bool includeBusy = false, bool include_incomplete = false);
public:
MyAgent();
void OnGameStart() override;
void OnStep() override;
};
\ No newline at end of file
#include "Common.h"
#include "IDABot.h"
#include "MyAgent.h"
#include "Util.h"
#include "sc2utils/sc2_manage_process.h"
......@@ -22,7 +23,8 @@ int main(int argc, char* argv[])
sc2::Difficulty enemyDifficulty = sc2::Difficulty::Easy;
// Add the custom bot, it will control the players.
IDABot bot1;
MyAgent bot1;
//IDABot bot1;
//IDABot bot2;
// WARNING: Bot logic has not been thorougly tested on step sizes > 1
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment