File size: 3,163 Bytes
78738de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#!/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()