Skip to content

feat(scratch-vm): 甲子園 実ゲームサーバー通信モード(RemoteClient)[Draft/最後にマージ]#746

Draft
takaokouji wants to merge 8 commits into
developfrom
feature/issue-741-koshien-remote-client
Draft

feat(scratch-vm): 甲子園 実ゲームサーバー通信モード(RemoteClient)[Draft/最後にマージ]#746
takaokouji wants to merge 8 commits into
developfrom
feature/issue-741-koshien-remote-client

Conversation

@takaokouji

Copy link
Copy Markdown

⚠️ Draft / 最後にマージ。本ブランチは #742(#739) + #745(#740) の上に構築しています(ベースブランチ提案どおり)。#742#745 が develop にマージされたら develop に rebase し、本 PR の差分は RemoteClient 分のみになります。

Summary

甲子園拡張機能が、設定された実ゲームサーバーと HTTP 通信して実際にゲームを進められるようにします(#741)。サーバー未接続時は固定値の MockClient#739)にフォールバックするため、サーバーにつながなくても AI を作成できる状態を維持します。Epic #738

アーキテクチャ

ブロックメソッド (index.js)
        │
        ▼
  KoshienClient 互換インターフェース (duck-typed)
        ├── MockClient    … 固定値(未接続時 = #739)
        └── RemoteClient  … 実ゲームサーバーへ HTTP(接続時 = 本 PR)

index.js のコンストラクタが runtime.getKoshienRemoteOptions() から endpoint を取得できれば RemoteClient、なければ MockClient を選択します(追加のみ・#739 の挙動は不変)。

Changes Made

  • remote-client.js: ai_lib.rb(参照クライアント)の HTTP プロトコルを JS へ移植。
    • 非同期ネットワーク: connectGame / getMapArea / moveTo / setDynamite / setBomb / setMessage / turnOver。応答から my_map / x / y / goal / enemy / other_player を更新。
    • 同期レポーター: map / mapAll / mapFrom / targetCoordinate はキャッシュ状態を読む(ai_lib と同様、get_map_areamy_map を埋め map(...) がそれを参照)。
    • calcRoute はダイクストラ(壁=不可・水=重・未探索=より重)を移植、locateObjectsmy_map 走査。
    • fetch は注入可能(テスト用)。
  • index.js: バックエンド選択(RemoteClient / MockClient フォールバック)。
  • テスト: test: 甲子園クライアント契約テスト基盤(実サーバーのゴールデン記録 + jest) #740 のゴールデン fixture(実サーバー応答)を注入 fetch で RemoteClient に流し込み、実応答を正しく解釈することを検証(契約テスト, tap 25 assert)。バックエンド選択も検証。

Test Coverage

  • koshien_remote_client.js(tap): getMapArea(15x15+enemy) / connectGame(x,y,goal) / turnOver(unwrap) / calcRoute(経路・壁回避) / locateObjects / mapAll-mapFrom。
  • extension_koshien.js: RemoteClient/MockClient の選択。
  • 全 koshien スイート 154 assert pass、lint 緑。

まだやっていないこと(後続)

本 PR は RemoteClient 本体(プロトコル移植 + ゴールデン契約テスト) が中心です。以下は別途設計が必要なため本 PR には含めません:

  • GUI/接続フロー連携: runtime.getKoshienRemoteOptions() を供給する UI(endpoint 設定、player side/uuid、接続/未接続の判定)。
  • CORS / ホスティング: 実サーバーは現状 127.0.0.1:3000 で CORS 未対応。ブラウザ直叩きには CORS 許可 or プロキシが必要。
  • 接続失敗時のフォールバック: connectGame 失敗時に MockClient へ自動降格する挙動。
  • エディタ上での 2 人対戦ゲームループ(turnOver のブロッキング等)の実機検証。

Definition of Done

  • RemoteClient が実サーバー応答(ゴールデン)を正しく解釈(契約テスト緑)
  • endpoint 設定で RemoteClient、未設定で MockClient フォールバック
  • lint / tap 緑
  • CI green
  • GUI 連携 + CORS 対応(後続)
  • 実サーバーに対する end-to-end 動作確認(後続)

Related Issues

Refs #741 / Epic #738。ベース: #742 / #745

🤖 Generated with Claude Code

takaokouji and others added 4 commits June 13, 2026 03:22
甲子園拡張機能のブロックがクリック/実行時に意味のある値を返すようにした。
KoshienClient をインターフェース化し、未接続時用の固定値クライアント
MockClient を実装してブロックメソッドを接続した(将来の RemoteClient と
差し替え可能な構造)。

- map=0(空間), mapAll=15x15 全空間文字列, mapFrom=マップ文字列をパース
- targetCoordinate: player=1:1 / goal=13:13 / other_player=enemy=7:7
- calcRoute/calcGoalRoute/locateObjects は指定リスト変数へ実際に書き込む
- getMapArea/moveTo/setItem/turnOver/setMessage は安全な no-op

Refs #739

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…al server

過去にゲームサーバー再実装が「仕様不明確」で頓挫した反省を踏まえ、実ゲームサーバーの
挙動をゴールデン JSON として記録し、クライアントが従うべき契約をテストで固定する基盤を
追加する (#740)。

- tmp/smalruby-koshien/game_server (Ruby 3.1) の in-process API ドライバ
  (SpecHelper::Server) を流用した記録ドライバで、決定的シナリオ (SK_RANDOM_SEED=1) を
  実行し各 API の返り値を fixture 化:
  - move_basic: move_to は予約で、座標は turnTransition 後に確定 (before [1,1] → after [2,1])
  - get_map_area: getMapArea は 15x15 の自分マップ (未探索 -1) + enemy + other_player を返す
  - two_actions: 1 ターン 2 行動 (move + getMapArea)
- test/unit/extensions/koshien_golden_contract.js (tap): 上記の実挙動を検証し、
  将来の RemoteClient (#741) が replay できる契約の基盤とする。
- 記録ドライバ/サーバー起動手順は notes/koshien に整備 (git 管理外)。

Refs #740 / Epic #738

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…th mock fallback

甲子園拡張機能が、設定された実ゲームサーバーと通信して実際にゲームを進められるよう
にする (#741)。未接続時は固定値の MockClient にフォールバックするため、サーバーに
つながなくても AI を作成できる。

- remote-client.js: ai_lib.rb の HTTP プロトコルを JS へ移植した RemoteClient。
  - 非同期: connectGame / getMapArea / moveTo / setDynamite / setBomb /
    setMessage / turnOver。返り値で my_map / x / y / goal / enemy / other_player を更新。
  - 同期レポーター: map / mapAll / mapFrom / targetCoordinate はキャッシュ状態を読む
    (ai_lib と同じく get_map_area が my_map を埋め、map(...) がそれを参照)。
  - calc_route はダイクストラ (壁=通行不可、水=重い、未探索=さらに重い) を移植。
    locate_objects は my_map 走査。
- index.js: runtime.getKoshienRemoteOptions() が endpoint を返せば RemoteClient、
  なければ MockClient を使う (オフラインで AI 作成可能)。
- テスト: #740 のゴールデン fixture (実サーバー応答) を注入 fetch で流し込み、
  RemoteClient が実応答を正しく解釈することを検証 (契約テスト)。バックエンド選択も検証。

ベース: #739 (MockClient) + #740 (ゴールデン) の上に構築。#741 は最後にマージ予定。

Refs #741 / Epic #738

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

takaokouji and others added 3 commits June 13, 2026 06:18
…llback

接続できれば実サーバー、できなければ Mock 値、という #741 の中核挙動を実装。

- コンストラクタ: MockClient を常に保持し、endpoint 設定時のみ RemoteClient を併設。
  起動時は RemoteClient (あれば) を使う。
- connectGame: remote のときは実サーバーへ非同期接続し、失敗 (到達不可/CORS/エラー)
  したら MockClient へフォールバック (つながなくても AI が動く)。mock のときは従来どおり同期接続。
- getMapArea / setItem / turnOver が client の結果 (remote では Promise) を返すように
  変更し、シーケンサが await して順序を保てるようにした。
- テスト: 接続成功で RemoteClient 維持 + 実座標、接続失敗で MockClient へフォールバック +
  固定値。tap 全 110 assert pass。

Refs #741

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
接続先は「プログラム」ではなく「Smalruby の設定」で行う方針 (#741) の土台。
接続設定 (接続先URL / プレイヤー側 / ゲームコード) を localStorage に保存し、
VM 拡張が読む contract `vm.runtime.getKoshienRemoteOptions()` を供給する。

- src/lib/koshien-connection.js:
  - load/saveKoshienConnection: localStorage 永続化 (side は 1/2 に正規化)。
  - buildKoshienRemoteOptions: endpoint 未設定なら null (= Mock)、設定時は
    {endpoint, side, gameCode, playerId(自動生成・安定)} を返す。
  - wireKoshienRemoteOptions(vm): runtime に getter を差し込む (拡張が読む)。
  - testKoshienConnection(endpoint): 設定モーダルの「接続テスト」用の到達性確認。
- 単体テスト (jest, 5 件) pass。

後続 (本 PR or 別 PR): 甲子園メニューに「接続設定」項目 + 設定モーダル UI
(URL/側/コード/テストボタン) + wireKoshienRemoteOptions の起動時呼び出し。

Refs #741

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
接続先を「Smalruby の設定」で行う方針 (#741) の UI を実装。甲子園拡張機能を有効にすると
表示される甲子園メニューに「接続設定」項目を追加し、設定モーダルで接続先 URL / プレイヤー側
(1/2) / ゲームコードを設定する。

- src/components/koshien-settings-modal/: 設定モーダル (URL/側/コード + 接続テストボタン)。
  保存で localStorage 永続化 + wireKoshienRemoteOptions(vm) を再実行。本文に不透明背景
  (modals ルール準拠)。
- reducers/modals.js: koshienSettingsModal の open/close を追加。
- menu-bar.jsx: 甲子園メニューに「接続設定」項目 + ハンドラ + Redux 接続。
- components/gui.jsx, containers/gui.jsx: モーダルのマウントと state/dispatch マッピング。
- vm-manager-hoc.jsx: VM 初期化時に wireKoshienRemoteOptions(vm) を呼び、起動時から
  保存済み設定を拡張が読めるようにする (Smalruby マーカー付き)。
- locales (ja/ja-Hira/en) + prettier ホワイトリスト + マーカー一覧を更新。
- 単体テスト: モーダルのフィールド描画 + 保存で localStorage 永続化 + runtime getter 設置。

注: 設定変更は次回の拡張機能ロード (リロード) 時に反映される (拡張は構築時に一度読む)。

Refs #741

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@takaokouji

Copy link
Copy Markdown
Author

追加実装: 接続フォールバック + CORS + 接続設定 UI

#741 を以下まで前進させました(すべて push 済 / 単体テスト緑 / lint 緑)。

1. 接続フォールバック(VM)

connectGame で実サーバー接続を試み、失敗(到達不可/CORS/エラー)したら MockClient へ降格(つながなくても AI が動く)。getMapArea/setItem/turnOver は結果(Promise)を返却しシーケンサが await。tap 110 assert。

2. ゲームサーバー CORS(サーバー側パッチ)

game_server/lib/api/base.rb に CORS + OPTIONS(localhost / *.smalruby.app / *.smalruby.jp 許可)→ patches/game-server-cors.patch(このスレッドに添付)。ブラウザ拡張が実サーバーへ直接通信できる。

3. 接続設定 UI(GUI)— 「プログラムではなく Smalruby の設定で」

  • src/lib/koshien-connection.js: 接続設定(URL/側/コード)を localStorage 保存、vm.runtime.getKoshienRemoteOptions() contract を供給、testKoshienConnection(到達性確認)。
  • 甲子園メニュー(拡張有効時に表示)に「接続設定」項目を追加 → 設定モーダル(URL / プレイヤー側(1/2) / ゲームコード + 接続テストボタン + 保存)。
  • vm-manager-hoc で VM 初期化時に getter を差し込み、起動時から保存済み設定を拡張が読む。
  • 単体テスト(モーダル + connection lib)緑。

残(後続)

#741 は引き続き 最後にマージ予定(#742/#745 マージ後に develop へ rebase)。

@takaokouji

Copy link
Copy Markdown
Author

✅ ブラウザ DoD 確認: 接続設定モーダル(更新プレビュー・CI 全 green 後)

https://smalruby.jp/smalruby3-editor/feature/issue-741-koshien-remote-client/ を Playwright MCP で検証:

  1. 甲子園拡張機能をロード → メニューバーに甲子園メニューが出現。
  2. 甲子園メニューを開くと項目に 「接続設定」(「AIを試す」と「参加申し込み」の間)が追加されている。
  3. 「接続設定」クリック → 設定モーダルが開く(接続先URL / プレイヤー側 / ゲームコード / 接続テスト / 保存 が全て表示)。
  4. URL=https://stg.koshien.example/3000 / 側=player2 / コード=game-abc を入力 → 保存
    • localStorage["smalruby:koshienConnection"] = {"endpoint":"https://stg.koshien.example/3000","side":2,"gameCode":"game-abc"} を確認。
    • モーダルが閉じる。
    • vm.runtime.getKoshienRemoteOptions(){endpoint, side:2, gameCode, playerId} を返す(= VM 拡張がこれを読んで RemoteClient を選択する経路が成立)。
  5. 検証後、テストデータは localStorage から削除(プロファイルを汚さない)。

→ 「メニュー → 設定モーダル → 入力/保存 → runtime contract」の一連が動作。DoD のブラウザ確認を満たしました。

…oteClient HTTP test

test:unit は `tap ./test/unit/*.js`(再帰なし)のため、test/unit/extensions/ 配下の
koshien テストは CI で実行されていなかった。mesh と同じく top-level へ移動して確実に走らせる。

- test/unit/extensions/koshien_{golden_contract,remote_client}.js を test/unit/ へ移動
  (require パス修正)。
- RemoteClient の実 HTTP 経路を検証する統合テストを追加:
  - test/fixtures/koshien/mock-server.js: CORS 付き Koshien API モックサーバー
    (canned 応答, CLI 起動可: `npm run koshien:mock-server [port]`)。
  - test/unit/koshien_remote_client_http.js: in-process モックサーバーに対して
    RemoteClient を実 fetch で駆動(connect/getMapArea/move/turnOver)。
- prettier ホワイトリスト / マーカー一覧 / package.json スクリプトを更新。
- 全 koshien vm テスト 89 assert pass、lint 緑。

Refs #741

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant