#!/usr/bin/env python3 """V12 同种子复放比对器(卡② 效果引擎无状态化批):解析两次 MC 采集的 RUNSUM 行按 seed 配对判读。 两判据模式——parity:整条记录逐位相等(off 组,未受影响路径须透明); input-parity:输入面八字段 + 顶层字段集恒等、种子集与局数一致(formed/rules 组,输出面允许 RNG 级联位移)。 退出码 0 绿 / 1 红 / 2 用法或解析错。字段集从基线文件推导,禁硬编码。""" import sys, json, argparse INPUT_KEYS = ["policy", "seed", "atk_pct", "hp_pct", "route", "attune", "equipped", "feat_mode"] def load(path): recs = {} with open(path, encoding="utf-8") as f: for line in f: line = line.strip() if not line.startswith("RUNSUM "): continue try: d = json.loads(line[len("RUNSUM "):]) except json.JSONDecodeError: print("解析失败行:", line[:80]) sys.exit(2) recs[d.get("seed")] = d return recs def main(): ap = argparse.ArgumentParser() ap.add_argument("--mode", choices=["parity", "input-parity"], required=True) ap.add_argument("--base", required=True) ap.add_argument("--post", required=True) ap.add_argument("--expect", type=int, required=True) a = ap.parse_args() base, post = load(a.base), load(a.post) if len(base) != a.expect or len(post) != a.expect: print("局数不符:base=%d post=%d expect=%d" % (len(base), len(post), a.expect)) return 1 if set(base) != set(post): print("种子集不一致") return 1 red = False if a.mode == "parity": diff = [s for s in base if base[s] != post[s]] if diff: red = True print("off 组逐位差异 %d 局: %s" % (len(diff), diff[:10])) for s in diff[:3]: k = [kk for kk in base[s] if base[s].get(kk) != post[s].get(kk)] print(" seed", s, "差异字段:", k) else: bkeys = set(next(iter(base.values())).keys()) pkeys = set(next(iter(post.values())).keys()) if bkeys != pkeys: red = True print("顶层字段集不一致 base-p=%s post-b=%s" % (bkeys - pkeys, pkeys - bkeys)) for s in base: for kk in INPUT_KEYS: if base[s].get(kk) != post[s].get(kk): red = True print("输入面字段 %s 在 seed %s 不一致: %r vs %r" % (kk, s, base[s].get(kk), post[s].get(kk))) # levels_cleared 直方图仅报告(方向不作判据) lc = post[s].get("levels_cleared") if post else None print("判读:%s" % ("FAIL" if red else "PASS")) return 1 if red else 0 if __name__ == "__main__": sys.exit(main())