botZilla : A Programming Challenge.
by Steve Baker
INTRODUCTION |
SCREENSHOTS |
ABOUT |
RULES |
FAQ |
INTERNALS |
DOWNLOADS |
INSTALLING |
KNOWN BUGS |
PROJECT PAGE |
MAILING LIST
About this Document
As I write this document, I am still tuning the game parameters.
So rather than telling you specific numbers for ranges, speeds,
damage rates and such and risking making a mistake in transcribing
them into this document, I'll simply refer you to the 'zilla_internals.h'
header file which has the values for all of these things clearly
defined.
Whilst I do not intend to change any of these numbers
on a frequent basis, it would be wise to code your
program to use those symbols where possible so that an unforseen
need to change the rules won't totally destroy your careful work!
The Rules:
Each contestant submits a C or C++ program to control
a virtual BotZilla. These programs will be linked into a
simulation that lets these monsters battle it out in realtime
in a virtual city while we feeble humans can only stand and watch.
It's a contest of programming skill and algorithmic subtlety.
The Software You Must Write:
The software you write must consist of a single source file that
compiles into either a '.DLL' or a '.so' library. These libraries
are dynamically linked into the 'City Simulator' that runs the game.
Your library should export just one symbol - a function
called 'runZilla'. For C++ programmers: This function must
be declared 'extern "C"' so that it can be called with C
calling conventions.
The runZilla() function takes a pointer to a simple structure
as it's input and another pointer indicates where it should
write its results:
In C:
void runZilla ( struct ZillaInputs *input,
struct ZillaOutputs *output ) ;
In C++:
extern "C" void runZilla ( ZillaInputs *input,
ZillaOutputs *output ) ;
The contents of the two interface structures are defined in
"zilla_interface.h" which should be the only '#include' at the
top of the file.
Representation of Angles, Headings, Times and Distances
All distances in the game are in meters, all times are in seconds
and all angles are in degrees with positive angles being anticlockwise
and all numbers being in the range -180 to 180. Directions are
represented as an angle with Zero being north, 90 is therefore West,
-90 East and either +180 or -180 could be South.
ZillaInputs
The ZillaInputs structure contains all of the information about the
current state of your robot - including inputs from it's external
sensors and it's internal state. The entire structure is recomputed
at the start of each turn - so you can write to it if you want -
but whatever changes you make will be erased on the next iteration.
ZillaOutputs
The ZillaOutputs structure contains
all of the controls that your software uses to operate the virtual
robot. The City simulator checks that you don't demand more speed
or turn rate than the robot is capable of - and that you don't exceed
it's energy reserves. Speed and turn rate are clamped to the robot's
maximum and if you try to consume energy you don't have - then your
command simply won't happen.
The public Zilla structures look like this:
struct EnemyZilla
{
int visible ; /* Remainder of structure is invalid if this is FALSE */
float heading ; /* Degrees, Zero is NORTH, positive anticlockwise */
float energyRemaining ; /* Expressed as a percentage of maximum en
ergy */
float healthRemaining ; /* Expressed as a percentage of full healt
h */
enum ZillaAction action ; /* What is he doing? */
} ;
struct ZillaInputs
{
float energyRemaining ; /* Expressed as a percentage of maximum energy */
float healthRemaining ; /* Expressed as a percentage of full health */
int collectable ; /* Which collectable are you holding right now?
One of:
MAP_COLLECT_ENERGY
MAP_COLLECT_HEALTH
MAP_COLLECT_TIMESLOW
MAP_COLLECT_SPEED
or -1 if you have no collectable on board */
int usingSpeedup ; /* 1 if you are using a Speed collectible, 0 otherwise */
int usingTimeSlow ; /* 1 if you are using a Time-Slow collectible, 0 otherwise */
float elapsedTime ; /* Seconds elapsed since start of round */
float currentHeading ; /* Degrees: Zero is NORTH, positive anticlockwise */
/* Angle is always between -180 and 180. */
int numRobotsRemaining ; /* The number of other bots still alive */
unsigned char map [ MAP_SIZE ] [ MAP_SIZE ] ; /* See documentation */
struct EnemyZilla enemy [ NUM_ENEMY ] ; /* See documentation */
} ;
enum ZillaAction
{
MOVE ,
CHOMP,
SHOOT,
RAGE,
USE_COLLECTABLE
} ;
struct ZillaOutputs
{
float turnRate ; /* In degrees per second, positive is anticlockwise */
float moveSpeed ; /* In meters per second - always positive */
enum ZillaAction action ; /* Only one action per iteration! */
char name [ MAX_NAME_LENGTH ] ;
char speech [ MAX_LINES_OF_SPEECH ] [ MAX_SPEECH_LENGTH ] ;
} ;
The zilla_interface.h header also contains a large number of commented
#define's for various constants.
The 'map' and 'enemy' arrays in the ZillaInputs structure are described
below.
HEALTH AND ENERGY:
Each bot has both 'health' and 'energy' - both start out at 100% and
may be depleted or restored by various means.
- If your health is depleted by being hit by the other robots or by
touching the force field around the city. You are able to slowly
push your way through buildings without taking damage. If your
health hits zero, you die and are out of the game.
- Your energy is consumed by shooting - and drained
completely if you touch the force field or do a 'rage' attack.
Energy is replenished slowly
over time whenever you if you do MOVE actions (even if you set the
speed to zero in order to stand still). If your energy gets
too low, you won't be able to shoot any more. You need a 100% energy
burst to do a 'rage' attack. The robot is not damaged by running out
of energy - it just limits your options until the energy gradually
recovers.
There are on-screen indicators for health and energy so you can see how
your bot is doing relative to the competition.
By looking at the 'enemy' array (see below) allows you to see how much health and energy each visible enemy has.
THE MAP:
The ZillaInputs structure contains a two-dimensional array of unsigned
char's that represents a top-down view of the current state of your world.
The map is a MAP_SIZE x MAP_SIZE grid with your botZilla in the center.
Each square in
the grid represents an area GRID_SIZE x GRID_SIZE meters. The first array
index is the North/South direction (positive North), the second is East/West
(positive East). Hence, your BotZilla can 'sense' things out to about
MAP_SIZE*GRID_SIZE/2 in every direction. Hence the data in input->map[Y][X]
represents a point (Y-MAP_SIZE/2)*GRID_SIZE to the North of you, and
(X-MAP_SIZE/2) to the East.
The map contains data to show where buildings, force-fields, rubble and
enemy BotZilla's are. The value in each grid cell is either a number
in the range 0..(NUM_ENEMY-1) or a #define'd token above that range.
- Zillas are approximately circular and are ZILLA_SIZE in diameter so
you should expect to see many map grids filled with numbers in the
range 0 to (NUM_ENEMY-1) when another machine is within visibility
range. However, the resolution of the
map is low so that the bots may not look smoothly circular.
- MAP_SELF: Since the map is always centered around your bot, it'll
show a number of 'MAP_SELF' tokens clustered around the middle of the
map.
- MAP_BUILDING: The City is organised in perfectly regular 'blocks'
with North/South
and East/West roads between them. The blocks are all BLOCK_SIZE across
and the roads are ROAD_SIZE wide. Some blocks may contain empty space,
others will contain buildings - or (increasingly as the game progresses)
smoking rubble!
- MAP_RUBBLE: When a building is destroyed, it turns instantly to
rubble. On-screen,
this looks like a gradual process - but the bot's see an instantaneous
change in the entries of the map changing from MAP_BUILDING to MAP_RUBBLE.
If there is a robot standing amongst the rubble, the map will show the
number of that robot (or MAP_SELF if it's you).
- MAP_FORCE_FIELD: The humans in the area have managed to constrain
the area of combat using
force fields. If a BotZilla touches a force field, it's energy will be
instantly drained to zero and it'll take damage for every second it
remains in contact with it.
- MAP_COLLECT_ENERGY/MAP_COLLECT_HEALTH/MAP_COLLECT_TIMESLOW/MAP_COLLECT_SPEED: One of the available 'collectables' is at this location. Collectables are valuable and powerful objects - your robot only has to come in contact with it to pick it up. Collectables do not impeded motion or stop weapons effects.
- MAP_EMPTY: Areas of the map (roads) containing nothing else.
Zillas may move freely through empty areas (roads, etc) and across rubble. They
are stopped when they touch a building, a force field or another Zilla.
When a zilla just drives mindlessly into another bot or into a building, it'll gradually do damage to it - but not as fast as a series of 'CHOMP' attacks.
THE FORCEFIELD
The city is surrounded by a forcefield that does great damage for Zillas
that come in contact with it.
After some unspecified time (typically a couple of minutes), the force
field will start to shrink. This forces the game to end within a finite
amount of time no matter how stupidly (or effectively) the bots are being
driven. The forcefield will start to shrink when the 'elapsedTime' field
reaches FORCEFIELD_SHRINK_START_TIME and will have shrunk to zero size
(killing all 'bots in the process) by FORCEFIELD_SHRINK_FINISH_TIME.
If you touch the forcefield while it's shinking, it'll be very hard
to avoid being completely embedded in it which is certain death for
a bot. It's essential to give the forcefield a wide berth once it's
shrinking!
THE ENEMY ARRAY:
This array contains one element for every possible enemy (there could be as many as MAX_ENEMY). If this enemy is visible then the number you'll find in the
'map' can be used as an index into this array to find more information about
the robot you can see.
Each structure contain a boolean called 'visible' which tells you whether you can see this enemy on the map, also the current 'heading' that the robot is pointing at (0==NORTH, positive anticlockwise, all numbers in the range -180 to 180). There are also 'energyRemaining' and 'healthRemaining' fields so you can see how the other robot is doing.
ATTACKING:
Doing an attack takes a short amount of time during which
the Zilla can do nothing else and it's software does not get executed. The
time these take - and the energy they consume is defined in the
zilla_interface.h file.
If a Zilla does a 'CHOMP' action while standing next to a building - or
attempts to walk through the building, it'll damage it. Shooting or
doing a rage attack will also damage buildings that happen to get in the
way. The amount of this damage is listed in the header file. Whilst a
chomp attack consumes no energy, your bot doesn't recover energy during
the time the chomp takes - so it's not without cost.
Shooting drains the Zilla's energy reserves. Each shoot action reduces
your reserves by an amount defined in zilla_internals.h - and does damage
to any Zilla standing directly in front
of you, providing it is within range and with no buildings or other Zilla's
are in the way.
A 'Rage Attack' is a blast of energy that radiates out in all directions, and
consumes 100% of your energy reserves. The blast damages any Zilla within
range - regardless of whether they are hiding behind something - and it
instantly flattens any buildings that are within the blast range.
If you do an attack inappropriately (eg if you don't have enough energy - or
if you weren't close enough), you don't pay the energy penalty - but you
do get held up for the time an attack would actually take.
MOVING:
At any time when you aren't attacking, you can turn and move by
setting the turn_rate and move_speed variables in your ZillaOutputs structure.
The maximum forward speed and turn rate is defined in the 'zilla_internals.h'
file. You can also input a negative speed and move slowly backwards.
COLLECTABLES:
Scattered randomly around the arena are 'collectables'. These can be picked up
merely by walking into them - at which point you 'own' that collectable and
another one will pop up randomly somewhere else to replace it.
Each robot can only hold one collectable at a time. There is a special
action called 'USE_COLLECTABLE' which activates (and removes) whatever
collectable you are currently holding. You can then pick up another
one - even while the effects of the first are still working.
On-screen, the four collectable types are marked with a spinning icon
with a letter on it:
- E - Energy - Your energy level is instantly restored to 100%.
- H - Health - Using this collectable gives you 30% more health - but
it cannot increase health over 100% or resurrect a dead robot.
- S - Speed - Temporarily increases the maximum speed your robot can
move by a factor of SPEEDUP_RATE. The effect lasts for
SPEEDUP_DURATION.
- T - Time Slow - Temporarily slows the progress of time for everyone
and everything except your robot. From your perspective, buildings
collapse more slowly, other bots move slowly and attack less frequently.
It's better than 'Speed' because you can attack more rapidly than
everyone else and their energy replenishes more slowly than yours.
Time slows by a factor of 'TIME_SLOW_RATE' and the effect lasts
(from your perspective) for 'TIME_SLOW_DURATION'.
NOTE 1: If two or more robots activate TimeSlow collectibles - all operate at
normal speed while everyone else is slowed down. Even if your TimeSlow
time expires, you continue to operate normally until ALL TimeSlow effects
run out.
NOTE 2: BEWARE: If you slow time, the damage you take from a forcefield
accumulates at a normal rate for you - whilst everyone else is taking forcefield
damage more slowly.
You can tell what collectable you are carrying from the ZillaInputs
structure - also whether you are currently slowing everyone elses time
down and/or are under the influence of a Speed collectable. Robots who
are being slowed down can't tell (except by the fact that one of the
other robots is moving so much faster!)
SPEECH BALLOONS:
There is a 2 dimensional array of characters in your ZillaOutput
structure. If you write text into that array, it will appear in
a speech balloon over your robot's head. This has no effect whatever
on the game play. Please be VERY careful to NULL-terminate your
strings and not to overflow those arrays!
eg:
strcpy ( input.speech[0], "Hello" ) ;
strcpy ( input.speech[1], "World" ) ;
Each row of the 'speech' array represents a separate line of text.
There are MAX_LINES_OF_SPEECH rows of text, of MAX_SPEECH_LENGTH characters
each (including the null byte). MAX_LINES_OF_SPEECH will always be at
least 2 and MAX_SPEECH_LENGTH will always be at least 10 - so you can
definitely have two lines of text of 9 characters each (plus the null
byte).
INITIALISATION:
On the first occasion that your robot is called, you must strcpy the
name of your robot as a null-terminated string into 'output->name'.
The maximum length of this string (including the NULL byte at the end
is MAX_NAME_LENGTH. Doing this again after the first call has no effect.
For One-On-One competitions:
At the start of the competition, your robot will be placed at the center of a
random road intersection inside the City boundaries - but not within
MIN_INITIAL_RANGE of another robot. You'll be facing in a random direction.
For All-Against-All competitions:
Due to limited space in the City model, it may not be possible to start
off with the robots separated by MIN_INITIAL_RANGE - so all the system
will guarantee is that the robots won't be touching at the start.
The Fine Print:
The following rules must STRICTLY be followed - breaking one will
disqualify your program.
- The program must be delivered in a SINGLE source file called 'robot.c'
(or 'robot.cxx' if it's in C++). No header files, data files or other
libraries are allowed.
- All global symbols used within the program must be declared 'static'
except for ONE external function that must be named: runZilla.
- The only header file you are allowed to include is 'zilla_interface.h'
- The only library functions you are allowed to call are defined for
you in zilla_interface.h - they are:
- From the C standard library:
rand, malloc, free (new and delete are allowed in C++)
- From the C standard math library:
sin, cos, tan, atan, atan2, sqrt, pow
- From the C standard string library:
memcpy, memset, strcpy, strlen, strcat
Notice that no standard I/O or stream I/O functions are included.
- The runZilla() function takes a pointer to a simple structure
as it's input and another pointer indicates where it should
write its results:
In C:
void runZilla ( struct ZillaInputs *input,
struct ZillaOutputs *output ) ;
In C++:
extern "C" void runZilla ( ZillaInputs *input,
ZillaOutputs *output ) ;
The contents of the two interface structures are defined in
"zilla_interface.h"
- The runZilla function will be called between 10 and 100 times per
simulated second. (If the underlying hardware can't run things
that fast, simulation time will run slower than realtime). The
current simulation time is provided in the ZillaInputs structure
on each call. Don't rely on a particular iteration rate to make
your algorithm work!! When timeslow is activated, all robots
will be told time from their perspective.
- To keep the game running smoothly for the sake of the audience,
your function must return within 1 millisecond on our target
hardware (which will have at least a 2GHz CPU).
- Programs may not return Not-A-Number (NaN) or Infinite (Inf)
numbers in their ZillaOutput structures. The consequences of
doing so are to cause the 'bot to freeze for that iteration
- taking damage as usual but unable to move, shoot, rage or
chomp.
- You may not consume more than 1Mbyte of RAM within your library.
- The C or C++ library must conform to current C/C++ standards
without using compiler-specific extensions. The final contest
will be run on a Linux PC with a recent GCC/G++ compiler used
to compile the bot programs.
- All robot programs must be provided as source code via email to
the contest organiser in time for them to be judged for illegal
content and tested to give a reasonable indication that they
are robust and do not routinely crash.
- Code must be provided free of copyright and must not contain
licensed or patented material and will be published as example
programs for the following year's contest.
- Programs that attempt to gain advantage by deliberately
corrupting memory or otherwise disrupting the operation of
the simulation will be eliminated from the competition.
- Any program that crashes the simulation or breaks any of the
rules is eliminated from the competition. Since it is sometimes
hard to determine which part of the system caused a crash, the
Judge's opinion is final.
If any of those situations arise during the 'last man standing'
round then the round will be restarted without the offending
robot. Please work hard to make sure this doesn't happen because
of you!
- The City Simulator software is subject to change in the
unlikely case that serious bugs are found in it leading up
to the competition. All effort will be made to keep the
interface to the your function the same.
A WARNING ABOUT THE GRAPHICS:
The graphic display of the game is intended to look cool for the audience
and to help with debugging - but it is NOT intended to represent the 'ground
truth' of the game. As far as your bot is concerned, the world is a boring,
flat, two-dimensional place - buildings are solid and completely fill the
grassy area within their block - rubble is the same as empty space. No
matter how their graphic representation looks, all bots are circular and
identical in size.
For example: