# c3_judge_lines.py — 基準データ v2（段階B・cs:144 の18回）で、自動の判定の線のたたき台を確かめる
# 読むだけ。出力は標準出力（c3_judge_lines_output.txt に保存する）
# 使い方: cd /Volumes/KIOXIA/r2r2r-data/service_sim/unity && python3 stageB_cs144/c3_judge_lines.py
import csv, glob, json, statistics as st, collections

RUNS = sorted(glob.glob('20261025_stageB_cs144_*'))
LEDGER = 'unity_ledger_v1.csv'

def q(xs, p):
    if not xs: return None
    xs = sorted(xs); k = (len(xs) - 1) * p
    f = int(k); c = min(f + 1, len(xs) - 1)
    return round(xs[f] + (xs[c] - xs[f]) * (k - f), 1)

def rows(r, name):
    return list(csv.DictReader(open(f'{r}/{name}.csv', encoding='utf-8')))

print(f'回数: {len(RUNS)}')

# 1. ロボットの止まり（stops.csv）: 1回の止まりの長さ
allstop = []; stuck_dur = []; cause_long = collections.Counter()
per_run_max = []
for r in RUNS:
    ds = []
    for s in rows(r, 'stops'):
        d = float(s['duration_s'] or 0); ds.append(d); allstop.append(d)
        if s['resolved'] == '動けなくなった': stuck_dur.append(d)
        if d >= 30: cause_long[s['cause']] += 1
    per_run_max.append((r.split('cs144_')[1], round(max(ds), 1) if ds else 0, sum(d >= 30 for d in ds), sum(d >= 60 for d in ds)))
print('\n## 1. ロボットの止まり（1回の長さ・秒）')
print(f'件数 {len(allstop)}  p50 {q(allstop,.5)}  p90 {q(allstop,.9)}  p99 {q(allstop,.99)}  最大 {round(max(allstop),1)}')
for th in (10, 20, 30, 45, 60, 120):
    print(f'  {th}秒以上: {sum(d>=th for d in allstop)} 件')
print(f'「動けなくなった」と記録された止まり: {len(stuck_dur)} 件  p50 {q(stuck_dur,.5)}  最小 {min(stuck_dur) if stuck_dur else None}  最大 {max(stuck_dur) if stuck_dur else None}')
print('30秒以上の止まりの原因:', cause_long.most_common())
print('回ごと（条件 / 最長 / 30秒以上 / 60秒以上）:')
for x in per_run_max: print('  ', *x)

# 2. ロボットどうしの向かい合い（yields.csv の ロボット→ロボット）
rr = []; rr_over60 = collections.Counter()
for r in RUNS:
    for y in rows(r, 'yields'):
        if y['who'].startswith('ロボット') and y['to_whom'].startswith('ロボット'):
            w = float(y['waited_s'] or 0); rr.append(w)
            if w >= 60: rr_over60[r.split('cs144_')[1]] += 1
print('\n## 2. ロボットどうしで待った長さ（秒）')
print(f'件数 {len(rr)}  p50 {q(rr,.5)}  p90 {q(rr,.9)}  p99 {q(rr,.99)}  最大 {round(max(rr),1) if rr else None}')
for th in (30, 60, 120): print(f'  {th}秒以上: {sum(w>=th for w in rr)} 件')
print('60秒以上がある回:', dict(rr_over60))

# 3. 試算とのずれ（台帳の cs:144 の行）
L = [x for x in csv.DictReader(open(LEDGER, encoding='utf-8')) if x['unity_cs'] == 'cs:144' and 'stageB' in x['run_folder']]
pairs = [('entry_wait_mean', 'site_entry_wait_mean', '入店待ち'), ('dish_wait_mean', 'site_dish_wait_mean', '料理の待ち'),
         ('staff_wait_mean', 'site_staff_wait_mean', '席は空いているのに待った'), ('robot_share', 'site_robot_share', 'ロボットが運んだ割合')]
print('\n## 3. 試算とのずれ（条件ごとに種3つの平均・Unity ÷ 試算）')
by = collections.defaultdict(list)
for x in L: by[(x['store'], x['robots'])].append(x)
for (store, rb), xs in sorted(by.items()):
    out = []
    for u, s, name in pairs:
        uv = [float(x[u]) for x in xs if x[u] not in ('', None)]
        sv = [float(x[s]) for x in xs if x[s] not in ('', None)]
        if not uv or not sv or st.mean(sv) == 0: out.append(f'{name} -'); continue
        out.append(f'{name} {st.mean(uv):.2f}/{st.mean(sv):.2f}={st.mean(uv)/st.mean(sv):.2f}倍')
    print(f'  {store} ロボット{rb}台: ' + ' | '.join(out))

# 3b. ロボットの台数で入店待ちが変わる向き（0→1→2台）
print('\n## 3b. 入店待ちの向き（0→1→2台・種3つの平均）')
for store in sorted({k[0] for k in by}):
    u = [st.mean(float(x['entry_wait_mean']) for x in by[(store, str(n))]) for n in (0, 1, 2)]
    s = [st.mean(float(x['site_entry_wait_mean']) for x in by[(store, str(n))]) for n in (0, 1, 2)]
    print(f'  {store}: Unity ' + ' → '.join(f'{v:.2f}' for v in u) + '  試算 ' + ' → '.join(f'{v:.2f}' for v in s))

# 4. 種によるばらつき
print('\n## 4. 同じ条件で種を変えたときのばらつき（入店待ち・分）')
for (store, rb), xs in sorted(by.items()):
    v = sorted(float(x['entry_wait_mean']) for x in xs)
    print(f'  {store} ロボット{rb}台: ' + ', '.join(f'{a:.2f}' for a in v) + f'  （最大÷最小 {v[-1]/v[0]:.1f}倍）')

# 5. 回の健全さ（壊れた回を見つける材料）
print('\n## 5. 回の健全さ')
for x in L:
    print(f"  {x['run_folder'].split('cs144_')[1]}: status {x['status']} 全員帰った {x['everyone_left']} 実時間 {float(x['wall_seconds'])/60:.1f}分 "
          f"立ち往生 {x['stuck_count']} 打ち切り {x['timeouts']} 瞬間移動 {x['warped']} 卓を空けた {x['table_freed']} あきらめた {x['gave_up']} 片付け待ち600秒超え {x['clear_wait_over_count']}")
peopleskip = []
for r in RUNS:
    ri = json.load(open(f'{r}/run_info.json', encoding='utf-8'))
    peopleskip.append((r.split('cs144_')[1], ri['people_step_m']['max'], ri['frames_over_005m']))
print('人の1コマの移動の最大（m）とロボットの0.05m超えのコマ数:', peopleskip)
