#include "StrategyManager.h"
#include "ProductionManager.h"
#include "StrategyBossZerg.h"
#include "Random.h"

using namespace UAlbertaBot;

StrategyManager::StrategyManager()
	: _self(BWAPI::Broodwar->self())
	, _enemy(BWAPI::Broodwar->enemy())
	, _selfRace(BWAPI::Broodwar->self()->getRace())
	, _enemyRace(BWAPI::Broodwar->enemy()->getRace())
	, _emptyBuildOrder(BWAPI::Broodwar->self()->getRace())
	, _lingScore(0)
	, _hydraScore(0)
	, _mutaScore(0)
	, _lurkerScore(0)
	, _ultraScore(0)
	, _guardianScore(0)
	, _devourerScore(0)
	, _techTarget("None")
	, _minUnit("None")
	, _gasUnit("None")
	, _buildOrderZerg(BWAPI::Races::Zerg)
	, _droneEco(0)
	, _hasIslandBases(false)
	, _enemyIsRandom(false)
{
	if (BWAPI::Broodwar->enemy()->getRace() == BWAPI::Races::Unknown)
	{
		_enemyIsRandom = true;
	}

	_hasIslandBases = MapTools::Instance().hasIslandBases();
}

StrategyManager & StrategyManager::Instance() 
{
	static StrategyManager instance;
	return instance;
}

void  StrategyManager::update()
{
	_enemyRace = BWAPI::Broodwar->enemy()->getRace();
}

const int StrategyManager::getScore(BWAPI::Player player) const
{
	return player->getBuildingScore() + player->getKillScore() + player->getRazingScore() + player->getUnitScore();
}

const BuildOrder & StrategyManager::getOpeningBookBuildOrder() const
{
	if (_selfRace == BWAPI::Races::Zerg && BWAPI::Broodwar->mapFileName() == "(8)Big Game Hunters.scm")
	{
		Config::Strategy::StrategyName = "3HatchHydra_BHG";
	}
	else if (_selfRace == BWAPI::Races::Zerg && _hasIslandBases)
	{
		Config::Strategy::StrategyName = "2HatchMuta";
	}

	auto buildOrderIt = _strategies.find(Config::Strategy::StrategyName);

    // look for the build order in the build order map
	if (buildOrderIt != std::end(_strategies))
    {
        return (*buildOrderIt).second._buildOrder;
    }
    else
    {
        UAB_ASSERT_WARNING(false, "Strategy not found: %s, returning empty initial build order", Config::Strategy::StrategyName.c_str());
        return _emptyBuildOrder;
    }
}

const bool StrategyManager::shouldExpandNow() const
{
	// if there is no place to expand to, we can't expand
	if (MapTools::Instance().getNextExpansion(false, false) == BWAPI::TilePositions::None)
	{
        //BWAPI::Broodwar->printf("No valid expansion location");
		return false;
	}

	// if we have idle workers then we need a new expansion
	if (WorkerManager::Instance().getNumIdleWorkers() > 3)
	{
		return true;
	}

    // if we have a stockpile of minerals, expand
	if (BWAPI::Broodwar->self()->minerals() > 600)
    {
        return true;
    }

	size_t numDepots = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Command_Center)
		+ UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Nexus)
		+ UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hatchery)
		+ UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair)
		+ UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	int frame = BWAPI::Broodwar->getFrameCount();
	int minute = frame / (24 * 60);

	// we will make expansion N after array[N] minutes have passed
    std::vector<int> expansionTimes = {5, 10, 14, 17, 20, 22};

    for (size_t i(0); i < expansionTimes.size(); ++i)
    {
		if (numDepots < (i + 2) && minute > expansionTimes[i] && 
			BWAPI::Broodwar->self()->minerals() > 150)
        {
            return true;
        }
    }

	return false;
}

void StrategyManager::addStrategy(const std::string & name, Strategy & strategy)
{
    _strategies[name] = strategy;
}

void StrategyManager::addStrategyMatchup(const std::string & name, int weight)
{
	_strategiesMatchup[name] = weight;
}

const MetaPairVector StrategyManager::getBuildOrderGoal()
{
    if (_selfRace == BWAPI::Races::Protoss)
    {
		return getProtossBuildOrderGoal();
    }
	else if (_selfRace == BWAPI::Races::Terran)
	{
		return getTerranBuildOrderGoal();
	}

    return MetaPairVector();
}

const MetaPairVector StrategyManager::getProtossBuildOrderGoal() const
{
	// the goal to return
	MetaPairVector goal;

	int numWorkers			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Probe);
	int numZealots			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Zealot);
    int numPylons           = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Pylon);
	int numDragoons         = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Dragoon);
	int numProbes           = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Probe);
	int numNexusCompleted   = BWAPI::Broodwar->self()->completedUnitCount(BWAPI::UnitTypes::Protoss_Nexus);
	int numNexusAll         = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Nexus);
	int numCyber            = BWAPI::Broodwar->self()->completedUnitCount(BWAPI::UnitTypes::Protoss_Cybernetics_Core);
	int numCannon           = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Photon_Cannon);
    int numScout            = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Corsair);
    int numReaver           = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Reaver);
    int numDarkTeplar       = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Dark_Templar);

    if (Config::Strategy::StrategyName == "Protoss_ZealotRush")
    {
        goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Zealot, numZealots + 8));

        // once we have a 2nd nexus start making dragoons
        if (numNexusAll >= 2)
        {
            goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Dragoon, numDragoons + 4));
        }
    }
    else if (Config::Strategy::StrategyName == "Protoss_DragoonRush")
    {
        goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Dragoon, numDragoons + 6));
    }
    else if (Config::Strategy::StrategyName == "Protoss_Drop")
    {
        if (numZealots == 0)
        {
            goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Zealot, numZealots + 4));
            goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Shuttle, 1));
        }
        else
        {
            goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Zealot, numZealots + 8));
        }
    }
    else if (Config::Strategy::StrategyName == "Protoss_DTRush")
    {
        goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Dark_Templar, numDarkTeplar + 2));

        // if we have a 2nd nexus then get some goons out
        if (numNexusAll >= 2)
        {
            goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Dragoon, numDragoons + 4));
        }
    }
    else
    {
		BWAPI::Broodwar->printf("Warning: Unknown Protoss Strategy: %s", Config::Strategy::StrategyName.c_str());
    }

    // if we have 3 nexus, make an observer
    if (numNexusCompleted >= 3)
    {
        goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Observer, 1));
    }
    
    // add observer to the goal if the enemy has cloaked units
	if (InformationManager::Instance().enemyHasCloakTech())
	{
		goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Robotics_Facility, 1));
		
		if (BWAPI::Broodwar->self()->completedUnitCount(BWAPI::UnitTypes::Protoss_Robotics_Facility) > 0)
		{
			goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Observatory, 1));
		}
		if (BWAPI::Broodwar->self()->completedUnitCount(BWAPI::UnitTypes::Protoss_Observatory) > 0)
		{
			goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Observer, 1));
		}
	}

    // if we want to expand, insert a nexus into the build order
	if (shouldExpandNow())
	{
		goal.push_back(MetaPair(BWAPI::UnitTypes::Protoss_Nexus, numNexusAll + 1));
	}

	return goal;
}

const MetaPairVector StrategyManager::getTerranBuildOrderGoal() const
{
	// the goal to return
	std::vector<MetaPair> goal;

    int numWorkers      = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_SCV);
    int numCC           = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Command_Center);            
    int numMarines      = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Marine);
	int numMedics       = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Medic);
	int numWraith       = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Wraith);
    int numVultures     = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Vulture);
    int numGoliath      = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Goliath);
    int numTanks        = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode)
                        + UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode);
    int numBay          = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Engineering_Bay);

    if (Config::Strategy::StrategyName == "Terran_MarineRush")
    {
	    goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Marine, numMarines + 8));

        if (numMarines > 5)
        {
            goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Engineering_Bay, 1));
        }
    }
    else if (Config::Strategy::StrategyName == "Terran_4RaxMarines")
    {
	    goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Marine, numMarines + 8));
    }
    else if (Config::Strategy::StrategyName == "Terran_VultureRush")
    {
        goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Vulture, numVultures + 8));

        if (numVultures > 8)
        {
            goal.push_back(std::pair<MacroAct, int>(BWAPI::TechTypes::Tank_Siege_Mode, 1));
            goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode, 4));
        }
    }
    else if (Config::Strategy::StrategyName == "Terran_TankPush")
    {
        goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode, 6));
        goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Goliath, numGoliath + 6));
        goal.push_back(std::pair<MacroAct, int>(BWAPI::TechTypes::Tank_Siege_Mode, 1));
    }
    else
    {
        BWAPI::Broodwar->printf("Warning: Unknown Terran Strategy: %s", Config::Strategy::StrategyName.c_str());
    }

    if (shouldExpandNow())
    {
        goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_Command_Center, numCC + 1));
        goal.push_back(std::pair<MacroAct, int>(BWAPI::UnitTypes::Terran_SCV, numWorkers + 10));
    }

	return goal;
}


void StrategyManager::getProtossBuildOrder(BuildOrderQueue & queue)
{
	int minerals = std::max(0, _self->minerals() - BuildingManager::Instance().getReservedMinerals());
	int gas = std::max(0, _self->gas() - BuildingManager::Instance().getReservedGas());
	int nMinPatches = InformationManager::Instance().getMyNumMineralPatches();
	int nSupplyUsed = BWAPI::Broodwar->self()->supplyUsed();
	int nSupplyTotal = BWAPI::Broodwar->self()->supplyTotal();

	int nGasWorkers = WorkerManager::Instance().getNumGasWorkers();
	int nMinWorkers = WorkerManager::Instance().getNumMineralWorkers();
	int numWorkers = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Probe);
	int numZealots = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Zealot);
	int numDragoons = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Dragoon);
	int numProbes = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Probe);
	int numScout = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Corsair);
	int numReaver = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Reaver);
	int numDarkTeplar = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Dark_Templar);
	int numShuttle = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Shuttle);
	int numObserver = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Observer);

	int numPylons = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Protoss_Pylon);
	int numNexus = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Protoss_Nexus);
	int numNexusAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Protoss_Nexus);
	int numCyber = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Protoss_Cybernetics_Core);
	int numCannon = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Protoss_Photon_Cannon);
	int numGates = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Protoss_Gateway);
	int numCores = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Protoss_Cybernetics_Core);

	int workerMax = maxWorkers();

	// compensate for ingame min / gas counter, counting 16 each frame
	if (nMinWorkers > 9)
	{
		minerals = std::max(0, minerals - 16);
	}
	if (nGasWorkers > 3)
	{
		gas = std::max(0, gas - 16);
	}

	int mineralsLeft = minerals;
	int gasLeft = gas;
	int supplyLeft = nSupplyTotal - nSupplyUsed;

	if (Config::Strategy::StrategyName == "9-9Gate")
	{
		if (numCyber > 0 && numGates > 0 && mineralsLeft > 0 && supplyLeft > 0 &&
			numDragoons < 0.33 * numZealots) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Dragoon));
			mineralsLeft -= 125;
			gasLeft -= 50;
			supplyLeft -= 4;
		}
		if (numGates > 0 && mineralsLeft > 0 && supplyLeft > 0) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Zealot));
			mineralsLeft -= 100;
			supplyLeft -= 4;
		}

		if (numWorkers >= 9 && numGates < 3 * numNexus && minerals > 100 &&
			!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Protoss_Gateway) &&
			!queue.anyInQueue(BWAPI::UnitTypes::Protoss_Gateway) && 
			!queue.buildingInQueue()) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Gateway));
		}
		if (numWorkers >= 9 && numGates >= 2 && minerals > 150 && numZealots > 8 && numCyber == 0 &&
			!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Protoss_Cybernetics_Core) &&
			!queue.anyInQueue(BWAPI::UnitTypes::Protoss_Cybernetics_Core) &&
			!queue.buildingInQueue()) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Cybernetics_Core));
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Assimilator));
			WorkerManager::Instance().setCollectGas(true);
		}
	}
	else
	{
		BWAPI::Broodwar->printf("Warning: Unknown Protoss Strategy: %s", Config::Strategy::StrategyName.c_str());
	}

	if (numWorkers < workerMax) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Probe));
	}

	// if we want to expand, insert a nexus into the build order
	if (shouldExpandNow() && numNexusAll < 4 &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Protoss_Nexus) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Protoss_Nexus))
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Nexus));
	}
}


