6.370 Home: Specifications: Example Code

Example Players

The source code for the following players is provided in package team2222 in the downloadable jar file. They are mirrored here for your convenience.

  • Player.java is a simple primordial software that reboots into a different player based on the type of its handle's moniker.
  • HemlockPlayer.java tests all the passive abilities and prints output based on their results.
  • BeetlePlayer.java scans its immediate surroundings for ore, travels to any that it finds, and stores it.

The Droid Handle

You are passed an instance of colony.common.DroidHandle in the static main method of your player. You will use this handle to control your hardware and to get information from the game universe. See the DroidHandle javadoc for details of all the methods you can call.

There is only one DroidHandle associated with each piece of droid hardware. This same handle is passed into every piece of software that controls a particular piece of hardware, every time the hardware reboots. However, within each software, you might want to make a local copy so you can share it between methods.

Using Abilities

Abilities are invoked by calling the droid handle's useAbility method. This method lets your player control the droid hardware and interact with the closed universe. See the Droid Abilities section for a general description of each ability; see the PlayerConstants javadoc for specific syntax information for each ability.

An example of how to use this method is given below:

Object[] results = handle.useAbility(PlayerConstants.SENSOR, null, null);

In this case, you have invoked the Sensor ability by passing in the appropriate moniker constant from PlayerConstants; you do not need to specify a target or a parameter, since a sensor always operates from your present location, so both of these are null. The returned values (monikers of nearby objects) are stored in results.

Example Code Explained

To clarify the specifications, we will provide a blow-by-blow description of one of the sample player provided, BeetlePlayer.java.

Note: The sample code is meant to convey syntax only; it is not meant to imply that you should write your solution in any particular way. The definitive answer for what must be in your solution can be found in the Writing Your Solution checklist.


01    package team2222;

02    import colony.common.*;

03    public class BeetlePlayer {
	
04        public static DroidHandle handle;
05        public static Moniker selfMoniker;
	
06        public static void main(DroidHandle newHandle) {
07            handle = newHandle;
08            selfMoniker = handle.getMoniker();
09            try {
10                mainAction();
11            }
12            catch (Throwable e) {
13                System.err.println("BeetlePlayer exception: " + e.toString());
14                mainAction();
15            }
16        }
	
17        public static void mainAction() {

18            MapPoint destination = new MapPoint(95,399);
19            handle.useAbility(PlayerConstants.TRAVEL, null, destination);
20            MapPoint position;

21            do {
22                position =
23                    (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
24                                                selfMoniker, null)[0];
25            } while (!position.equals(destination,
26                     PlayerConstants.MAP_DEST_TOLERANCE));

27            Object[] nearbyObjects =
28                handle.useAbility(PlayerConstants.SENSOR, null, null);
29            MapPoint orePosition = null;
30            Moniker oreMoniker = null;

31            for (int k = 0; k < nearbyObjects.length;k++) {

32                oreMoniker = (Moniker)nearbyObjects[k];

33                if (oreMoniker.getSubtype().equals
34                                            (PlayerConstants.ORE_SUBTYPE)) {
35                    orePosition =
36                    (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
37                                                oreMoniker, null)[0];
38                    handle.useAbility(PlayerConstants.TRAVEL,
39                                      null, orePosition);

40                    do {
41                        position = (MapPoint)handle.useAbility
42                                             (PlayerConstants.GET_POSITION,
43                                              selfMoniker, null)[0];
44                    } while (!position.equals(orePosition,
45                             PlayerConstants.MAP_DEST_TOLERANCE));

46                    handle.useAbility(PlayerConstants.STORE,
47                                      oreMoniker, null);
48                }

49            }

50            position =
51                (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
52                                            selfMoniker, null)[0];
53            Integer oreAmount =
54                (Integer)handle.useAbility(PlayerConstants.GET_ORE_AMOUNT,
55                                           selfMoniker, null)[0];
	    
56            destination = new MapPoint(120, 380);
57            handle.useAbility(PlayerConstants.TRAVEL, null, destination);

58            do {
59                position =
60                    (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
61                                                selfMoniker, null)[0];
62            } while (!position.equals(destination,
63                     PlayerConstants.MAP_DEST_TOLERANCE));

64            oreMoniker = (Moniker)handle.useAbility(PlayerConstants.GET_STORAGE,
65                                                    selfMoniker, null)[0];

66            handle.useAbility(PlayerConstants.DISCARD, oreMoniker, null);
67            destination = new MapPoint(10, 10);
68            handle.useAbility(PlayerConstants.TRAVEL, null, destination);

69            do {
70                position =
71                    (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
72                                                selfMoniker, null)[0];
73            } while (!position.equals(destination,
74            PlayerConstants.MAP_DEST_TOLERANCE));
75        }

76    }

Preamble


01    package team2222;

02    import colony.common.*;

