65 lines
3 KiB
Python
65 lines
3 KiB
Python
#!/usr/bin/env python3
|
|
"""exp15 — build the EXPANDED S2' (full-power, owner decision 2026-07-18) from the first N 节 of 蛊真人.
|
|
|
|
Produces TWO aligned versions of the same content (only the seam separator differs), + seam ledger:
|
|
S2prime_big_flat.txt seams = single '\\n' -> greedy A0 is BLIND (blind-budget cuts)
|
|
S2prime_big_blanklines.txt seams = '\\n\\n' -> greedy ORACLE cuts AT every seam (paragraph flush)
|
|
Within a 节 the body is normalized to single '\\n' (collapse internal blank lines) so the ONLY '\\n\\n'
|
|
are at seams — that is what makes the oracle cut exactly at scene boundaries.
|
|
|
|
Labels for seams are NOT hand-classified here (too many); Q1b uses the oracle's cut-at-all-seams behavior
|
|
and the covariate is per-seam. Deterministic straddle-trap finding happens in straddle_traps.py.
|
|
Run: python build_s2prime_big.py [N] # default N=45
|
|
"""
|
|
from __future__ import annotations
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import build_s2prime as B
|
|
|
|
OUT = Path("/home/ubuntu/books/gu-zhenren/exp15")
|
|
|
|
|
|
def main():
|
|
n = int(sys.argv[1]) if len(sys.argv) > 1 else 45
|
|
secs = B.sections(B.load_text())[:n]
|
|
bodies = []
|
|
for idx, header, body in secs:
|
|
b = body.replace("\r\n", "\n").replace("\r", "\n")
|
|
b = re.sub(r"\n{2,}", "\n", b).strip("\n") # single-\n within 节
|
|
bodies.append((idx, b))
|
|
flat = "\n".join(b for _, b in bodies)
|
|
blank = "\n\n".join(b for _, b in bodies)
|
|
|
|
# seam offsets in each version (start of body i, i>0)
|
|
def seam_offsets(sep):
|
|
offs, cur = [], 0
|
|
for i, (idx, b) in enumerate(bodies):
|
|
if i > 0:
|
|
cur += len(sep)
|
|
if i > 0:
|
|
offs.append({"seam_id": i, "prev_sec": bodies[i - 1][0], "next_sec": idx, "rune_offset": cur})
|
|
cur += len(b)
|
|
return offs
|
|
|
|
(OUT / "S2prime_big_flat.txt").write_text(flat, encoding="utf-8")
|
|
(OUT / "S2prime_big_blanklines.txt").write_text(blank, encoding="utf-8")
|
|
(OUT / "S2prime_big_seams_flat.json").write_text(json.dumps(
|
|
{"version": "flat", "n_sections": n, "section_ids": [b[0] for b in bodies],
|
|
"total_runes": len(flat), "seams": seam_offsets("\n")}, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
(OUT / "S2prime_big_seams_blank.json").write_text(json.dumps(
|
|
{"version": "blanklines", "n_sections": n, "section_ids": [b[0] for b in bodies],
|
|
"total_runes": len(blank), "seams": seam_offsets("\n\n")}, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
print(f"S2'-big: {n}节 flat={len(flat)} runes blank={len(blank)} runes seams={n-1}")
|
|
# verify seam alignment
|
|
fs = seam_offsets("\n"); bs = seam_offsets("\n\n")
|
|
ok = all(flat[s["rune_offset"]:s["rune_offset"]+3] == bodies[i+1][1][:3] for i, s in enumerate(fs))
|
|
okb = all(blank[s["rune_offset"]:s["rune_offset"]+3] == bodies[i+1][1][:3] for i, s in enumerate(bs))
|
|
print(f"seam alignment: flat={ok} blank={okb}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|