void StrategyManager::getTerranBuildOrder(BuildOrderQueue & queue)
{
	int minerals = std::max(0, _self->minerals() - BuildingManager::Instance().getReservedMinerals());
	int gas = std::max(0, _self->gas() - BuildingManager::Instance().getReservedGas());
	int nMinPatches = InformationManager::Instance().getMyNumMineralPatches();
	int nSupplyUsed = BWAPI::Broodwar->self()->supplyUsed();
	int nSupplyTotal = BWAPI::Broodwar->self()->supplyTotal();

	int nGasWorkers = WorkerManager::Instance().getNumGasWorkers();
	int nMinWorkers = WorkerManager::Instance().getNumMineralWorkers();
	int numWorkers = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_SCV);
	int numMarines = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Marine);
	int numMedics = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Medic);
	int numWraith = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Wraith);
	int numVultures = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Vulture);
	int numGoliath = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Goliath);
	int numTanks = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode)
		+ UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode);

	int numCC = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Terran_Command_Center);
	int numCCAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Command_Center);
	int numBay = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Engineering_Bay);
	int numRax = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Terran_Barracks);
	int numAcademy = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Terran_Academy);

	int workerMax = maxWorkers();

	// compensate for ingame min / gas counter, counting 16 each frame
	if (nMinWorkers > 9)
	{
		minerals = std::max(0, minerals - 16);
	}
	if (nGasWorkers > 3)
	{
		gas = std::max(0, gas - 16);
	}

	int mineralsLeft = minerals;
	int gasLeft = gas;
	int supplyLeft = nSupplyTotal - nSupplyUsed;

	if (Config::Strategy::StrategyName == "8Rax")
	{
		if (numRax > 0 && numAcademy > 0 && mineralsLeft > 0 && supplyLeft > 0 &&
			numMedics < 0.33 * numMarines) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Medic));
			mineralsLeft -= 50;
			gasLeft -= 25;
			supplyLeft -= 2;
		}
		if (numRax > 0 && mineralsLeft > 0 && supplyLeft > 0) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Marine));
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Marine));
			mineralsLeft -= 100;
			supplyLeft -= 4;
		}

		if (numWorkers >= 8 && numRax < 4 * numCC && minerals > 150 &&
			!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Terran_Barracks) &&
			!queue.anyInQueue(BWAPI::UnitTypes::Terran_Barracks) &&
			!queue.buildingInQueue()) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Barracks));
		}
		if (numWorkers >= 8 && numAcademy == 0 && numRax >= 2 && minerals > 150 && numMarines > 8 &&
			!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Terran_Academy) &&
			!queue.anyInQueue(BWAPI::UnitTypes::Terran_Academy) &&
			!queue.buildingInQueue()) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Academy));
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Refinery));
			WorkerManager::Instance().setCollectGas(true);
		}
	}
	else
	{
		BWAPI::Broodwar->printf("Warning: Unknown Terran Strategy: %s", Config::Strategy::StrategyName.c_str());
	}

	if (numWorkers < workerMax) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_SCV));
	}

	if (shouldExpandNow() && numCCAll < 4 &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Terran_Command_Center) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Terran_Command_Center))
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Terran_Command_Center));
	}

}

