What your model sees
The Ants observation is a JSON object for one colony on one turn. Every adapter in your manifest receives this object directly, as its whole document. There is no setup message or terminal observation; when a seat stops playing, its model stops receiving calls.
Fields
| Field | Shape | Meaning |
|---|---|---|
size | [rows, columns] | Board dimensions |
mine | [[row, column], …] | All your living ants, sorted by row then column |
foes | [[row, column, owner], …] | Currently visible enemy ants |
food | [[row, column], …] | Currently visible food |
hills | [[row, column, owner], …] | Currently visible standing hills |
water.rle | [value, count, value, count, …] | Row-major known-water mask |
vis.rle | [value, count, value, count, …] | Row-major mask of what you can see this turn |
Coordinates are zero-based and wrap as described in The world.
Empty lists are valid. Do not treat a list index as a permanent ant identity:
mine is sorted afresh, and births, deaths, and movement change its order.
Ownership labels
Owners are relative to you. In hills, owner 0 is yours and 1 upward is an
opponent’s. In foes, the owner is 1 upward and never 0, because a foe is by
definition not you. On a two-seat board that makes the label constant: your hills
are 0, every enemy hill and ant is 1.
There is no self-seat field and you do not need one. Ask which seat you occupy and
the answer is always the same: seat 0, as far as the observation is concerned.
Relabel every seat in the world and ask the same player again, and the bytes are
identical — that property is what makes the two seats of one match two samples of
one distribution, which is what a self-play trainer depends on.
Known water
Read RLE as (value, count) pairs. Values are 0 or 1, and the counts cover
rows × columns cells in row-major order. A 1 means discovered water. A 0 can be
known land or an unexplored square. Water discovered earlier remains known
even when no ant currently sees it.
For a small encoding example, size: [2, 3] and rle: [0, 2, 1, 1, 0, 3]
expand to [[0, 0, 1], [0, 0, 0]]. This illustrates the encoding, not a board a
season could play. Use rle_expand to build the tensor without a JSON loop over cells.
What you can see this turn
vis is the mask the engine filtered this observation through: a 1 at every cell within squared
radius 77 of one of your ants, wrapped, and a 0 everywhere else. It is the same RLE encoding as
water, so rle_expand builds the plane.
It is the difference between “there is nothing here” and “I cannot see here”. A 0 in the enemy
plane where vis is 1 means the square is empty; a 0 where vis is 0 means you do not know. Almost
every useful encoder wants that distinction, and without it a network learns “no enemy” from cells
it could not have seen.
A trainer can derive
visfrommineand the radius in four lines of numpy. An adapter cannot: its expression language cannot address an enclosing iterator’s element, so it cannot union a disk per ant. That is why the engine sends it. If your trainer derives this plane itself, check that the two agree.
What is hidden
Enemies, food, and hills are filtered to current vision. Only known water has memory. The payload supplies no scores, turn number, hive count, explored mask, or persistent model state.
A model call is a function of one observation. Recurrent outputs are not fed back on the next turn, so an architecture requiring that state channel is not supported. Train with the same missing information you will encounter during competition.
A worked example
This illustrative seat-0 observation has two ants and no known water:
{
"size": [64, 96],
"mine": [[12, 30], [13, 30]],
"foes": [[12, 33, 1]],
"food": [[11, 31]],
"hills": [[12, 30, 0]],
"water": {"rle": [0, 6144]},
"vis": {"rle": [0, 1054, 1, 11, 0, 5079]}
}
The second ant is mine[1], so the second action must address [13, 30]. The
water field does not say the whole board is land; it says there is no discovered
water in this view. The enemy and food coordinates are within current vision.
The viewer cannot show this yet. A replay frame carries the board as the referee sees it — every ant, all the water — because that is what re-simulating an action stream reconstructs. What a seat knew at a turn is a different thing, and
replay-decodedoes not answer it. Until it does, a replay here would show the opposite of the point.