021 逆向き倉庫 教材QA（2026-10-05）

対象
5本の独立した教材: 01_walk.py / 02_push.py / 03_reverse.py /
04_validate.py / 05_goals_undo.py。
01〜03は原作Aの段階的導入、04〜05は原作B。最終版はBの1室のみ。
既存Site、原作ファイル、公開先の変更は行っていません。

実行環境
Python 3.12.14、Pyxel 2.9.9、Linuxのheadless環境。
Python 3.11の構文検査には合格しましたが、3.11ランタイム自体では実行していません。
新規インストールは行わず、指定済みの仮想環境を使いました。

1. 純粋ルール・配布内容検査
コマンド: .venv/bin/python -B -m unittest -v test_stages
結果: 30件合格。うち純粋ルール21件、stub UI 6件、配布内容3件。
詳細: qa/test_results.txt。

壁歩行、単箱の押し、逆向きの2箱、どちらの箱からも押せること、相棒の壁、
手前の壁、共通の行先、位置交換、人の旧・新位置への侵入、無効入力を検査。
拒否時は同じStateオブジェクトを返し、手数・押し数が不変。
05では既存履歴を持つ場合も拒否で増えないことを確認。
同番号の目標、成功時の停止、歩行/押しのundo、空履歴、undo後の分岐、resetを確認。

人の新位置への侵入は箱の旧位置交換にも当たるため、理由はcross。
人の旧位置への侵入は、正常な別々の2箱が逆向きに1マス動く条件では発生しません。
この防御分岐は、候補の直接検査と重複箱を持つ人工状態で検査しました。
人工状態を遊べる配置や実際のプレイ履歴だとは扱っていません。

2. 原作との一致・解けること
原作model.pyの読み取り専用コピーをUTF-8でロード。
コピーの本体SHA-256はqa/canonical_source.jsonに記録した原本と一致。
コピーにはMIT全文を追加。原作の場所にはpycacheを作成していません。

03: 原作Aの全1,122状態 × 4方向で受理/拒否、理由、人、箱、手数、押し数が一致。
04: 原作Bの全1,024状態 × 4方向で同項目が一致。
05: 原作Bの全1,024状態 × 4方向で同項目が一致。
05では通常の成功停止と、探索用terminal=Falseの両方を比較。
合計16,776組の原作比較。加えてBの1,024状態 × 4方向を別の占有座標式でも検証。

最終Bは全到達状態1,024、最短解DRULUR、受理6手、押し2回。
探索は深さ打切りなし。手数と履歴を除いた位置状態をキーにします。
成功後も物理的に行ける状態を数えるため、探索ではterminal=False。
縦押しを禁止した限定探索では解がなく、相棒を縦へ逃がす意味も確認しました。
C、原作UI全体、日本語字形、予告、部屋切替の同等性は対象外です。

3. stub UI（実OS入力ではない）
複数矢印の拒否、Rの後のキー解放待ち、最終解とZ、描画テキストのASCIIと画面内収容、
12描画フレーム後の保存・終了、自動入力列の決定性を検査。
stubでは実Pyxel描画や物理キーボードは使っていません。

4. 実Pyxelのheadless描画・合成操作
qa/render_runs.jsonの14回がすべて終了コード0。各12フレーム、PNGは512×568。
各段階のinitialとmeaningfulを、実際のPyxel 2.9.9描画で保存しました。

01_walk_meaningful.png: RRDU、人(3, 3)、MOVES 4。
02_push_meaningful.png: R、人(2, 3)、箱(3, 3)、MOVES/PUSHES 1。
03_reverse_meaningful.png: R、箱(3, 3)/(5, 3)、MOVES/PUSHES 1。
04_validate_meaningful.png: R拒否、BOX 2 WALL、初期位置とMOVES 0を維持。
05_goals_undo_meaningful.png: RDRULUR、初手拒否後に成功、MOVES 6/PUSHES 2/UNDO 6。
追加: 03の共通行先拒否、04のDRU受理、05の成功後Z、05のXリセット。

PNG14枚を実際に表示して目視確認済み（エージェントの画像確認）。
座標ラベル、人・番号付き箱、相棒の壁、赤い拒否候補、黄色の目標、緑の到着枠、
成功表示、undo/resetの数値を確認。文字や盤面の切れ、重なりは見つかりませんでした。
人によるゲームプレイや面白さの評価ではありません。

環境の音声デバイス初期化警告が出ましたが、全14回の描画・保存・終了は成功。
この教材には音声処理・サウンド再生がありません。音声動作を検証したとは扱いません。
初回のQA起動ではランナーが仮想環境pythonのリンクを実体へ解決してしまい、
システムPythonでPyxelが見つからず停止しました。仮想環境のパスを保持するよう
QAランナーを直し、全14回を再実行して成功。教材コードや環境の導入変更は不要でした。

5. 独立性・エンコーディング
qa/portability_results.json参照。
5本をそれぞれ日本語・空白入り一時フォルダーへコピーし、別cwdから各1本だけで
実Pyxel headless + 合成D入力 + PNG保存が終了コード0。
これら追加5回は独立性確認用で、配布PNG14枚とは別です。
CP932出力/Cロケールの模擬設定でも30テスト合格。全fixture・JSON・テキスト読込はUTF-8指定。
Windowsの実機検査ではありません。通常GUI、macOS/Windows、OSネイティブ入力、
ブラウザー、音声、人の操作性・面白さは未検証です。

6. 固定
qa/freeze_manifest.jsonに配布ファイルのSHA-256を記録。
READMEのコマンドは展開ZIPルートの.venvを使います。requirementsはpyxel==2.9.9。
5本は各々MIT全文を持ち、原作のフォント素材・画像・音声を必要としません。