void StrategyManager::getZergBuildOrder(BuildOrderQueue & queue)
{
	//queue.clearAll();

	int minerals		= std::max(0, _self->minerals() - BuildingManager::Instance().getReservedMinerals());
	int gas				= std::max(0, _self->gas() - BuildingManager::Instance().getReservedGas());
	int nLarvas			= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Larva);
	int nMinPatches		= InformationManager::Instance().getMyNumMineralPatches();
	int nSupplyUsed		= BWAPI::Broodwar->self()->supplyUsed();
	int nSupplyTotal	= BWAPI::Broodwar->self()->supplyTotal();

	// Tech stuff. It has to be completed for the tech to be available.
	int nLairs			= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	int nLairsAll		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	int nHives			= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	int nHivesAll		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	int nHatches		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hatchery)
						+ nLairsAll + nHivesAll;
	int nHatchesAll		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hatchery)
						+ nLairsAll + nHivesAll;

	int nGas			= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Extractor);
	int nGasAll			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Extractor);
	int nEvo			= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Evolution_Chamber);
	int nEvoAll			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Evolution_Chamber);
	bool hasPool		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spawning_Pool) > 0;
	bool hasPoolAll		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Spawning_Pool) > 0;
	bool hasDen			= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den) > 0;
	bool hasDenAll		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den) > 0;
	bool hasLurker		= _self->hasResearched(BWAPI::TechTypes::Lurker_Aspect);
	bool hasSpire		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0 ||
						UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasSpireAll	= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0 ||
						UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasUltra		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) > 0;
	bool hasUltraAll	= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) > 0;
	bool hasGreaterSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasGreaterSpireAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasQueensNest	= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Queens_Nest) > 0;
	bool hasQueensNestAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Queens_Nest) > 0;

	// Unit stuff. This includes uncompleted units.
	int nDrones			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Drone);
	int nGasDrones		= WorkerManager::Instance().getNumGasWorkers();
	int nMinDrones		= WorkerManager::Instance().getNumMineralWorkers();
	int nLings			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Zergling);
	int nHydras			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk);
	int nHydrasComp		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk);
	int nLurkers		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lurker);
	int nMutas			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Mutalisk);
	int nMutasComp		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Mutalisk);
	int nScourge		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Scourge);
	int nUltras			= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk);
	int nGuardians		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Guardian);
	int nDevourers		= UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Devourer);
	int nGasUnits		= nHydras + nLurkers + nMutas + + nScourge + nUltras + nGuardians + nDevourers;
	int nMinUnits		= nLings;

	// hasLairTech means "can research stuff in the lair" (not "can research stuff that needs lair").
	bool hasHive		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hive) > 0;
	bool hasHiveTech	= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hive) > 0;
	bool hasLair		= UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Lair) > 0;
	bool hasLairTech	= hasLair || nHives > 0;

	BWAPI::UpgradeType melee = BWAPI::UpgradeTypes::Zerg_Melee_Attacks;
	BWAPI::UpgradeType missile = BWAPI::UpgradeTypes::Zerg_Missile_Attacks;
	BWAPI::UpgradeType armor = BWAPI::UpgradeTypes::Zerg_Carapace;
	BWAPI::UpgradeType airAttack = BWAPI::UpgradeTypes::Zerg_Flyer_Attacks;
	BWAPI::UpgradeType airArmor = BWAPI::UpgradeTypes::Zerg_Flyer_Carapace;
	BWAPI::TechType lurkerAspect = BWAPI::TechTypes::Lurker_Aspect;
	int meleeUps		= _self->getUpgradeLevel(melee);
	int missileUps		= _self->getUpgradeLevel(missile);
	int armorUps		= _self->getUpgradeLevel(armor);
	int airarmorUps		= _self->getUpgradeLevel(airArmor);

	// minimum drones to mine minerals at all
	int droneMin = 1 + (3 * nGas);
	// maximum drones must never fall to 0!
	int droneMax = maxWorkers();
	// global drone eco
	double droneEco = (double)(nDrones * 2) / (double)(nSupplyTotal + 1);
	_droneEco = droneEco;

	// compensate for ingame min / gas counter, counting 16 each frame
	//nLarvas = std::max(0, nLarvas - 1);
	if (nMinDrones > 9)
	{
		minerals = std::max(0, minerals - 16);
	}
	if (nGasDrones > 3)
	{
		gas = std::max(0, gas - 16);
	}

	// emergency
	int ourForce = InformationManager::Instance().getPower(_self);
	int enemyForce = InformationManager::Instance().getPower(_enemy);
	int ourForceGround = InformationManager::Instance().getMyPowerGroundWeapon(true);
	int enemyForceGround = InformationManager::Instance().getEnemyPowerGroundUnits();
	int enemyForceGroundNearby = InformationManager::Instance().getEnemyPowerGroundUnitsIncoming();
	bool emergencyGroundDefense = StrategyBossZerg::Instance().emergencyGroundDefense();

	// Decide if we have enough units to tech up
	bool hasEnoughUnits = nLings + nHydras + nLurkers + nMutas + nScourge + nUltras + nGuardians + nDevourers >= 12;
	bool hasEnoughUnitsMelee = nLings + nUltras >= 6;
	bool hasEnoughUnitsMissile = nHydras + nLurkers >= 6;
	bool hasEnoughUnitsGround = nLings + nHydras + nLurkers + nUltras >= 6;
	bool hasEnoughUnitsAir = nMutas + nScourge + nGuardians + nDevourers >= 6;

	// Decide mineral unit
	chooseMinUnit();
	// Decide gas unit
	chooseGasUnit();
	// Decide techTarget
	chooseTechTarget();

	// Decide unit mix
	bool makeHydras = false;
	bool makeLurkers = false;
	bool makeMutas = false;
	bool makeUltras = false;
	bool makeGuardians = false;
	bool makeDevourers = false;
	
	// Hydras
	if (_gasUnit == "Hydras") { makeHydras = true; }
	// Lurkers
	if (_gasUnit == "Lurkers") { makeLurkers = true; }
	// Mutas
	if (_gasUnit == "Mutas") { makeMutas = true; }
	// Ultras
	if (_gasUnit == "Ultras") { makeUltras = true; }
	// Guardians
	if (_gasUnit == "Guardians") { makeGuardians = true; }
	// Devourers
	if (_gasUnit == "Devourers") { makeDevourers = true; }

	/*
	// Ultras
	// Do not produce ultras if we are low on resources
	if ((minerals > 125 && gas > 125) && nLarvas > 1 && hasUltra && makeLurkers) { makeUltras = true; }
	if ((minerals > 100 && gas > 100) && nLarvas > 1 && hasUltra && makeMutas) { makeUltras = true; }
	if ((minerals > 75 && gas > 25) && nLarvas > 1 && hasUltra && makeHydras) { makeUltras = true; }
	if ((minerals > 200 && gas > 200) && nLarvas > 1 && hasUltra) { makeUltras = true; }
	if (hasUltra && !makeLurkers && !makeMutas && !makeHydras) { makeUltras = true; }

	// Guardians
	// Do not produce guardians if we are low on resources
	if ((minerals > 125 && gas > 125) && hasGreaterSpire && makeLurkers) { makeGuardians = true; }
	if ((minerals > 100 && gas > 100) && hasGreaterSpire && makeMutas) { makeGuardians = true; }
	if ((minerals > 75 && gas > 25) && hasGreaterSpire && makeHydras) { makeGuardians = true; }
	if ((minerals > 200 && gas > 200) && hasGreaterSpire && makeUltras) { makeGuardians = true; }
	*/


	// Make drones?
	if (nDrones < nMinPatches) { _minUnit = "Drone"; }
	if (BWAPI::Broodwar->enemy()->getRace() == BWAPI::Races::Zerg)
	{
		if (ourForce >= 1.00 * enemyForce) { _minUnit = "Drone"; }
		if (ourForceGround < enemyForceGround) { _minUnit = "None"; }
	}
	else {
		if (ourForce >= 0.75 * enemyForce) { _minUnit = "Drone"; }
	}
	//if (nLings < 6 && ourForce >= enemyForce && nDrones >= 9) { _minUnit = "None"; }
	if (emergencyGroundDefense) { _minUnit = "None"; }
	if (minerals > 800 && nHatches > 0) { _minUnit = "None"; }


	// Decide drones or lings?
	if (_minUnit == "Drone" && nDrones < 1.0 * droneMax && !emergencyGroundDefense)
	{
		_minUnit = "Drone";
	}
	else if ((!_hasIslandBases || nLings < 6) &&
		(_gasUnit == "None" || nLings < (nMutas + nHydras + nLurkers + nUltras + nGuardians + nDevourers)))
	{
		_minUnit = "Lings";
	}
	else
	{
		_minUnit = "None";
	}


	// Start gas if making gas units
	if (_gasUnit != "None" &&
		gas < 100 && !WorkerManager::Instance().isCollectingGas())
	{
		if (nGas > 0 && nDrones > 3 * nGas)
		{
			WorkerManager::Instance().setCollectGas(true);
		}
	}

	int larvasLeft = nLarvas;
	int mineralsLeft = minerals;
	int gasLeft = gas;
	int supplyLeft = nSupplyTotal - nSupplyUsed;

	
	// STRATEGY CODE

	//BWAPI::Broodwar->printf("Get production");
	// UNITS

	// Drones
	if (_minUnit == "Drone" && !emergencyGroundDefense) {
		//for (int i = 0; i < std::max(9, droneMax - nDrones); ++i)
		//{
			if (larvasLeft > 0 && mineralsLeft > 0 && supplyLeft > 1) {
				queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Drone));
				--larvasLeft;
				mineralsLeft -= 50;
				supplyLeft -= 2;
			}
		//}
	}

	// Lurkers vs Terran, make more lurkers
	if (BWAPI::Broodwar->enemy()->getRace() == BWAPI::Races::Terran &&
		hasDen && hasLurker && makeLurkers && nHydrasComp > 0 && nLurkers < 2 * nHydras &&
		mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 3) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Lurker));
		mineralsLeft -= 125;
		gasLeft -= 125;
		supplyLeft -= 4;
	}

	// Lurkers vs Protoss, make fewer lurkers
	else if (BWAPI::Broodwar->enemy()->getRace() == BWAPI::Races::Protoss &&
		hasDen && hasLurker && makeLurkers && nHydrasComp > 3 && 3 * nLurkers < nHydras &&
		mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 3) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Lurker));
		mineralsLeft -= 125;
		gasLeft -= 125;
		supplyLeft -= 4;
	}

	// Hydralisks
	else if (hasDen && (makeHydras || makeLurkers) && larvasLeft > 0 &&
		mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 1) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Hydralisk));
		--larvasLeft;
		mineralsLeft -= 75;
		gasLeft -= 25;
		supplyLeft -= 2;
	}

	// Guardians
	if (hasGreaterSpire && makeGuardians && nMutasComp > 0 && nGuardians < 2 * nMutas &&
		mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 3) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Guardian));
		--larvasLeft;
		mineralsLeft -= 150;
		gasLeft -= 200;
		supplyLeft -= 4;
	}

	// Devourers
	else if (hasGreaterSpire && makeDevourers && nMutasComp > 0 && nDevourers < 2 * nMutas &&
		mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 3 && nDevourers < 9) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Devourer));
		--larvasLeft;
		mineralsLeft -= 150;
		gasLeft -= 50;
		supplyLeft -= 4;
	}

	// Mutalisks
	else if (hasSpire && (makeMutas || makeDevourers || makeGuardians) &&
		larvasLeft > 0 && mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 3) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Mutalisk));
		--larvasLeft;
		mineralsLeft -= 100;
		gasLeft -= 100;
		supplyLeft -= 4;
	}

	// Ultralisks
	if (hasUltra && makeUltras && larvasLeft > 0 && mineralsLeft > 0 && gasLeft > 0 && supplyLeft > 3) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Ultralisk));
		--larvasLeft;
		mineralsLeft -= 200;
		gasLeft -= 200;
		supplyLeft -= 4;
	}

	// Enemy has air. Make scourge if possible (but in ZvZ only after we have some mutas).
	int nScourgeNeeded = std::min(18, InformationManager::Instance().nScourgeNeeded());
	int totalScourge = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Scourge) +
		2 * StrategyBossZerg::Instance().numInEgg(BWAPI::UnitTypes::Zerg_Scourge) +
		2 * queue.numInQueue(BWAPI::UnitTypes::Zerg_Scourge);

	if (hasSpire && InformationManager::Instance().enemyHasAirTech() &&
		(_enemyRace != BWAPI::Races::Zerg || nMutas >= 6))
	{
		if (nScourgeNeeded > totalScourge && larvasLeft > 0 && gasLeft > 75 && supplyLeft > 1)
		{
			// Not too many, and not too many at once. They cost a lot of gas.
			// Allow one if we have 75 gas.
			queue.queueAsLowestPriority(BWAPI::UnitTypes::Zerg_Scourge);
			--larvasLeft;
			gasLeft -= 75;
			supplyLeft -= 2;
		}
		// And keep going.
	}

	// Zerglings - only make a small amount on island maps
	if (hasPool && _minUnit == "Lings" && larvasLeft > 0 && mineralsLeft > 0 && supplyLeft > 1) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Zergling));
		--larvasLeft;
		mineralsLeft -= 50;
		supplyLeft -= 2;
		//BWAPI::Broodwar->printf("Lings!");
	}

	// Drones
	if ((_minUnit == "Drone" || _minUnit == "None" || mineralsLeft > 0) && 
		nDrones < droneMax && larvasLeft > 0 /*&& mineralsLeft > 0*/ && supplyLeft > 1) {
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Drone));
		--larvasLeft;
		mineralsLeft -= 50;
		supplyLeft -= 2;
	}

	// BUILDINGS

	// Hydralisks
	if (_techTarget == "Hydras" && hasPool && !hasDenAll && nDrones >= 12 && nGas > 0 &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Hydralisk_Den) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Hydralisk_Den) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Hydralisk_Den));
	}

	// Lurkers
	else if (_techTarget == "Lurkers" && hasPool && !hasDenAll && nLairsAll > 0 && nDrones >= 9 && nGas > 0 &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Hydralisk_Den) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Hydralisk_Den) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Hydralisk_Den));
	}

	// Mutalisks + Scourge
	if ((_techTarget == "Mutas" || nScourgeNeeded > 3) && 
		!hasSpireAll && hasLairTech && nDrones >= 9 && nGas > 0 && hasEnoughUnits &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Spire) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Spire) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Spire));
	}

	// Guardians + Devourers
	if ((_techTarget == "Guardians" || _techTarget == "Devourers") &&
		hasSpire && !hasGreaterSpireAll && hasHiveTech && nDrones >= 0.7 * droneMax && nGas >= 3 && hasEnoughUnits &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Zerg_Flyer_Carapace) &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Zerg_Flyer_Attacks) &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Greater_Spire) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Greater_Spire) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Greater_Spire));
	}

	// Ultralisks
	if (_techTarget == "Ultras" && !hasUltraAll && hasHiveTech && nDrones >= 0.7 * droneMax && nGas >= 3 && hasEnoughUnits &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern));
	}

	// Zerglings
	if (!hasPoolAll &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Spawning_Pool) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Spawning_Pool) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Spawning_Pool));
		// If we're low on drones, replace the drone.
		if (nDrones <= 9) {
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Drone));
		}
	}

	// Make lair early for mutalisks / lurkers 
	if ((_techTarget == "Mutas" || _techTarget == "Lurkers") && hasPool &&
		(nLairsAll + nHivesAll == 0) && nGas > 0 && nDrones >= 9 &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Lair) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Lair) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Lair));
	}
	// Make lair later for Ultralisks / Guardians / Devourers
	else if ((_techTarget == "Ultras" || _techTarget == "Guardians" || _techTarget == "Devourers") && hasPool &&
		(nLairsAll + nHivesAll == 0) && nDrones >= 0.5 * droneMax && nGas >= 2 &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Lair) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Lair) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Lair));
	}
	// Make lair for upgrades.
	else if ((armorUps == 1 || meleeUps == 1 || missileUps == 1 || airarmorUps == 1) &&
		hasPool && (nLairsAll + nHivesAll == 0) &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Lair) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Lair) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Lair));
	}

	// Make Pneumatized_Carapace before moving on to hive tech
	if ((_techTarget == "Ultras" || _techTarget == "Guardians" || _techTarget == "Devourers" ||
		armorUps == 2 || meleeUps == 2 || missileUps == 2 || airarmorUps == 2) &&
		hasLair && nHivesAll == 0 && nDrones >= 0.6 * droneMax && nGas >= 2 && hasEnoughUnits &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Pneumatized_Carapace) &&
		_self->getUpgradeLevel(BWAPI::UpgradeTypes::Pneumatized_Carapace) == 0 &&
		!queue.anyInQueue(BWAPI::UpgradeTypes::Pneumatized_Carapace) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Pneumatized_Carapace));
	}

	// Make a queen's nest for Ultralisks / Guardians / Devourers
	if ((_techTarget == "Ultras" || _techTarget == "Guardians" || _techTarget == "Devourers") && 
		!hasQueensNestAll && hasLair && nDrones >= 0.7 * droneMax && nGas >= 2 && hasEnoughUnits &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Queens_Nest) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Queens_Nest) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		// Get some intermediate units before moving toward hive.
		if (nHydras >= 8){
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Queens_Nest));
		}
		else if (nMutas >= 6){
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Queens_Nest));
		}
	}
	// Make a queen's nest for upgrades.
	else if ((armorUps == 2 || meleeUps == 2 || missileUps == 2 || airarmorUps == 2) &&
		hasEnoughUnits && !hasQueensNestAll && hasLair &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Queens_Nest) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Queens_Nest) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		// Get some intermediate units before moving toward hive.
		if (nHydras >= 8){
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Queens_Nest));
		}
		else if (nMutas >= 6){
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Queens_Nest));
		}
	}

	// Make a hive for Ultralisks / Guardians / Devourers
	if ((_techTarget == "Ultras" || _techTarget == "Guardians" || _techTarget == "Devourers") &&
		hasQueensNest && hasLair && nHivesAll == 0 && nDrones >= 0.7 * droneMax && nGas >= 3 && hasEnoughUnits &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Pneumatized_Carapace) &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Ventral_Sacs) &&
		(_self->getUpgradeLevel(BWAPI::UpgradeTypes::Pneumatized_Carapace) == 1 || _enemyRace == BWAPI::Races::Zerg) &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Hive) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Hive) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Hive));
	}
	// Make a hive for upgrades.
	else if ((armorUps == 2 || meleeUps == 2 || missileUps == 2 || airarmorUps == 2) &&
		hasQueensNest && hasLair && nHivesAll == 0 && hasEnoughUnits &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Pneumatized_Carapace) &&
		!_self->isUpgrading(BWAPI::UpgradeTypes::Ventral_Sacs) &&
		(_self->getUpgradeLevel(BWAPI::UpgradeTypes::Pneumatized_Carapace) == 1 || _enemyRace == BWAPI::Races::Zerg) &&
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Hive) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Hive) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Hive));
	}

	// Evo chamber
	if (hasPool && 
		!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Zerg_Evolution_Chamber) &&
		!queue.anyInQueue(BWAPI::UnitTypes::Zerg_Evolution_Chamber) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		BWAPI::Unit ourNatural = nullptr;
		const BWEM::Base * ourNaturalLocation = InformationManager::Instance().getMyNaturalLocation();
		ourNatural = InformationManager::Instance().getBaseDepot(ourNaturalLocation);
		/*
		// BUG: This will wall us self in on maps with small natural :(
		// Natural Evo vs Protoss for simcity
		if (BWAPI::Broodwar->enemy()->getRace() == BWAPI::Races::Protoss && 
			minerals > 75 && nEvoAll < 1 && UnitUtil::IsValidUnit(ourNatural))
		{
			const UnitInfo & ourBaseUnitInfo = InformationManager::Instance().getUnit(_self, ourNatural);

			if (ourBaseUnitInfo.completedFrame + 3000 < BWAPI::Broodwar->getFrameCount())
			{
				MacroLocation loc = MacroLocation::Natural;
				queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Evolution_Chamber, loc), false, DefenseLocation::Chokepoint);
			}
		}
		*/
		// Main Evo for upgrades
		if (nEvoAll < 1 && minerals > 100 && gas > 100)
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Evolution_Chamber));
		}

		// Main Evo for upgrades
		else if (nEvoAll < 2 && minerals > 150 && gas > 150 &&
			(_self->isUpgrading(armor) || _self->isUpgrading(melee) || _self->isUpgrading(missile)))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UnitTypes::Zerg_Evolution_Chamber));
		}
	}


	// UPGRADES AND TECH

	// Decide about zergling upgrades
	if (hasPool && _minUnit == "Lings" && minerals > 50 && nGas > 0 &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		if (nDrones >= 9 && nLings >= 6 &&
			_self->getUpgradeLevel(BWAPI::UpgradeTypes::Metabolic_Boost) == 0 &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Metabolic_Boost) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Metabolic_Boost))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Metabolic_Boost));
		}
		else if (hasHiveTech && nDrones >= 12 && nLings >= 8 &&
			_self->getUpgradeLevel(BWAPI::UpgradeTypes::Metabolic_Boost) == 1 &&
			_self->getUpgradeLevel(BWAPI::UpgradeTypes::Adrenal_Glands) == 0 &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Adrenal_Glands) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Adrenal_Glands))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Adrenal_Glands));
		}
	}

	// Decide about lurker aspect tech.
	if (hasDen && hasLair && _techTarget == "Lurkers" && minerals > 50 && nGas > 0 &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		if (!_self->hasResearched(BWAPI::TechTypes::Lurker_Aspect) &&
			!_self->isResearching(BWAPI::TechTypes::Lurker_Aspect) &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Muscular_Augments) &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Grooved_Spines) &&
			!queue.anyInQueue(BWAPI::TechTypes::Lurker_Aspect) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Muscular_Augments) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Grooved_Spines))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::TechTypes::Lurker_Aspect));
		}
	}

	// Decide about hydralisk upgrades.
	else if (hasDen && makeHydras && nHydras >= 3 && nDrones >= 12 && minerals > 50 && nGas > 0 &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		if (_self->getUpgradeLevel(BWAPI::UpgradeTypes::Muscular_Augments) == 0 &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Muscular_Augments) &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Grooved_Spines) &&
			!_self->isResearching(BWAPI::TechTypes::Lurker_Aspect) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Muscular_Augments) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Grooved_Spines) &&
			!queue.anyInQueue(BWAPI::TechTypes::Lurker_Aspect))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Muscular_Augments));
		}
		else if (_self->getUpgradeLevel(BWAPI::UpgradeTypes::Muscular_Augments) == 1 &&
			_self->getUpgradeLevel(BWAPI::UpgradeTypes::Grooved_Spines) == 0 &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Muscular_Augments) &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Grooved_Spines) &&
			!_self->isResearching(BWAPI::TechTypes::Lurker_Aspect) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Muscular_Augments) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Grooved_Spines) &&
			!queue.anyInQueue(BWAPI::TechTypes::Lurker_Aspect))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Grooved_Spines));
		}
	}

	// Decide about ultralisk upgrades.
	if (hasUltra && makeUltras && nUltras >= 3 && nDrones >= 0.8 * droneMax && 
		nGas >= 3 && minerals > 150 && gas > 150 && 
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		if (_self->getUpgradeLevel(BWAPI::UpgradeTypes::Anabolic_Synthesis) == 0 &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Anabolic_Synthesis) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Anabolic_Synthesis))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Anabolic_Synthesis));
		}
		else if (_self->getUpgradeLevel(BWAPI::UpgradeTypes::Anabolic_Synthesis) != 0 &&
			_self->getUpgradeLevel(BWAPI::UpgradeTypes::Chitinous_Plating) == 0 &&
			!_self->isUpgrading(BWAPI::UpgradeTypes::Chitinous_Plating) &&
			!queue.anyInQueue(BWAPI::UpgradeTypes::Chitinous_Plating))
		{
			queue.queueAsLowestPriority(MacroAct(BWAPI::UpgradeTypes::Chitinous_Plating));
		}
	}

	// ground emgergency, do not make upgrades
	if (emergencyGroundDefense) {
		return;
	}

	// Decide about flyer armor upgrades.
	if (nDrones >= 12 && nGas > 0 && minerals > 150 && gas > 150 &&
		hasPool && hasSpire && makeMutas && hasEnoughUnitsAir && 
		!_self->isUpgrading(airArmor) && !queue.anyInQueue(airArmor) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		if (airarmorUps == 0 ||
			airarmorUps == 1 && hasLairTech ||
			airarmorUps == 2 && hasHiveTech) {
			queue.queueAsLowestPriority(MacroAct(airArmor));
		}
	}

	// Decide about ground armor upgrades.
	if (nEvo >= 1 && nDrones >= 12 && nGas > 0 && minerals > 150 && gas > 150 &&
		(hasPool || hasDen || hasUltra) && hasEnoughUnitsGround &&
		!_self->isUpgrading(armor) && !queue.anyInQueue(armor) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		// But delay if we're going mutas or lurkers and don't have many yet. They want the gas.
		if (!(hasSpire && makeMutas && gas < 300 && nMutas < 6) && 
			!(hasDen && makeLurkers && gas < 300 && nLurkers < 6))
		{
			if (armorUps == 0 ||
				armorUps == 1 && hasLairTech ||
				armorUps == 2 && hasHiveTech) 
			{
				queue.queueAsLowestPriority(MacroAct(armor));
			}
		}
	}

	// Decide about ground melee upgrades.
	if ((nEvo >= 2 || nEvo >= 1 && armorUps == 3) && nDrones >= 12 && nGas > 1 && minerals > 150 && gas > 150 &&
		(hasPool || hasUltra) && hasEnoughUnitsMelee &&
		!_self->isUpgrading(melee) && !queue.anyInQueue(melee) &&
		!_self->isUpgrading(missile) && !queue.anyInQueue(missile) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		// But delay if we're going mutas or lurkers and don't have many yet. They want the gas.
		if (!(hasSpire && makeMutas && gas < 300 && nMutas < 6) &&
			!(hasDen && makeLurkers && gas < 300 && nLurkers < 6))
		{
			if (meleeUps == 0 ||
				meleeUps == 1 && hasLairTech ||
				meleeUps == 2 && hasHiveTech)
			{
				queue.queueAsLowestPriority(MacroAct(melee));
			}
		}
	}

	// Decide about ground missile upgrades.
	if ((nEvo >= 2 || nEvo >= 1 && armorUps == 3) && nDrones >= 12 && nGas > 1 && minerals > 150 && gas > 150 &&
		hasDen && hasEnoughUnitsMissile &&
		!_self->isUpgrading(missile) && !queue.anyInQueue(missile) &&
		!_self->isUpgrading(melee) && !queue.anyInQueue(melee) &&
		!queue.upgradeInQueue() && !queue.buildingInQueue())
	{
		// But delay if we're going mutas or lurkers and don't have many yet. They want the gas.
		if (!(hasSpire && makeMutas && gas < 300 && nMutas < 6) &&
			!(hasDen && makeLurkers && gas < 300 && nLurkers < 6))
		{
			if (missileUps == 0 ||
				missileUps == 1 && hasLairTech ||
				missileUps == 2 && hasHiveTech)
			{
				queue.queueAsLowestPriority(MacroAct(missile));
			}
		}
	}


}

