072 純粋モデル接続仕様
Python標準ライブラリのみ。model.pyは描画・入力・発音・ファイル保存を行いません。

Level（frozen dataclass）
key, title, initial_T, initial_H, outside_T, outside_H, sun, plants, ticks=8, target=3。
LEVELSはA/B/Cの順のtuple。public_id属性は072-A-v1等。idはkeyの別名。
数値ベクトルはtupleです。初期値・外気は0～12、sunは0～12の整数です。

State（frozen dataclass）
t, T[3], H[3], windows(int bit), g[2], log(tuple TickReport)。
windowsのbit0=左、bit1=中央、bit2=右。1が開、0が閉。
inputsはlog内のactionを取り出すtuple、switchesは待つ以外の回数、endedはt==8。
Stateは試験用の局所状態も作れるため、tとlogの長さの一致はコンストラクタでは強制しません。
実プレイの保存はinitialからstepを呼び、record/replay_recordで履歴の整合性を確認します。

基本関数
initial(level) -> State
step(level, state, action=-1, mode='base') -> 新State
  action=-1:待つ、0:左窓、1:中央窓、2:右窓を反転して1刻。
  不正なaction/modeはValueError。boolはactionの整数とみなしません。
  終了済みで正常なactionなら同じStateを返し、履歴を追加しません。
forecast(level, state, action=-1, mode='base') -> TickReport / 終了時None
  stepはこの関数をそのまま使います。予告だけならStateや時刻は変わりません。
forecast_state(...) -> stepと同じ仮適用後のState。
outcome(level,state) -> 'playing' / 'success' / 'incomplete'
  8刻前は成長3以上でもplaying。8刻終了時だけ最終結果です。
replay(level,actions,mode) -> 初期状態から再実行。9件以上の入力はValueError。
undo(level,state,mode) -> 最後の1判断を除いて再実行。初期状態はそのまま。
unchanged_forecast(level,state,mode) -> 以後待つだけで刻8まで仮実行。

mode
'base'は拡散あり。'compare'はAのみ拡散なし。B/CへのcompareはValueError。
画面側は状況/比較切替時にinitialへ戻すこと。undoと全予告にも現modeを渡してください。
既存のbase履歴に途中からcompareを混ぜる操作は公開ルールにありません。

TickReportの値
 t:実行後の刻(1～8)、action、windows:反転適用後のbit。
 outside_T/H:この刻の外気、sun:この刻の日差し。
 before_T/H/g:実行前。
 after_heat_T/H:加熱・土の蒸発後。途中の13以上も保存。
 vent_T/H:窓からの符号付き増減。プラス=外気から入った、マイナス=外へ出た。
 after_vent_T/H:換気後。
 flow_T/H:2境界の符号付き流量。正=左→中/中→右、負=反対。順序は左中、中右。
 before_clamp_T/H:同時拡散後、0～12制限前。
 final_T/H:制限後。growth:各鉢の0/1増加。final_g:成長の新累計。
 reasons:各鉢の日本語理由2件。「育つ」、または範囲外理由を「・」で連結。
 diffusion:比較版false、基本版true。

純粋な補助関数
approach(value,outside):最大2で近づく。制限なし。
diffusion(triple): (同時移動後triple,符号付き流量pair)。制限なし。
growth_reason(plant,T,H):範囲内なら「育つ」。

正規化記録
normalized_json(value): UTF-8想定、キー順ソート、区切り空白なし、NaN禁止。
state_payload(level,state,mode):ルール版、公開ID、mode、全Level、全State、結果。
state_digest(...): state_payload正規化JSONのUTF-8 SHA-256。
record(...): state_payloadにsha256を追加。時刻・PC依存パス・乱数は含めません。
replay_record(payload):公開盤面、入力、全中間ログ、終了状態、sha256を再照合。
  誤った盤面・改ざん・規則混在はValueError。ファイルI/Oは呼出側が行います。

開発用コマンド
python -m unittest test_model test_experiments -v
python experiments.py --output policy_results.json
python verify_policies.py --input policy_results.json --output oracle_results.json
後二つだけが明示したパスへ生成物を書きます。テストは元資料へ書き戻しません。
実行ディレクトリは072_shared_greenhouseを想定します。
