Tinrail
Lay track and ride the rails. Keep your engine on the table longer than G-Bot!
Goal
You and G-Bot each drive an engine on a 6x6 table of tile slots. Your engine waits at a notch: an empty cell plus the port it will enter that cell by. On your turn you MUST place one tile from your 3-tile hand, at any rotation, into the one cell your engine faces (there is no other legal slot and no pass). The tile's rails then carry every engine facing that cell across the connected track until it comes to rest at the edge of an empty cell, or rides off the table. An engine that leaves the table is out of the game. You win the moment G-Bot's engine is eliminated while yours survives, no matter whose placement caused it; your own placement can eliminate you, and G-Bot's can eliminate G-Bot. If one placement eliminates both engines, the match is a draw.
Scoring
Scored once, at the end. tilesSurvived is the number of placements (by either side) after which your engine was still on the table. Win: 100 plus 5 per tile survived. Draw (both engines eliminated in one resolution, or all 35 tiles placed with both engines still riding): 50 plus 2 per tile survived. Any other result: 2 per tile survived. That total is multiplied by 1.0 against Wise G-Bot or 0.6 against Easy, then rounded down. Higher is better; the ceiling is 275.
Rules
The board is a 6x6 grid of cells indexed 0..35 as index = row * 6 + col, row 0 at the top, col 0 at the left. Every cell boundary has 8 ports numbered 0..7 clockwise from top-left: top edge ports {0 left third, 1 right third}, right edge {2 top, 3 bottom}, bottom edge {4 right, 5 left}, left edge {6 bottom, 7 top}. Port p on a cell lines up with port ADJ[p] on the neighbor across that edge, ADJ = [5,4,7,6,1,0,3,2] (an involution: 0-5, 1-4, 2-7, 3-6). The neighbor by port group: ports {0,1} lead to (row-1, col), {2,3} to (row, col+1), {4,5} to (row+1, col), {6,7} to (row, col-1); outside the grid is the table edge. A tile is a perfect matching of its 8 ports into 4 rail strands, e.g. [[0,5],[1,4],[2,7],[3,6]]; strands may cross visually but never interact. The deck is the 35 unique tiles (all matchings deduplicated so no tile is a rotation of another), each appearing exactly once. Rotation r in {0,1,2,3} is r quarter-turns clockwise: strand [a,b] becomes [(a+2r)%8, (b+2r)%8]. The observation text lists every placed tile's strands with rotation already applied, and your hand tiles' strands at all four rotations, so you never need to derive the tile set yourself. At the start the deck is shuffled with a seeded generator; you are dealt 3 tiles, then G-Bot 3, and the remaining 29 form the face-down draw pile. You never see G-Bot's hand or the pile order. Your engine starts at cell 32 entering by port 4 (bottom edge, facing up); G-Bot's starts at cell 3 entering by port 0 (top edge, facing down). You move first and turns alternate; G-Bot replies automatically inside your action, so the state you receive is always your turn again or the finished match. A turn is exactly one 'place' of a hand tile (tileIndex into your live hand, rotation 0..3) into the cell your engine faces; every tile at every rotation is legal, even one that eliminates your own engine. When a tile lands at cell C, every surviving engine facing C rides: starting at its entry port p, it exits the tile at p's strand partner q, then crosses edge q; off the grid ends the ride off the table (eliminated), an empty neighbor ends it at rest at that cell entering by port ADJ[q], and a tiled neighbor continues the ride entering at ADJ[q]. Itineraries are computed independently for each rider, but if both engines rode and their itineraries traverse any common strand in opposite directions, they have met head on and both are eliminated. Exactly one engine eliminated ends the match immediately in favor of the survivor; both eliminated in one resolution is a draw. If the match continues the placer draws the top tile of the pile while it lasts. If it becomes a player's turn and they have no tiles (only possible with all 35 tiles placed), the match is a draw. An invalid action (wrong type, tileIndex outside your live hand, bad rotation, or acting after the match ended) is refused and costs nothing: no tile is placed, no turn is spent and G-Bot does not reply.
Action grammar
{
"type": "object",
"properties": {
"type": {
"const": "place"
},
"tileIndex": {
"type": "integer",
"minimum": 0,
"maximum": 2
},
"rotation": {
"type": "integer",
"enum": [
0,
1,
2,
3
]
}
},
"required": [
"type",
"tileIndex",
"rotation"
]
}Start a game
curl -s "https://gameboard.gg/api/games/tinrail/init?seed=7"
Then POST { state, action } to https://gameboard.gg/api/games/tinrail/action, carrying state forward each call. See the API overview for the full loop.