GithubHelp home page GithubHelp logo

krogenth / additionalpylons Goto Github PK

View Code? Open in Web Editor NEW
1.0 1.0 0.0 220 KB

A StarCraft Broodwar AI written in C++20

License: MIT License

PowerShell 0.21% C++ 99.63% C 0.16%
starcraft broodwar bwapi cpp cpp20

additionalpylons's People

Contributors

0x416c657852 avatar pricea810 avatar soulsteeler123 avatar tevinter avatar

Stargazers

 avatar

Watchers

 avatar

additionalpylons's Issues

Implement Map Distance calculation

The BWAPI UnitInterface's getDistance function completely ignores walkability and returns the distance in pixels, both can be problematic for determining what Unit(s) we should target in combat. We'll need to compare rough walking/Flying distances between Units to determine which Unit should be focused. With #5 we can calculate the distance between by counting the number of tiles required to reach the destination.

Implement Worker order handling

With the Strategist created in #12, we now are able to issue orders to units. All unit wrappers can call upon the Strategist's getUnitOrder(BWAPI::UnitType type) method to retrieve the next possible build to perform. A Worker should only be asking for a build order when they are not busy. Any build that fails should not be discarded, but retried(possibly double checking that a building does not already exist in that location).

To retrieve a building location, you can use BWEB to retrieve a location. BWEB's functions for onUnitDiscover, onUnitMorph, etc. should also be added to Module to properly keep track of what is where in the map as well.

Create basic Build Order lists

We'll want to keep a rough idea of opening plays we can/should make depending on knowledge we have at the time. For now, we will only consider the map size.

As such, we'll want 3 different types of opening Build Orders based on map size:

  • small
  • medium
  • large

All Build Orders should use an std::initializer_list to define the list of std::pair's of BWAPI::Unit and int(supply used) to define when a build order should occur.

Broodwar counts the majority of units as 2 supply instead of 1, except zerglings, they are counted as 1 supply.
A good resource for various Zerg build orders can be found here: https://liquipedia.net/starcraft/Category:Zerg_Build_Orders

Implement Worker wrapper class

Using the BWAPI::Unit wrapper interface from #7, we'll want to create a custom BWAPI::Unit wrapper specifically for workers. For now, workers can go to the nearest minerals to the starting location.

Workers will have several potential jobs to perform:

  • mine minerals
  • extract gas
  • morph into new buildings
  • scout

We may eventually implement some form of attacking as well, primarily for the event where an enemy has rushed us.

Implement Strategist Scouting Location Logic

During the early and possibly late game, we may need to handle searching for the enemy to direct our army towards. Once a starting location has been scouted, it should not be checked again until all possible starting locations have been scouted.

In order to appropriately handle early and late game, BWEM/BWEB may be required in some of the logic handling.

notes:

  • this task is extremely ambiguous since I do not know exactly how it should properly be implemented
  • I would recommend using the OverlordWrapper's onFrame to test this functionality

Implement BWAPI Unit wrapper interface

We'll want units to handle orders in potentially special ways, therefore we will need wrapper classes for our Player Units to better implement such requirements.

For now, this class should:

  • store the final target destination
  • store the BWAPI::TilePosition queue they are need move to reach the final target destination
  • the Unit's id

functionality:

  • pure virtual function to determine if the Unit is busy
  • pure virtual function to manage the Unit's orders
  • capability to draw the Unit's next destination/final target destination

Implement Army wrapper classes

Army units, like NonArmy units, come in a variety of types. The Army types for now will include:

  • Zergling
  • Hydralisk
  • Lurker
  • Mutalisk

For now, since the Strategist does not handle combat simulation nor scouting, the Army Units can simply grab the main or natural's choke location, and move there to defend.

Add Resource tracking to Map Singleton

We'll want to keep track of what resources are on the game map and where they are located. For Map initialization, we will want to retrieve and store all Minerals and Geysers in the match. To do this, we can use BWAPI::Game::getStaticMinerals and BWAPI::Game::getStaticGeysers to retrieve the full listing of resources.

For Mineral patches that we cannot see, we may not get an event trigger of that Mineral patch being mined out. Though it should be a very low probability that we take a base another player has mined out or used in general.

Investigate the idea of marking Areas in the Map

With the way StarCraft maps are typically laid out, it would be beneficial to mark sections of the Map as an "Area", and specify what is located within that area, or near it(Chokepoints, other Areas, etc.). This requires a lot of graph theory/mathematics, specifically Voronoi Diagrams and the use of Manhattan Distance calculation. Additionally, having some form of rough bounding box, or boxes for the Area may help. Some consideration of reading what BWEM does may prove beneficial.

If possible, we will want to specify what is within an Area on the Map, including:

  • Bases
  • Minerals
  • Geysers
  • Chokepoints

Implement Map Chokepoint area detection

We'll need to have some form of chokepoint detection in order to in more optimal positions to defend/place defenses. BWAPI does give us a list of regions through BWAPI::Broodwar->getAllRegions(), where region->getDefensePriority() == 2 is what Broodwar auto-determined to be a chokepoint area. Unfortunately, these chokepoints contain many false-positives(including mineral groups, walls, etc.), so attempting to use the BWAPI::RegionInterface to grab all chokepoints will still require a large amount of calcuating.

Chokepoints should be generated upon Map initialization.

Implement basic Map Tile class

We'll want to keep a record of all game map tiles in local memory for efficiency and better possible calculations. Each tile will be equivalent to a BWAPI::TilePosition point.

Each tile should at least keep track of:

  • walkability
  • buildability
  • last frame seen
  • if the tile has creep on it
  • altitude

The tiles walkability, buildability, and last frame seen values should have getters and setters.

Add BWEM and BWEB libraries to the project

Due to the limited time of this project, and the requirement to compete within the ELO ladders, we'll need to limit the scope of what is worked on. To reach that goal, we can use the community libraries for handling map analysis and building placement.

Specifically, we can include:

These should be included as submodules within the repository, and added to the projects, including adding basic handling of any onStart, onFrame, onUnitCreate, and onUnitDestroy functionality they include.

Add Player Race tracking

To make better strategic calls, we will want to keep track of what each player's race is, therefore, we should attempt to keep track of each player's race inside the Player class object.

The Player class should hold:

  • a BWAPI::Race to hold what race the player is

The Player class should implement:

  • functionality to print the Player's race
  • updating the BWAPI::Race if the stored BWAPI::Race is BWAPI::Races::Unknown once a unit from that player is discovered
  • storing the initial Player's BWAPI::Race upon initialization
  • add functionality to retrieve the stored BWAPI::Race

For any bots that choose to play a Random race, we will not know what race they are playing until a unit of theirs is seen.

Implement Strategist play decisions

In order to better determine what our Army and scouting units should do, we will need the Strategist to specify what type of play we should be doing. These plays will include:

  • scouting
  • attacking
  • defending

While the Strategist itself will not directly command units, these decisions will help facilitate the unit's individual decision making.

At the start of a game, the opening decision should be to scout, in an attempt to discover where the enemy's main base is. But as the game progresses, we will want to potentially adjust what the general decision is.

Implement basic Debugging information drawing

As we continue to implement more and more complex logic for our unit wrappers, we will want to have ways to more easily discern what is occurring and when. All debugging information should be limited to the Debug configuration. As such, we will want at least the following information:

  • how much time(in ms) the Module's onFrame method takes
  • how much time(in ms) the collection of each units is taking to execute their onFrame method(not their displayInfo method)
  • the rough estimation of time per unit in the above collection
  • the current unit's orders in their respective displayInfo method

for all timing calculations, you can generally follow this: https://stackoverflow.com/a/37984848

Create ZvZ ZvP ZvT Build Order lists

Some opening plays are difficult to predict/counter. While Zergling rushes may work 50/50, it may not always be the best possible play to open with every match. As such, we'll want to consider opening plays that at least tend to work well against specific races, while taking into account the size of the map. Since the map size may potentially play a part in determining how we should proceed. For example, a small map would favor a Zergling rush, but a larger map would favor a Hydralisk/Mutalisk play.

These lists should behave the same way as the Build Orders lists in #4, specifying the BWAPI::UnitType and the amount of supply used to trigger the build.

Add onUnitMorph support to Player class

For Zerg, morphing is the basis of progress. When the Module onUnitMorph is triggered, we will only know what the new version of the unit is. The onUnitMorph event is triggered twice: when the unit initially morphs, and when the unit finally ends morphing. For the Player's onUnitMorph, the original unit should be removed from all maps, and the new unit inserted into whatever maps it belongs to(the onUnitCreate method can be leveraged for this).