void StrategyManager::readResults()
{
    if (!Config::Modules::UsingStrategyIO)
    {
        return;
    }

    std::string enemyName = BWAPI::Broodwar->enemy()->getName();
    std::replace(enemyName.begin(), enemyName.end(), ' ', '_');

	std::string enemyResultsFileTraining = Config::Strategy::ReadDirTraining + "result_" + enemyName + ".txt";
    std::string enemyResultsFile = Config::Strategy::ReadDir + "result_" + enemyName + ".txt";
    
    std::string strategyName;
    int wins = 0;
    int losses = 0;

	FILE *file1 = fopen(enemyResultsFileTraining.c_str(), "r");
	FILE *file2 = fopen(enemyResultsFile.c_str(), "r");
	FILE *file = file2;

	if (file1 != nullptr && file2 == nullptr) {
		file = file1;
	}

    if ( file != nullptr )
    {
        char line [ 4096 ]; /* or other suitable maximum line size */
        while ( fgets ( line, sizeof line, file ) != nullptr ) /* read a line */
        {
            std::stringstream ss(line);

            ss >> strategyName;
            ss >> wins;
            ss >> losses;

            //BWAPI::Broodwar->printf("Results Found: %s %d %d", strategyName.c_str(), wins, losses);

            if (_strategies.find(strategyName) == _strategies.end())
            {
                //BWAPI::Broodwar->printf("Warning: Results file has unknown Strategy: %s", strategyName.c_str());
            }
            else
            {
                _strategies[strategyName]._wins = wins;
                _strategies[strategyName]._losses = losses;
            }
        }

        fclose ( file );
    }
    else
    {
        //BWAPI::Broodwar->printf("No results file found: %s", enemyResultsFile.c_str());
    }
}

void StrategyManager::writeResults()
{
    if (!Config::Modules::UsingStrategyIO)
    {
        return;
    }

    std::string enemyName = BWAPI::Broodwar->enemy()->getName();
    std::replace(enemyName.begin(), enemyName.end(), ' ', '_');

    std::string enemyResultsFile = Config::Strategy::WriteDir + "result_" + enemyName + ".txt";
	BWAPI::Broodwar->sendText("Adapting result %s", enemyResultsFile.c_str());

    std::stringstream ss;

    for (auto & kv : _strategies)
    {
        const Strategy & strategy = kv.second;

		int wins = strategy._wins;
		int losses = strategy._losses;
		//double ratio = wins / (wins + losses);

		// Normalize wins and losses to maximum 10
		if (strategy._wins > 10 || strategy._losses > 10)
		{
			wins = (int)(10 * strategy._wins / std::max(strategy._wins, strategy._losses));
			losses = (int)(10 * strategy._losses / std::max(strategy._wins, strategy._losses));
		}

		// Write strategy if it has been played
		if (wins + losses > 0) 
		{
			ss << strategy._name << " " << wins << " " << losses << "\n";
		}
    }

    Logger::LogOverwriteToFile(enemyResultsFile, ss.str());
}

void StrategyManager::readHistory()
{
	if (!Config::Modules::UsingStrategyIO)
	{
		return;
	}

	std::string enemyName = BWAPI::Broodwar->enemy()->getName();
	std::replace(enemyName.begin(), enemyName.end(), ' ', '_');

	std::string & enemyHistoryFile = Config::Strategy::ReadDir + "history_" + enemyName + ".txt";
	HistoryRecord record;

	std::ifstream stream(enemyHistoryFile);
	std::string line;
	std::string myRace;
	std::string enemyRace;

	while (std::getline(stream, line))
	{
		std::replace(line.begin(), line.end(), ' ', '_');
		std::replace(line.begin(), line.end(), ';', ' ');
		std::stringstream ss(line);

		ss >> record.fileFormatVersion
			>> record.time
			>> record.mapStartLocations
			>> record.mapName
			>> record.myName
			>> myRace
			>> record.enemyName
			>> enemyRace
			>> record.enemyIsRandom
			>> record.isWinner
			>> record.myStrategyName;
			//>> record.enemyStrategyPlan
			//>> record.enemyStrategyPlanExpected
			//>> record.frameGameEnds;

		record.myRace = ReadRace(myRace);
		record.enemyRace = ReadRace(enemyRace);

		_historyRecords.push_back(record);
	}

}

void StrategyManager::writeHistory()
{
	if (!Config::Modules::UsingStrategyIO)
	{
		return;
	}

	std::string mapName = BWAPI::Broodwar->mapFileName();
	std::replace(mapName.begin(), mapName.end(), ' ', '_');
	_currentRecord.mapName = mapName;

	std::string myName = BWAPI::Broodwar->self()->getName();
	std::replace(myName.begin(), myName.end(), ' ', '_');
	_currentRecord.myName = myName;

	std::string enemyName = BWAPI::Broodwar->enemy()->getName();
	std::replace(enemyName.begin(), enemyName.end(), ' ', '_');
	_currentRecord.enemyName = enemyName;

	std::string strategyName = Config::Strategy::StrategyName;
	std::replace(strategyName.begin(), strategyName.end(), ' ', '_');
	_currentRecord.myStrategyName = strategyName;

	_currentRecord.myRace = _selfRace;
	_currentRecord.enemyRace = _enemyRace;
	_currentRecord.enemyIsRandom = _enemyIsRandom;
	_currentRecord.isWinner = _isWinner;
	_currentRecord.frameGameEnds = BWAPI::Broodwar->getFrameCount();

	std::string enemyHistoryFile = Config::Strategy::WriteDir + "history_" + enemyName + ".txt";
	std::stringstream ss;
	std::string del = ";";
	std::string eol = "\n";

	for (auto & record : _historyRecords)
	{
		ss << record.fileFormatVersion << del
			<< record.time << del
			<< record.mapStartLocations << del
			<< record.mapName << del
			<< record.myName << del
			<< WriteRace(record.myRace) << del
			<< record.enemyName << del
			<< WriteRace(record.enemyRace) << del
			<< record.enemyIsRandom << del		
			<< record.isWinner << del
			<< record.myStrategyName << eol;
			//<< record.enemyStrategyPlan << del
			//<< record.enemyStrategyPlanExpected << del
			//<< record.frameGameEnds << eol;
	}

	ss << _currentRecord.fileFormatVersion << del
		<< _currentRecord.time << del
		<< _currentRecord.mapStartLocations << del
		<< _currentRecord.mapName << del
		<< _currentRecord.myName << del
		<< WriteRace(_currentRecord.myRace) << del
		<< _currentRecord.enemyName << del
		<< WriteRace(_currentRecord.enemyRace) << del
		<< _currentRecord.enemyIsRandom << del	
		<< _currentRecord.isWinner << del
		<< _currentRecord.myStrategyName << eol;
		//<< _currentRecord.enemyStrategyPlan << del
		//<< _currentRecord.enemyStrategyPlanExpected << del
		//<< _currentRecord.frameGameEnds << eol;

	Logger::LogOverwriteToFile(enemyHistoryFile, ss.str());
}

