#!/usr/bin/env python3 """ Demo pooltool cho PoolCoach — minh hoạ những hook cần cho RL position play. Cài đặt (máy local, nên dùng Python 3.10-3.12): pip install pooltool-billiards Chạy headless (không GUI): python pooltool_demo.py Bật GUI 3D xem cú đánh (cần màn hình; ESC để thoát): python pooltool_demo.py --gui """ import sys import pooltool as pt def build_nineball_shot(): """Dựng một hệ 9-ball chuẩn + một cú đánh có spin.""" table = pt.Table.default() balls = pt.get_rack(pt.GameType.NINEBALL, table) # rack 9-ball đúng luật cue = pt.Cue(cue_ball_id="cue") shot = pt.System(table=table, balls=balls, cue=cue) # === ĐÂY LÀ "ACTION" CỦA AGENT === # V0 = lực, phi = góc hướng, a = english (side spin), b = draw/follow shot.cue.set_state( V0=8.0, # power phi=pt.aim.at_ball(shot, "1"), # angle: nhắm bi số 1 (luật 9-ball: bi nhỏ nhất trước) a=0.0, # spin_x: english trái/phải b=0.2, # spin_y: follow (dương) / draw (âm) -> điều bi cái ) return shot def observation(shot): """OBSERVATION cho RL: toạ độ (x, y) của mọi bi còn trên bàn.""" obs = {} for ball_id, ball in shot.balls.items(): xyz = ball.state.rvw[0] # vị trí [x, y, z] obs[ball_id] = (round(float(xyz[0]), 4), round(float(xyz[1]), 4)) return obs def summarize_events(shot): """REWARD source: đọc events sau cú đánh.""" pocketed, cushions, ball_hits = [], 0, 0 for e in shot.events: etype = str(e.event_type) if "pocket" in etype.lower(): pocketed.append([a.id for a in e.agents]) elif "cushion" in etype.lower(): cushions += 1 elif etype.lower().endswith("ball_ball"): ball_hits += 1 return pocketed, cushions, ball_hits def main(): shot = build_nineball_shot() print("== Vị trí bi TRƯỚC cú đánh ==") for bid, xy in observation(shot).items(): print(f" {bid:>4}: {xy}") # === "STEP": mô phỏng headless, rất nhanh nhờ event-based + Numba === pt.simulate(shot, inplace=True) print(f"\nSố sự kiện mô phỏng: {len(shot.events)}") print(f"Thời gian mô phỏng (giây): {shot.t:.3f}") pocketed, cushions, ball_hits = summarize_events(shot) print("\n== Tín hiệu cho REWARD ==") print(f" Bi vào lỗ: {pocketed if pocketed else 'không'}") print(f" Số lần chạm băng: {cushions}") print(f" Số va chạm bi-bi: {ball_hits}") print("\n== Vị trí bi SAU cú đánh (đặc biệt xem 'cue' -> position play) ==") for bid, xy in observation(shot).items(): print(f" {bid:>4}: {xy}") # GUI 3D: KHÔNG simulate lại (shot đã simulate ở trên — hệ hết năng # lượng, simulate nữa là rỗng); pt.show tự continuize theo fps. if "--gui" in sys.argv: pt.show(shot, title="PoolCoach demo — break 9-ball") if __name__ == "__main__": main()