JNIBWAPI Tutorial

This guide should teach you the basics of StarCraft bot programming in JAVA with JNIBWAPI. We will be using a JNIBWAPI Starter Pack, that has been specifically prepared for the SSCAI tournament (it's compatible with BWAPI 3.7.4).

Installation

Download the JNIBWAPI Starter Pack, extract it, and follow the instructions in the README.txt file. Once everything works, we will be modifying the example bot implemented in JavaBot.java file. You should study its source code along with this text.

Units in a StarCraft match

Almost everything we see in the game is an object of a type Unit. Our bot can access those objects and call their methods. Note, that buildings, mineral patches, and geysers are units too in BWAPI.
  • bwapi.getMyUnits() retrieves the ArrayList of all my own units and buildings.
  • bwapi.getEnemyUnits() retrieves the ArrayList of all currently visible enemy units and buildings.
  • bwapi.getNeutralUnits() retrieves the ArrayList of all currently visible mineral patches, vespene geysers, critters, or any other neutral units/buildings.
We can easily iterate over one of these ArrayLists like this:

for (Unit unit : bwapi.getMyUnits()) {
	// do something with this unit
}

Note: Take a look at the act() function in your JavaBot.java file. You can see, that we iterate over the ArrayList of our own units twice. We do this to issue some orders to our units.

Unit Types

Every unit has a certain type. For example, a Terran Marine has unit type UnitTypes.Terran_Marine. Imagine, that we want to send all our workers (Terran SCVs) to mine minerals. We will iterate over all our units, and compare their type to UnitTypes.Terran_SCV like this:

for (Unit unit : bwapi.getMyUnits()) {
	if (unit.getTypeID() == UnitTypes.Terran_SCV.ordinal()) {
		// some code, that will send them to gather minerals
	}
}

Note: There is a wide variety of orders we can issue to our units, and we will get to them soon. But first imagine, that you want to send your unit to move to a certain place. You need to know how to specify this place within the map.

Map

The position within a map in SC can be specified in three ways (with different precision):
  • Pixel Position (most precise)
    We use the pixel position for example when issuing move order to our units. The bwapi.move(unit.getID(), 2896, 4289) call sends our unit to exact pixel position (2896,4289). Every unit also has two functions: unit.getX() and unit.getY(), which return current pixel coordinates of this unit.
  • Build Tile (used for building placement)
    One build tile is a square of 32x32 pixels. Build tiles are used for specifying where to build our buildings. If we want to order one of our workers to build a Supply Depot at a build tile position (79,122), we call the bwapi.build(unit.getID(), 79, 122, UnitTypes_Terran_Supply_Depot) function.
    To get the build tile coordinates of a certain unit, we can either call the unit.getTileX() and unit.getTileY() functions, or simply divide their pixel coordinates by 32.
  • Region (advanced)
    Region is the partition of a SC map that represents for example one base area (the map is divided into several polygons - Regions). It has its own class called Region. To retrieve the ArrayList of all the regions, we can call bwapi.getMap().getRegions(). However, for most basic stuff you don't need to use them.
Note: Basically, we use pixel positions for attacking and unit movement, and build tiles for building placement. You can ignore regions for now.
Note 2: Take a look at the second for loop in the act() function. Notice, how we compute the euclidean distance between pixel positions of two units, in order to find the mineral patch that is closest to our worker.

Orders

There is a collection of orders, that you can issue to your units. For example, if you want to order your unit to attack a position (3421,1290), you will call bwapi.attack(unit.getID(),3421,1290). The most used orders are:
  • rightclick( unitID, pixelPositionX, pixelPositionY ): Does the same thing, as if we had this unit selected, and right-clicked on some position in the game.
  • rightclick( unitID, targetUnitID ): Does the same thing, as if we had this unit selected, and right-clicked on target unit.
  • move( unitID, pixelPositionX, pixelPositionY )
  • build( unitID, tilePositionX, tilePositionY, unitTypeToBuild ): If our unit is a worker, this sends it to construct a building on a given build tile.
  • gather( unitID, targetUnitID ): Sends our unit to gather resources from target mineral patch or Refinery/Assimilator/Extractor.
  • attack( unitID, pixelPositionX, pixelPositionY ): Makes our unit to move to a specified location, while attacking all the enemies in its path.
  • attack( unitID, targetUnitID ): Makes our unit chase and attack target unit (ignoring everything else).
  • train( buildingID, unitTypeID ): Trains a unit of a specified type in our building.
  • upgrade( buildingID, upgradeID ): Starts the upgrade of a specified type in our building (e.g. damage or armor). Use UpgradeTypes.something to specify upgrades.
  • research( buildingID, technologyID ): Starts researching a specified technology/ability in our building (e.g. Stimpacks or Parasite). Use TechTypes.something to specify technology.

Resources

Before building new buildings or training new units, you should check, if you have enough resources. To do that, use:
  • bwapi.getSelf().getMinerals()
  • bwapi.getSelf().getGas()
  • (bwapi.getSelf().getSupplyTotal() - bwapi.getSelf().getSupplyUsed())/2: To get the actual free supply, we need to substract the used supply from our total supply. Finally, we divide the number by 2, because these two functions return double the value, that is displayed in the game (the value is mutiplied by 2 in order to avoid using foat type, since Zerglings take up 0.5 supply).
For example:

// check if we have at least 50 minerals and 1 free supply
if ( (bwapi.getSelf().getMinerals() >= 50) && 
     ( ((bwapi.getSelf().getSupplyTotal() - bwapi.getSelf().getSupplyUsed())/2) >= 1) ) {
	for (Unit unit : bwapi.getMyUnits()) { 
		if (unit.getTypeID() == UnitTypes.Terran_Barracks.ordinal()) { 
			// and train a new marine in this Barracks (if we aren't already training some other unit here)
			if (unit.getTrainingQueueSize() == 0) bwapi.train(unit.getID(), UnitTypes.Terran_Marine.ordinal());
		} 
	}
}

Note: In the last part of our act() function, we build a new Supply Depot whenever our free supply is less than 3, and we have 100 minerals. Try changing these values and see what happens.

Speed

For debugging purposes, it is possible to change the game speed to a certain integer value. Game speed value 30 approximately corresponds to a typical speed of human-played matches. Speed value of 0 is the fastest possible setting. Values higher than 30 slow the game down (SSCAI tournament is played at speed 20). There are two ways to change the speed:
  • Call the bwapi.setGameSpeed(10) function from your bot's source.
  • Type the /speed 10 command into the in-game chat.

Debugging

There are several functions, that let you display some debug information during the game. Most used are:
  • bwapi.printText(message), which displays a string message for a couple of seconds in the left part of the screen.
  • bwapi.drawText(x, y, msg, screenCoords) displays a string msg in the specified portion of the screen or on specific position on the map (according to the value of boolean parameter screenCoords). This message is only displayed in the current frame, so we need to call it repeatedly on every frame.
  • bwapi.drawCircle(...) and bwapi.drawLine(...) functions work similarly, but instead of text, they can draw simple geometric shapes.
  • System.out.println(arg0) can also be used to print anything into the console.
Note: Take a look at the drawDebugInfo() function, that we prepared for you. This is called on every frame and displays the text in the top-left part of the screen, as well as some circles over your workers. You should now be able to modify it. Try adding the current number of your workers somewhere on the screen (hint: you'll need to iterate over bwapi.getMyUnits() and count them).

Important functions in your agent's code

In the source code of your example agent (JavaBot.java), you will find two functions, that are the most important. You will implement the majority of bot's behaviour inside them:
  • public void gameStarted(): This is called at the beginning of every match (even before the first frame is displayed). Here, you can do all the preparations (for example initialize some data structures, that you will use. You can for example initialize an array of seen enemy building locations, so that you know where to attack later).
  • public void act(): This function is called approximately once a second. Almost everything that your agent does, can be implemented here. However, if you want to execute some code more often, take a look at the gameUpdate() function - it's called on every frame.
We have also prepared three bonus functions to save you some time:
  • getNearestUnit(int unitTypeID, int x, int y): This will find my unit of a given type, that is closest to a pixel position (x,y). Returns the ID of that unit, or -1 if I don't have any units of that type.
  • getBuildTile(int builderID, int buildingTypeID, int x, int y): This function finds an appropriate build tile near pixel position (x,y) where we can place a building. We need to provide the ID of our worker and a type of the building we want to build. It returns a Point object, or Point(-1,-1) if suitable position is not found.
  • weAreBuilding(int buildingTypeID): Returns true if we are currently constructing the building of a given type.
Additionally, there is a couple of event-related functions, that you might want to implement and use. For example, the unitDestroy(int unitID) is called whenever we see a unit being destroyed. These functions are:
  • gameEnded()
  • matchEnded(boolean winner)
  • nukeDetect(int x, int y)
  • nukeDetect()
  • playerLeft(int id)
  • unitCreate(int unitID)
  • unitDestroy(int unitID)
  • unitEvade(int unitID)
  • unitMorph(int unitID)
  • unitShow(int unitID)
  • keyPressed(int keyCode)
Note: Try printing out some message, whenever new unit is created, to test how this works. Hint: You can get the string representation of unit's type by calling bwapi.getUnitType(bwapi.getUnit(unitID).getTypeID()).getName().

Finding the enemy base

On every map, there are several places where it makes sense to build a base (because there are resources). The list of all these places can be easily accessed by the bwapi.getMap().getBaseLocations() function. Some of these base locations are also a start locations (there are 2-8 start locations, depending on a map). This means, that enemy player's base can be there at the beginning of the game. We can easily iterate over all the base locations, and check which of them are possible start locations with the following code:

for (BaseLocation b : bwapi.getMap().getBaseLocations()) {
	// If this is a possible start location,
	if (b.isStartLocation()) {
		// do something. For example send some unit to a position 
		// b.getX(),b.getY() to see, if the enemy is there.  
	}
}

Note: There comes a time in the game, when we want to attack our opponent. When we don't know where his base is, we should send a scout (basically any cheap unit) to every start location. With the code above, you should be able to do that, and find the enemy. When you discover some enemy buildings, you should remember their location, so that you don't need to look for them in future.
Note 2: If later in the game comes a time when you've destroyed every known building, you need to look for other remaining buildings. Some of them may not be close to start locations. In that case, you should scout all the base locations on the map.

Submitting your bot

To submit your bot to SSCAI Tournament, you need to go to the Log In & Submit Bots subpage. You should upload a zip archive containing these 3 things:
  1. README file (TXT or PDF) with the description of your bot.
  2. Compiled bot. Either a .JAR file if it's coded in JNIBWAPI, or .dll file if you used C++.
  3. Source code of your bot.
We will not publish any of this.