092 海底図書館 / 日本語Pyxel五段階教材

開く順序
1. LESSON.md: 概念、連続した実コード、期待結果、よくある間違い、編集演習
2. 01_swim.py: 9×5、入口(0,2)、0.2秒刻、空手2刻で一マス、題名の発見
3. 02_one_book.py: 所有者0/1/2/3/4、一冊だけ運ぶ、床へ置く、三冊返却
4. 03_open_currents.py: 棚を抜くと4刻ごとの流れ、床本も移動、開示済み水路の予告
5. 04_robot_oxygen.py: 原作全モデル。巡回、手渡し、酸素、救助
6. 05_checkpoint_save.py: 原作Controller。保存・読込・停止・全世界巻戻し

必要環境
Python 3.10以上とPyxel 2.9.9。実検証はPython 3.12.14/Pyxel 2.9.9。
必要時: python -m pip install -r requirements.txt
起動例: python 01_swim.py
それぞれのPython一個で動きます。別の画像、データ、モデルPythonは不要です。
別フォルダへ移動・改名可能。Pyxelを入れた同じPythonで実行してください。

変更の反映
エディタで保存→ゲームを終了→同じコマンドで再実行。
Rはゲームの新規開始であり、コード再読込ではありません。
01〜04はEscで終了。05はEsc→全解放→Enterで確認して終了。
日本語を含むファイルはUTF-8。標準の文字コードを仮定して読みません。

操作
長押し矢印で泳ぐ。空手2刻、本所持3刻。一刻は0.2秒。
左右同時は相殺。斜め入力も移動0。待ち時間は動かない間もたまります。
02以降: Zは同マスの床本/隣の棚から取得、入口へ返却。Xは今の空床へ置く。
04以降: Zは隣の空載ロボットへ手渡し。ただし入口は自分の返却優先。
Pは停止/再開。全てのモード切替後、一度全キーを離します。
01〜04: 1/2/3で館A/B/Cへ初期化。03〜04: Bで水流ON/OFFし初期化。
05: Space/Enter開始。1/2/3とBは表紙のみ。H説明、M表紙確認、Rやり直し確認。
05の確認: Enter実行、X取消。停止中Backspaceは直前の空気時点へ巻戻す。
05: S保存、L読込して停止、P再開、F12画像保存。停止中も地図が読めます。

原作に沿った数量と優先順
三冊・一冊容量・9×5・酸素60。追加ノルマはありません。
移動→本操作→4刻水流→ロボット→酸素消費→空気補給または救助。
空気に到達した刻は、酸素が1から0になっても60へ戻り救助されません。
救助は持本をその場へ残し入口へ戻る処理。開栓や探索、返却は残ります。
巻戻しは全Stateを空気時点へ戻す処理。ロボット、書架、時刻、履歴も戻ります。
水流の予告は発見済み書架の全範囲のみ。紫=開栓したら流れる、緑=現在有効。
ロボットだけが持つ未知の本は返却するまで題名を隠します。
01〜03は明示された省略版です。Stateなど将来の足場を先に定義しています。
04/05のmodel定義・定数、05のController/KEYS/DIRを原作とAST照合します。

保存・出力
05の既定セーブは起動元作業フォルダ/092_save.json。Sで更新、Lで読込。
保存可能なのは入口、空気鐘、結果。任意の海底位置では保存せず停止します。
例: python 05_checkpoint_save.py --save-file "記録/試行.json"
親フォルダを先に作ってください。壊れた保存や未知版は元ファイルを消しません。
検証PNG/状態JSONは既存ファイルを拒否。相対パスはPyxel初期化前に起動元へ解決。

再現コマンド
凍結検査を保つため、検査出力は../で配布フォルダの外へ置きます。
python -B test_stages.py
python -B qa/exercise_checks.py
python -B qa/cp932_check.py
python -B qa/runtime_checks.py "../新しい QA 出力"
python -B qa/render_boundaries.py "../新しい 境界 出力"
python -B qa/playthrough_check.py "../新しい 通し 出力"
python -B qa/relocation_check.py "../新しい 移動先"
python -B qa/reference_check.py "原作092フォルダ" "../新しい照合.json"
python -B qa/verify_freeze.py

画像を一枚出す例
python 05_checkpoint_save.py --headless-smoke --demo-checkpoint --screenshot "初回.png" --state-json "初回.json"
CLIは無効な組合せ、親フォルダなし、拡張子違い、既存出力をPyxel import前に拒否。
--boundaryは狙った境界用。open-current/comparisonは03以降、
rescue/hidden-robot/terminalは04以降、undoは05。端点に人工fixtureも使います。
原作解法を使うterminalは初期状態と意味入力列からの再生です。

配布内容
lessons.json: 5行の教材データ。lesson_meta.json: 規則・操作・相違・検証限界。
diffs: 隣接4差分。exercises: 印刷された編集をコピーに施し実行する5演習。
test_stages.pyとqa: 検証スクリプト、実測ログ、実画面、凍結情報。
FROZEN_SHA256.json: 自分自身とキャッシュ以外の全ファイルのSHA-256一覧。
編集すると凍結検査に失敗するのは正常です。学習前の配布状態の確認に使います。

権利と制限
原作LICENSE/LICENSE.txtをそのまま収録し、各単独PythonにもMIT全文を記載。
Noto/Pyxel原文通知はassets。NotoグリフをこのASCII画面では使いません。
原作日本語題名はそのままソースに残し、画面表示だけ英語にしています。
原作の継続media記録、日本語フォント、泡の演出は教材では省略。
実PyxelのLinux headlessと合成入力を確認。物理キー、Windows/macOS GUI、
ブラウザー、聞こえる音、人の学習効果は未確認。ゲームは無音。
狙った境界と有限入力列の検証であり、全操作の証明や最短解の主張ではありません。
詳細はqa/REVIEW.mdを参照してください。
