021 逆向き倉庫
実装・機能検証済み／面白さは人による確認待ち

同じ紋章のふた箱を押すと、相棒は逆向きへ。
遠い箱の行先も見ながら、番号1と2を、それぞれ同じ番号の床へ運ぶ小さな箱押しです。
時間制限、残機、失敗の減点はありません。歩きも押しも一手ずつ戻せます。

起動
Windows: Python 3をインストールし、setup.batを一度実行。その後play.bat。
Linux/macOS: bash setup.sh、次にbash play.sh。
要Python 3.10以上、Pyxel 2.9.9（requirements.txtに固定）。ネット接続は初回導入に必要です。
launch.shは開発用の共有環境も探しますが、配布先ではsetup/playで独立して動きます。
Windows実機GUIとmacOSは未検証。Linux実画面、cp932既定の模擬環境、日本語・空白を含む配置先、任意cwdを別々に確認します。

操作
タイトル: 左右で3室を選び、SPACEで開始
矢印: 1マス歩く。箱へ歩くと押す。長押しは12フレーム後から4フレームごとに反復
同時に複数の矢印を押した場合: 動かない
SPACE: 押す前の行先予告を表示／非表示（隣の箱へ接した時）
Z: 歩行も含め、ひとつ前の確定行動へ戻す
R: 同じ室の最初へ
P: 説明と一時停止
M: 部屋選択へ
成功後N: 次の室へ
ESC: 終了、F12: 実行中の画面をmedia/manual_runtime.pngへ保存
開始、次の室、説明再開、初期化、部屋選択では、持ち越した操作キーを離してから次の入力を受け付けます。
固定の待ち時間はありません。離して押し直す素早い操作は受け付けます。

3室の違い
A「ふたつとも、一歩」: 1回押して逆向きの関係を見つける、短い導入。
B「向こうの壁を避ける」: 手前の行先は空いていても相棒の壁で止まる。まず縦にずらす理由がある。
C「手が届かない裏側」: 箱1の裏側へは立てない。上の通路を回り、箱2から遠い箱1を運ぶ。
Cは箱1だけを押せる制約では解がありません。長い通路や無意味な回収で手数を増やしていません。

逆向きの荷揚げ機
これは上下左右を反転する架空の機械です。天井の点線ケーブルと滑車がペアを示します。
箱は同じ色と紋章に、別々の番号。行先も同番号です。入れ替えは成功になりません。
白い矢印は押す箱、青い矢印は相棒、赤い枠と×は塞がれた行先。
箱を押す1手は「人の新位置、箱1の新位置、箱2の新位置」を旧状態から全部検査して一括で確定します。
どちらかが壁、同じ行先、相棒の旧位置、または人の旧／新位置へ入るなら全員そのまま。
箱同士の位置交換・すれ違いも禁止です。禁止入力で手数とundo履歴は増えません。
同時移動を順番に適用して一瞬だけ片方を動かすことはありません。
角へ押し込んでも自動敗北にはしません。Zで試し直せます。

技術の入口
model.py: 不変Level/State、純粋transition、原子的Result、Timelineとundo
solver.py: 歩行を含む全到達状態の幅優先探索。手数カウンターを状態キーから外す
app.py: 入力、番号・ケーブル・予告、Pyxel描画。モデルはPyxelをimportしない
test_model.py: 原子性、片側壁、衝突、不変量、独立座標オラクル、undo、B/Cの発見
 test_ui.py: 開始／次／停止の同時・時間差入力、速い押し直し、全表示文と文字幅

検証の再実行
python -m unittest -v
python solver.py
python experiment.py
python tools/test_portability.py
python app.py --headless --demo
python tools/export_video.py
比較実画面: python tools/capture_comparison.py --baseline と、引数なしの同スクリプト
文字を追加した場合: PillowとNoto CJKがある環境で python tools/build_font.py
実行用の文字画像データは同梱済みで、遊ぶ時にPillowやシステムの日本語フォントは不要です。

解の記録（自分で考えたい場合はここで読むのを止めてください）
U上 R右 D下 L左。A: R。B: DRULUR。C: UUURRRRRDD。
全探索: A 1,122状態／最短1手、B 1,024状態／最短6手、C 390状態／最短10手。
全探索に深さ打切りはなく、この有限の到達グラフ内で完了しています。最短解の唯一性は要求しません。

同梱映像
media/reverse_crates_automatic_silent.mp4: 実際のPyxel描画を自動入力で記録。無音。
同名GIFの全フレームを一枚ずつ順に30fpsへ変換し、秒数をGIFの10ms単位の丸めに依存させません。
media/timing_validation.json: フレーム数・尺・音声なしのffprobe検査
media/replay.json: 操作イベント、状態hash、モデルとアプリのsource hash
静止画はmedia/runtime_*.png、比較はexperiment_*.png。
音は実装していません。人による面白さ評価、Windows実機の動作評価とは区別しています。
