123
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"""把评测机一局的日志归档到 ``examples/replay/``,供无 referee-data 时复现插图。
|
||||
|
||||
``referee-data/`` 与 ``output/`` 都在 .gitignore 里:本机清掉评测机数据后,
|
||||
``docs/figures/*.png`` 就无法复现。本脚本把"作图用到的那几局"抽出来,
|
||||
命名成 ``<case_code>.<module>.jsonl`` 存进 ``examples/replay/``(入库、几万行以内),
|
||||
``scripts/make_figures.py`` 在没有评测机数据时自动改读这里。
|
||||
|
||||
uv run python scripts/archive_replay_logs.py # 归档作图默认选中的两局
|
||||
uv run python scripts/archive_replay_logs.py BBM9-HZG2-77N5-NKKC # 指定案例
|
||||
uv run python scripts/archive_replay_logs.py --list # 只看有哪些局
|
||||
|
||||
归档是**内容等价**的:只做 JSON 重新格式化(逐行 compact 输出),不改字段,
|
||||
并在文件头写入一条 ``session_start``(评测机日志本就自带),保持解析兼容。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SESSIONS_DIR = ROOT / 'referee-data' / 'sessions'
|
||||
ARCHIVE_DIR = ROOT / 'examples' / 'replay'
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
from make_figures import discover_runs, pick_run # noqa: E402
|
||||
|
||||
|
||||
def archive(run: dict, out_dir: Path) -> Path:
|
||||
"""把一局日志写成归档 JSONL:逐行原样紧凑输出(字段不变,只重新格式化)。"""
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
target = out_dir / f"{run['case_code']}.{run['module']}.jsonl"
|
||||
lines: list[str] = []
|
||||
for line in run["source"].read_text(encoding='utf-8').splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
doc = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
lines.append(json.dumps(doc, ensure_ascii=False, separators=(',', ':')))
|
||||
target.write_text('\n'.join(lines) + '\n', encoding='utf-8')
|
||||
return target
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
parser.add_argument('cases', nargs='*', help='案例编码;缺省=作图默认选中的那几局')
|
||||
parser.add_argument('--list', action='store_true', help='列出可选案例后退出')
|
||||
parser.add_argument('--out', default=str(ARCHIVE_DIR))
|
||||
args = parser.parse_args()
|
||||
|
||||
runs = discover_runs()
|
||||
if not runs:
|
||||
print('没有找到可归档的日志:referee-data/sessions/*/log.jsonl 与 '
|
||||
'examples/replay/*.jsonl 都为空。')
|
||||
return 1
|
||||
if args.list:
|
||||
for run in runs:
|
||||
clears = sum(1 for a in run["actions"] if a["path"] == "/clear")
|
||||
print(f"{run['case_code']} {run['module']:<12} 真值 {len(run['truth']):2d} "
|
||||
f"动作 {len(run['actions']):4d} 清除 {clears:3d}")
|
||||
return 0
|
||||
|
||||
picked = []
|
||||
if args.cases:
|
||||
wanted = set(args.cases)
|
||||
picked = [r for r in runs if r["case_code"] in wanted]
|
||||
missing = wanted - {r["case_code"] for r in picked}
|
||||
if missing:
|
||||
print(f'未找到案例:{sorted(missing)}')
|
||||
return 1
|
||||
else:
|
||||
for module, truth in (('q3', 10), ('q4', 12)):
|
||||
run = pick_run(runs, module=module, truth=truth)
|
||||
if run and run not in picked:
|
||||
picked.append(run)
|
||||
|
||||
for run in picked:
|
||||
path = archive(run, Path(args.out))
|
||||
print(f"[archive] {run['case_code']:<20} {run['module']:<12} "
|
||||
f"真值 {len(run['truth']):2d} 动作 {len(run['actions']):4d} → "
|
||||
f"{path.relative_to(ROOT)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user