masterdanh's picture
deploy: snapshot for HF Space
78738de
Raw
History Blame Contribute Delete
3.67 kB
"""Hình học chọn lỗ — tách nguyên từ scripts/recommend_shot.py (23/07/2026)."""
from __future__ import annotations
def feasible_pockets(env_h, cue_xy, b1_xy):
"""[(pocket_idx, phi_deg, cos_cut)] — cùng logic _ghost_dirs_any, giữ
kèm pocket_idx để gọi tên lỗ. Fallback best_cut nếu không lỗ nào dương."""
import numpy as np
from poolcoach_rl.envs.position_env import BALL_R
to_tgt = b1_xy - cue_xy
to_tgt = to_tgt / np.linalg.norm(to_tgt)
out = []
for i, p in enumerate(env_h._pockets):
v = p - b1_xy
n = float(np.linalg.norm(v))
if n < 1e-9:
continue
v = v / n
cut = float(np.dot(to_tgt, v))
if cut <= 0.0:
continue
g = (b1_xy - 2.0 * BALL_R * v) - cue_xy
gn = float(np.linalg.norm(g))
if gn < 1e-9:
continue
g = g / gn
out.append((i, float(np.degrees(np.arctan2(g[1], g[0])) % 360.0), cut))
if not out: # hiếm — mọi lỗ ngược hướng: dùng lỗ cut lớn nhất
best_i, best_cut = 0, -np.inf
for i, p in enumerate(env_h._pockets):
v = p - b1_xy
n = float(np.linalg.norm(v))
if n < 1e-9:
continue
cut = float(np.dot(to_tgt, v / n))
if cut > best_cut:
best_i, best_cut = i, cut
g = env_h._ghost_aim_point(cue_xy, b1_xy) - cue_xy
g = g / np.linalg.norm(g)
out.append((best_i,
float(np.degrees(np.arctan2(g[1], g[0])) % 360.0),
best_cut))
return out
def _seg_point_dist(p, a, b):
"""Khoảng cách từ điểm p đến ĐOẠN thẳng ab (không phải đường thẳng)."""
import numpy as np
ab = b - a
t = float(np.dot(p - a, ab)) / max(float(np.dot(ab, ab)), 1e-12)
t = min(max(t, 0.0), 1.0)
return float(np.linalg.norm(p - (a + t * ab)))
def feasible_pockets_full(env_h, balls, target):
"""Full-rack (phase B, §4.3): như feasible_pockets nhưng biết bi chắn.
Bi khác (không cue, không target) nằm cách đường ``cue→ghost`` hoặc
``target→pocket`` < 2R → lỗ bị đánh dấu ``blocked`` và hạ xuống CUỐI
danh sách — KHÔNG loại hẳn (đôi khi sim vẫn thành công nhờ sượt qua).
Mục đích duy nhất: thứ tự ưu tiên grid; sự thật vẫn do sim quyết.
Trả [(pocket_idx, phi_deg, cos_cut, blocked)] — nhóm thoáng trước,
trong nhóm giữ nguyên thứ tự của feasible_pockets.
"""
import numpy as np
from poolcoach_rl.envs.position_env import BALL_R
cue_xy, tgt_xy = balls["cue"], balls[target]
others = [xy for bid, xy in balls.items()
if bid not in ("cue", target)]
out = []
for i, phi, cut in feasible_pockets(env_h, cue_xy, tgt_xy):
p = env_h._pockets[i]
v = p - tgt_xy
n = float(np.linalg.norm(v))
ghost = tgt_xy - 2.0 * BALL_R * (v / n) if n > 1e-9 else tgt_xy
blocked = any(
_seg_point_dist(o, cue_xy, ghost) < 2 * BALL_R
or _seg_point_dist(o, tgt_xy, p) < 2 * BALL_R
for o in others)
out.append((i, phi, cut, blocked))
out.sort(key=lambda t: t[3]) # stable: thoáng trước, thứ tự cũ giữ nguyên
return out
def pocket_name(env_h, idx: int) -> str:
p = env_h._pockets[idx]
w, l = env_h.w, env_h.l
lr = "trái" if p[0] < w / 2 else "phải"
if p[1] < l / 3:
return f"góc dưới-{lr}"
if p[1] > 2 * l / 3:
return f"góc trên-{lr}"
return f"giữa-{lr}"