#!/usr/bin/env python3
# Filename: A15_pool_shots.py
"""
Pool-shot scenarios for the air table physics simulation.
This module illustrates a pattern for sharing functionality between the _m_ series
(A10-A14) and the final baseline (A15+). It uses runtime detection of the script
name to determine which version is running and automatically selects the appropriate
Puck class and globals module.
This allows us to maintain a single implementation of pool game functions that work
across both versions, avoiding code duplication while preserving compatibility.
"""
import sys, math
from pygame.color import THECOLORS
from A09_vec2d import Vec2D
def get_Puck_and_g():
"""Get the appropriate Puck class and globals based on script name."""
# sys.argv[0] is the name of the script being run (e.g. 'A15c_2D_perfect_kiss_serverN.py')
# Check if the 'A15' or 'A16' substrings are in the name to determine which version to use.
if any(substring in sys.argv[0] for substring in ['A15','A16']):
import A15_globals as g
from A15_air_table_objects import Puck
return Puck, g
else:
import A10_m_globals as g
return g.air_table.Puck, g # A10 version
def pool_line_of_balls(offset_percent=0):
Puck, g = get_Puck_and_g()
g.air_table.inhibit_wall_collisions = True
density = 1.0
radius_m = 0.1
# A group of n target pucks at the same y position but separated by a gap.
n_targets = 20
gap_m = radius_m * 1.50
# Start group of targets at 3 puck diameters from the right edge.
right_edge_x = g.game_window.UR_2d_m.x
pos_first_target = Vec2D(right_edge_x - 3 * (2 * radius_m), g.game_window.center_2d_m.y)
pos_x_2d_m = pos_first_target.copy()
for i in range(n_targets):
pos_x_2d_m -= Vec2D(2 * radius_m + gap_m, 0)
Puck(pos_x_2d_m, radius_m, density,
coef_rest=1.0, CR_fixed=True,
bullet=True, color=THECOLORS['gold'], border_px=0)
# Position the cue ball at the right edge of the screen slightly offset in the
# y direction.
y_offset_m = radius_m * offset_percent / 100.0
print(f"y_offset_m = {y_offset_m} ({offset_percent}%)")
p1_init_pos_2d_m = Vec2D(right_edge_x - radius_m, g.game_window.center_2d_m.y + y_offset_m)
p1 = Puck(p1_init_pos_2d_m, radius_m, density,
coef_rest=1.0, CR_fixed=True,
bullet=True, color=THECOLORS['royalblue'], border_px=0)
g.air_table.throw_puck(p1, Vec2D(-1, 0) * 5.0, delay_s=1.0)
def pool_trick_shot():
Puck, g = get_Puck_and_g()
puck_parms = {
'border_px': 0,
'coef_rest': 1.0,
'CR_fixed': True
}
if g.air_table.engine == 'box2d':
g.air_table.buildFence(onoff={'L':False,'R':False,'T':False,'B':False}) # no fence:
puck_parms['friction'] = 0
puck_parms['friction_fixed'] = True
puck_parms['angularVelocity_rps'] = 0
else:
g.air_table.inhibit_wall_collisions = True
density = 1.0
n_pucks = 15
radius_m = 0.4
# The polygon radius is the distance from the center of the polygon to any of its
# vertices. This formula calculates the radius so that the sides of the polygon are
# equal to twice the puck radius. If pucks are positioned on the vertices they will
# just be touching.
polygon_radius_m = radius_m / math.sin(math.pi/n_pucks)
# Place the pucks a little farther out than their touching point.
little_extra_m = 0.3
center_to_puck_2d_m = Vec2D(0.0, polygon_radius_m + little_extra_m)
for i in range(0, n_pucks-2):
angle = (360 / n_pucks) * i
rotated_c_to_puck_2d_m = center_to_puck_2d_m.rotated(angle)
puck_position_2d_m = g.game_window.center_2d_m + rotated_c_to_puck_2d_m
Puck(puck_position_2d_m, radius_m, density, color=THECOLORS['darkkhaki'], **puck_parms)
# This cue-ball puck will be automatically shot or flung or by the user at the target pucks.
cueBall_density = density
cueBall_r_m = radius_m
# To the right of the first puck.
cB_init_pos_2d_m = g.air_table.pucks[0].pos_2d_m + Vec2D(4.0, 0.0)
p1 = Puck(cB_init_pos_2d_m, cueBall_r_m, cueBall_density, color=THECOLORS['royalblue'],
bullet=True, **puck_parms)
g.air_table.throw_puck(p1, Vec2D(-1, 0) * 4.0, delay_s=1.0)
def burst_of_pucks(n_pucks, speed_mps=1, radius_m=0.05):
Puck, g = get_Puck_and_g()
g.air_table.inhibit_all_puck_collisions = True
puck_parms = {
'border_px': 0,
'coef_rest': 1.0,
'CR_fixed': True
}
if g.air_table.engine == 'box2d':
puck_parms['friction'] = 0
puck_parms['friction_fixed'] = True
puck_parms['angularVelocity_rps'] = 0
puck_parms['groupIndex'] = -1
else:
pass
base_v_2d_mps = Vec2D(speed_mps, speed_mps)
for i in range(n_pucks):
v_2d_mps = base_v_2d_mps.rotated(i * 360 / n_pucks)
Puck(g.game_window.center_2d_m, radius_m, 1.5, vel_2d_mps=v_2d_mps,
color=THECOLORS['lavender'], **puck_parms)