ヨンモクは、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 |
2 |
CPU探索の最大深度(上限として作用) |
CPU_TIME_LIMIT_MS |
8 |
CPU探索の制限時間(上限として作用) |
PBKDF2_ITERATIONS |
100000 |
パスワードハッシュの反復回数 |
SESSION_SECRET だけは Secret として登録します。
cd worker
npx wrangler secret put SESSION_SECRET # openssl rand -base64 32 などで生成ローカル開発では未設定でも動きます(開発用の既定値が使われます)。
Workers の 無料プランは 1リクエストあたり CPU 10ms の制限があります。 既定値(深さ2 / 8ms)はこの枠に収まるよう絞ってあります。
Workers 有料プラン($5/月) に切り替えると CPU 時間が 30秒 まで伸びるので、
wrangler.jsonc を次のように変えるだけで本来の強さに戻ります。
# クライアントをビルドして 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はミニマックス法(アルファベータ枝刈り)を使用して最善手を探索します。
| 難易度 | 探索深度 | 制限時間 |
|---|---|---|
| Easy | 2 | 120ms |
| Normal | 3 | 240ms |
| Hard | 4 | 420ms |
| Strong | 5 | 700ms |
上の値は「難易度ごとの上限」で、実際には CPU_MAX_DEPTH / CPU_TIME_LIMIT_MS で
さらに切り詰められます(無料プランの既定は深さ2 / 8ms)。
評価関数は以下の要素を考慮:
- ラインスコア: 連続した駒の数(盤上の4目は「1枚抜いて戻せば勝ち」の脅威なので高得点)
- 駒数スコア: 盤面上の駒の差
- 機動力スコア: 動かせる先の空きマス数と打てる手数の概算
手番のプレイヤーに合法手が無い場合は自動的にパスし、双方とも手が無ければ引き分けになります。
ヨンモクゲームの原作は logygames 様に帰属します。
- ゲームデザイン: logygames
- 開発: ScriptArts