ヨンモクは、1996年に logygames 様が考案した5x5盤面の2人対戦ボードゲームをオンラインで遊べるWebアプリケーションです。
- 各プレイヤーは6個の持ち駒を使用
- 1手につき「駒を打つ」または「駒を動かす」を選択
- 移動で相手の駒を挟むとオセロのように反転
- 駒を動かして4目を成立させると勝ち(駒を打って4目並べても勝ちにならない)、5目並べると負け
👉 公式ルール説明
- 🔐 ユーザー認証 - ID/パスワードでログイン・新規登録
- 🏠 ロビー - 複数ルームから選んで入室
- ⚔️ リアルタイム対戦 - WebSocketによる低遅延通信
- 🤖 CPU対戦 - 4段階の難易度(Easy/Normal/Hard/Strong)
- 👀 観戦機能 - 他プレイヤーの対局を観戦
- 💬 チャット - ルーム内でリアルタイムチャット
| 技術 | バージョン | 用途 |
|---|---|---|
| React | 19.2 | UIライブラリ |
| Vite | 7.2 | ビルドツール |
| React Router | 7.9 | ルーティング |
| Tailwind CSS | 4.1 | スタイリング |
| Socket.io Client | 4.7 | リアルタイム通信 |
| Radix UI | - | UIコンポーネント |
| Lucide React | - | アイコン |
| 技術 | 用途 |
|---|---|
| Cloudflare Workers | 実行環境(サーバーレス) |
| Durable Objects | ルーム状態・対局・チャット・WebSocket接続の保持 |
| Cloudflare D1 | ユーザー情報の永続化(SQLite) |
| WebSocket (生) | リアルタイム通信 |
| Web Crypto | パスワードハッシュ(PBKDF2)・セッション署名(HMAC) |
Worker 側は 外部ライブラリを一切使っていません(
wranglerのみ開発依存)。
Cloudflare Worker
├── 静的アセット → client/dist を同一オリジンで配信(SPAフォールバック)
├── /api/* → D1(ユーザー)/ Durable Object(ルーム情報)
└── /ws → WebSocket を Durable Object へ橋渡し
Durable Objects
├── RoomDurableObject … 1ルーム1インスタンス。座席・対局・チャット・CPU思考・接続を保持
└── LobbyDurableObject … 全ルームのサマリを集約し、ロビーへ配信
対局状態は Durable Object に永続化されるため、デプロイやアイドルを挟んでも失われません。 フロントとバックが同一オリジンなので、CORS 設定やクロスサイトCookieは不要です。
- Node.js 20.x 以上
- Cloudflare アカウント(無料プランで動作します)
git clone https://github.com/your-username/yonmoque.git
cd yonmoque
npm install
npm --prefix client install
npm --prefix worker installcd worker
# 1. データベースを作成し、表示された database_id を wrangler.jsonc に貼り付ける
npx wrangler d1 create yonmoque
# 2. テーブルを作成(ローカル用と本番用)
npx wrangler d1 execute yonmoque --local --file schema.sql
npx wrangler d1 execute yonmoque --remote --file schema.sql# クライアントをビルドしてから wrangler dev を起動
npm run devwrangler dev は D1 も Durable Objects もローカルでエミュレートするため、
Cloudflare へ接続せずに全機能を試せます。
フロントを Vite の HMR で開発したい場合:
npm run dev:vite # worker(8787) と vite(5173) を同時起動Vite が /api と /ws を 8787 へプロキシします。
デモユーザーを作るスクリプトはありません。画面から新規登録してください。
設定は worker/wrangler.jsonc の vars に記述します。
| 変数名 | 既定値 | 説明 |
|---|---|---|
ROOM_COUNT |
12 |
作成するルーム数 |
CPU_MAX_DEPTH |
20 |
CPU探索の深さの上限(難易度ごとの値を切り詰める) |
CPU_NODE_BUDGET |
30000 |
CPUが1手で探索するノード数の上限(難易度ごとの値を切り詰める) |
PBKDF2_ITERATIONS |
100000 |
パスワードハッシュの反復回数 |
SESSION_SECRET だけは Secret として登録します。
cd worker
npx wrangler secret put SESSION_SECRET # openssl rand -base64 32 などで生成ローカル開発では未設定でも動きます(開発用の既定値が使われます)。
Workers の 無料プランは 1リクエストあたり CPU 10ms の制限がありますが、 Durable Object の fetch とアラームにはこの制限がかかりません(2026-10-06 に実測。 docs/cloudflare-cpu-limit.md)。 CPU の思考はルームの Durable Object のアラーム1回の中で行うので、無料プランのままで動きます。
# クライアントをビルドして Worker ごとデプロイ
npm run deploy初回のみ、事前に以下を済ませておいてください。
npx wrangler d1 create yonmoqueで作ったdatabase_idをworker/wrangler.jsoncに反映npx wrangler d1 execute yonmoque --remote --file schema.sqlでテーブル作成npx wrangler secret put SESSION_SECRETで署名鍵を登録
main への push で自動デプロイするには、Cloudflare ダッシュボードで
Git 連携を設定します(Workers & Pages > yonmoque > Settings > Builds > Connect)。
| 設定項目 | 値 |
|---|---|
| Root directory | worker |
| Build command | npm run build |
| Deploy command | npx wrangler deploy |
| Production branch | main |
worker/package.json の build スクリプトがクライアントの lint とビルドを行い、
wrangler.jsonc の assets.directory(../client/dist)がその成果物を配信します。
lint かビルドが失敗した場合はデプロイされません。
GitHub App のインストールには、対象 organization の owner または GitHub Apps Manager 権限が必要です。
注意点
- ダッシュボードの Worker 名と
wrangler.jsoncのnameが一致していないとビルドが失敗します(どちらもyonmoque) - Durable Object を使う Worker のため、本番以外のブランチではプレビューURLが生成されません
- D1 のスキーマ変更は自動適用されません。 テーブル定義を変えたときは
npm run db:remoteを手動で実行してください
-
永続化の範囲
- ユーザー情報は D1(
usersテーブル) - ルーム・座席・対局・チャットは Durable Object のストレージ
- チャットは最終発言から30分で自動削除されます
- ユーザー情報は D1(
-
無料プランの上限
- Durable Objects: 10万リクエスト/日(WebSocketのメッセージも消費します)
- Durable Objects は SQLiteバックエンドのみ利用可能(設定済み)
-
Durable Object の配置
- 最初にアクセスされた地域の近くに作られ、以後そこに固定されます
| メソッド | パス | 説明 |
|---|---|---|
POST |
/api/auth/register |
新規登録 |
POST |
/api/auth/login |
ログイン |
POST |
/api/auth/logout |
ログアウト |
GET |
/api/me |
ログインユーザー取得 |
POST |
/api/me/nickname |
ニックネーム更新 |
GET |
/api/rooms |
ルーム一覧取得 |
GET |
/api/rooms/:roomId |
ルーム詳細取得 |
/ws?roomId=N(対局ルーム)と /ws?lobby=1(ロビー)の2種類の接続があります。
メッセージ形式は Socket.io 互換ではなく、以下の最小プロトコルです。
送信 { t: 'req', id, event, payload } // id を付けると ack が返る
受信 { t: 'res', id, payload } // ack
受信 { t: 'ev', event, payload } // サーバーからのプッシュ
| イベント | 方向 | 説明 |
|---|---|---|
room:join |
→ Server | ルーム入室 |
room:leave |
→ Server | ルーム退室 |
seat:take |
→ Server | 着席 |
seat:leave |
→ Server | 離席 |
game:ready |
→ Server | 準備完了 |
game:place |
→ Server | 駒を打つ |
game:move |
→ Server | 駒を動かす |
chat:send |
→ Server | チャット送信 |
cpu:configure |
→ Server | CPU設定 |
rooms:update |
← Server | ルーム一覧更新 |
room:state |
← Server | ルーム状態更新 |
game:state |
← Server | ゲーム状態更新 |
chat:new |
← Server | 新着チャット |
chat:cleared |
← Server | チャット履歴の期限切れ |
room:presence |
← Server | 在室人数の更新 |
room:forfeit |
← Server | 対局中の離脱(不戦敗) |
CPUはミニマックス法(アルファベータ枝刈り+反復深化)を使用して最善手を探索します。
| 難易度 | 深さの上限 | 1手のノード数 |
|---|---|---|
| Easy | 1 | 1,000 |
| Normal | 2 | 4,000 |
| Hard | 4 | 8,000 |
| Strong | 20 | 30,000 |
画面から選べるのは Strong だけです。
1手の思考はルームの Durable Object のアラーム1回の中で行い、探索量をノード数で
頭打ちにしています(時間で測れない理由は ai.js のコメント参照)。
Strong は手元の Mac で1手あたり CPU 30ms 前後、本番ではその約3倍です。
思考中はそのルームのほかのイベントが待たされるので、数百 ms 以内に収めています。
深さは固定ではなく上限です。浅い深さから順に読み、予算が尽きた時点で読み切れて
いる最良の結果を使うため、上限を上げても読み切れないだけで弱くはなりません。
上の値は CPU_MAX_DEPTH / CPU_NODE_BUDGET でさらに切り詰められます。
評価関数は以下の要素を考慮:
- ラインスコア: 連続した駒の数(盤上の4目は「1枚抜いて戻せば勝ち」の脅威なので高得点)
- 駒数スコア: 盤面上の駒の差
- 機動力スコア: 動かせる先の空きマス数と打てる手数の概算
手番のプレイヤーに合法手が無い場合は自動的にパスし、双方とも手が無ければ引き分けになります。
ヨンモクゲームの原作は logygames 様に帰属します。
- ゲームデザイン: logygames
- 開発: ScriptArts