Tutorial

Purpose of this tutorial is to familiarize you with the timeline functionality of the Pogamut, what is it and how to use it. In order to do that, you will first create a bot that will try to capture the flag in the CTF game of UT2004. After finishing the bot, the functionality of timeline will be demonstrated using the bot. If you are only interested in timeline functionality, create the CTF bot from the Pogamut samples ( FileNew ProjectPogamutUT2004 and UE2RuntimeSamplesFSM CTF Bot ) and continue to the second part of the tutorial.

At the end of the tutorial, you will have CTF bot that is providing additional data to the Pogamut Netbeans plugin that makes it easier to know what, where, and when was the CTF bot doing.

Rules of Capture The Flag game

Before we even start thinking about creation of a bot, we have to know about the CTF game rules, that are little more complicated than free-for-all DeathMatch.

CTF is a two-team game, where each team tries to score points by taking enemy flag and carrying it to its team base. Rules of the Capture The Flag game:

  • There are at least two teams, each team have flag and home base.

  • At the start of the game, each flag is located at the respective There are two teams, each team have flag and home base.

  • Every bot belongs to one team

  • At the start of the game, flags are located at their respective flag base.

  • Team member can pick the enemy flag by running over it.

  • If team member carrying enemy flag is killed, the flag is dropped.

  • If team member runs over its team dropped flag, the flag returned to its home base.

  • Team scores, when team member carries enemy flag to its home base, while its team flag is at home base.

  • Game ends when it is over specified time limit (posted in GameInfo message) or if at least one team achieves score higher than GoalTeamScore (also from GameInfo message).

Team selection and team balancing

Every bot can specify the team it belongs to in a team attribute of the INIT message that is send during handshake of the bot with the Unreal. The INIT message supplies data necessary for creation of the bot's avatar in the game (such as initial location, name, skin or team) and GameBots creates the avatar upon recieving. Bot can get total number of teams in the game from attribute MaxTeams of GameInfo message.

    @AgentScoped
    public class Hunter extends UT2004BotModuleController<UT2004Bot> {
        // ...
        @Override
        public Initialize getInitializeCommand() {
            int maxTeams = this.game.getMaxTeams();
            return new Initialize().setName("NavigationBot").setTeam(1);
        }
        // ...
    }
                
                

Valid team number is from 0 to GameInfo.MaxTeams - You should also note, that team's size is not unlimited, the maximal size of each team is GameInfo. MaxTeamSize. If the team is not specified in the INIT message, GameBots will use team number 255 in DeathMatch and team number 0 in CTF games.

In some situations, you may want to select team of a bot based on number of players per team or some other metric thus balancing number of players in teams. This is useful in many situations, such as running same bot program multiple times and still creating two teams with same number of players (one team may have one player more than the other) or if you want your bot to join the team that has lower score and many other situations.

For further details, consult GameBots documentation.

Information about flags and bases

In CTF game, information about flags is send as synchronous message from the GameBots, so the bot always have up-to-date info about flags. You can retrieve current info from the worldview, e.g. like this:

    public FlagInfo getFlag(int team) {
        Collection<FlagInfo> flags = bot.getWorldView().getAll(FlagInfo.class).values();
        for (FlagInfo flagInfo : flags) {
            if (flagInfo.getTeam() == team) {
                return flagInfo;
            }
        }
        throw new IllegalStateException("FlagInfo about with seeked team not found.");
    }
                    
                    

You'll get location and possibly holder if you can see the flag (and possibly its carrier), other information is always available. FlagInfo. getState() is probably most useful.

Unfortunately, you can't directly query the worldview for info about the team base, but we know that base is located at navpoint with a specific id. Following code is showing how to find the home for the specified team:

    // unreal assigns names to teams by default, team 0 is red, 1 is blue, 2 is green and 3 is gold
    private static String[] teamColors = new String[] {"Red", "Blue", "Green", "Gold"};

    public NavPoint getTeamBase(int team) {
        String flagBaseStr = "x" + teamColors[team] + "FlagBase0";

        for (NavPoint navpoint : bot.getWorldView().getAll(NavPoint.class).values()) {
            if (navpoint.getId().getStringId().contains(flagBaseStr)) {
                return navpoint;
            }
        }
        throw new IllegalStateException("Unable to find base for " + teamColors[team] + " team.");
    }
                    
                    

