dsh-plugin-gomoku

August 14, 2026 · View on GitHub

English | 简体中文

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; Esc closes 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:

  1. Start the server normally (dsh web). The relay binds 0.0.0.0:34567 (Windows Firewall rules dsh-gomoku-34567 / dsh-gomoku-3080 are added on install; re-add them in an elevated shell if missing).
  2. Machine A (the host): open the DSH GUI, click 联机对战连接创建房间. Note the room code (e.g. K7QX).
  3. 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.
  4. 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 加入房间.
  5. 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.x and have the partner use the full GUI at http://192.168.x.x:3080.

Relay

  • The relay listens on 0.0.0.0:34567 by default so LAN clients can reach it. Change the port with a loader config entry in cordis.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/info returns 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)

  1. node_modules\dsh-plugin-gomoku is a junction to <profile>/dsh-plugin-gomoku; the profile package.json pins it with "dsh-plugin-gomoku": "file:./dsh-plugin-gomoku" so pnpm won't prune it.
  2. cordis.patch.yml inserts the loader entry:
    - insert:
        - id: gomoku
          name: 'dsh-plugin-gomoku'
    
  3. 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 apply cordis.patch.yml changes live.)
  4. After that, editing lib/client.js hot-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 over https:// would need wss:// (not supported by the built-in relay) — serve DSH over http on the LAN.
  • If you edit lib/index.js, standalone/ or the profile wiring, restart the server; lib/client.js changes apply on the next page refresh.