BWAPI::Race StrategyManager::ReadRace(std::string str)
{
	if (str == "Zerg")
	{
		return BWAPI::Races::Zerg;
	}
	if (str == "Protoss")
	{
		return BWAPI::Races::Protoss;
	}
	if (str == "Terran")
	{
		return BWAPI::Races::Terran;
	}
	return BWAPI::Races::Unknown;
}

std::string StrategyManager::WriteRace(BWAPI::Race race)
{
	if (race == BWAPI::Races::Zerg)
	{
		return "Zerg";
	}
	if (race == BWAPI::Races::Protoss)
	{
		return "Protoss";
	}
	if (race == BWAPI::Races::Terran)
	{
		return "Terran";
	}
	return "Unknown";
}

// NOTE Incomplete test! We don't measure the distance of enemy units from the enemy base,
//      so we don't recognize all the rushes that we should.
bool StrategyManager::recognizeWorkerRush()
{
	BWAPI::Position myOrigin = InformationManager::Instance().getMyMainBaseLocation()->Center();

	int enemyWorkerRushCount = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(BWAPI::Broodwar->enemy()).getUnits())
	{
		const UnitInfo & ui(kv.second);

		if (ui.type.isWorker() && ui.unit->isVisible() && myOrigin.getDistance(ui.unit->getPosition()) < 1000)
		{
			++enemyWorkerRushCount;
		}
	}

	return enemyWorkerRushCount >= 3;
}

void StrategyManager::onEnd(const bool isWinner)
{
    if (!Config::Modules::UsingStrategyIO)
    {
        return;
    }

    if (isWinner)
    {
        _strategies[Config::Strategy::StrategyName]._wins++;
		_isWinner = true;
    }
    else
    {
        _strategies[Config::Strategy::StrategyName]._losses++;
		_isWinner = false;
    }

    writeResults();
	writeHistory();
}

void StrategyManager::setRandomStrategy()
{
	if (Config::Strategy::PlayGoodStrategiesFirst == true) {

		// 1.1 Collect weights and strategies with at least 1 game played and winrate > 0.75
		std::vector<std::string> strategies1;    // strategy name
		std::vector<int> weights1;               // cumulative weight of strategy
		int totalWeight1 = 0;                    // cumulative weight of last strategy (so far)

		for (auto & kv : _strategiesMatchup)
		{
			std::string name1 = kv.first;
			int weight1 = kv.second;

			if (weight1 > 0)
			{
				int strategyGamesPlayed1 = _strategies[name1]._losses + _strategies[name1]._wins;
				int strategyWins = _strategies[name1]._wins;
				double strategyWinRate = (double)strategyWins / (double)strategyGamesPlayed1;
				if (strategyGamesPlayed1 > 0 && strategyWinRate >= 0.75)
				{
					strategies1.push_back(name1);
					totalWeight1 += weight1;
					weights1.push_back(totalWeight1);
				}
			}
		}
	
	
		// 1.2 Do we have a strategy with at least 1 game played and winrate > 0.75?
		int w1 = Random::Instance().index(totalWeight1);
		//int rnd = Random::Instance().index(6);
		//BWAPI::Broodwar->printf("Using random good strategy: %d", w1);

		for (size_t i = 0; i < weights1.size(); ++i)
		{
			int strategyGamesPlayed1 = _strategies[strategies1[i]]._losses + _strategies[strategies1[i]]._wins;
			if (strategyGamesPlayed1 > 0)
			{
				int strategyWins = _strategies[strategies1[i]]._wins;
				double strategyWinRate = (double)strategyWins / (double)strategyGamesPlayed1;
				//BWAPI::Broodwar->printf("Using random good strategy: games = %d  winrate = %.3lf", strategyGamesPlayed1, strategyWinRate);
				if (strategyWinRate >= 0.90)
				{
					Config::Strategy::StrategyName = strategies1[i];
					//BWAPI::Broodwar->printf("Using good strategy: rnd = %d  w = %d", rnd, w1);
					//BWAPI::Broodwar->printf("Using good strategy: games = %d  winrate = %.3lf", strategyGamesPlayed1, strategyWinRate);
					return;
				}
				if (w1 < weights1[i] && strategyWinRate >= 0.75)
				{
					Config::Strategy::StrategyName = strategies1[i];
					//BWAPI::Broodwar->printf("Using good strategy: rnd = %d  w = %d", rnd, w1);
					//BWAPI::Broodwar->printf("Using good strategy: games = %d  winrate = %.3lf", strategyGamesPlayed1, strategyWinRate);
					return;
				}
			}
		}

	}
	

	// 2.1 Collect weights and strategies with less than minGamesToPlay games played
	std::vector<std::string> strategies2;    // strategy name
	std::vector<int> weights2;               // cumulative weight of strategy
	int totalWeight2 = 0;                    // cumulative weight of last strategy (so far)
	int minGamesToPlay = 2;					 // Minimum games to play

	if (Config::Strategy::PlayGoodStrategiesFirst != true)
	{
		minGamesToPlay = 5;
	}

	for (auto & kv : _strategiesMatchup)
	{
		std::string name2 = kv.first;
		int weight2 = kv.second;

		if (weight2 > 0)
		{
			int strategyGamesPlayed2 = _strategies[name2]._losses + _strategies[name2]._wins;
			if (strategyGamesPlayed2 < minGamesToPlay)
			{
				strategies2.push_back(name2);
				totalWeight2 += weight2;
				weights2.push_back(totalWeight2);
			}
		}
	}

	// 2.2 Choose a strategy at random by weight with less than minGamesToPlay games played
	int w2 = Random::Instance().index(totalWeight2);
	//BWAPI::Broodwar->printf("Using random new strategy: %d", w2);

	for (size_t i = 0; i < weights2.size(); ++i)
	{
		int strategyGamesPlayed2 = _strategies[strategies2[i]]._losses + _strategies[strategies2[i]]._wins;
		//BWAPI::Broodwar->printf("Using random new strategy: games = %d", strategyGamesPlayed2);
		if (w2 < weights2[i] && strategyGamesPlayed2 < minGamesToPlay)
		{
			Config::Strategy::StrategyName = strategies2[i];
			//BWAPI::Broodwar->printf("Using random strategy: rnd = %d  w = %d", rnd, w2);
			//BWAPI::Broodwar->printf("Using random strategy: games = %d", strategyGamesPlayed2);
			return;
		}
	}

	// 3. Choose default strategy
	return;
}

void StrategyManager::setLearnedStrategy()
{
    // we are currently not using this functionality for the competition so turn it off 
    //return;

    if (!Config::Modules::UsingStrategyLearning)
    {
        return;
    }

	// if we are using an enemy specific strategy
	//if (Config::Strategy::FoundEnemySpecificStrategy)
	//{
	//	return;
	//}

	int totalGamesPlayed = 0;

    // get the total number of games played so far with this race
    for (auto & kv : _strategies)
    {
        Strategy & strategy = kv.second;
		if (strategy._race == _selfRace)
        {
            totalGamesPlayed += strategy._wins + strategy._losses;
        }
    }

	// random strategy
	setRandomStrategy();

	
	std::string & strategyName = Config::Strategy::StrategyName;
	Strategy & currentStrategy = _strategies[strategyName];
	int strategyGamesPlayed = currentStrategy._wins + currentStrategy._losses;
	double strategyWinRate = strategyGamesPlayed > 0 ? currentStrategy._wins / static_cast<double>(strategyGamesPlayed) : 0;
	int rnd = Random::Instance().index(3);
	
	int minGamesToPlay = 2; // Minimum games to play
	if (Config::Strategy::PlayGoodStrategiesFirst != true)
	{
		minGamesToPlay = 5;
	}

	// if our win rate with the current strategy is high don't change
	// also don't change if insufficient games have been played
	if (strategyGamesPlayed < minGamesToPlay ||
		(strategyGamesPlayed > 0 && strategyWinRate >= 0.75))
	{
		BWAPI::Broodwar->sendText("Default strategy: games = %d  winrate = %.3lf", strategyGamesPlayed, strategyWinRate);
		return;
	}


    // calculate the UCB value and store the highest
    double C = 0.5;
    std::string bestUCBStrategy;
    double bestUCBStrategyVal = std::numeric_limits<double>::lowest();
	double bestUCBWinRate = std::numeric_limits<double>::lowest();
	int bestUCBStrategyGames = 0;
    for (auto & kv : _strategies)
    {
		// Do not consider strategies for another race
		Strategy & strategy = kv.second;
		if (strategy._race != _selfRace)
        {
            continue;
        }

		// Do not consider strategies that we have not played
		int sGamesPlayed = strategy._wins + strategy._losses;
		if (sGamesPlayed == 0)
		{
			continue;
		}

		double sWinRate = sGamesPlayed > 0 ? strategy._wins / static_cast<double>(sGamesPlayed) : 0;
        double ucbVal = C * sqrt( log( (double)totalGamesPlayed / sGamesPlayed ) );
        double val = sWinRate + ucbVal;

        if (val > bestUCBStrategyVal)
        {
            bestUCBStrategy = strategy._name;
            bestUCBStrategyVal = val;
			bestUCBStrategyGames = sGamesPlayed;
			bestUCBWinRate = sWinRate;
        }
    }

	BWAPI::Broodwar->sendText("Adapting strategy: games = %d  winrate = %.3lf  ucb1 = %.3lf", bestUCBStrategyGames, bestUCBWinRate, bestUCBStrategyVal);
    Config::Strategy::StrategyName = bestUCBStrategy;
}

void StrategyManager::handleUrgentProductionIssues(BuildOrderQueue & queue)
{
	if (_selfRace == BWAPI::Races::Zerg)
	{
		StrategyBossZerg::Instance().handleUrgentProductionIssues(queue);
	}
	else
	{
		// detect if there's a supply block once per second
		if ((BWAPI::Broodwar->getFrameCount() % 24 == 0) && detectSupplyBlock(queue) &&
			!queue.anyInQueue(BWAPI::Broodwar->self()->getRace().getSupplyProvider()))
		{
			if (Config::Debug::DrawBuildOrderSearchInfo)
			{
				BWAPI::Broodwar->printf("Supply block, building supply!");
			}

			queue.queueAsHighestPriority(MacroAct(BWAPI::Broodwar->self()->getRace().getSupplyProvider()));
		}

		// If they have mobile cloaked units, get some static detection.
		if (InformationManager::Instance().enemyHasMobileCloakTech())
		{
			if (BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Protoss)
			{
				if (BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Protoss_Forge) == 0 &&
					!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Protoss_Forge) &&
					!queue.anyInQueue(BWAPI::UnitTypes::Protoss_Forge) &&
					!queue.buildingInQueue())
				{
					queue.queueAsHighestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Forge));
				}

				if (BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Protoss_Photon_Cannon) < 2 &&
					BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Protoss_Forge) > 0 &&
					!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Protoss_Photon_Cannon) &&
					!queue.anyInQueue(BWAPI::UnitTypes::Protoss_Photon_Cannon) &&
					!queue.buildingInQueue())
				{
					queue.queueAsHighestPriority(MacroAct(BWAPI::UnitTypes::Protoss_Photon_Cannon));
				}
			}
			else if (BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Terran)
			{
				if (BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Terran_Engineering_Bay) == 0 &&
					!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Terran_Engineering_Bay) &&
					!queue.anyInQueue(BWAPI::UnitTypes::Terran_Engineering_Bay) &&
					!queue.buildingInQueue())
				{
					queue.queueAsHighestPriority(MacroAct(BWAPI::UnitTypes::Terran_Engineering_Bay));
				}

				if (BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Terran_Missile_Turret) < 3 &&
					BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Terran_Engineering_Bay) > 0 &&
					!BuildingManager::Instance().isBeingBuilt(BWAPI::UnitTypes::Terran_Missile_Turret) &&
					!queue.anyInQueue(BWAPI::UnitTypes::Terran_Missile_Turret) &&
					!queue.buildingInQueue())
				{
					queue.queueAsHighestPriority(MacroAct(BWAPI::UnitTypes::Terran_Missile_Turret));
				}

			}

			if (Config::Debug::DrawBuildOrderSearchInfo)
			{
				BWAPI::Broodwar->printf("Enemy has cloaking tech!");
			}
		}
	}
}

