Added Read Me file

This commit is contained in:
Brian Fertig 2026-04-12 09:00:01 -06:00
parent 23b43a6f2a
commit 004fc8d033
1 changed files with 188 additions and 0 deletions

188
README.md Normal file
View File

@ -0,0 +1,188 @@
# iPuzzle
A browser-based multiplayer jigsaw puzzle game built with [Phaser 3](https://phaser.io/) and vanilla ES6 JavaScript. Pick an image, choose a difficulty, and solve the puzzle solo or with friends in real time — no account required.
---
## Features
- **55+ puzzle images** — a curated library of illustrated and photographic artwork, from fantasy landscapes to wildlife
- **7 difficulty levels** — 20, 40, 60, 100, 140, 180, or 250 pieces
- **Procedurally generated pieces** — interlocking tab/blank connectors generated from a grid so every edge fits its neighbor
- **Real-time multiplayer** — create a room and share the 4-character code (or a direct URL) for others to join instantly
- **Player color labels** — each player's held piece shows their name tag in a unique color
- **Snap detection with glow** — matching edges glow as you bring a piece close; release to lock
- **Group movement** — snapped pieces move as a unit
- **Pan and zoom** — middle-click drag or right-click drag to pan; scroll wheel to zoom
- **Box selection** — click-drag on empty space to grab multiple pieces at once
- **Background themes** — Dark Wood or Green Felt table surface
- **Puzzle timer** — tracks time from first interaction to completion
- **Completion stats** — snap rate chart, corner/edge/quadrant milestones, per-player snap counts
- **Background music** — 8 in-game tracks + main menu music with mute toggle
- **Session persistence** — progress is saved to `localStorage`; resume an unfinished puzzle from the main menu
- **Fullscreen support**
- **Responsive canvas** — scales to fit any window size via `Phaser.Scale.FIT`
---
## Running the Game
Because Phaser loads assets over XHR, the game must be served over HTTP (not opened as a `file://` URL).
### Option A — Node.js server (multiplayer)
The Node server handles both static file serving and WebSocket multiplayer.
```bash
cd server
npm install
node server.js
# open http://localhost:8080
```
Set a custom port with the `PORT` environment variable:
```bash
PORT=3000 node server.js
```
### Option B — Simple HTTP server (single-player only)
Any static file server works if you only need single-player:
```bash
# Python
python3 -m http.server 8080
# Node (npx)
npx serve .
```
Then open `http://localhost:8080`.
---
## Multiplayer
1. One player starts a **New Puzzle** — a 4-character room code is generated (e.g. `X4KR`).
2. Share the code or the full URL (e.g. `http://your-host:8080/?room=X4KR`).
3. Other players click **Join Puzzle** and enter the room code, or open the share URL directly.
4. All players see each other's pieces in real time. Piece claiming prevents two players from grabbing the same piece simultaneously.
Rooms persist on the server for 5 minutes after the last player disconnects, allowing brief reconnections.
---
## Controls
| Action | Input |
|---|---|
| Pick up / release piece | Left-click |
| Pan the board | Middle-click drag or right-click drag |
| Zoom | Scroll wheel |
| Box-select multiple pieces | Left-click drag on empty space |
| Fullscreen | Button on main menu |
---
## Project Structure
```
iPuzzle/
├── index.html # Entry point; loads all scripts via <script> tags
├── server/
│ ├── server.js # Node HTTP + WebSocket server
│ └── package.json
├── js/
│ ├── main.js # Phaser game config; registers all scenes
│ ├── scenes/
│ │ ├── MainMenuScene.js # Animated main menu, join/create room UI
│ │ ├── NewPuzzleScene.js # Image picker, difficulty selector
│ │ └── PuzzleScene.js # Core gameplay loop
│ ├── puzzle/
│ │ ├── ConnectorGeometry.js # Tab/blank connector math
│ │ ├── PuzzleGenerator.js # Divides image into piece grid
│ │ ├── PieceRenderer.js # Draws each piece to a canvas texture
│ │ ├── PieceObject.js # Phaser Image wrapper for a piece
│ │ ├── GroupManager.js # Tracks which pieces are locked together
│ │ ├── SnapDetector.js # Proximity checks and glow segment generation
│ │ └── RoomCode.js # 4-character room code generator
│ ├── state/
│ │ ├── PuzzleState.js # In-memory canonical puzzle state
│ │ └── StorageManager.js # localStorage persistence helpers
│ └── net/
│ └── NetworkManager.js # WebSocket client singleton
└── assets/
├── puzzles.json # Manifest of available puzzle images
├── thumbnails.json # Manifest of thumbnail paths
├── images/
│ ├── puzzles/ # Full-resolution puzzle images + thumbnails
│ └── ui/ # Backgrounds, logo, sub-logo
└── audio/
├── music/ # main_menu.mp3, track_0108.mp3, tracks.json
└── fx/ # click.mp3, grab.mp3, stats.mp3
```
---
## Architecture
### Scenes
| Scene | Responsibility |
|---|---|
| `MainMenuScene` | Animated intro, Continue / New Puzzle / Join Puzzle / Fullscreen buttons, join dialog |
| `NewPuzzleScene` | Scrollable image gallery with thumbnails, difficulty picker, background picker |
| `PuzzleScene` | Piece rendering, interaction, snap/glow, pan/zoom, multiplayer relay, completion stats |
### Puzzle piece generation
1. `PuzzleGenerator` divides the chosen image into a `cols × rows` grid.
2. `ConnectorGeometry` assigns tab or blank connectors to each shared edge, ensuring that piece A's tab aligns with piece B's blank.
3. `PieceRenderer` draws each piece — with its connectors — to an off-screen canvas, then registers it as a Phaser texture.
4. `PieceObject` wraps a Phaser `Image` and tracks grid position, group membership, and world coordinates.
### Snap detection
Each frame while a piece is held, `SnapDetector.check()` compares every piece in the held group to its grid neighbors. If any neighbor is within `snapRadius` (35% of piece width), the pieces lock together via `GroupManager`. A larger `glowRadius` (140% of piece width) drives the edge-glow preview.
### Multiplayer protocol (WebSocket)
| Message | Direction | Description |
|---|---|---|
| `create_room` | client → server | Host sends initial puzzle state |
| `room_created` | server → client | Confirms room code and player ID |
| `join_room` | client → server | Joiner requests to enter a room |
| `room_joined` | server → client | Returns full puzzle state, player list, and existing claims |
| `player_joined` / `player_left` | server → all | Presence events |
| `claim` | client → server | Request exclusive hold on a piece group |
| `claim_ok` / `claim_denied` | server → client | Claim result |
| `move` | client → server | Throttled (50 ms) position delta while dragging |
| `release` | client → server | Drop with updated positions and merged groups |
| `completed` | server → all | Broadcast when all pieces form one group |
### State persistence
`StorageManager` serialises the current `PuzzleState` to `localStorage` on every significant event (snap, release). On "Continue Puzzle" the state is restored from storage. All state is keyed by room code to support future multi-room play.
---
## Adding New Puzzles
1. Add a 16:9 PNG or JPG to `assets/images/puzzles/`.
2. Create a thumbnail (any size, same basename prefixed with `thumb_`) in the same folder.
3. Add an entry to `assets/puzzles.json` and `assets/thumbnails.json`.
A helper script for bulk thumbnail generation is provided at `assets/images/puzzles/make_thumbnails.sh`.
---
## Tech Stack
| Concern | Technology |
|---|---|
| Game engine | [Phaser 3.90](https://phaser.io/) (CDN) |
| Language | Vanilla ES6 (no build step, no npm on the client) |
| Multiplayer | WebSocket via the [`ws`](https://github.com/websockets/ws) library |
| Server | Node.js (built-in `http` + `ws`) |
| Persistence | Browser `localStorage` |