dsh-plugin-gomoku
August 14, 2026 · View on GitHub
Gomoku (五子棋) with LAN online play, vs-AI, AI-vs-AI and hot-seat modes — a client + host plugin for the DeepSeek Harness web UI.
What it does
- Adds a floating 五 button (bottom-right of the chat UI) that toggles a draggable Gomoku window, plus a second launcher entry in the sidebar footer actions slot.
- LAN online (联机对战): the host half (
lib/index.js) starts a self-contained WebSocket relay (RFC 6455, zero dependencies) on port 34567 (configurable). Two browsers on the same LAN (or the same machine) play against each other: one creates a room and gets a 4-character code, the other joins with that code. The relay is authoritative — it validates turns and moves, detects five-in-a-row and draws, and supports resign, rematch (colors swap each game), disconnect handling and a small chat. - vs AI (人机对战): a greedy pattern-scoring AI (attack + defense).
- AI vs AI (巅峰对决): watch two AIs play each other move by move, with replay speed control (slow / medium / fast) and pause/resume, so you can study openings and tactics at your own pace. The AI adds a small random jitter to near-equal moves, so every game is different — while it never gives up an immediate win or block.
- Hot-seat (本机双人): two players, one screen.
- Canvas board with wood texture, star points, last-move marker, win-line
highlight and hover ghost stone. The window is draggable;
Esccloses it. - Panel and inputs use fixed light-on-dark colors (independent of the host theme), so text is always readable.
How to play on the LAN
The DSH web GUI intentionally refuses --host 0.0.0.0 ("would expose remote
code execution to the network"), so the web UI stays on loopback. LAN play is
handled by the relay's standalone page on port 34567 instead — the partner
needs no DSH install and the GUI is never exposed:
- Start the server normally (
dsh web). The relay binds0.0.0.0:34567(Windows Firewall rulesdsh-gomoku-34567/dsh-gomoku-3080are added on install; re-add them in an elevated shell if missing). - Machine A (the host): open the DSH GUI, click 五 → 联机对战 →
连接 → 创建房间. Note the room code (e.g.
K7QX). - Tell your partner two things: the LAN address
http://192.168.x.x:34567/(find it via the relay's/info, or the panel's chat log after connecting) and the room code. - Machine B: open
http://<server-lan-ip>:34567/in any browser (this is the standalone game page served by the relay — no DSH GUI, no install), enter a nickname, click 连接, type the room code, click 加入房间. - Black moves first (the room creator). 认输 to resign; after a finished game 再来一局 requests a rematch — when both players agree, a new game starts with colors swapped.
Two players on the same machine can also play: one tab creates a room, a second tab (or another browser) joins with the code.
Optional: if you accept exposing the entire DSH web UI to the LAN, you may instead bind to the specific LAN interface with
dsh web --host 192.168.x.xand have the partner use the full GUI athttp://192.168.x.x:3080.
Relay
- The relay listens on
0.0.0.0:34567by default so LAN clients can reach it. Change the port with a loaderconfigentry incordis.patch.yml:- insert: - id: gomoku name: 'dsh-plugin-gomoku' config: port: 34568 http://<lan-ip>:34567/(and/play) serves the standalone game page;http://<lan-ip>:34567/inforeturns JSON (host, port, active rooms / players).- Rooms are in-memory only: a waiting room expires after 60 minutes, and a game in progress ends (the remaining player wins) if an opponent disconnects.
- Connections are long-lived: the server sends a keep-alive ping every 30 s (the browser auto-pongs), so an idle connection stays warm for hours; a connection that stops answering is reaped only after 90 minutes.
- No authentication — it is a LAN game relay; do not expose port 34567 beyond your trusted network.
Package layout
package.json dsh.client { platform: "web", inject: [@deepseek-ai/dsh-client-runtime] }
lib/index.js host half — WebSocket relay server (node:http + node:crypto, no deps)
lib/client.js browser half — hand-written bundle (no build step), the game
standalone/ gomoku.html — LAN client page served by the relay at /play
test/ engine + smoke + relay tests (run with `npm test`)
The client bundle is a factory registered via window.__ModuleLoader__.load
and uses only platform seed modules (react, react/jsx-runtime,
react-dom/client) plus the slots service, so it needs no bundler.
Install
For other DeepSeek Harness users
The plugin is a regular npm package with zero runtime dependencies (the host half uses only Node built-ins; the browser half uses the DSH platform's react seeds). Two ways to get it:
Option A — from the npm registry (once published):
dsh plugin --profile web add dsh-plugin-gomoku
Option B — directly from GitHub (no npm publish needed, repo must be public):
dsh plugin --profile web add github:Intelligent-GuoXin/dsh-plugin-gomoku
Then register the loader entry in the profile's patch layer
(~/.dsh/profiles/web/cordis.patch.yml):
- insert:
- id: gomoku
name: 'dsh-plugin-gomoku'
Finally restart the server (dsh web), refresh the browser page, and the
五 button appears bottom-right of the chat UI. LAN play needs the firewall
to allow inbound TCP on the relay port 34567 (and the standalone page is
served by the relay at http://<lan-ip>:34567/).
If you fork or rename the package, update the entry's name to match, and
dsh.client/exports["./client"] in package.json must stay intact for the
browser half to load.
On this machine (development install)
node_modules\dsh-plugin-gomokuis a junction to<profile>/dsh-plugin-gomoku; the profilepackage.jsonpins it with"dsh-plugin-gomoku": "file:./dsh-plugin-gomoku"so pnpm won't prune it.cordis.patch.ymlinserts the loader entry:- insert: - id: gomoku name: 'dsh-plugin-gomoku'- The host graph (and thus the relay) is loaded at server boot — restart
the server (
dsh --profile web/dsh web) once after installing, then refresh the browser page so the boot manifest includes the new row. (Patch hot-reload is unreliable mid-session: on the current instance the running server does not applycordis.patch.ymlchanges live.) - After that, editing
lib/client.jshot-reloads via the client HMR chain; the host half still needs a restart for changes.
Tests
npm test # or run them individually:
node test/gomoku-engine.test.mjs # pure engine: win detection, draw, AI
node test/gomoku-smoke.mjs # client bundle: mount, slots, cleanup
node test/gomoku-relay.test.mjs # host relay end-to-end: rooms, moves, win,
# rematch, resign, leave, disconnect, dispose
Notes / known limits
- Text inputs are uncontrolled so typing (including IME Chinese input) can never be reset by re-renders; nothing steals focus from them.
- Keyboard input is ignored while typing in the composer / any input.
- The relay is plain WebSocket (
ws://); a page served overhttps://would needwss://(not supported by the built-in relay) — serve DSH overhttpon the LAN. - If you edit
lib/index.js,standalone/or the profile wiring, restart the server;lib/client.jschanges apply on the next page refresh.