尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A complete implementation of the Catan board game in TypeScript, with a procedurally drawn 3D board, bots at three difficulty levels and local-network multiplayer with an authoritative server.
No install needed: play against bots or hot-seat on the same device. Local-network multiplayer needs the server from this repo (npm run lan).
| Layer | Technology |
|---|---|
| Language | TypeScript 5 (strict), ES2022 |
| Build & dev server | Vite 5 |
| Rendering | Canvas 2D with a custom 3D projection, SVG |
| Audio | Web Audio API (oscillators and filtered noise) |
| Networking | WebSocket (ws) on the same HTTP port that serves the game |
| Testing | Vitest, jsdom, node-canvas, fuzzing with tsx |
| Deployment | GitHub Actions → GitHub Pages (tests gate every deploy) |
src/engine/ never touches the DOM, never calls Math.random and never reads the clock. Its interface is a reducer:
const state = createGame(players, { seed }); // deterministic match
const moves = getLegalActions(state, playerId); // everything they can do
const result = applyAction(state, moves[0]); // (state, action) -> state
Randomness lives inside the state (a seeded mulberry32 generator), so a whole match can be rebuilt from the seed plus the action list. That shape made bots and networking almost free: a bot is just a function that picks among legal actions, and the server re-validates every move with the same validate() the client uses — no duplicated logic. → src/engine/
Clients only propose moves. Before sending state to each player, the server strips what that player isn't allowed to see: other players' hands, other players' development cards, the deck order, and the RNG state, which would let anyone compute every future dice roll. Redacted fields are left empty rather than faked, and public counts travel separately. A test guards against any new secret field leaking. → src/net/redact.ts, server/room.ts
Instead of computing corners as floats and deciding with an epsilon whether two of them match, the board uses a doubled integer grid (X = 2q + r, Y = 3r): neighboring hexes produce literally the same pair for a shared corner, so vertex identity is plain integer equality. Harbors aren't hand-placed: the code walks the island's perimeter along single-hex edges and spaces them evenly, and the same code serves both board sizes. → src/engine/hex.ts, src/engine/board.ts
243 tests across 12 files. The most valuable ones aren't unit tests:
Requires Node.js 18+.
npm install
npm run dev # local match at http://localhost:5173
npm run lan # build and start the server for local-network play
npm test # 243 tests
npm run fuzz 500 # 500 bot matches checking invariants
npm run build # production build in dist/
Network scope: designed for a trusted network. Traffic is unencrypted and the only access control is the room code; don't expose it to the internet as-is.
src/
engine/ rules engine: pure TypeScript, no DOM, no external randomness
ai/ bot contract, random and heuristic bots, self-play loop
net/ protocol, state redaction and WebSocket client with reconnection
ui/ 3D scene, procedural art, sound and match controller
server/ HTTP + WebSocket on one port; rooms and the authoritative match
tools/ headless board-to-PNG rendering, fuzzer and bot tournaments
tests/ rules, board, bots, UI and networking
The full design document, covering every rules and UI decision, is in docs/DISENO.md.
Non-commercial personal project. Not affiliated with or endorsed by Catan GmbH; "Catan" is a trademark of its respective owners.