018 逆さの池
Python 3.11+ / Pyxel 2.9.9

同じ棚を、反対側から読む小さな探索アクション。
池を抜けると重力が逆向きになります。左右の操作と位置はそのまま。
跳んだ勢いを持ち込んで深く潜り、棚の縁を回って裏側へ着地します。
手作り３部屋。制限時間・残機・酸素・敵はありません。音なしで遊べます。

起動
Windows: Python 3.11以降を導入し setup.bat、次に play.bat。
macOS/Linux: bash setup.sh、次に bash play.sh。
フォルダ移動後は setup を再実行してください。仮想環境は同梱しません。
launch.sh は制作環境専用です。通常配布先では play を使います。

操作
タイトル: 左右で池を選ぶ、Spaceで開始、Escで終了。
左右: 加速／向きを変える。離すと少し滑り、地面では早く止まります。
Space: 足場にいるとき、重力と逆に跳ぶ。水面で浮いた状態からも跳べます。
Z: 直前の池横断の直前へ戻す。位置・速度・取得済みの宝も戻ります。
R: 同じ部屋を最初から。P: 停止／再開。N: 成功後に次の部屋。
F12: 制作確認用PNG。--baseline: 池横断で速度を０にする比較版。
画面切替・停止解除後はキーを一度すべて離してから押してください。

見方と目的
水面の点線が通り抜け口、白いハッチが固い岸です。
上側では下向き、下側では上向きの重力。人物横の矢印と足が向きを示します。
短い軌跡は実際に通った道です。未来の正解軌道ではありません。
１「裏側の月」: 月を取り、下棚の裏へ着地すれば成功。
２「頭の上は道」: 上の葉、下の月を取り、小屋の岸へ帰る。
３「同じ形、違う着地」: 短い下棚と奥の棚。葉と月を持ち帰る。
宝は取ると輪だけが残り、帰れる状態では小屋を枠で示します。
失敗回数や速さによる順位はありません。横断回数は振り返り用です。

池の近似
横断時に位置を鏡映しません。十分な勢いがあれば速度を保ち、重力だけ反転。
面から８px離れるまで再反転しません。弱い入水では、細かく振動させず、
水面上で半分沈んだ姿勢へ静止する近似を使います。位置は横断点のまま。
無料の加速はありません。浮いた状態はSpaceで現在の側へ跳び出せます。
そのため「浮いたらSpace」と「跳んで深く潜る」が両方成り立ちます。
池は現実の水を再現する物理模型ではありません。

技術
model.py は標準ライブラリだけの純粋モデル。描画は384×352、30fps。
世界192×128、人物の半幅３／半高４px。固定1/120秒を描画ごとに４回。
水平加速度100、速度上限36px/s。跳躍76、重力80px/s²、落下上限70px/s。
地上抵抗120／空中12px/s²。左右は画面基準のままです。
矩形を人物サイズ分膨らませて線分掃引。最初の接触を解決し、法線へ0.001px。
１刻み最大４接触。岸の衝突面はy=64の厚さ０、上／下で対称です。
画面上下の見える石壁は安全な境界。強制的に画面外へ出た場合も安全岸へ復帰。
同梱のNoto由来12px日本語字形はSIL OFL、その他はMIT。

再現と確認
python -m unittest -v
python solver.py
python experiment.py
python tools/test_portability.py
successful_replays.json は全３面の入力・軌跡・成功時ハッシュです。
cp932を既定にした模擬試験、日本語／空白パス、作業場所を変えた起動を確認。
Windows実機、音の実聴、人による面白さの確認は未実施。
Linux実GUIの確認範囲は QA.txt を参照してください。

映像
media/inverted_pond_automatic_silent.mp4 は実際のPyxel描画を自動操作した無音デモ。
人のプレイ映像ではありません。839枚をすべて１回ずつ30fpsに配置し、27.966667秒。
元GIFの25.17秒は1/100秒単位の丸めによるもの。MP4でゲームの30Hzへ補正しました。
PNG、replay.json、video_validation.json に出所とフレーム数を記録しています。
