024 まちがい時計
実装・機能検証済み／面白さは人による確認待ち

文字盤を外された時計工房。残っているのは「どちらが何時間進むか」という検査札です。
整備規格と札を合わせ、正確である可能性のある時計を全て選ぶ、3票の小さな相対推理パズルです。
絶対時刻を当てる問題ではありません。可能性が二つ残れば、その二つを答えにします。

起動
Windows: Python 3.10以上を入れ、setup.batを一度実行。その後play.bat。
Linux/macOS: bash setup.sh、次にbash play.sh。
Pyxel 2.9.9固定。初回導入のみネット接続が必要。文字画像は同梱済みです。
配布先での起動はこのフォルダーだけで完結します。launch.shは開発用共有環境への予備経路も持ちます。
WindowsのBATはASCIIのみ・パス引用符付き・失敗時にpauseします。
Windows実機とmacOSは未検証。Linux実画面、cp932既定模擬、日本語・空白パス、任意cwdを確認しました。

操作
タイトル: 左右で票を選び、SPACEで開始。
矢印: 時計カーソル移動。上下/左右は前後の時計へ。複数矢印の同時押しは無効。
SPACE: 答えの候補チェックを付ける／外す。長押し連打なし。
ENTER: 現在の候補集合を提出。誤答の残機・減点・待ち時間なし。
X: カーソルの時計を「もし正確なら」と仮置き。同じ時計で再度Xは非表示。
  仮置きは青い「仮」ラベルで区別。答えのチェックと履歴を変えません。
Z: 最後の候補チェック変更を戻す。提出や仮置きは履歴を増やしません。
  成功後にも戻せます。戻した後の別の選択は新しい分岐になります。
R: 同じ票を最初から。
P: 説明／停止。M: 票選択。成功後N: 次の票。ESC: 終了。
F12: media/manual_runtime.pngに実行中画面、media/gui_events.jsonに操作記録を保存。
開始・次の票・説明復帰・リセット・票選択では操作キーを離してから新しい入力を受けます。
固定の待ち時間はありません。矢印だけ12フレーム後から4フレームごとにカーソル移動を反復します。
同フレームの入力優先順はM、R、P、Z、成功時N、X、ENTER、SPACE、矢印です。

全ての規則
ずれは整数の時間差。12時間の循環や昼夜またぎはありません。
札「BはAより1時間進む」は、ずれB−ずれA=1。
全時計のずれは票の規格 -m..+m 以内で、正確（ずれ0）はちょうど1台。
検査札は全て正しく、全時計は札でつながっています。隠れた正解時計や実時刻は保存していません。
全制約を満たす割当のどれかで正確になれる時計を、過不足なく選べば成功です。
2台を候補に選ぶことは、2台が同時に正確だという意味ではありません。
Xの仮置きでは、差の鎖から全時計のずれを導きます。規格外の数値と規格表示を線で結び、理由を表示。
誤答は候補に入れた不成立の時計を1台示すか、「まだ成立する時計がある」と表示します。
未選択の正解を全て自動で表示・登録することはありません。

3票の違い
A「真ん中の時計」: 1時間ずつの差をつなぎ、端の時計を仮定した時の矛盾を読む。
B「離れた札をつなぐ」: 時計の並びはC,A,D,B。画面の中央ではなく、札の端点をたどる。
  最初の2枚だけでも候補Bまで絞れます。3枚目D−C=1はCのずれを確定する任意の検算です。
  全札の閲覧やXの操作はクリア条件ではなく、分かればすぐ提出できます。
C「まだ二つある」: 規格内に収まる仮説が二つ。根拠のない一択へ絞らず、両方を残す。

技術の入口・再検証
model.py: 不変Level/State、純粋transition、候補選択Timeline、undo。
  全割当を有限列挙するassignmentsと、差の辺から伝播するhypothesisは別実装。
solver.py: 最大5^4=625割当を全検査し、候補ごとの成立割当または矛盾を保存。
app.py: 入力とPyxel描画。480×480、30Hz、音なし。中心のIDは大きい字形でも表示。
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
python tools/capture_comparison.py --baseline
フォント再生成のみPillowとNoto CJKが必要: python tools/build_font.py
実行時は追加フォント不要。日本語文字画像のライセンスはassets/NOTO-LICENSE.txt。

解の記録（自分で考える場合はここで読むのを止めてください）
A: Bだけ。ずれ[A,B,C]=[-1,0,+1]。27割当を全列挙し成立1個。
B: Bだけ。ずれ[A,B,C,D]=[-2,0,+1,+2]。625割当を全列挙し成立1個。
C: BとC。[-1,0,+1,+2] と [-2,-1,0,+1] の二つ。625割当を全列挙し成立2個。
どの割当も0は1個。Cの2割当は全時計のずれを一律1時間変えても差が保たれる例です。
全40候補集合を検査し、各票で唯一の正しい候補集合が採点されることを確認しました。
正しい時計そのものが全票で一意とは主張していません。

映像
media/wrong_clocks_automatic_silent.mp4: 実際のPyxel描画、自動入力、無音。
同名GIFの全718フレームを一枚ずつ順に30fpsへ変換。尺23.933333秒。
GIFの丸められたdelayを映像の時計にせず、各元フレームをn/30秒へ対応させています。
media/timing_validation.jsonに元フレームhash、変換方法、ffprobeの枚数・尺・音声なし検査。
media/runtime_*.pngは実描画、native_*.pngはLinuxのキー操作確認時の実描画。
media/replay.jsonとgui_events.jsonはそれぞれ自動／ネイティブ操作ログとソースhash。
エージェントの操作確認は人の試遊ではありません。この作品の人による面白さ確認はまだです。
