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
ClassCastException s.
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 Moniker s.
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.
|