Combat Agents¶
This section will give you a basic overview of how you command your different units to fight. A basic enemy AI is included so that you can experiment with the API.
Setting up the Combat Agent¶
To start download the combat_files.zip. This zip file contains two folders:
data
, and agents
. Copy the file from the data folder into your
projects data folder and copy the agents folder to your project. You
should now see these files in the Eclipse Package Explorer tab. If you
don’t you may need to refresh by right clicking on the package name
and selecting Refresh
or by pressing F5.
Next you will need to make an empty class. Just as in the Resource
Collection tutorial right click on the src
folder and create a new
java class that inherits from the Agent
class. This tutorial
assumes you name your agent MyCombatAgent
. Once you have created
your default agent open the CombatAgentConfig.xml
in the data
folder and modify the first <ClassName>
tag to be the classpath of
your newly created agent.
Next duplicate one of the run configurations from the previous
tutorials. On the Arguments
tab change the config file to the new
CombatAgentConfig.xml
. Finally click on the classpath tab and add
the new agents
folder to the class path by clicking the
Advanced
button and then select Add Folders
. Save the run
configuration.
At this point you can use the run configuration which will start a new map. In the top right corner are the enemy units and in the middle are your units. The f units are Footmen. These units can only attack adjacent squares. The T unit is a tower, which is an immobile unit with long attack range. The a units are archers, they have long attack range, but low health. The b units are Ballista they have medium range and high attack, but low health.
Attacking the Enemy¶
To attack an enemy unit you need the unit ID of your unit that is attacking and the unit ID of the enemy unit being attacked. The easiest way to attack is to use a CompoundAttack action. This will take care of moving the agent towards the specified enemy unit until that unit is within range.
Here is a simple example of an agent that just assigns every unit to attack a single enemy unit until that unit is killed. Obviously this is a terrible strategy and in your assignments you will attempt to do better, but this demonstrates how to make a compound attack action. You can download the MyCombatAgent.java file here.
public class MyCombatAgent extends Agent {
private int enemyPlayerNum = 1;
public MyCombatAgent(int playernum, String[] otherargs) {
super(playernum);
if(otherargs.length > 0)
{
enemyPlayerNum = new Integer(otherargs[0]);
}
System.out.println("Constructed MyCombatAgent");
}
@Override
public Map<Integer, Action> initialStep(StateView newstate,
HistoryView statehistory) {
// This stores the action that each unit will perform
// if there are no changes to the current actions then this
// map will be empty
Map<Integer, Action> actions = new HashMap<Integer, Action>();
// This is a list of all of your units
// Refer to the resource agent example for ways of
// differentiating between different unit types based on
// the list of IDs
List<Integer> myUnitIDs = newstate.getUnitIds(playernum);
// This is a list of enemy units
List<Integer> enemyUnitIDs = newstate.getUnitIds(enemyPlayerNum);
if(enemyUnitIDs.size() == 0)
{
// Nothing to do because there is no one left to attack
return actions;
}
// start by commanding every single unit to attack an enemy unit
for(Integer myUnitID : myUnitIDs)
{
// Command all of my units to attack the first enemy unit in the list
actions.put(myUnitID, Action.createCompoundAttack(myUnitID, enemyUnitIDs.get(0)));
}
return actions;
}
@Override
public Map<Integer, Action> middleStep(StateView newstate,
HistoryView statehistory) {
// This stores the action that each unit will perform
// if there are no changes to the current actions then this
// map will be empty
Map<Integer, Action> actions = new HashMap<Integer, Action>();
// This is a list of enemy units
List<Integer> enemyUnitIDs = newstate.getUnitIds(enemyPlayerNum);
if(enemyUnitIDs.size() == 0)
{
// Nothing to do because there is no one left to attack
return actions;
}
int currentStep = newstate.getTurnNumber();
// go through the action history
for(ActionResult feedback : statehistory.getCommandFeedback(playernum, currentStep-1).values())
{
// if the previous action is no longer in progress (either due to failure or completion)
// then add a new action for this unit
if(feedback.getFeedback() != ActionFeedback.INCOMPLETE)
{
// attack the first enemy unit in the list
int unitID = feedback.getAction().getUnitId();
actions.put(unitID, Action.createCompoundAttack(unitID, enemyUnitIDs.get(0)));
}
}
return actions;
}
@Override
public void terminalStep(StateView newstate, HistoryView statehistory) {
System.out.println("Finished the episode");
}
@Override
public void savePlayerData(OutputStream os) {
// TODO Auto-generated method stub
}
@Override
public void loadPlayerData(InputStream is) {
// TODO Auto-generated method stub
}
}
First thing to notice is that this agent accepts additional arguments in its constructor. All of the arguments are passed to the agent as strings. So you will need to convert them to the correct data type depending on the application.
Secondly in this agent we have a separate initial and middle step. This is because the first step commands every unit to attack a single enemy unit. Every other step checks for the completion of the previous command. If an action has completed its command it gets assigned another attack action. This repeats until either all of the enemy’s are killed or all of the agent’s units are killed. In this case all of the agent’s units will die.
The statehistory is also useful for detecting events like unit deaths and changes in health. These features may be useful when constructing your own agents. For instance you may want to check in the middle step whether any of your units have died in the last step and if so readjust your strategy.