// Return true if we're supply blocked and should build supply.
// Note: This understands zerg supply but is not used when we are zerg.
bool StrategyManager::detectSupplyBlock(BuildOrderQueue & queue)
{

	// If we are maxed out there is no block.
	if (BWAPI::Broodwar->self()->supplyTotal() >= 400)
	{
		return false;
	}

	// If supply is being built now, there is no block.
	if (BuildingManager::Instance().isBeingBuilt(BWAPI::Broodwar->self()->getRace().getSupplyProvider()))
	{
		return false;
	}

	// Terran and protoss calculation:
	int supplyAvailable = BWAPI::Broodwar->self()->supplyTotal() - BWAPI::Broodwar->self()->supplyUsed();

	// Zerg calculation:
	// Zerg can create an overlord that doesn't count toward supply until the next check.
	// To work around it, add up the supply by hand, including hatcheries.
	if (BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Zerg) {
		supplyAvailable = -BWAPI::Broodwar->self()->supplyUsed();
		for (auto & unit : BWAPI::Broodwar->self()->getUnits())
		{
			if (unit->getType() == BWAPI::UnitTypes::Zerg_Overlord)
			{
				supplyAvailable += 16;
			}
			else if (unit->getType() == BWAPI::UnitTypes::Zerg_Egg &&
				unit->getBuildType() == BWAPI::UnitTypes::Zerg_Overlord)
			{
				return false;    // supply is building, return immediately
				// supplyAvailable += 16;
			}
			else if ((unit->getType() == BWAPI::UnitTypes::Zerg_Hatchery && unit->isCompleted()) ||
				unit->getType() == BWAPI::UnitTypes::Zerg_Lair ||
				unit->getType() == BWAPI::UnitTypes::Zerg_Hive)
			{
				supplyAvailable += 2;
			}
		}
	}

	int supplyCost = 0;
	if (!queue.isEmpty())
	{
		supplyCost = queue.getHighestPriorityItem().macroAct.supplyRequired();
	}

	// Available supply can be negative, which breaks the test below. Fix it.
	supplyAvailable = std::max(0, supplyAvailable);

	//BWAPI::Broodwar->printf("supplyAvailable %d", supplyAvailable);
	//BWAPI::Broodwar->printf("supplyCost %d", supplyCost);

	// if we don't have enough supply, we're supply blocked
	if (supplyAvailable < supplyCost + 8)
	{
		// If we're zerg, check to see if a building is planned to be built.
		// Only count it as releasing supply very early in the game.
		if (BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Zerg
			&& BuildingManager::Instance().buildingsQueued().size() > 0
			&& BWAPI::Broodwar->self()->supplyTotal() <= 18)
		{
			return false;
		}
		return true;
	}

	return false;
}


int StrategyManager::updateLingScore()
{
	double score = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
	{
		const UnitInfo & ui(kv.second);

		// Mobile combat units
		if (!ui.type.isWorker() && !ui.type.isBuilding() &&
			ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Zergling, ui.type);
			score += dps;
		}
		// Static defense
		else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
			ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Zergling, ui.type);
			score += dps;
		}
	}

	//BWAPI::Broodwar->sendText("LingScore: %.3lf", score);
	_lingScore = (int)score;

	return (int)score;
}


int StrategyManager::updateHydraScore()
{
	double score = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
	{
		const UnitInfo & ui(kv.second);

		// Mobile combat units
		if (!ui.type.isWorker() && !ui.type.isBuilding() &&
			ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Hydralisk, ui.type);
			score += dps;
		}
		// Static defense
		else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
			ui.type == BWAPI::UnitTypes::Terran_Bunker ||
			ui.type == BWAPI::UnitTypes::Protoss_Photon_Cannon)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Hydralisk, ui.type);
			score += dps;
		}
	}

	//BWAPI::Broodwar->sendText("LingScore: %.3lf", score);
	_hydraScore = (int)score;

	return (int)score;
}

int StrategyManager::updateMutaScore()
{
	double score = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
	{
		const UnitInfo & ui(kv.second);

		// Mobile combat units
		if (!ui.type.isWorker() && !ui.type.isBuilding() &&
			ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Mutalisk, ui.type);
			score += dps;
		}
		// Static defense
		else if (ui.type == BWAPI::UnitTypes::Zerg_Sunken_Colony)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Mutalisk, ui.type);
			score += dps;
		}
	}

	//BWAPI::Broodwar->sendText("LingScore: %.3lf", score);
	_mutaScore = (int)score;

	return (int)score;
}


int StrategyManager::updateLurkerScore()
{
	double score = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
	{
		const UnitInfo & ui(kv.second);

		// Mobile combat units
		if (!ui.type.isWorker() && !ui.type.isBuilding() &&
			ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Lurker, ui.type);
			score += dps;
		}
		// Static defense
		else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
			ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Lurker, ui.type);
			score += dps;
		}
	}

	//BWAPI::Broodwar->sendText("LingScore: %.3lf", score);
	_lurkerScore = (int)score;

	return (int)score;
}


int StrategyManager::updateUltraScore()
{
	double score = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
	{
		const UnitInfo & ui(kv.second);

		// Mobile combat units
		if (!ui.type.isWorker() && !ui.type.isBuilding() &&
			ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Ultralisk, ui.type);
			score += dps;
		}
		// Static defense
		else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
			ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Ultralisk, ui.type);
			score += dps;
		}
	}

	//BWAPI::Broodwar->sendText("LingScore: %.3lf", score);
	_ultraScore = (int)score;

	return (int)score;
}

int StrategyManager::updateGuardianScore()
{
	double score = 0;

	for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
	{
		const UnitInfo & ui(kv.second);

		// Mobile combat units
		if (!ui.type.isWorker() && !ui.type.isBuilding() &&
			ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Guardian, ui.type);
			score += dps;
		}
		// Static defense
		else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
			ui.type == BWAPI::UnitTypes::Terran_Bunker ||
			ui.type == BWAPI::UnitTypes::Protoss_Photon_Cannon ||
			ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony ||
			ui.type == BWAPI::UnitTypes::Zerg_Sunken_Colony)
		{
			double dps = UnitUtil::CalculateDPS(BWAPI::UnitTypes::Zerg_Guardian, ui.type);
			score += dps;
		}
	}

	//BWAPI::Broodwar->sendText("LingScore: %.3lf", score);
	//_guardianScore = (int)score;

	return (int)score;
}

// Unused for now
// Ling score, cost / supply factor = 25
int StrategyManager::getLingScore()
{
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				// Gate units
				if (ui.type == BWAPI::UnitTypes::Protoss_Dragoon ||
					ui.type == BWAPI::UnitTypes::Protoss_High_Templar)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Fusion
				if (ui.type == BWAPI::UnitTypes::Protoss_Dark_Archon)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Robotics Facility
				if (ui.type == BWAPI::UnitTypes::Protoss_Reaver)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Bio
				if (ui.type == BWAPI::UnitTypes::Terran_Marine ||
					ui.type == BWAPI::UnitTypes::Terran_Medic ||
					ui.type == BWAPI::UnitTypes::Terran_Ghost)	
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
				
				// Tanks
				if (ui.type == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode ||
					ui.type == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
		}
	}

	// vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
		}
	}

	//int costLing = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Zergling);
	//score -= costLing;
	score = std::max(0, score);

	// Island hack
	_hasIslandBases = MapTools::Instance().hasIslandBases();
	if (_hasIslandBases)
	{
		score = 0;
	}

	//techScores[int(TechUnit::Zerglings)] = 0;
	//techScores[int(TechUnit::Devourers)] = 0;
	techScores[int(TechUnit::Scourge)] = 0;
	_lingScore = score;

	return score;
}

// Hydra score, cost / supply factor = 25
int StrategyManager::getHydraScore()
{
	if (Config::Strategy::StrategyName == "5Pool Zergling Hell") return 0;
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				//Gate units
				//if (ui.type == BWAPI::UnitTypes::Protoss_Zealot)
				//{
				//	score += (ui.type.mineralPrice() + ui.type.gasPrice());
				//}
				//Do not go Hydra vs Dark Templars
				if (ui.type == BWAPI::UnitTypes::Protoss_Dark_Templar)
				{
					score -= (ui.type.mineralPrice() + ui.type.gasPrice());
				}
				//Do not go Hydra vs High Templars
				if (ui.type == BWAPI::UnitTypes::Protoss_High_Templar)
				{
					score -= (ui.type.mineralPrice() + ui.type.gasPrice());
				}
				//Do not go Hydra vs Dragoons
				if (ui.type == BWAPI::UnitTypes::Protoss_Dragoon)
				{
					score -= (ui.type.mineralPrice() + ui.type.gasPrice());
				}

				// Air
				if (ui.type == BWAPI::UnitTypes::Protoss_Carrier ||
					ui.type == BWAPI::UnitTypes::Protoss_Scout ||
					ui.type == BWAPI::UnitTypes::Protoss_Corsair)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Fusion
				if (ui.type == BWAPI::UnitTypes::Protoss_Archon || 
					ui.type == BWAPI::UnitTypes::Protoss_Dark_Archon)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					//score += InformationManager::Instance().getCostUnit(ui.type);
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Protoss_Photon_Cannon)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{				
				// Vulture + Goliath
				if (ui.type == BWAPI::UnitTypes::Terran_Vulture || 
					ui.type == BWAPI::UnitTypes::Terran_Goliath)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			
				// Air
				if (ui.type == BWAPI::UnitTypes::Terran_Battlecruiser || 
					ui.type == BWAPI::UnitTypes::Terran_Wraith)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
				ui.type == BWAPI::UnitTypes::Terran_Bunker)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}

		}
	}
	

	//int costArmy = InformationManager::Instance().getCostArmy(_self);
	//score += costArmy;

	//int costHydra = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Hydralisk);
	//score -= costHydra;
	score = std::max(0, score);


	// Activate hydras when score is above techcost
	int techcost = 0;

	int nDenAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den);
	if (nDenAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Hydralisk_Den).mineralPrice() + (BWAPI::UnitTypes::Zerg_Hydralisk_Den).gasPrice();
	}

	score -= techcost;
	score = std::max(0, score);


	// Bias
	if (/*_enemyRace == BWAPI::Races::Terran || */_enemyRace == BWAPI::Races::Protoss)
	{
		score += 300;
	}

	// Hysteresis
	if (_techTarget == "Hydras") { score += 300; };


	// Disable hydralisks vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		score = 0;
	}

	// Island hack
	_hasIslandBases = MapTools::Instance().hasIslandBases();
	if (_hasIslandBases)
	{
		score = 0;
	}

	techScores[int(TechUnit::Hydralisks)] = score;
	_hydraScore = score;

	return score;
}

// Muta score, cost / supply factor = 25
int StrategyManager::getMutaScore()
{
	if (Config::Strategy::StrategyName == "5Pool Zergling Hell") return 0;
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				// Gate units
				if (ui.type == BWAPI::UnitTypes::Protoss_Dragoon || 
					ui.type == BWAPI::UnitTypes::Protoss_High_Templar ||
					ui.type == BWAPI::UnitTypes::Protoss_Dark_Templar)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
				//Do not go Mutas vs Zealots, they are counted in can not target air
				if (ui.type == BWAPI::UnitTypes::Protoss_Zealot)
				{
					score -= ui.type.mineralPrice() + ui.type.gasPrice();
				}
				
				// Robotics Facility
				if (ui.type == BWAPI::UnitTypes::Protoss_Reaver)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice() + 150;
				}

				// Can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Mutalisks are good against anything from the robo fac.
			else if (ui.type == BWAPI::UnitTypes::Protoss_Robotics_Facility)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
			// Mutalisks are especially good against reavers.
			else if (ui.type == BWAPI::UnitTypes::Protoss_Robotics_Support_Bay)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice() + 150;
			}
		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Mutalisks are especially good against tanks
				if (ui.type == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode || 
					ui.type == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice() + 300;
				}

				// Vulture
				if (ui.type == BWAPI::UnitTypes::Terran_Vulture)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

		}
	}

	// vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{				
				// Can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Zerg_Sunken_Colony)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
		}
	}

	//int costArmy = InformationManager::Instance().getCostArmy(_self);
	//score += costArmy;

	//int costMuta = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Mutalisk);
	//score -= costMuta;
	score = std::max(0, score);


	// Activate mutas when score is above techcost
	int techcost = 0;

	int nLairsAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	if (nLairsAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Lair).mineralPrice() + (BWAPI::UnitTypes::Zerg_Lair).gasPrice();
	}

	int nSpireAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Spire) + UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire);
	if (nSpireAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Spire).mineralPrice() + (BWAPI::UnitTypes::Zerg_Spire).gasPrice();
	}

	score -= techcost;
	score = std::max(0, score);


	// Bias
	if (/*_enemyRace != BWAPI::Races::Terran && */_enemyRace != BWAPI::Races::Protoss)
	{
		score += 300;
	}

	// Hysteresis
	if (_techTarget == "Mutas") { score += 300; };


	// Island hack
	_hasIslandBases = MapTools::Instance().hasIslandBases();
	if (_hasIslandBases)
	{
		score += 300;
	}


	techScores[int(TechUnit::Mutalisks)] = score;
	_mutaScore = score;

	return score;
}