CTF bot schema

We are going to create our simple CTF bot using FSM(Finite State Machine), so before we start programming anything, it is necessary to specify how the bot should make decisions. You should always start with planning architecture of a bot's logic, it usually saves a lot of trouble and time. We are going to use work in progress library for FSMs that is currently being developed.

The bot has three different state types and multiple transitions. Bot starts at the hunt state, so it should wander around map, collect add-ons and kill enemies. When he sees enemy's flag he switches to pickup enemy flag and picks it up, and after he picks it up, he switches into go home. Unfortunately, team flag may be missing, because enemy bot got to it, so bot can switch into hunt mode and hoping to find enemy and kill him (NOTE: that won't return flag home, but if level is small enough, there is a good chance bot will accidently move over the dropped flag while in hunt mode). After flag has been delivered home, go back to the hunt mode. The rest of transitions is for events, when something doesn't go in the expected way, e.g. transition from hunt to go home is for case, when bot accidently moves over enemy flag, that he couldn't see before.

As you are aware, FSM consists of list of states, start state and transitions between states. Start state is the state "Hunt," bot will switch into that state when he enters the environment or when he is killed.

States used in the FSMBot library are not exactly states in classical FSM definition, but more like activities the bot is supposed to do. List of states:

  • Hunt - in this state bot should behave like hunter, kill members of enemy team and collect weapons, and other add-ons.

  • Pick up enemy flag - go to the place where is the enemy's flag and pick it up. This state expects that bot knows where the enemy's flag is (e.g. bot sees it).

  • Go home - return to home base. This state should be used when bot holds enemy's flag.

There are many shortcoming, e.g. state Hunt should preferably shoot at enemy holding our flag or state "Pick up enemy flag" and "Go home" should shoot at visible enemy while racing for their destination.

Transitions are a little tricky, basically there are two types of transitions that differs by eligibility (under what circumstances are they considered for potential transition from state A to B).

  • Sequential transition - is eligible only after state has finished(e.g. if bot is supposed to move somewhere, the sequential transition won't be eligible until bot is at the destination). In order for it to be eligible, source state method isFinished must return true.

  • Interrupt transition - is always eligible and if triggered, it interrupts the activity the state was doing, e.g. if bot is supposed to move somewhere, interrupt transition is eligible while bot is moving and after state has finished.

FSM State flow

In order to properly utilize FSMBot library, you need to have an idea how do states look like, how to program them and how are they processed. Every state has to implement interface IFSMState. Careful, there is an interface with the same name in afsm library, that is used during communication with GameBots.

There are basically two paths state can go:

  • State is not interrupted by interrupt transition during its execution:

    1. init
    2. loop method isReady , until it returns true
    3. if isFinished returns true
      1. cleanup
      2. loop method isCleaned, until it returns true
      3. The state has been finished, for now and FSM will start to execute another
    4. run
    5. Go to point 2
  • State is interrupted by interrupt transition:

    1. init
    2. loop method isReady , until it returns true
    3. if FSM detects eligible interrupt transition that can be triggered
      1. interrupt
      2. loop method isInterrupted, until it returns true
      3. The state has been interrupted, FSM will start to execute another
    4. run
    5. Go to point 3

I attemp to make sure that destination state of transition sees the environment same way the transition did, so the condition that triggered the transition itself is still valid even in a destination state (e.g. if transition is supposed to trigger when visible player appears, the destination state will see player at the same position and still visible). This behavior is not guaranteed if state has to loop over any of following methods:

  • isReady
  • isInterrupted
  • isCleaned

Creating the project

In this section we will create the project and set up dependencies necessary for us to build FSM bot.

  1. Start up Netbeans
  2. Once Netbeans are loaded, create a new project:
    1. Choose FileNew Project (Ctrl+Shift+N). Under category Pogamut UT2004 select Java Bot Project and click Next.
    2. In the Name and Location panel, type CTFBot in the Project Name. Choose Project Location to appropriate directory on your computer, such as c:/development. Click Finish.
    The IDE will create and open new project CTFBot with one package ctfbot and default empty bot.
  3. Add FSMBot library as library to your project: