mirror of
https://github.com/tiennm99/gomoku.git
synced 2026-07-16 14:16:53 +00:00
The project-level docs described the pre-caro-port architecture (TCP on
9999, JSON {data:base64} wire format, web/ + core/ + api/ + bot/ dirs,
server flags -w/-t/-s, state strings like '1'/'2'/'7,7') and referenced
files that no longer exist. The README also described an owner-controlled
Start button, 5-second heartbeat, 'database/' directory, AuthRequest
handshake, and linked to now-deleted plan files.
CLAUDE.md
- Rewritten for the current architecture: single WS endpoint at
:1999/gomoku, protobuf binary frames, server/{consts,game,lobby,
network,state,pkg/log}, client/src/{config,scenes,services,ui,
objects,generated}, auto-start PVP semantics, heartbeat 50s/deadline
90s, ClientExit self-vs-peer helper, enum serialization notes, Hard
AI description, and a "When editing" section covering proto regen,
state flow tests, and scene cleanup patterns.
README.md
- Features: owner-controlled start → auto-start on 2nd join.
- Heartbeat: 5s → 50s; add server deadline + reconnect back-off.
- Project structure: database/ → lobby/; services list corrected.
- Protocol: replace AuthRequest story with real SetNickname handshake.
- Credits: trimmed from a multi-bullet block to a single attribution
line ("Based on ratel-online by ainilili"). Removed caro references
and the link to the deleted plans/ implementation history.
docs/system-architecture.md
- Fully rewritten. Drops old TCP+JSON protocol tables, CLI client box,
obsolete state flow diagram. Adds: current state registry
(welcome/setNickname/home/waiting/gamePvp/gamePve/gameover/watching),
cross-goroutine sync channels (StartCh/GameOverCh/RematchCh), typed
enum list, kickStaleRoomPeers note, ClientExit self-vs-peer semantics,
and client module table.
docs/deployment-guide.md
- Remove stale pointer to deleted plans/ directory.
Code comment cleanup (user: "don't mention too much"):
- server/network/handlers_stateless.go handleHeartbeat: drop caro
reference, explain behavior in terms of the reader loop.
- server/game/ai_eval.go: "ported from caro's GomokuAI scoring" →
generic "threat-pattern weights used by the Hard AI".
- server/game/ai.go positionScore: drop caro attribution, describe
the heuristic directly.
- server/lobby/room.go: drop "caro Java domain" annotation, describe
what Room actually embeds.
- client/src/services/game-state-service.js WATCH_GAME_SUCCESS: drop
historical rename comment.
go vet + go test ./... + npm run build all green.
6.3 KiB
6.3 KiB
Gomoku (Five-in-a-Row)
Multiplayer online Gomoku with PVP, PVE (3 AI difficulties), and spectator mode. Go server + Phaser 3 browser client over a typed protobuf WebSocket.
Features
- PVP — two human players in a room. Game auto-starts as soon as the second player joins — no explicit "Start Game" click.
- PVE — three AI difficulties:
- Easy — random legal move
- Medium — heuristic threat scoring (win > block > center-weighted)
- Hard — minimax depth 3 with alpha-beta pruning
- Spectator mode — watch any in-progress game in real time
- Live lobby — room list refreshes on demand
- Heartbeat / auto-reconnect — client pings every 50 s; server closes idle connections after 90 s; client reconnects with exponential back-off
Quick Start
Docker (recommended)
docker compose up -d
- Browser client: http://localhost:8080
- WebSocket endpoint: ws://localhost:1999/gomoku
Tear down:
docker compose down
Local Development
Prerequisites: Go 1.23+, Node 22+
# Terminal 1 — Go server
go -C server run . -p 1999
# Terminal 2 — Vite dev server
npm --prefix client install
npm --prefix client run dev
Open http://localhost:5173 in two browser tabs to play against yourself.
Architecture
Browser tab A ─────────────────────────────────────────────────┐
Browser tab B ──► http://localhost:8080 (nginx / client:8080) │
│ │
Phaser 3 + protobufjs │
│ WS binary frames │
▼ │
ws://localhost:1999/gomoku (Go server) │
│ │
┌──────────┴──────────┐ │
│ state machine │ one goroutine per player │
│ per-player │ home → waiting → game │
├─────────────────────┤ │
│ in-memory lobby │ rooms, players (no DB) │
├─────────────────────┤ │
│ game engine │ 15×15 board, win detect │
└─────────────────────┘ │
│ │
common/proto/*.proto (single source of truth)────┘
Key design decisions:
- Server-authoritative — all move validation and win detection server-side; browser never modifies board locally
- Protobuf binary frames over WebSocket (
gorilla/websocket);common/proto/is the single source of truth, enums replace stringly-typed fields - State machine per player —
home → waiting → game → gameover; states registered inserver/state/ - Auto-start PVP — handleJoinRoom flips the room to Playing the instant a
second player joins; both goroutines transition via
room.StartCh - In-memory store — rooms and players live in hashmaps; restart clears all state (intentional — games are short-lived)
Project Structure
gomoku/
├── server/ Go game server (:1999, endpoint /gomoku)
│ ├── main.go
│ ├── consts/ StateID enum, difficulty constants
│ ├── game/ pure game engine: Board, win detection, AI
│ ├── lobby/ in-memory Player + Room store
│ ├── network/ WS upgrade, reader/writer, dispatch
│ ├── protocol/ generated Go protobuf stubs
│ ├── state/ per-player state machine
│ ├── Dockerfile multi-stage → distroless/static-debian12
│ └── Makefile build / test / docker / proto targets
│
├── client/ Phaser 3 browser client (Vite, nginx :8080)
│ ├── src/
│ │ ├── config/ game-config, protocol-constants
│ │ ├── scenes/ boot, menu, game
│ │ ├── services/ event-bus, connection-service, game-state-service
│ │ ├── ui/ menu-ui, menu-ui-rooms, game-ui (DOM overlay)
│ │ ├── objects/ Phaser Board, Stone
│ │ └── generated/ committed protobuf JS stubs
│ ├── Dockerfile Vite build → nginx:1.27-alpine
│ └── nginx.conf SPA fallback, gzip, asset caching
│
├── common/proto/ request.proto / response.proto (shared wire format)
├── docker-compose.yml two services: server :1999, client :8080
└── docs/ architecture + deployment guide
Game Rules
- Board: 15 × 15 intersections
- Black moves first
- First player to place 5 stones in a row (horizontal, vertical, or diagonal) wins
- Full board with no winner is a draw
Protocol
- Transport: WebSocket binary frames at
ws://<host>:1999/gomoku - Encoding: protobuf (see
common/proto/request.proto,common/proto/response.proto). Theoneof payloadcase IS the event code. - Nickname handshake: on connect the server sends
ClientConnectResponsethenNicknameSetResponse{invalid_length: 0}as a prompt. The client replies withSetNicknameRequest{nickname}. On success the server sendsNicknameSetResponse{invalid_length: 0}+ShowOptionsResponseand the client shows the lobby. - Heartbeat: client →
HeartbeatRequestevery 50 s. Server closes the WS if no frame is received within 90 s. Client auto-reconnects on close with exponential back-off (up to 10 attempts).
Deployment
See docs/deployment-guide.md for:
- Docker Compose quick start
- Building individual images
- Reverse proxy / TLS setup (Caddy, Traefik, nginx)
Credits
Based on ratel-online by ainilili (MIT).
License
Apache 2.0 — see LICENSE.