Spaces:
Sleeping
Sleeping
| """Quét seed bàn mẫu "thoáng" cho app — BRIEF 31/07/2026 việc B.5. | |
| Sinh bàn 9 bi ngẫu nhiên (ĐÚNG generator ``_gen_table`` của eval_fullrack — | |
| import, không copy), rồi TỰ CHƠI bằng chính zone V2 trong sim: mỗi cú lấy | |
| top-1 của ``recommend_v2``, áp ``balls_final``, lặp tới khi ăn bi 9 (thắng) | |
| hoặc hết đường. Seed nào chạy HẾT BỘ mới vào danh sách; app runtime chỉ bốc | |
| ngẫu nhiên trong danh sách đó (``app/static/demo_boards.json``). | |
| Sim tất định ⇒ seed đạt là đạt mãi — VỚI ĐÚNG BẢN TIÊU CHÍ NÀY (design §8 | |
| V2, 31/07/2026). Đổi tiêu chí là phải quét lại; trường ``criteria`` trong | |
| JSON tồn tại để lần đổi đó không im lặng. | |
| KHÔNG PHẢI EVAL (BRIEF bối cảnh 11b): đây là curation cho demo/UX. Mọi phép | |
| đo sau này dùng seed riêng của script eval như cũ — file này không được dùng | |
| chung với chúng, kẻo "tỉ lệ chạy hết" của demo bị đọc nhầm thành số đo. | |
| Số PHẢI BÁO trong HANDOFF: quét bao nhiêu seed, đạt bao nhiêu — tỉ lệ này | |
| chính là số "hết đường tự nhiên" trên bàn ngẫu nhiên (Bối cảnh 4). | |
| python scripts/gen_demo_boards.py --want 10 --max-seeds 100 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| sys.path.insert(0, str(ROOT)) # import scripts.eval_fullrack | |
| OUT = ROOT / "app" / "static" / "demo_boards.json" | |
| MAX_SHOTS = 12 # ván 9-ball tối đa 9 cú; dư là có gì đó sai | |
| def self_play(env_h, balls): | |
| """Tự chơi bằng V2. Trả (ok, n_shots, reason).""" | |
| import numpy as np | |
| from poolcoach_rl.recommend import recommend_v2 | |
| cur = {bid: np.asarray(xy, dtype=np.float64) for bid, xy in balls.items()} | |
| for i in range(MAX_SHOTS): | |
| res = recommend_v2(cur, env_h=env_h, alternatives=0, n_render=0) | |
| if not res.shots: | |
| return False, i, f"het duong o cu {i + 1} (target {res.target})" | |
| s = res.shots[0] | |
| if s.win: | |
| return True, i + 1, "an bi 9" | |
| cur = {bid: np.asarray(xy, dtype=np.float64) | |
| for bid, xy in s.balls_final.items() if xy is not None} | |
| if not any(b != "cue" for b in cur): | |
| # hết bi mà chưa win — bàn không có bi 9? không xảy ra với full | |
| return False, i + 1, "het bi ma khong win" | |
| return False, MAX_SHOTS, "qua 12 cu — vong lap bat thuong" | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--want", type=int, default=10, | |
| help="số bàn đạt cần gom đủ thì dừng") | |
| ap.add_argument("--max-seeds", type=int, default=100, | |
| help="trần số seed quét (chặn thời gian chạy)") | |
| ap.add_argument("--start-seed", type=int, default=1000, | |
| help="seed đầu; CỐ Ý xa dải seed của các script eval " | |
| "(eval dùng 7/42/123...) — bối cảnh 11b") | |
| args = ap.parse_args() | |
| import numpy as np | |
| from poolcoach_rl.envs import PositionPlayEnv | |
| from poolcoach_rl.envs.position_env import BALL_R | |
| from poolcoach_rl.recommend import warmup | |
| from scripts.eval_fullrack import _gen_table | |
| env_h = PositionPlayEnv() | |
| print("[demo] warmup JIT (~40s lan dau)...", flush=True) | |
| warmup(env_h) | |
| boards, scanned = [], 0 | |
| t0 = time.time() | |
| for seed in range(args.start_seed, args.start_seed + args.max_seeds): | |
| if len(boards) >= args.want: | |
| break | |
| scanned += 1 | |
| rng = np.random.default_rng(seed) | |
| balls = _gen_table(rng, env_h.w, env_h.l, BALL_R, full=True) | |
| ok, n, reason = self_play(env_h, balls) | |
| print(f"[demo] seed {seed}: {'DAT' if ok else 'rot'} " | |
| f"({n} cu, {reason})", flush=True) | |
| if ok: | |
| boards.append({ | |
| "seed": seed, | |
| "n_shots": n, | |
| "balls": {bid: [round(float(xy[0]), 6), round(float(xy[1]), 6)] | |
| for bid, xy in balls.items()}, | |
| }) | |
| out = { | |
| "criteria": "ZonePlanner V2 - design S8, chot 31/07/2026 " | |
| "(loc i-v, xep roll_len -> |d-1m| -> enumerate)", | |
| "note": "Curation cho demo/UX, KHONG phai eval (BRIEF 31/07 boi canh " | |
| "11b). Doi tieu chi la phai quet lai bang " | |
| "scripts/gen_demo_boards.py.", | |
| "generator": "scripts.eval_fullrack._gen_table, seed ghi tung bang", | |
| "scanned": scanned, | |
| "passed": len(boards), | |
| "boards": boards, | |
| } | |
| OUT.write_text(json.dumps(out, ensure_ascii=False, indent=1), | |
| encoding="utf-8") | |
| el = time.time() - t0 | |
| print(f"\n[demo] quet {scanned} seed -> {len(boards)} dat " | |
| f"({100.0 * len(boards) / max(scanned, 1):.0f}%) trong {el:.0f}s") | |
| print(f"[demo] ghi {OUT}") | |
| return 0 if boards else 1 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |