119 lines
3.8 KiB
Markdown
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":"..."}`.
|