# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Multiplayer Tetris Battle Royale game with 2-8 player real-time battles via WebSocket. Players clear rows to send garbage to opponents; last player standing wins. ## Commands ```bash # Install dependencies cd server && npm install # Start server cd server && npm start # or cd server && node index.js # Run tests cd server && npm test # Run tests with coverage cd server && npm run test:coverage # Server runs on http://localhost:3000 (or PORT env variable) ``` ## Architecture ### Server-Client Model - **Server** (`server/`): Express + Socket.io handles all game logic authoritatively - `index.js`: Main server with socket.io event handlers - `game-logic.js`: Pure game logic functions (testable, no socket dependencies) - `__tests__/`: Jest test suite with 57 tests - Manages single global lobby with `lobby.players` Map - Game tick runs at 50ms intervals via `gameTick()` - Broadcasts state updates to all connected clients via `broadcastState()` - **Client** (`public/js/`): Vanilla JavaScript with module pattern - `network.js`: Socket.io client wrapper, manages player/game state caching - `game.js`: `TetrisGame` class for local game state (mirrors server) - `renderer.js`: `TetrisRenderer` class - Canvas rendering for all player boards - `ui.js`: `UIManager` class - screen transitions, DOM manipulation - `app.js`: Entry point, ties modules together, game loop via `requestAnimationFrame` ### Game Flow 1. **Lobby**: Players join global lobby via `join-lobby` socket event 2. **Ready**: All players click READY; game starts when all ready (2-8 players) 3. **Game**: Server authoritative - client inputs sent via socket, server broadcasts state 4. **Elimination**: Player eliminated when piece locks at top or garbage fills board 5. **Victory**: Game ends when 1 active player remains ### Key Constants - `BOARD_WIDTH = 10`, `BOARD_HEIGHT = 20`, `CELL_SIZE = 24` - `LOBBY_ROOM = 'global-lobby'` - Socket.io room for all players - Garbage rules: 2 lines cleared -> 1 garbage row, 3 -> 2, 4 (Tetris) -> 4 rows ### Piece Spawn & Negative Y Overflow Pieces spawn at `y = 0` (top of visible board). The coordinate system allows **negative Y values** as intentional overflow space: - **Negative Y allowed**: `isValidPosition()` only checks `newY >= BOARD_HEIGHT`, not `newY < 0` - **Purpose**: When garbage is received, pieces are pushed up (y decreases) into negative territory - **Player survival**: Player is NOT eliminated when piece goes negative — only when piece LOCKS with blocks in rows 0-1 - **Recovery**: Piece drops naturally back into visible area; player can continue playing This design gives players a buffer zone to recover from garbage attacks before elimination. ### Socket Events | Event | Direction | Payload | |-------|-----------|---------| | `join-lobby` | Client->Server | `{ playerName }` | | `ready` | Client->Server | - | | `player-move` | Client->Server | `{ playerId, direction }` | | `player-rotate` | Client->Server | `{ playerId }` | | `player-drop` | Client->Server | `{ playerId, hard }` | | `player-joined` | Server->Client | `{ player, players }` | | `player-left` | Server->Client | `{ playerId, players }` | | `game-started` | Server->Client | `{ players, states }` | | `state-update` | Server->Client | `states[]` | | `game-over` | Server->Client | `{ states }` | ### Rendering - Each player gets a dynamically created board with canvas + info divs - `renderer.setActivePlayer()` marks current player's board as `.main`, others as `.spectator` - Battle grid layout classes: `.single-player`, `.two-players`, `.multi-player`