Files
plateformer-evolve/AI_TCP_API.md
T

119 lines
3.8 KiB
Markdown

# AI TCP API
The AI handler listens on `127.0.0.1:8765` by default. Set `AI_HOST`,
`AI_PORT`, or `AI_MAX_WORKERS` to change the server configuration. Messages
are UTF-8 JSON objects separated by newlines. The connection stays open for
multiple requests.
## Ping
Request: `{"type":"ping"}`
Response: `{"ok":true,"type":"pong"}`
## Start a simulation
The server chooses the map. For the first training simulation it selects the
map closest to difficulty 0. After a simulation finishes, the server computes
each model's score from coins, progress toward the finish, and whether it won.
The best score becomes the next training difficulty. Validation requests ignore
the training score and randomly select a map with difficulty at least 70, or
any map when no such map exists.
The score is from 0 to 100:
`50 * coin_progress + 10 * distance_progress + 40 * won`
where `coin_progress` is collected coins divided by the map's total coins,
`distance_progress` is progress from the spawn point to the finish, and `won`
is 1 when the finish was reached and 0 when the model stopped elsewhere,
including on spikes.
Send one model entry per model. The model entries contain only an ID; moves
are requested separately on every turn.
```json
{"type":"simulate","validation":false,"models":[{"id":"model-a"},{"id":"model-b"}]}
```
Every model gets a separate `Game` instance and can run in parallel. Valid
moves are `left_down`, `left`, `left_up`, `up`, `right_up`,
`right`, and `right_down`. Send `null` for a model when it has no more moves;
that model is stopped for the current generation.
The initial response is a `move_request`. It contains the selected map and the
initial state of every model. The map is sent only in this initial response;
clients should cache it for the rest of the simulation.
```json
{
"ok":true,
"type":"move_request",
"map":{"name":"default","difficulty":30.0,"layout":[["stone", "stone"]]},
"results":{"model-a":{"won":false,"coins":0,"moves":0,"position":[1,4],"finished":false,"changes":[]},"model-b":{"won":false,"coins":0,"moves":0,"position":[1,4],"finished":false,"changes":[]}}
}
```
After receiving this response, send one move for every model:
```json
{"type":"step","moves":{"model-a":"right","model-b":"right_up"}}
```
## Advance one turn
The client must send exactly one move for every model on each turn. The server
advances all model simulations in parallel and replies before the client must
predict the next moves.
```json
{"type":"step","moves":{"model-a":"right","model-b":null}}
```
The response is another `move_request` containing the updated state and score
for every model. Each model also receives `changes`, a cumulative list of
blocks that differ from the base map. For example, a collected coin is sent as
`{"position":[4,7],"type":null}`. Unchanged turns return an empty list. It
asks the client for the next move of every unfinished model. A model that has
no more moves can send `null`:
```json
{"type":"step","moves":{"model-a":"right","model-b":null}}
```
When every model has finished, the response type becomes `simulation_result`.
It contains the final state and score for every model, plus the best score used
to choose the next training map:
```json
{
"ok":true,
"type":"simulation_result",
"results":{
"model-a":{
"won":true,
"coins":3,
"moves":18,
"position":[23,1],
"finished":true,
"changes":[{"position":[4,7],"type":null}],
"score":92
},
"model-b":{
"won":false,
"coins":1,
"moves":7,
"position":[8,4],
"finished":true,
"changes":[],
"score":24
}
},
"score":92
}
```
The score is calculated by the server using the weights described above. The
top-level `score` is the best model score for the completed generation.
Malformed requests receive `{"ok":false,"type":"error","error":"..."}`.