I wrote a program in Pygame that basically acts as a physics engine for a ball. You can knock the ball around and your strokes are counted, plus an extra punch to push the limits. If I continue to do that, I would make the angle and power display switchable, but I'm happy to show it now:

```
Import Pygame as pg
Import math
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
WINDOW_COLOR = (100, 100, 100)
BALL_COLOR = (255, 255, 255)
BALL_OUTLINE_COLOR = (255, 0, 0)
LINE_COLOR = (0, 0, 255)
ALINE_COLOR = (0, 0, 0)
START_X = int (.5 * SCREEN_WIDTH)
START_Y = int (.99 * SCREEN_HEIGHT)
POWER_MULTIPLIER = .85
SPEED_MULTIPLIER = 2
BALL_RADIUS = 10
pg.init ()
pg.display.set_caption (& # 39; Golf & # 39;)
window = pg.display.set_mode ((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.event.set_grab (True)
p.Maussatzcursor ((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0 , 0) 0))
strokeFont = pg.font.SysFont ("Monospace", 50)
STROKECOLOR = (255, 255, 0)
powerFont = pg.font.SysFont ("arial", 15, bold = true)
POWERCOLOR = (0, 255, 0)
angleFont = pg.font.SysFont ("arial", 15, bold = true)
ANGLECOLOR = (0, 255, 0)
PenaltyFont = pg.font.SysFont ("georgia", 40, bold = true)
PENALTYCOLOR = (255, 0, 0)
Class Ball (Object):
def __init __ (self, x, y, rad, c, oc):
self.x = x
self.y = y
self.radius = rad
self.color = c
self.outlinecolor = oc
def show (self, window):
pg.draw.circle (window, self.outlinecolor, (self.x, self.y), self.radius)
pg.draw.circle (window, self.color, (self.x, self.y), self.radius - int (.4 * self.radius))
@staticmethod
def path (x, y, p, a, t):
vx, vy = p * math.cos (a), p * math.sin (a) #Velocities
dx, dy = vx * t, vy * t - 4.9 * t ** 2 #distance
print (& # 39; x-pos:% spx & # 39;% str (round (dx + x)))
print (& # 39; y-pos:% spx & # 39;% str (round (abs (dy - y))))
Return round (dx + x), round (y - dy)
@staticmethod
def quadrant (x, y, xm, ym):
if so < y and xm > x:
back 1
elif ym <y and xm < x:
return 2
elif ym > y and xm < x:
return 3
elif ym > y and xm> x:
back 4
otherwise:
return incorrectly
def draw_window ():
window.fill (WINDOW_COLOR)
Ball show (window)
if not shoot:
arrow (window ALINE_COLOR, ALINE_COLOR, aline[0], a line[1]5)
Arrow (window, LINE_COLOR, LINE_COLOR, line[0], Line[1]5)
stroke_text = & # 39; Strokes:% s & # 39;% Strokes
stroke_label = strokeFont.render (stroke_text, 1, STROKECOLOR)
if not punches:
window.blit (stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
otherwise:
window.blit (stroke_label, (SCREEN_WIDTH - (.21 + .02 * math.floor (math.log10 (dashes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
power_text = & # 39; shot strength:% sN #% power_display
power_label = powerFont.render (power_text, 1, POWERCOLOR)
If not shoot: window.blit (power_label, (Cursor_pos.)[0] + .008 * SCREEN_WIDTH, Cursor_pos[1]))
angle_text = & # 39; angle:% s ° & # 39;% angle_display
angle_label = angleFont.render (angle_text, 1, ANGLECOLOR)
If not shoot: window.blit (angle_label, (ball.x - .06 * SCREEN_WIDTH, ball.y - .01 * SCREEN_HEIGHT))
if punishment:
Penalty_text = & # 39; Out of Bounds! +1 stroke & # 39;
Penalty_Label = PenaltyFont.render (Penalty, 1, PENALTYCOLOR)
Penalty_rect = Penalty_Label.get_rect (center = (SCREEN_WIDTH / 2, .225 * SCREEN_HEIGHT))
window.blit (Penalty_Label, Penalty_rect)
pg.display.flip ()
Def angle (Cursor_Pos):
x, y, xm, ym = sphere.x, sphere.y, cursor_pos[0], cursor_pos[1]
if x-xm:
Angle = math.atan ((y - ym) / (x - xm))
elif y> ym:
Angle = math.pi / 2
otherwise:
Angle = 3 * math.pi / 2
q = ball quadrant (x, y, xm, ym)
if q: angle = math.pi * math.floor (q / 2) - angle
when round (angle * 180 / math.pi) == 360:
Angle = 0
if x> xm and round (angle * 180 / math.pi) == 0:
Angle = math.pi
Return angle
def arrow (screen, colored, tri-color, start, end, trirad):
pg.draw.line (screen, color, start, end, 2)
rotation = math.degrees (math.atan2 (start[1] - The End[1], The End[0] - Begin[0])) + 90
pg.draw.polygon (screen, tricolor, ((end[0] + trirad * math.sin (math.radians (rotation)),
The End[1] + trirad * math.cos (math.radians (rotation)))),
(The End[0] + trirad * math.sin (math.radians (rotation - 120)),
The End[1] + trirad * math.cos (math.radians (rotation - 120))))
(The End[0] + trirad * math.sin (math.radians (rotation + 120)),
The End[1] + trirad * math.cos (math.radians (rotation + 120)))))
def Distance (x, y):
Return math.sqrt (x ** 2 + y ** 2)
x, y, time, power, ang, strokes = 0, 0, 0, 0, 0, 0
xb, yb = none, none
shoot, punishment = wrong, wrong
p_ticks = 0
Ball = Ball (START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
quit = false
BARRIER = 1
To attempt:
while not stopping:
Seconds = (pg.time.get_ticks () - p_ticks) / 1000
If seconds> 1.2: penalty = false
cursor_pos = pg.mouse.get_pos ()
line = [(ball.x, ball.y), cursor_pos]
line_ball_x, line_ball_y = Cursor_pos[0] - ball.x, cursor_pos[1] - ball
aline [(ball.x, ball.y), (ball.x + .015 * SCREEN_WIDTH, ball.y)]
if not shoot:
power_display = round (
Distance (line_ball_x, line_ball_y) * POWER_MULTIPLIER / 10)
angle_display = round (angle (cursor_pos) * 180 / math.pi)
when shoot:
if ball.y <SCREEN_HEIGHT:
if BARRIER <ball.x <SCREEN_WIDTH:
Time + = .3 * SPEED_MULTIPLIER
Print (& # 39; time:% ss & # 39;% round (time, 2))
po = ball.path (x, y, force, ang, time)
ball.x, ball.y = po[0]po[1]
otherwise:
print (& # 39; Out of Bounds! & # 39;)
Punishment = true
p_ticks = pg.time.get_ticks ()
Dashes + = 1
shoot = wrong
if BARRIER <xb <SCREEN_WIDTH:
ball.x = xb
otherwise:
ball.x = START_X
ball.y = yb
otherwise:
shoot = wrong
ball.y = START_Y
for an event in pg.event.get ():
if event.type == pg.QUIT:
quit = true
if event.type == pg.KEYDOWN:
If event.key == pg.K_ESCAPE:
quit = true
If event.type == pg.MOUSEBUTTONDOWN:
if not shoot:
shoot = true
x, y = sphere x, sphere y
xb, yb = sphere.x, sphere.y
Time, power = 0, (
Distance (line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
print (& # 39; n nBall hit! & # 39;)
print (& # 39; nPower:% sN & # 39;% Round (Power, 2))
ang = angle (Cursor_Pos)
print (angle angle:% s °%% round (ang * 180 / math.pi, 2))
print (& # 39; cos (a):% s & # 39;% round (math.cos (ang), 2)), print (& # 39; sin (a):% s & # 39;% round (math .sin (ang), 2))
Dashes + = 1
draw_window ()
print (" nTurn off ...")
pg.quit ()
Exception exception as error:
print (fatal error ({error}) has occurred. The program shuts down. & # 39;)
pg.quit ()
```

Criticism gone!