Tic-tac-toe — Rules & board
Board layout, win conditions, draw, reconnect window, and all possible game:over reasons.
Board layout
Standard 3×3 grid. The board is a flat array of length 9. Position formula: row * 3 + col. Positions 0–8, left-to-right, top-to-bottom.
Win condition
Three consecutive marks of the same type — across a row, column, or diagonal. The server checks all 8 lines after every move. When a win is found, game:over includes winningLine — exactly 3 board positions. Use these to highlight the winning line.
[0,1,2] [3,4,5] [6,7,8] (rows) · [0,3,6] [1,4,7] [2,5,8] (cols) · [0,4,8] [2,4,6] (diagonals)Draw
A draw occurs only when all 9 cells are filled with no winner. There are no draw offers. game:over fires with winner: null and reason: "draw".
Marks & turn order
| Mark | Goes first | players[] index |
|---|---|---|
X | Yes | 0 |
O | No | 1 |
The first player to join (or the human in a bot game) is always assigned X. currentTurn in all events is an index into players[], not a mark string.
AFK timeout
Tic-tac-toe has no per-game clock, but AFK timeout is supported. Defaults: 60s timeout, 20s warning. Configure per session via matchmaking:join. Making a move clears the timer and emits tictactoe:afk_warning_cleared.
Reconnect
When a player disconnects, a 60-second window opens. The opponent receives player:disconnected. Reconnect by emitting game:reconnect { playerId }. If the window expires, game:over fires with reason: "disconnect".
Bot difficulty
| Difficulty | Strategy |
|---|---|
easy | Plays randomly with no threat detection. |
medium | Wins or blocks immediately when possible, otherwise plays randomly. |
hard | Minimax — plays optimally. Best possible outcome is a draw against a perfect opponent. |
game:over — reason reference
| reason | winner | winningLine | Triggered by |
|---|---|---|---|
"victory" | playerId | number[3] | A player placed three in a row. |
"draw" | null | null | All 9 cells filled with no winner. |
"resignation" | playerId | null | Player emitted game:resign. |
"disconnect" | playerId | null | Opponent did not reconnect within 60 seconds. |