Connect 4 — Rules & board
Board layout, win conditions, draw, reconnect window, and all possible game:over reasons.
Board layout
Standard 7 columns × 6 rows. The board is a flat array of length 42. Position formula: row * 7 + col. Row 0 is the top row, row 5 is the bottom. Pieces fall to the lowest empty cell in a column — gravity is simulated server-side.
board[col] !== null (top cell of each column is index 0–6).Win condition
Four consecutive pieces of the same color — horizontally, vertically, or diagonally. The server checks all four directions after every move. When a win is found, game:over includes winningCells — exactly 4 board positions. Use these to highlight the winning line.
Draw
A draw occurs only when all 42 cells are filled with no winner. There are no draw offers in Connect 4. game:over fires with winner: null and reason: "Board full - Draw".
Colors & turn order
| Color | Goes first | players[] index |
|---|---|---|
red | Yes | 0 |
yellow | No | 1 |
The first player to join (or the human in a bot game) is always assigned red. currentTurn in all events is an index into players[], not a color string.
No clock
Connect 4 has no per-game clock. The only time pressure is the AFK timeout — see the AFK timeout page.
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 | Think time | Strategy |
|---|---|---|
easy | 300–800ms | Plays randomly. No threat detection. |
medium | 500–1200ms | 30% chance to play a winning move, 30% chance to block opponent's win, otherwise random. |
hard | 800–1500ms | Always wins or blocks immediately. Evaluates center control and connected pieces for positional play. |
game:over — reason reference
| reason | winner | winningCells | Triggered by |
|---|---|---|---|
"Four in a row!" | playerId | number[4] | A player connected four pieces in a line. |
"Board full - Draw" | null | null | All 42 cells filled with no winner. |
"resignation" | playerId | null | Player emitted game:resign. |
"afk_timeout" | playerId | null | Player was idle past afkTimeoutSeconds. |
"disconnect" | playerId | null | Opponent did not reconnect within 60 seconds. |