// Lurker score, cost / supply factor = 25
int StrategyManager::getLurkerScore()
{
	if (Config::Strategy::StrategyName == "5Pool Zergling Hell") return 0;
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				// Gate units
				if (ui.type == BWAPI::UnitTypes::Protoss_Zealot)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Bio
				if (ui.type == BWAPI::UnitTypes::Terran_Marine || 
					ui.type == BWAPI::UnitTypes::Terran_Medic ||
					ui.type == BWAPI::UnitTypes::Terran_Firebat ||
					ui.type == BWAPI::UnitTypes::Terran_Ghost)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	// vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Zerglings
				if (ui.type == BWAPI::UnitTypes::Zerg_Zergling)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	//int costArmy = InformationManager::Instance().getCostArmy(_self);
	//score += costArmy;

	//int costLurker = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Lurker);
	//score -= costLurker;
	score = std::max(0, score);


	// Activate lurkers when score is above techcost
	int techcost = 0;

	int nDenAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den);
	if (!nDenAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Hydralisk_Den).mineralPrice() + (BWAPI::UnitTypes::Zerg_Hydralisk_Den).gasPrice();
	}

	int nLairsAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	if (nLairsAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Lair).mineralPrice() + (BWAPI::UnitTypes::Zerg_Lair).gasPrice();
	}

	bool lurkerAsp = _self->hasResearched(BWAPI::TechTypes::Lurker_Aspect);
	if (!lurkerAsp) {
		techcost += (BWAPI::TechTypes::Lurker_Aspect).mineralPrice() + (BWAPI::TechTypes::Lurker_Aspect).gasPrice();
	}

	score -= techcost;
	score = std::max(0, score);


	// Bias

	// Hysteresis
	if (_techTarget == "Lurkers") { score += 300; };


	// Disable lurkers at hive tech vs Terran
	bool hasHive = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hive) > 0;
	if (hasHive && _enemyRace == BWAPI::Races::Terran)
	{
		score = 0;
	}

	// Activate lurkers at hive tech vs Zerg
	if (!hasHive && _enemyRace == BWAPI::Races::Zerg)
	{
		score = 0;
	}

	// Island hack
	_hasIslandBases = MapTools::Instance().hasIslandBases();
	if (_hasIslandBases)
	{
		score = 0;
	}

	techScores[int(TechUnit::Lurkers)] = score;
	_lurkerScore = score;

	return score;
}

// Ultra score, cost / supply factor = 25
int StrategyManager::getUltraScore()
{
	if (Config::Strategy::StrategyName == "5Pool Zergling Hell") return 0;
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				// Gate units
				if (ui.type == BWAPI::UnitTypes::Protoss_High_Templar ||
					ui.type == BWAPI::UnitTypes::Protoss_Dark_Templar)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Fusion
				if (ui.type == BWAPI::UnitTypes::Protoss_Archon ||
					ui.type == BWAPI::UnitTypes::Protoss_Dark_Archon)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Protoss_Photon_Cannon)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}

		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Bio
				if (ui.type == BWAPI::UnitTypes::Terran_Marine ||
					ui.type == BWAPI::UnitTypes::Terran_Medic ||
					ui.type == BWAPI::UnitTypes::Terran_Firebat ||
					ui.type == BWAPI::UnitTypes::Terran_Ghost)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
					ui.type == BWAPI::UnitTypes::Terran_Bunker)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}

		}
	}

	// vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Can not target ground
				if (!UnitUtil::TypeCanAttackGround(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony ||
				ui.type == BWAPI::UnitTypes::Zerg_Sunken_Colony)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
		}
	}

	//int costUltra = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Ultralisk);
	//score -= costUltra;
	score = std::max(0, score);


	// Activate ultras when score is above techcost
	int techcost = 0;

	int nLairsAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	if (nLairsAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Lair).mineralPrice() + (BWAPI::UnitTypes::Zerg_Lair).gasPrice();
	}

	int nHiveAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	if (nHiveAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Hive).mineralPrice() + (BWAPI::UnitTypes::Zerg_Hive).gasPrice();
	}

	int nUltraAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) > 0;
	if (nUltraAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Ultralisk_Cavern).mineralPrice() + (BWAPI::UnitTypes::Zerg_Ultralisk_Cavern).gasPrice();
	}

	score -= techcost;
	score = std::max(0, score);


	// Bias

	// Hysteresis
	if (_techTarget == "Ultras") { score += 300; };


	// Island hack
	_hasIslandBases = MapTools::Instance().hasIslandBases();
	if (_hasIslandBases)
	{
		score = 0;
	}

	techScores[int(TechUnit::Ultralisks)] = score;
	_ultraScore = score;

	return score;
}

// Guardian score, cost / supply factor = 25
int StrategyManager::getGuardianScore()
{
	if (Config::Strategy::StrategyName == "5Pool Zergling Hell") return 0;
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				// Slow defense units
				if (ui.type == BWAPI::UnitTypes::Protoss_Reaver || 
					ui.type == BWAPI::UnitTypes::Protoss_High_Templar)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Guardians are especially good against reavers.
			else if (ui.type == BWAPI::UnitTypes::Protoss_Robotics_Support_Bay)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice() + 150;
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Protoss_Photon_Cannon)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}

		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Slow defense units
				if (ui.type == BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode ||
					ui.type == BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode)
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Terran_Missile_Turret ||
				ui.type == BWAPI::UnitTypes::Terran_Bunker)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}

		}
	}

	// vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type))
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}

			// Static defense
			else if (ui.type == BWAPI::UnitTypes::Zerg_Spore_Colony ||
				ui.type == BWAPI::UnitTypes::Zerg_Sunken_Colony)
			{
				score += ui.type.mineralPrice() + ui.type.gasPrice();
			}
		}
	}

	//int costUltra = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Ultralisk);
	//score -= costUltra;
	score = std::max(0, score);


	// Activate guardians when score is above techcost
	int techcost = 0;

	int nLairsAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	if (nLairsAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Lair).mineralPrice() + (BWAPI::UnitTypes::Zerg_Lair).gasPrice();
	}

	int nHiveAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	if (nHiveAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Hive).mineralPrice() + (BWAPI::UnitTypes::Zerg_Hive).gasPrice();
	}

	int nSpireAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0;
	if (nSpireAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Spire).mineralPrice() + (BWAPI::UnitTypes::Zerg_Spire).gasPrice();
	}

	int nGreaterSpireAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	if (nGreaterSpireAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Greater_Spire).mineralPrice() + (BWAPI::UnitTypes::Zerg_Greater_Spire).gasPrice();
	}

	score -= techcost;
	score = std::max(0, score);


	// Bias
	
	// Hysteresis
	if (_techTarget == "Guardians") { score += 300; };


	techScores[int(TechUnit::Guardians)] = score;
	_guardianScore = score;

	return score;
}

// Guardian score, cost / supply factor = 25
int StrategyManager::getDevourerScore()
{
	if (Config::Strategy::StrategyName == "5Pool Zergling Hell") return 0;
	int score = 0;

	// vs Protoss
	if (_enemyRace == BWAPI::Races::Protoss)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding() &&
				ui.type != BWAPI::UnitTypes::Protoss_Interceptor)
			{
				// Flying
				if (ui.type.isFlyer())
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Flying can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type) && ui.type.isFlyer())
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	// vs Terran
	if (_enemyRace == BWAPI::Races::Terran)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Flying
				if (ui.type.isFlyer())
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Flying can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type) && ui.type.isFlyer())
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	// vs Zerg
	if (_enemyRace == BWAPI::Races::Zerg)
	{
		for (const auto & kv : InformationManager::Instance().getUnitData(_enemy).getUnits())
		{
			const UnitInfo & ui(kv.second);

			// Mobile combat units
			if (!ui.type.isWorker() && !ui.type.isBuilding())
			{
				// Flying
				if (ui.type.isFlyer())
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}

				// Flying can not target air
				if (!UnitUtil::TypeCanAttackAir(ui.type) && ui.type.isFlyer())
				{
					score += ui.type.mineralPrice() + ui.type.gasPrice();
				}
			}
		}
	}

	//int costUltra = InformationManager::Instance().getCostUnit(_self, BWAPI::UnitTypes::Zerg_Ultralisk);
	//score -= costUltra;
	score = std::max(0, score);


	// Activate guardians when score is above techcost
	int techcost = 0;

	int nLairsAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Lair);
	if (nLairsAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Lair).mineralPrice() + (BWAPI::UnitTypes::Zerg_Lair).gasPrice();
	}

	int nHiveAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	if (nHiveAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Hive).mineralPrice() + (BWAPI::UnitTypes::Zerg_Hive).gasPrice();
	}

	int nSpireAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0;
	if (nSpireAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Spire).mineralPrice() + (BWAPI::UnitTypes::Zerg_Spire).gasPrice();
	}

	int nGreaterSpireAll = UnitUtil::GetAllUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	if (nGreaterSpireAll == 0) {
		techcost += (BWAPI::UnitTypes::Zerg_Greater_Spire).mineralPrice() + (BWAPI::UnitTypes::Zerg_Greater_Spire).gasPrice();
	}

	score -= techcost;
	score = std::max(0, score);


	// Bias

	// Hysteresis
	if (_techTarget == "Devourers") { score += 300; };


	techScores[int(TechUnit::Devourers)] = score;
	_devourerScore = score;

	return score;
}

// Choose which mineral unit to create
void StrategyManager::chooseMinUnit()
{
	int lingcore = getLingScore();
	int hydrascore = getHydraScore();

	// Mark which tech units are available for the unit mix.
	// If we have the tech for it, it can be in the unit mix.
	std::array<bool, int(TechUnit::Size)> available;
	setAvailableTechUnits(available);

	_minUnit = "None";
	if (available[int(TechUnit::Hydralisks)] && _hydraScore > _lingScore) _minUnit = "Hydras";
	else if (available[int(TechUnit::Zerglings)]) _minUnit = "Lings";

	// min unit and gas unit = Hydralisks
	if (_minUnit == "Hydras" && _gasUnit == "Hydras") { _minUnit = "Lings"; }
}

// Choose which gas unit to create
void StrategyManager::chooseGasUnit()
{
	int mutascore = getMutaScore();
	int hydrascore = getHydraScore();
	int lurkerscore = getLurkerScore();
	int ultrascore = getUltraScore();
	int guardianscore = getGuardianScore();
	int devourerScore = getDevourerScore();

	/*
	bool hasDen = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den) > 0;
	bool hasLurker = _self->hasResearched(BWAPI::TechTypes::Lurker_Aspect);
	bool hasSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0 ||
		UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasGreaterSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasUltra = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) > 0;

	if (_enemyRace == BWAPI::Races::Protoss ||
		_enemyRace == BWAPI::Races::Terran)
	{
		_gasUnit = "None";
		if (hasDen && hasLurker && hasSpire) {
			if (lurkerscore > mutascore && lurkerscore > hydrascore)
			{
				_gasUnit = "Lurker";
			}
			else if (mutascore > lurkerscore && mutascore > hydrascore)
			{
				_gasUnit = "Mutalisk";
			}
			else if (hydrascore > lurkerscore && hydrascore > mutascore)
			{
				_gasUnit = "Hydralisk";
			}
		}
		else if (hasDen && hasLurker && !hasSpire) {
			_gasUnit = lurkerscore > hydrascore ? "Lurker" : "Hydralisk";
		}
		else if (hasDen && !hasLurker && hasSpire) {
			_gasUnit = hydrascore > mutascore ? "Hydralisk" : "Mutalisk";
		}
		else if (!hasDen && !hasLurker && hasSpire) {
			_gasUnit = "Mutalisk";
		}
		else if (hasDen && !hasLurker && !hasSpire) {
			_gasUnit = "Hydralisk";
		}
	}

	// Otherwise enemy is zerg or random. Always go spire.
	else
	{
		if (hasSpire) {
			_gasUnit = "Mutalisk";
		}
	}
	*/

	// Mark which tech units are available for the unit mix.
	// If we have the tech for it, it can be in the unit mix.
	std::array<bool, int(TechUnit::Size)> available;
	setAvailableTechUnits(available);

	// Find the best available unit to be the main unit of the mix.
	// Special case: Zerglings are not a gas units.
	// Other units are excluded above by calling them unavailable.
	TechUnit bestUnit = TechUnit::None;
	int techScore = -99999;
	for (int i = int(TechUnit::None); i < int(TechUnit::Size); ++i)
	{
		if (available[i] && techScores[i] > techScore && TechUnit(i) != TechUnit::Zerglings)
		{
			bestUnit = TechUnit(i);
			_gasUnit = techTargetToString(bestUnit);
			techScore = techScores[i];
		}
	}
}