03    public class BeetlePlayer {
  • Line 01 declares that this module is part of your team package.
  • Line 02 imports the colony.common package which contains DroidHandle, MapPoint, Goal, PlayerConstants.
  • Line 03 declares the name of this player.

Static Variables


04        public static DroidHandle handle;
05        public static Moniker selfMoniker;
  • Lines 04-05 declare static variables, which are needed if you are going to reference them from the static main method below.

The main and mainAction Methods


06        public static void main(DroidHandle newHandle) {
07            handle = newHandle;
08            selfMoniker = handle.getMoniker();
09            try {
10                mainAction();
11            }
12            catch (Throwable e) {
13                System.err.println("BeetlePlayer exception: " + e.toString());
14                mainAction();
15            }
16        }
	
  • Line 06 is the required signature for your main method, which accepts the droid handle of the associated droid hardware.
  • Line 07 assigns the droid handle to a local copy so it can be shared between methods and classes.
  • Line 08 assigns the moniker of the associated droid hardware to a local copy, also for sharing purposes.
  • Line 10 calls another method, in this mainAction to execute the bulk of this player's code,
  • wrapped in a try-catch block.
  • Line 12 catches any exceptions or errors thrown by mainAction, which is a good idea, because any uncaught throwables will be passed up to the engine and cause it to remove this DVM. If that happens, your droid hardware is uncontrolled and useless to your army.
  • Line 13 prints the exception to stderr.
  • Line 14 tries calling mainAction again, because if at first you don't succeed, you should just fail, fail again.

The mainAction Method


17        public static void mainAction() {
  • Line 17 is the declaration of the mainAction method, which must be static in order to be called from the static main method.
  • In this method, the Beetle basically travels to a point, scans for ore, moves to the location of the ore, and stores it.

Traveling to the First Location


18            MapPoint destination = new MapPoint(95,399);
19            handle.useAbility(PlayerConstants.TRAVEL, null, destination);
20            MapPoint position;

21            do {
22                position =
23                    (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
24                                                selfMoniker, null)[0];
25            } while (!position.equals(destination,
26                     PlayerConstants.MAP_DEST_TOLERANCE));
  • Line 18 defines a new MapPoint for your destination.
  • Line 19 invokes the Travel ability to this destination.
  • Lines 21-26 loop until the droid hardware's position is close enough to the destination to stop. Note that MAP_DEST_TOLERANCE is the guaranteed maximum distance that any droid will stop from its destination if it travel unimpeded, and is also guaranteed to be less than STORE_DISTANCE.
  • Lines 22-23 invoke the GetPosition ability to determine this droid hardware's position on the map. Note that we immediately cast and reference the returned Object[] array because we know that this droid hardware is always within range of its sensors and that this ability always returns an array of one MapPoint under this condition.
  • Also note that the returned array cannot be cast to another array type, like MapPoint[]; rather, you must reference and cast each array element individually, as done above.
  • While not absolutely necessary, we could have called the Yield ability in this loop instead of busywaiting, to reduce the load on the game engine.
  • In the general case, it's always good to check the length of the returned array and catch ClassCastExceptions.

Using Sensors


27            Object[] nearbyObjects =
28                handle.useAbility(PlayerConstants.SENSOR, null, null);
29            MapPoint orePosition = null;
30            Moniker oreMoniker = null;

31            for (int k = 0; k < nearbyObjects.length;k++) {

32                oreMoniker = (Moniker)nearbyObjects[k];
  • Line 27 invokes the Sensor ability, which returns the monikers of all free (uncarried) physical objects within range.
  • Line 31 begins a loop which iterates through the returned monikers.
  • Line 32 performs a safe cast, because the Sensor ability returns an Object[] which contains only Monikers.

Travelling to Ore and Storing It


33                if (oreMoniker.getSubtype().equals
34                                            (PlayerConstants.ORE_SUBTYPE)) {
35                    orePosition =
36                    (MapPoint)handle.useAbility(PlayerConstants.GET_POSITION,
37                                                oreMoniker, null)[0];
38                    handle.useAbility(PlayerConstants.TRAVEL,
39                                      null, orePosition);

40                    do {
41                        position = (MapPoint)handle.useAbility
42                                             (PlayerConstants.GET_POSITION,
43                                              selfMoniker, null)[0];
44                    } while (!position.equals(orePosition,
45                             PlayerConstants.MAP_DEST_TOLERANCE));

46                    handle.useAbility(PlayerConstants.STORE,
47                                      oreMoniker, null);
48                }
  • Line 33 compares the subtype of the current moniker to see if the associated physical object is a piece of ore. The specific name of a moniker isn't incredibly useful, because they are generated independently by a name generator; sides are useful for determining whom to attack; types are too broad to be of much use; subtypes really contain most of the identification information you need from a moniker.
  • Line 35 retrieves the position of the ore.
  • Line 38 invokes the Travel ability with the ore's position as its destination.
  • Lines 40-45 is another busywait loop that stalls until the Beetle reaches the ore.
  • Line 46-47 invokes the Store ability on the ore.
  • The rest of the program just travels to some random locations.

Final Thoughts

  • Although this example player lets execution drop out the bottom of the main, your player shouldn't; most embedded programs in real-world applications run in an infinite loop simulating a finite state machine or a task scheduler. If you let execution return to the engine, you'll never get it back, and your DVM will be removed from the engine.
  • Keep a local copy of your droid handle, since you only given this once.
  • Catch your own exceptions so you can handle them yourself; otherwise, the engine will catch them and remove your DVM from its collection.
  • There is a lot of overhead associated with rebooting your program (loading common classes, for example), so try to avoid it when possible.