Implement Strategist starting build order switching

In every game, we'll only know that the enemy is an Unknown race at the start. As such, we will need a way to switch what we are attempting to do based on newly acquired information through scouting. If an enemy unit is seen, we will need to call to the Strategist to retrieve the Player's currently existing units and buildings, and update the starting build order queue to only hold BWAPI::UnitType for what we do not have within the newly selected initializer_list.

In order to facilitate this functionality, you will also need to add to the Player class:

  • retrieving all buildings of the player
  • retrieving all units of the player

Implement Army Unit Attack Strategy handling

The last Strategist play decision, attack, is as it sounds: order our units to go on the offensive, attacking enemy units as they can. There are some major caveats to this however:

  • what prioritizes a target over another?
  • is attacking the right call for unit x?

This decision is completely situation dependent, based on the unit in question, what units the enemy has, what enemy units are near by, what player units are near by, etc. All of this factors in to what a unit should do in some way.

  • If a unit is isolated, and has no allies around to support it, it should attempt to run away. Failing to find a path to take to run, or is taken towards the enemy units to run, should cause the unit to fight for its life(negotiable for this task if proven too difficult to handle).
  • For air-borne units, like Mutalisks, if there is no enemy anti-air units, can swiftly decide to route around and try to pick off enemy supply workers.
  • Enemy Army units should be first to be attacked. After that, supply workers, then buildings(preferably supply providing buildings, then others).

This task is to implement any such logic for ALL Army unit wrappers.

Implement upgrade handling

Currently, our BuildingWrapper can only handle morphing itself into a new building. Some buildings are capable of researching upgrades for our units(upgraded movement, attack rate, attack range). Upgrade are completely independent of BWAPI::UnitTypes, classified as BWAPI::UpgradeTypes.

As such, we will need multiple things:

  • new queues in the strategist to store each building's possible upgrades
  • functionality in the strategist to retrieve an upgrade by BWAPI::UnitType
  • BuildingWrapper logic to retrieve an upgrade and begin upgrading
  • any UnitWrapper isBusy() changes required to pause a building from attempting to create or upgrade anything else
  • tracking what level of upgrade the player is currently at

Implement Map Pathfinding algorithm(s)

We'll need to handle calculating a relatively optimal path to some point on the Map from #2. Recommended researching various pathfinding algorithms, but A-Star may be best to implement at minimum. There should be a way to specify if walkability is a requirement for the path calculation or not).

Some resources that may help with regards to A-Star:
https://theory.stanford.edu/~amitp/GameProgramming/
https://github.com/daancode/a-star
https://github.com/Rikora/A-star

The returned value should be a queue of BWAPI::TilePosition's.

Implement building order handling

For Zerg buildings, orders are fairly straight forward: morph or make upgrades, so a building's handling of an order will only contain the BWAPI::UnitType to make without a BWAPI::TilePosition or any positional checking. Just like the order handling in #31, orders can be retrieved from the Strategist's getUnitOrder(BWAPI::UnitType type) method.

Implement Post-Starting Build Orders

Currently, we only specify a series of things to build at the very beginning of a match, and nothing else. Now we will need to handle non-starting build orders, to continue building and eventually (hopefully) win the match.

This task can be broken into 2 separate PRs:

  • an initial handling, where only zerglings are created
  • a later, more proper handling of calculated build orders, gauging what strategy the enemy is attempting and trying to counter it

In order for this to work, you will need to fulfill several things:

  • verify all per-requisites are fulfilled to create whatever is determined(if not, create that first)
  • for gas requirements, this will be much more tricky: we will need to validate an extractor exists and validate workers are associated with extracting gas(this can be done by checking the ratio of gas:mineral workers, if 0.0 or FLOAT_MAX, we likely have none)
  • the same above applies for minerals as well: validate we have mineral patches, and that we have workers associated with them(this will only be FLOAT_MAX for the ratio of gas:mineral workers)
  • there may be requirements for the requirements

Implement expansion capturing logic

As a game progresses, we will need to expand our claim of territory in the map. While the Strategist does provide a BWAPI::UnitTypes::Zerg_Hatchery for workers to morph into, currently workers will only pick the nearest open BWEB block the hatchery fits in. We will need to add logic to both the worker and strategist to handle if we should take an expansion, and which expansion we should take. Typically, the first expansion will always be the "natural", which typically holds more minerals, a geyser, and is typically the closest base by ground distance. All expansions after the natural expansion can be arbitrarily determined by the strategist based on some form of criteria(is it possible to secure, is the enemy there or near by, can we give enough supplies(workers, defense, etc.) to an expansion, really whatever you can think of).

With this, the worker building a hatchery should request if this is meant to be an expansion, and if so, query what expansion is best.

Implement Larva wrapper class

Similar to Workers, for Zerg, there are larva, which will morph into other units. We will need to keep track of these larva and issue build orders to them as needed. Larva should only be busy when they are morphing, and can only be morphed once. Larva will do nothing except eventually morphing into a different unit.

Add Base tracking on Map Singleton

With the Resources added to the Map in #16, we will need to generate a listing of all possible Bases in the Map. A Base should essentially be a BWAPI::TilePosition that is optimal for a Hatchery to be placed in proximity to any Minerals and Geysers. We can gather the starting locations through BWAPI::Game::getStartLocations, however this does not include and Naturals or other expansions. We will need to calculate these Base locations based on the Mineral and Geyser positions.

The Map should now be able to:

  • return a list of all Bases

Implement Concave Chokepoint defense Rally Point handling

When defending, we will want to completely maximize our chances of success against the enemy. To better facilitate this, we will want to generate a concave formation of melee units on a chokepoint, to maximize the ratio of units engaged in the fight in our favor.

In order to generate a concave formation at any chokepoint, we will need:

  • to retrieve the BWEM chokepoint data: the ends(sides) of the chokepoint, the center of the chokepoint, and the radius of the chokepoint
  • generate a perpendicular line to the chokepoint ends, you can use BWEB::Map::perpendicularLine
  • determine which side of the chokepoint is closer to our area(s) of the map based on the perpendicular line
  • generate the arch of walkable areas at the chokepoint where we can place units

This is just a general idea of how this should work. Further research should be done to refine how this should appropriately work.

Implement Worker Geyser building and Gas collection

We have so far implemented handling mineral collection to a slight degree, however we will also need to handle the secondary resources: gas. Gas is a special resource, requiring an BWAPI::UnitTypes::Extractor to be built on top of a Vespene Gas Geyser. The majority of starting bases will have at least one such geyser.

In order to properly implement this, there will need to be multiple considerations:

  • there should be a rough ratio of mineral:gas workers we have
  • for all resources, there should only be 3 workers associated with that resource

For this to be properly implemented, there may be need to use BWEM, and possibly a wrapper class for the resources BWEM gives in order to associate units to that resource(possibly keeping a count of workers in the resource class, while a worker holds a pointer to the resource it is mining).

Implement basic Building Placement

We'll need a way to decide the best possible place to build buildings. For now, this should be a very basic handling, based on the starting location of the Player.

Since Zerg requires creep to build buildings, we will need to verify that creep exists on the tile and that the tile is buildable before returning the tile.

Implement basic Strategist Singleton

We'll need to analyze the state of the game, and attempt to estimate the best possible course to win. For now, we will only choose an initial Build Order based on Map size from #4 to initialize a starting building queue. The Strategist should check what the first order is on each frame to see if the requirements(and resources) are available to make the order. Like the Map in #2, this will be a Singleton. For now, we will use queues to store Build Orders for Units to ask for.

The Strategist should contain:

  • the amount of minerals spent(including Worker/Building issued orders)
  • the total amount of supply(including Overlords in Build Order queue)
  • at least a Zerg_Larva, Zerg_Drone, and Zerg_Hatchery Build Order queues

Remember, there may be nothing in the Build Order queue for the Unit asking, it's recommended to use std::optional for this.

Implement Army wrapper interface

Army units come in a wide variety, from melee to ranged to air and even burrowed. Each "type" of unit can better use certain tactics over others(hit and run vs grouping). As such, it would be beneficial to handle different units in different ways, using different classes for their specific use case(s). This class will act as the template for these units, building off the Unit wrapper from #7.

This interface should include:

  • a BWAPI::Unit of their current target

Implement Strategist Army unit Rally Points

With the Strategist play decisions from #43, we can now successfully handle giving orders to our units. For this task we will only consider the defending decision. When defending, we should place our units in the most optimal of locations possible when defending, this is typically:

  • on our side of the chokepoint at our base(preferable with Sunken Colonies to help defend)
  • on high ground near a chokepoint

For Zerg, our unit range is rather low, so if a chokepoint expands past a Hydralisk's range from the high ground, defending the chokepoint itself should take precedence. Chokepoints can be retrieved from BWEM.

Add capability to retrieve all Units associated with a Player class instance

In order for the Strategist's combat engines to better handle calculations, we will need ways to retrieve listings of units associated with a Player.

There will need to be functionality for:

  • retrieving all units associated with a Player
  • retrieving all specific BWAPI::UnitType units associated with a Player
  • retrieving all units within a given area associated with a Player(for now, this can be considered a square area)

Implement basic Map Singleton

We'll want to keep a record of the entire map state. This map will be a singleton, only one instance of a Map should ever exist at any time. This task only deals with regards to map initialization and tiling, no units/resources. The Map should be capable of resetting between matches, and should be called in the bot Module's onStart() or onEnd().

This includes:

  • records of each tile on the map, see #1
  • storing the map size in terms of #1 Tile size, store in a BWAPI::TilePosition

functionality:

  • capability to draw all Tiles to the game map
  • capability to return a const references to the underlying tiles
  • capability to return a const reference to the map size

For more information on the Singleton design pattern: https://stackoverflow.com/questions/1008019/c-singleton-design-pattern

Implement Overlord wrapper class

Overlords are the supply providers for Zerg, and as such are vital to ensure we can continue producing additional units. Overlords are capable of moving, and can be used for early game scouting along side a drone. For now, Overlords should try to stay in the safest position possible, in the back of our Base.

Overlords can have only one job, depending on if the Strategist wants them to:

  • scouting

Implement basic Player class

We'll want to keep track of what each player owns. We will be considering every match to be a 1v1, regardless of how many players are actually participating. As such, there should only be two global instances: a player(us) and enemy(not us). Each player should be capable of resetting between matches, and should be called in the bot Module's onStart() or onEnd(). Any neutral units do not fall under either category of player.

Each player should keep track of:

  • what units they own(for now, just use the BWAPI::Unit) in a map
  • what buildings they own(again, just use the BWAPI::Unit) in a map

The Player class should implement:

  • functionality to print to screen information about the Player(# of units, # of buildings, possibly broken down by each type)
  • onFrame
  • onNukeDetect
  • onUnitDiscover
  • onUnitEvade
  • onUnitShow
  • onUnitHide
  • onUnitCreate
  • onUnitDestroy
  • onUnitMorph
  • onUnitRenegade
  • onUnitComplete

Implement Strategist Combat analysis engines

We'll need a way to determine if it is possible to win an engage against the enemy. Handling issues like altitude difference, distance, etc. for every unit will be too computationally extreme. For now at least, we can based everything off of all Army units each player has, and consider the distance from the average of both groups. The general idea of StarCraft's unit balancing is generally a rock-paper-scissors match. With this, we can roughly calculate based on what counters what for each race, to determine which side SHOULD win in a fight.

We will want 3 main Combat engines, one against each race to help split up the code base into more manageable pieces.

The Strategist should call whatever Combat engine is required(if the enemy's race has been determined) to determine what the play should be.

Investigate the idea of marking Areas in the Map

For more complicated Building Placement, we may want to consider the idea of marking parts of the Map as an Area. An Area is not required to contain a Base location/resources.

A possibility is to mark an Area based on the buildability/walkability and altitude of the tile(s) it will/can contain.

Implement Building wrapper class

Like Workers and Army units, Buildings can have their own individual logic to better perform their tasks. Same as the WorkerWrapper #11 and ArmyWrapper #14, this will also inherit from the UnitWrapper #7. For Zerg, no buildings are capable of moving, but are capable of producing upgrades and potentially morphing.

As such, we will need to implement:

  • isBusy
  • onFrame
  • displayInfo

For now, this wrapper will do nothing, but the isBusy method should be implemented to the best we can manage at the moment. The displayInfo should only display what state the building is currently in(this can be determined via the isBusy method.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.