// Choose the next tech to aim for
void StrategyManager::chooseTechTarget()
{
	/*
	const bool ZvZ = _enemyRace == BWAPI::Races::Zerg;
	bool hasDen = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den) > 0;
	bool hasLurker = _self->hasResearched(BWAPI::TechTypes::Lurker_Aspect);
	bool hasSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0 ||
		UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasGreaterSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasUltra = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) > 0;

	// True if we DON'T want it: We have it or choose to skip it.
	const bool den = ZvZ || hasDen;
	const bool lurker = ZvZ || hasLurker;
	const bool spire = hasSpire;
	const bool gr_spire = hasGreaterSpire;
	const bool ultra = hasUltra;

	// Default. Value at the start of the game and after all tech is available.
	_techTarget = "None";

	// From low tech to high.
	if (!den && !spire && !ultra && !lurker)
	{
		_techTarget = _lurkerScore > _mutaScore && _lurkerScore > _hydraScore ? "Lurkers" : "None";
		if (_techTarget == "None")
		{
			_techTarget = _mutaScore > _hydraScore && _mutaScore >= _lurkerScore ? "Mutalisks" : "None";
		}
		if (_techTarget == "None")
		{
			_techTarget = _hydraScore >= _mutaScore && _hydraScore >= _lurkerScore ? "Hydralisks" : "Ultralisks";
		}
	}
	//else if (!den && !spire && !ultra)
	//{
	//		_techTarget = _hydraScore > _mutaScore ? "Hydralisks" : "Mutalisks";
	//}
	else if (!den && spire && !ultra)
	{
		_techTarget = _hydraScore >= _mutaScore ? "Hydralisks" : "Ultralisks";
	}
	else if (den && !spire && !ultra)
	{
		if (!lurker)
			_techTarget = _lurkerScore > _mutaScore ? "Lurkers" : "Mutalisks";
		else
			_techTarget = _hydraScore >= _mutaScore ? "Ultralisks" : "Mutalisks";
	}
	else if (den && spire && !ultra)
	{
		if (!lurker)
			_techTarget = _lurkerScore > _hydraScore && _lurkerScore > _mutaScore ? "Lurkers" : "Ultralisks";
		else
			_techTarget = "Ultralisks";
	}
	else if (!den && !spire && ultra)
	{
		_techTarget = _hydraScore >= _mutaScore ? "Hydralisks" : "Mutalisks";
	}
	else if (!den && spire && ultra)
	{
		_techTarget = _hydraScore >= _mutaScore ? "Hydralisks" : "None";
	}
	else if (den && !spire && ultra)
	{
		if (!lurker)
			_techTarget = _lurkerScore > _mutaScore ? "Lurkers" : "Mutalisks";
		else
			_techTarget = _hydraScore >= _mutaScore ? "None" : "Mutalisks";
	}
	else if (den && spire && ultra)
	{
		if (!lurker)
			_techTarget = _lurkerScore > _hydraScore && _lurkerScore > _mutaScore ? "Lurkers" : "None";
	}
	*/

	bool hasLurker = _self->hasResearched(BWAPI::TechTypes::Lurker_Aspect);
	bool hasSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0 ||
		UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;

	bool hasLair = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Lair) > 0;
	int nHives = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hive);
	bool hasHiveTech = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hive) > 0;
	bool hasLairTech = hasLair || nHives > 0;

	// Find our current tech tier.
	int theTier = 1;           // we can assume a spawning pool
	if (hasHiveTech)
	{
		theTier = 3;
	}
	else if (hasLairTech)
	{
		theTier = 2;
	}

	// 1a: Mark which tech units are available as tech targets.
	// If we already have it, it's not a target.
	std::array<bool, int(TechUnit::Size)> targetTaken;
	setAvailableTechUnits(targetTaken);

	// 1b: Find the score of the best taken tech unit up to our current tier,
	// considering only positive scores. We never want to take a zero or negative score.
	// Do this before adding fictional taken techs.
	// Skip over the potential complication of a lost lair or hive: We may in fact have tech
	// that is beyond our current tech level because we have been set back.
	int maxTechScore = 0;
	for (int i = int(TechUnit::None); i < int(TechUnit::Size); ++i)
	{
		if (targetTaken[i] && techScores[i] > maxTechScore && techTier(TechUnit(i)) <= theTier)
		{
			maxTechScore = techScores[i];
		}
	}

	// 1c: If we don't have either lair tech yet, and they're not both useless,
	// then don't jump ahead to hive tech. Fictionally call the hive tech units "taken".
	// A tech is useless if it's worth 0 or less, or if it's worth less than the best current tech.
	// (The best current tech might have negative value, though it's rare.)
	if (!hasSpire && !hasLurker &&
		(techScores[int(TechUnit::Mutalisks)] > 0 || techScores[int(TechUnit::Lurkers)] > 0) &&
		(techScores[int(TechUnit::Mutalisks)] >= maxTechScore || techScores[int(TechUnit::Lurkers)] >= maxTechScore))
	{
		targetTaken[int(TechUnit::Ultralisks)] = true;
		targetTaken[int(TechUnit::Guardians)] = true;
		targetTaken[int(TechUnit::Devourers)] = true;
	}

	// 1d: On an island map, go all air until nydus canals are established.
	_hasIslandBases = MapTools::Instance().hasIslandBases();
	if (_hasIslandBases)
	{
		targetTaken[int(TechUnit::Hydralisks)] = true;
		targetTaken[int(TechUnit::Lurkers)] = true;
		targetTaken[int(TechUnit::Ultralisks)] = true;
	}

	// Default. Value at the start of the game and after all tech is taken.
	_techTarget = "None";

	// Choose the tech target, an untaken tech.
	// 2. If a tech at the current tier or below beats the best taken tech so far, take it.
	// That is, stay at the same tier or drop down if we can do better.
	// If we're already at hive tech, no need for this step. Keep going.
	if (theTier != 3)
	{
		int techScore = maxTechScore;    // accept only a tech which exceeds this value
		for (int i = int(TechUnit::None); i < int(TechUnit::Size); ++i)
		{
			if (!targetTaken[i] && techScores[i] > techScore && techTier(TechUnit(i)) <= theTier)
			{
				_techTarget = techTargetToString(TechUnit(i));
				techScore = techScores[i];
			}
		}
		if (_techTarget != "None")
		{
			return;
		}
	}

	// 3. Otherwise choose a target at any tier. Just pick the highest score.
	// If we should not skip from tier 1 to hive, that has already been coded into targetTaken[].
	int techScore = maxTechScore;    // accept only a tech which exceeds this value
	for (int i = int(TechUnit::None); i < int(TechUnit::Size); ++i)
	{
		if (!targetTaken[i] && techScores[i] > techScore)
		{
			_techTarget = techTargetToString(TechUnit(i));
			techScore = techScores[i];
		}
	}
}

// A tech unit is available for selection in the unit mix if we have the tech for it.
// That's what this routine figures out.
// It is available for selection as a tech target if we do NOT have the tech for it.
void StrategyManager::setAvailableTechUnits(std::array<bool, int(TechUnit::Size)> & available)
{
	const bool ZvZ = _enemyRace == BWAPI::Races::Zerg;
	int nGas = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Extractor);
	bool hasPool = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spawning_Pool) > 0;
	bool hasDen = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Hydralisk_Den) > 0;
	bool hasLurker = _self->hasResearched(BWAPI::TechTypes::Lurker_Aspect);
	bool hasSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Spire) > 0 ||
		UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasGreaterSpire = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Greater_Spire) > 0;
	bool hasUltra = UnitUtil::GetCompletedUnitCount(BWAPI::UnitTypes::Zerg_Ultralisk_Cavern) > 0;

	available[int(TechUnit::None)] = false;       // avoid doing nothing if at all possible

	// Tier 1
	available[int(TechUnit::Zerglings)] = hasPool;
	available[int(TechUnit::Hydralisks)] = hasDen && nGas >= 1;

	// Tier 2: Lair tech
	available[int(TechUnit::Lurkers)] = hasLurker && nGas >= 1;
	available[int(TechUnit::Mutalisks)] = hasSpire && nGas >= 1;
	available[int(TechUnit::Scourge)] = hasSpire && nGas >= 1;

	// Tier 3: Hive tech
	available[int(TechUnit::Ultralisks)] = hasUltra && nGas >= 2;
	available[int(TechUnit::Guardians)] = hasGreaterSpire && nGas >= 2;
	available[int(TechUnit::Devourers)] = hasGreaterSpire && nGas >= 2;
}

int StrategyManager::techTier(TechUnit techUnit) const
{
	if (techUnit == TechUnit::Zerglings || techUnit == TechUnit::Hydralisks)
	{
		return 1;
	}

	if (techUnit == TechUnit::Lurkers || techUnit == TechUnit::Mutalisks || techUnit == TechUnit::Scourge)
	{
		// Lair tech
		return 2;
	}

	if (techUnit == TechUnit::Ultralisks || techUnit == TechUnit::Guardians || techUnit == TechUnit::Devourers)
	{
		// Hive tech
		return 3;
	}

	return 0;
}

std::string StrategyManager::techTargetToString(TechUnit target)
{
	if (target == TechUnit::Zerglings) return "Zerglings";
	if (target == TechUnit::Hydralisks) return "Hydras";
	if (target == TechUnit::Lurkers) return "Lurkers";
	if (target == TechUnit::Mutalisks) return "Mutas";
	if (target == TechUnit::Scourge) return "Scourge";
	if (target == TechUnit::Ultralisks) return "Ultras";
	if (target == TechUnit::Guardians) return "Guardians";
	if (target == TechUnit::Devourers) return "Devourers";
	return "None";
}

TechUnit StrategyManager::techTargetToTextUnit(std::string target)
{
	if (target == "Zerglings") return TechUnit::Zerglings;
	if (target == "Hydras") return TechUnit::Hydralisks;
	if (target == "Lurkers") return TechUnit::Lurkers;
	if (target == "Mutas") return TechUnit::Mutalisks;
	if (target == "Scourge") return TechUnit::Scourge;
	if (target == "Ultras") return TechUnit::Ultralisks;
	if (target == "Guardians") return TechUnit::Guardians;
	if (target == "Devourers") return TechUnit::Devourers;
	return TechUnit::None;
}

// Return true if the building is in the building queue with any status.
bool StrategyManager::isBeingBuilt(BWAPI::UnitType unitType)
{
	UAB_ASSERT(unitType.isBuilding(), "not a building");
	return BuildingManager::Instance().isBeingBuilt(unitType);
}

int StrategyManager::maxWorkers()
{
	int droneMax;
	droneMax = std::max(9, std::min(WorkerManager::Instance().getMaxWorkers(), absoluteMaxDrones));

	return droneMax;
}

void StrategyManager::drawStrategyInformation(int x, int y)
{
	if (!Config::Debug::DrawStrategyInfo)
	{
		return;
	}

	BWAPI::Broodwar->drawTextScreen(x, y, "\x04Strategy:");
	BWAPI::Broodwar->drawTextScreen(x+50, y, "\x03%s%s  %s%s  %s%s  %s%s  %s%d  %s%d  %s%d  %s%d  %s%d  %s%d  %s%d",
		Config::Strategy::StrategyName.c_str(), Config::Strategy::FoundEnemySpecificStrategy ? " (enemy specific)" : "",
		"T: ", _techTarget.c_str(),
		"MU: ", _minUnit.c_str(),
		"GU: ", _gasUnit.c_str(),
		"Z: ", _lingScore,
		"H: ", _hydraScore,
		"M: ", _mutaScore,
		"L: ", _lurkerScore,
		"U: ", _ultraScore,
		"G: ", _guardianScore,
		"D: ", _devourerScore);
}
