by John Davin, Patrick Riley, Yang Gu, and Manuela Veloso
We are also providing a C++ library for generating and parsing messages in this standard.
This standard uses variable length messages, and the player is free to include any combination of messages that it can fit in a say message string (the server currently allows 10 characters (say_msg_size), with 73 possible values for each ASCII character).
Each character will represent one piece of information (like a player number, message type, X position, etc.). While this is not the most efficient coding for space, it greatly simplifies the parsing of the messages.
The message encoding does not include the player number of the sending player because the server includes that information with say messages.
Updates:
11/30/03 - Modified OpponentPos and
TeammatePos message types to encode the player number in the message
type ID. This allows these two message types to be sent with a cost of
4 units rather than 5. The new message type format is described in
section 2 ("Message Types").
12/15/03 - Corrected a typo in the section 1 table (the ranges for the X and Y datatypes were mixed up).
These are the types of data that will be represented. The range (in integral units) is listed for each type. Each data type will be represented by 1 character.
X | x coordinate for a position | [-53, 53) |
Y | y coordinate for a position | [-34, 34) |
dX, dY | velocity values | [-2.7,2.7) |
#cycles | confidence values for the other parameters of the message. (number of cycles since saw ball, or player) | [0, 72) |
player# | a player number | [0,11]* |
msg type ID | the msg ID (first char of every message) | [0,29]** |
The first character in each message identifies the message type.
Message Type | Syntax | Cost |
---|---|---|
Our Pos: | [0, X, Y] | 3 |
Ball Pos: | [1, X, Y, #cycles] | 4 |
Ball Velocity: | [2, dX, dY, #cycles] | 4 |
We have ball: | [3, player#]* | 2 |
Opponent has ball: | [4, player#]* | 2 |
Passing to (player#): | [5, player#] | 2 |
Passing to (X,Y): | [6, X, Y] | 3 |
Want pass: | [ 7 ] | 1 |
Opponent(player#) Pos:** | [8+player#-1, X, Y, #cycles] | 4 |
Teammate(player#) Pos:** | [19+player#-1, X, Y, #cycles] | 4 |
char validchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSUVWXYZ().+-*/?<>_";
#define NUMCHARS 73 (note that validchars has 73 chars, so max array index is 72)
int to char:
char get_printablechar(int i){
Specification:
Returns validchars[i] if i>=0 and i<NUMCHARS
Returns validchars[0] if i<0 (error case)
Returns validchars[NUMCHARS-1] if i>=NUMCHARS
}
Rounding conventions: Divisions and multiplications in the
following tables will be done with floating point arithmetic, and the
final value will be cast to an int to be used as the index into the
validchars array. This will be standard C integer casting - ie, the
integer value will be the floor of the float.
Examples (int to char):
(20+53)/106 * NUMCHARS = 50.27 should return validchar[50].
(25+53)/106 * NUMCHARS = 53.72 should return validchar[53].
Data type value | Encoded character value |
---|---|
X | get_printablechar( (X+53)/106 * NUMCHARS ) |
Y | get_printablechar( (Y+34)/68 * NUMCHARS ) |
dX | get_printablechar( (dX+2.7)/5.4 * NUMCHARS ) |
dY | get_printablechar( (dY+2.7)/5.4 * NUMCHARS ) |
#cycles | get_printablechar(#cycles) |
player# | get_printablechar(player#) |
msg type | get_printablechar( msg type ) |
let i = index of the character in validchars[] (use char to int conversion from section 3 above)
(eg, for message char '2', i=2, and for message char 'a', i=10).
Data type for current char | Data value (eg, X,Y,dX,dY,etc) |
---|---|
X | (i/NUMCHARS * 106) - 53 |
Y | (i/NUMCHARS * 68) - 34 |
dX | (i/NUMCHARS * 5.4)-2.7 |
dY | (i/NUMCHARS * 5.4)-2.7 |
#cycles | i |
player# | i |
msg type | i |
Examples of possible messages that a player could say within the 10 character say_msg_size limit:
Decoding: A player who received this message would decode it by determining the indices in the validchars array for each character, and then reading in each message using table 5 above. The msg type ID (first character of each message unit) would be used to determine the meanings of the subsequent parameters.
To decode "0Op1V*554":
let i0 .. i8 = the index into the validchars array for each of the 9 characters received for this message (so i0=0 since validchars[0]='0', i1=50 since validchars[50]='O', i2=25 since validchars[25]='p', etc.)
i0 indicates an Our Pos message (which is the position of the player who sent the msg, as identified by the server's say msg parameters).
Our Pos X = (i1/NUMCHARS * 106) - 53 = 19.6
Our Pos Y = (i2/NUMCHARS * 68) - 34 = -10.7
i3 indicates a Ball Pos message.
Ball Pos X = (i4/NUMCHARS * 106) - 53 = 29.8
Ball Pos Y = (i5/NUMCHARS * 68) - 34 = 28.4
#cycles since saw ball = i6 = 5
i7 indicates a Passing to Player (#) message.
player# = i8 = 4
Other possible messages are:
Notes on Precision:
X: ~1.5 unit step size ( range is [-53,53] so step size in msg encoding is 106/73=1.45)
Y: 1 ( range [-34,34]=68 can be represented using the 73 char values)
dX,dY: .074 ([-2.7,2.7] so step size = 5.4/73 = .074)
The ranges and integer to char conversions (eg, (X+53)/106 *
NUMCHARS ) were chosen to evenly space the value range for each of the
character encodings. The omission of the maximum float value for some
of the data types (ie, X=[-53,53)) was done in order to use all of the
encoded char values to represent an equal range of float values. The
inability to represent 53.0 is not a significant drawback because 53.0
will just map to the same character that 52.99... maps to, and 53.0 is
greater than the field length anyway.