Spaces:
Sleeping
Sleeping
| """Verify kha thi cua thu vien drill (app/drills.json) — BRIEF 04/08/2026 buoc 1. | |
| CURATION, KHONG PHAI EVAL (tien le 11b ban giao 9): so "cu dat goal" o day chi | |
| de chung minh drill TAP DUOC — dung de lan vao so do engine trong research log. | |
| Voi moi drill: quet dung grid ky thuat x luc cua engine (11 ky thuat x nac | |
| 2-10 = 99 o, `v2_specs`) tren phi ngam ghost-ball vao lo goal (goal.pot.pocket | |
| = "any" thi quet moi lo kha thi tu `feasible_pockets_full`), sim tung cu bang | |
| `simulate_shot_multi`, dem: | |
| n_pot : cu pot SACH bi muc tieu (khong scratch, first_contact dung bi, | |
| bi vao lo) — gate cung: moi drill phai co >= 1 | |
| n_pass : cu dat TRON goal = pot sach + cue dung trong cue_zone | |
| (scoring.pass "pot && cue_zone") — drill 0 cu dat la drill | |
| khong tap duoc: chinh zone/khoang cach roi quet lai | |
| Gate G1: 100% drill layout hop le (validate_full khong nem) VA n_pot >= 1. | |
| Bang so n_pass tung drill di vao HANDOFF. | |
| Chay SERIAL co chu dich, khong pool: tong scan ~9 drill x 99-500 sim ~ 10-30s | |
| sau warmup, trong khi moi worker process phai tra ~40s JIT Numba rieng — | |
| pool o day cham hon serial. Khong hardcode so worker vi khong co worker nao | |
| (POOLCOACH_WORKERS chi danh cho cho nao that su co pool). | |
| python scripts/verify_drills.py [--drill stop_shot_short] [--drills-file ...] | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| if str(ROOT / "src") not in sys.path: | |
| sys.path.insert(0, str(ROOT / "src")) | |
| DRILLS_FILE = ROOT / "app" / "drills.json" | |
| def load_drills(path: Path) -> list[dict]: | |
| data = json.loads(path.read_text(encoding="utf-8")) | |
| return data["drills"] | |
| def check_layout(env_h, balls, target): | |
| """Layout hop le = validate_full khong nem + bi muc tieu co mat. | |
| Tra ve None neu ok, message loi neu khong.""" | |
| from poolcoach_rl.recommend import validate_full | |
| if target not in balls: | |
| return f"goal.pot.ball={target!r} khong co trong layout" | |
| try: | |
| validate_full(env_h, balls) | |
| except ValueError as e: | |
| return str(e) | |
| return None | |
| def scan_drill(env_h, drill): | |
| """Quet grid engine tren MOT drill. Tra dict so lieu cho bang bao cao.""" | |
| import numpy as np | |
| from poolcoach_rl.recommend import feasible_pockets_full | |
| from poolcoach_rl.recommend.zone_v2 import v2_specs | |
| from poolcoach_rl.recommend import simulate_shot_multi | |
| balls = {bid: np.array([p["x"], p["y"]], dtype=np.float64) | |
| for bid, p in drill["layout"].items()} | |
| target = drill["goal"]["pot"]["ball"] | |
| want_pocket = drill["goal"]["pot"]["pocket"] | |
| zone = drill["goal"]["cue_zone"] | |
| zc = np.array([zone["cx"], zone["cy"]], dtype=np.float64) | |
| err = check_layout(env_h, balls, target) | |
| if err: | |
| return {"id": drill["id"], "layout_err": err} | |
| pockets = feasible_pockets_full(env_h, balls, target) | |
| if want_pocket != "any": | |
| pockets = [p for p in pockets if p[0] == int(want_pocket)] | |
| specs = v2_specs() | |
| n_sim = n_pot = n_pass = 0 | |
| example = None | |
| t0 = time.time() | |
| for idx, phi, _cut, _blocked in pockets: | |
| for side, vert, v0 in specs: | |
| m = simulate_shot_multi(env_h, balls, phi, v0, side, vert) | |
| n_sim += 1 | |
| if m is None: | |
| continue | |
| pot_ok = (not m["scratch"] | |
| and m["first_contact"] == target | |
| and list(m["potted"]) == [target]) | |
| if not pot_ok: | |
| continue | |
| n_pot += 1 | |
| cue_final = m["balls_final"].get("cue") | |
| if cue_final is None: | |
| continue | |
| d_zone = float(np.linalg.norm( | |
| np.asarray(cue_final, dtype=np.float64) - zc)) | |
| if d_zone <= zone["r"]: | |
| n_pass += 1 | |
| if example is None: | |
| example = (side, vert, v0, d_zone) | |
| return {"id": drill["id"], "layout_err": None, | |
| "n_pockets": len(pockets), "n_sim": n_sim, | |
| "n_pot": n_pot, "n_pass": n_pass, | |
| "example": example, "elapsed_s": time.time() - t0} | |
| def main() -> int: | |
| # Console Windows co the la cp1252 — title tieng Viet khong duoc lam chet script | |
| if hasattr(sys.stdout, "reconfigure"): | |
| sys.stdout.reconfigure(encoding="utf-8", errors="replace") | |
| ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) | |
| ap.add_argument("--drill", help="chi quet 1 drill theo id (chinh sua nhanh)") | |
| ap.add_argument("--drills-file", type=Path, default=DRILLS_FILE) | |
| args = ap.parse_args() | |
| drills = load_drills(args.drills_file) | |
| if args.drill: | |
| drills = [d for d in drills if d["id"] == args.drill] | |
| if not drills: | |
| print(f"khong co drill id={args.drill!r}") | |
| return 2 | |
| print(f"[verify_drills] {len(drills)} drill tu {args.drills_file}") | |
| print("[verify_drills] dung env + warmup JIT Numba (~40s lan dau)...", | |
| flush=True) | |
| t0 = time.time() | |
| from poolcoach_rl.envs import PositionPlayEnv | |
| from poolcoach_rl.recommend import warmup | |
| env_h = PositionPlayEnv() | |
| warmup(env_h) | |
| print(f"[verify_drills] warmup xong sau {time.time() - t0:.1f}s", flush=True) | |
| rows = [] | |
| for d in drills: | |
| r = scan_drill(env_h, d) | |
| rows.append(r) | |
| if r["layout_err"]: | |
| print(f" {r['id']:18s} LAYOUT LOI: {r['layout_err']}", flush=True) | |
| else: | |
| ex = r["example"] | |
| ex_txt = (f"side {ex[0]:+.1f} vert {ex[1]:+.1f} v0 {ex[2]:.2f} " | |
| f"(cach tam zone {ex[3]:.3f} m)" if ex else "-") | |
| print(f" {r['id']:18s} pockets {r['n_pockets']} sim {r['n_sim']:4d}" | |
| f" pot {r['n_pot']:3d} PASS {r['n_pass']:3d}" | |
| f" vd: {ex_txt} [{r['elapsed_s']:.1f}s]", flush=True) | |
| # ------------------------------------------------------------- gate G1 | |
| bad_layout = [r["id"] for r in rows if r["layout_err"]] | |
| no_pot = [r["id"] for r in rows | |
| if not r["layout_err"] and r["n_pot"] == 0] | |
| no_pass = [r["id"] for r in rows | |
| if not r["layout_err"] and r["n_pot"] > 0 and r["n_pass"] == 0] | |
| print() | |
| print("| drill | pockets | sim | pot sach | dat tron goal |") | |
| print("|---|---|---|---|---|") | |
| for r in rows: | |
| if r["layout_err"]: | |
| print(f"| {r['id']} | - | - | - | LAYOUT LOI |") | |
| else: | |
| print(f"| {r['id']} | {r['n_pockets']} | {r['n_sim']} " | |
| f"| {r['n_pot']} | {r['n_pass']} |") | |
| print() | |
| if bad_layout: | |
| print(f"GATE DO: layout loi: {', '.join(bad_layout)}") | |
| return 1 | |
| if no_pot: | |
| print(f"GATE DO: khong ton tai cu pot: {', '.join(no_pot)}") | |
| return 1 | |
| if no_pass: | |
| # khong phai gate cung cua G1, nhung drill 0 cu dat tron goal la drill | |
| # khong tap duoc — BRIEF bat chinh zone/khoang cach roi quet lai | |
| print(f"CANH BAO: pot duoc nhung 0 cu dat tron goal (phai chinh + quet " | |
| f"lai): {', '.join(no_pass)}") | |
| return 1 | |
| print("GATE G1 DAT: 100% layout hop le, moi drill co cu pot va co cu dat " | |
| "tron goal.") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |