#!/usr/bin/env python3 """Split a script-format .ep_ file (setboard/solution/id/go blocks, as used by typhoon's `script` command) into two disjoint, shuffled halves for a train/holdout threshold sweep. Usage: split_ep.py [--seed N] Splits by whole position blocks (never mid-position), shuffled with a fixed seed so the split is reproducible but not biased by the file's original ordering (ECM.ep_ is not randomly ordered). """ import argparse import random import sys def read_blocks(path): """Each block is the lines from one 'setboard' up to and including the following 'go' line.""" blocks = [] current = [] with open(path) as f: for line in f: stripped = line.rstrip("\n") if stripped.startswith("setboard") and current: blocks.append(current) current = [] current.append(stripped) if current: blocks.append(current) return blocks def main(): ap = argparse.ArgumentParser(description=__doc__) ap.add_argument("input") ap.add_argument("out_a") ap.add_argument("out_b") ap.add_argument("--seed", type=int, default=0) args = ap.parse_args() blocks = read_blocks(args.input) if not blocks: print(f"ERROR: no position blocks found in {args.input}", file=sys.stderr) sys.exit(1) rng = random.Random(args.seed) order = list(range(len(blocks))) rng.shuffle(order) mid = len(order) // 2 half_a = sorted(order[:mid]) half_b = sorted(order[mid:]) with open(args.out_a, "w") as f: for i in half_a: f.write("\n".join(blocks[i]) + "\n") with open(args.out_b, "w") as f: for i in half_b: f.write("\n".join(blocks[i]) + "\n") print(f"{len(blocks)} positions in {args.input} -> " f"{len(half_a)} in {args.out_a}, {len(half_b)} in {args.out_b} " f"(seed={args.seed})") if __name__ == "__main__": main()