Golf game boilerplate












7












$begingroup$


I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right 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('Golf')
window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.event.set_grab(True)
pg.mouse.set_cursor((8, 8), (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 #Distances Traveled
print(' x-pos: %spx' % str(round(dx + x)))
print(' y-pos: %spx' % str(round(abs(dy - y))))

return round(dx + x), round(y - dy)

@staticmethod
def quadrant(x,y,xm,ym):
if ym < y and xm > x:
return 1
elif ym < y and xm < x:
return 2
elif ym > y and xm < x:
return 3
elif ym > y and xm > x:
return 4
else:
return False


def draw_window():
window.fill(WINDOW_COLOR)
ball.show(window)
if not shoot:
arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)

stroke_text = 'Strokes: %s' % strokes
stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
if not strokes:
window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
else:
window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))

power_text = '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 = 'Angle: %s°' % 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 Penalty:
penalty_text = 'Out of Bounds! +1 Stroke'
penalty_label = penaltyFont.render(penalty_text, 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 = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
if x-xm:
angle = math.atan((y - ym) / (x - xm))
elif y > ym:
angle = math.pi/2
else:
angle = 3*math.pi/2

q = ball.quadrant(x,y,xm,ym)
if q: angle = math.pi*math.floor(q/2) - angle

if 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, lcolor, tricolor, start, end, trirad):
pg.draw.line(screen, lcolor, start, end, 2)
rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
end[1] + trirad * math.cos(math.radians(rotation))),
(end[0] + trirad * math.sin(math.radians(rotation - 120)),
end[1] + trirad * math.cos(math.radians(rotation - 120))),
(end[0] + trirad * math.sin(math.radians(rotation + 120)),
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, Penalty = False, False
p_ticks = 0

ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
quit = False
BARRIER = 1

try:
while not quit:
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.y

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)

if shoot:
if ball.y < SCREEN_HEIGHT:
if BARRIER < ball.x < SCREEN_WIDTH:
time += .3 * SPEED_MULTIPLIER
print('n time: %ss' % round(time, 2))
po = ball.path(x, y, power, ang, time)
ball.x, ball.y = po[0], po[1]
else:
print('Out of Bounds!')
Penalty = True
p_ticks = pg.time.get_ticks()
strokes += 1
shoot = False
if BARRIER < xb < SCREEN_WIDTH:
ball.x = xb
else:
ball.x = START_X
ball.y = yb
else:
shoot = False
ball.y = START_Y

for 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 = ball.x, ball.y
xb, yb = ball.x, ball.y
time, power = 0, (
distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
print('nnBall Hit!')
print('npower: %sN' % round(power, 2))
ang = angle(cursor_pos)
print('angle: %s°' % round(ang * 180 / math.pi, 2))
print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
strokes += 1

draw_window()

print("nShutting down...")
pg.quit()

except Exception as error:
print(f'A fatal error ({error}) has occurred. The program is shutting down.')
pg.quit()


Feedback of any kind is very welcome!










share|improve this question











$endgroup$

















    7












    $begingroup$


    I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right 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('Golf')
    window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pg.event.set_grab(True)
    pg.mouse.set_cursor((8, 8), (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 #Distances Traveled
    print(' x-pos: %spx' % str(round(dx + x)))
    print(' y-pos: %spx' % str(round(abs(dy - y))))

    return round(dx + x), round(y - dy)

    @staticmethod
    def quadrant(x,y,xm,ym):
    if ym < y and xm > x:
    return 1
    elif ym < y and xm < x:
    return 2
    elif ym > y and xm < x:
    return 3
    elif ym > y and xm > x:
    return 4
    else:
    return False


    def draw_window():
    window.fill(WINDOW_COLOR)
    ball.show(window)
    if not shoot:
    arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
    arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)

    stroke_text = 'Strokes: %s' % strokes
    stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
    if not strokes:
    window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
    else:
    window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))

    power_text = '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 = 'Angle: %s°' % 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 Penalty:
    penalty_text = 'Out of Bounds! +1 Stroke'
    penalty_label = penaltyFont.render(penalty_text, 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 = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
    if x-xm:
    angle = math.atan((y - ym) / (x - xm))
    elif y > ym:
    angle = math.pi/2
    else:
    angle = 3*math.pi/2

    q = ball.quadrant(x,y,xm,ym)
    if q: angle = math.pi*math.floor(q/2) - angle

    if 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, lcolor, tricolor, start, end, trirad):
    pg.draw.line(screen, lcolor, start, end, 2)
    rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
    pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
    end[1] + trirad * math.cos(math.radians(rotation))),
    (end[0] + trirad * math.sin(math.radians(rotation - 120)),
    end[1] + trirad * math.cos(math.radians(rotation - 120))),
    (end[0] + trirad * math.sin(math.radians(rotation + 120)),
    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, Penalty = False, False
    p_ticks = 0

    ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
    quit = False
    BARRIER = 1

    try:
    while not quit:
    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.y

    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)

    if shoot:
    if ball.y < SCREEN_HEIGHT:
    if BARRIER < ball.x < SCREEN_WIDTH:
    time += .3 * SPEED_MULTIPLIER
    print('n time: %ss' % round(time, 2))
    po = ball.path(x, y, power, ang, time)
    ball.x, ball.y = po[0], po[1]
    else:
    print('Out of Bounds!')
    Penalty = True
    p_ticks = pg.time.get_ticks()
    strokes += 1
    shoot = False
    if BARRIER < xb < SCREEN_WIDTH:
    ball.x = xb
    else:
    ball.x = START_X
    ball.y = yb
    else:
    shoot = False
    ball.y = START_Y

    for 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 = ball.x, ball.y
    xb, yb = ball.x, ball.y
    time, power = 0, (
    distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
    print('nnBall Hit!')
    print('npower: %sN' % round(power, 2))
    ang = angle(cursor_pos)
    print('angle: %s°' % round(ang * 180 / math.pi, 2))
    print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
    strokes += 1

    draw_window()

    print("nShutting down...")
    pg.quit()

    except Exception as error:
    print(f'A fatal error ({error}) has occurred. The program is shutting down.')
    pg.quit()


    Feedback of any kind is very welcome!










    share|improve this question











    $endgroup$















      7












      7








      7


      2



      $begingroup$


      I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right 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('Golf')
      window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
      pg.event.set_grab(True)
      pg.mouse.set_cursor((8, 8), (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 #Distances Traveled
      print(' x-pos: %spx' % str(round(dx + x)))
      print(' y-pos: %spx' % str(round(abs(dy - y))))

      return round(dx + x), round(y - dy)

      @staticmethod
      def quadrant(x,y,xm,ym):
      if ym < y and xm > x:
      return 1
      elif ym < y and xm < x:
      return 2
      elif ym > y and xm < x:
      return 3
      elif ym > y and xm > x:
      return 4
      else:
      return False


      def draw_window():
      window.fill(WINDOW_COLOR)
      ball.show(window)
      if not shoot:
      arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
      arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)

      stroke_text = 'Strokes: %s' % strokes
      stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
      if not strokes:
      window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
      else:
      window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))

      power_text = '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 = 'Angle: %s°' % 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 Penalty:
      penalty_text = 'Out of Bounds! +1 Stroke'
      penalty_label = penaltyFont.render(penalty_text, 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 = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
      if x-xm:
      angle = math.atan((y - ym) / (x - xm))
      elif y > ym:
      angle = math.pi/2
      else:
      angle = 3*math.pi/2

      q = ball.quadrant(x,y,xm,ym)
      if q: angle = math.pi*math.floor(q/2) - angle

      if 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, lcolor, tricolor, start, end, trirad):
      pg.draw.line(screen, lcolor, start, end, 2)
      rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
      pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
      end[1] + trirad * math.cos(math.radians(rotation))),
      (end[0] + trirad * math.sin(math.radians(rotation - 120)),
      end[1] + trirad * math.cos(math.radians(rotation - 120))),
      (end[0] + trirad * math.sin(math.radians(rotation + 120)),
      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, Penalty = False, False
      p_ticks = 0

      ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
      quit = False
      BARRIER = 1

      try:
      while not quit:
      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.y

      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)

      if shoot:
      if ball.y < SCREEN_HEIGHT:
      if BARRIER < ball.x < SCREEN_WIDTH:
      time += .3 * SPEED_MULTIPLIER
      print('n time: %ss' % round(time, 2))
      po = ball.path(x, y, power, ang, time)
      ball.x, ball.y = po[0], po[1]
      else:
      print('Out of Bounds!')
      Penalty = True
      p_ticks = pg.time.get_ticks()
      strokes += 1
      shoot = False
      if BARRIER < xb < SCREEN_WIDTH:
      ball.x = xb
      else:
      ball.x = START_X
      ball.y = yb
      else:
      shoot = False
      ball.y = START_Y

      for 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 = ball.x, ball.y
      xb, yb = ball.x, ball.y
      time, power = 0, (
      distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
      print('nnBall Hit!')
      print('npower: %sN' % round(power, 2))
      ang = angle(cursor_pos)
      print('angle: %s°' % round(ang * 180 / math.pi, 2))
      print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
      strokes += 1

      draw_window()

      print("nShutting down...")
      pg.quit()

      except Exception as error:
      print(f'A fatal error ({error}) has occurred. The program is shutting down.')
      pg.quit()


      Feedback of any kind is very welcome!










      share|improve this question











      $endgroup$




      I wrote a program in pygame that basically acts as a physics engine for a ball. You can hit the ball around and your strokes are counted, as well as an extra stroke for going out of bounds. If I do further develop this, I'd make the angle and power display toggleable, but I do like showing them right 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('Golf')
      window = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
      pg.event.set_grab(True)
      pg.mouse.set_cursor((8, 8), (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 #Distances Traveled
      print(' x-pos: %spx' % str(round(dx + x)))
      print(' y-pos: %spx' % str(round(abs(dy - y))))

      return round(dx + x), round(y - dy)

      @staticmethod
      def quadrant(x,y,xm,ym):
      if ym < y and xm > x:
      return 1
      elif ym < y and xm < x:
      return 2
      elif ym > y and xm < x:
      return 3
      elif ym > y and xm > x:
      return 4
      else:
      return False


      def draw_window():
      window.fill(WINDOW_COLOR)
      ball.show(window)
      if not shoot:
      arrow(window, ALINE_COLOR, ALINE_COLOR, aline[0], aline[1], 5)
      arrow(window, LINE_COLOR, LINE_COLOR, line[0], line[1], 5)

      stroke_text = 'Strokes: %s' % strokes
      stroke_label = strokeFont.render(stroke_text, 1, STROKECOLOR)
      if not strokes:
      window.blit(stroke_label, (SCREEN_WIDTH - .21 * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))
      else:
      window.blit(stroke_label, (SCREEN_WIDTH - (.21+.02*math.floor(math.log10(strokes))) * SCREEN_WIDTH, SCREEN_HEIGHT - .985 * SCREEN_HEIGHT))

      power_text = '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 = 'Angle: %s°' % 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 Penalty:
      penalty_text = 'Out of Bounds! +1 Stroke'
      penalty_label = penaltyFont.render(penalty_text, 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 = ball.x, ball.y, cursor_pos[0], cursor_pos[1]
      if x-xm:
      angle = math.atan((y - ym) / (x - xm))
      elif y > ym:
      angle = math.pi/2
      else:
      angle = 3*math.pi/2

      q = ball.quadrant(x,y,xm,ym)
      if q: angle = math.pi*math.floor(q/2) - angle

      if 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, lcolor, tricolor, start, end, trirad):
      pg.draw.line(screen, lcolor, start, end, 2)
      rotation = math.degrees(math.atan2(start[1] - end[1], end[0] - start[0])) + 90
      pg.draw.polygon(screen, tricolor, ((end[0] + trirad * math.sin(math.radians(rotation)),
      end[1] + trirad * math.cos(math.radians(rotation))),
      (end[0] + trirad * math.sin(math.radians(rotation - 120)),
      end[1] + trirad * math.cos(math.radians(rotation - 120))),
      (end[0] + trirad * math.sin(math.radians(rotation + 120)),
      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, Penalty = False, False
      p_ticks = 0

      ball = Ball(START_X, START_Y, BALL_RADIUS, BALL_COLOR, BALL_OUTLINE_COLOR)
      quit = False
      BARRIER = 1

      try:
      while not quit:
      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.y

      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)

      if shoot:
      if ball.y < SCREEN_HEIGHT:
      if BARRIER < ball.x < SCREEN_WIDTH:
      time += .3 * SPEED_MULTIPLIER
      print('n time: %ss' % round(time, 2))
      po = ball.path(x, y, power, ang, time)
      ball.x, ball.y = po[0], po[1]
      else:
      print('Out of Bounds!')
      Penalty = True
      p_ticks = pg.time.get_ticks()
      strokes += 1
      shoot = False
      if BARRIER < xb < SCREEN_WIDTH:
      ball.x = xb
      else:
      ball.x = START_X
      ball.y = yb
      else:
      shoot = False
      ball.y = START_Y

      for 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 = ball.x, ball.y
      xb, yb = ball.x, ball.y
      time, power = 0, (
      distance(line_ball_x, line_ball_y)) * POWER_MULTIPLIER / 10
      print('nnBall Hit!')
      print('npower: %sN' % round(power, 2))
      ang = angle(cursor_pos)
      print('angle: %s°' % round(ang * 180 / math.pi, 2))
      print('cos(a): %s' % round(math.cos(ang), 2)), print('sin(a): %s' % round(math.sin(ang), 2))
      strokes += 1

      draw_window()

      print("nShutting down...")
      pg.quit()

      except Exception as error:
      print(f'A fatal error ({error}) has occurred. The program is shutting down.')
      pg.quit()


      Feedback of any kind is very welcome!







      python pygame physics






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited yesterday









      Alex

      856316




      856316










      asked yesterday









      alec935alec935

      1975




      1975






















          2 Answers
          2






          active

          oldest

          votes


















          7












          $begingroup$

          Some of this is nit-pickery, some is more fundamental:



          Import Order



          PEP-8 suggests an ordering to imports. No reason not to use it:




          Imports should be grouped in the following order:



          Standard library imports.
          Related third party imports.
          Local application/library specific imports.


          You should put a blank line between each group of imports.




          Code Organization: Constants



          You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.



          For example, you have a Ball class. Yet there are global constants named BALL_COLOR and BALL_OUTLINE_COLOR and BALL_RADIUS. Why is that? If they're related to your class, make them class constants.



          class Ball:
          BODY_COLOR = (255, 255, 255)
          OUTLINE_COLOR = (255, 0, 0)
          RADIUS = 10


          Code Organization: Types



          In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple or even two?



          import collections

          Size = collections.namedtuple('Size', 'width height')
          Position = collections.namedtuple('Position', 'x y')

          WINDOW_SIZE = Size(width=1500, height=800)
          START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)


          Code Organization: Functions



          You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main function, or some other-named function.



          def main():
          pg.init()
          pg.display.set_caption('Golf')
          ... etc ...


          You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window class or anything, but you might consider putting them into a Config class. (And using snake_case names.)



          Also, you have a lot of pygame boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:



          while still_playing:
          handle_events()
          update()
          render() # You call this "draw_window()" which is fine.


          Most of your logic, of course, will be in update(). In fact, since it mostly has to do with updating the position of the Ball object, it should mostly be in a call to ball.update_position(delay) (or some such name).



          You make use of a pair of temporaries x and y, but it seems like you could replace those with an old-position attribute on the Ball, or a second Ball object, or something.






          share|improve this answer









          $endgroup$





















            6












            $begingroup$

            Overall it isn't bad.



            Direct imports for common symbols



            Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.



            from pg.font import SysFont
            # ...
            strokeFont = SysFont("monospace", 50)


            snake_case



            i.e. stroke_font for variables and function names. Also, Penalty should be lower-case because it isn't a class.



            debug printing



            This kind of thing:



            print('     x-pos: %spx' % str(round(dx + x)))


            can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.



            Also: do you really need round? Could you instead go



            print(f'    x-pos: {dx + x:.0f}px')


            f-strings



            As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the % operator.



            Global clutter



            It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.



            Shadowing



            Don't call something time. time is already a thing in Python.



            Math



            I kind of had to jump through some hoops to take advantage of atan2. I don't recommend doing this, but here's a one-liner alternative to your quadrant function:



            return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1





            share|improve this answer











            $endgroup$













            • $begingroup$
              Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
              $endgroup$
              – Alex
              15 hours ago










            • $begingroup$
              I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
              $endgroup$
              – alec935
              15 hours ago










            • $begingroup$
              I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
              $endgroup$
              – Wombatz
              11 hours ago










            • $begingroup$
              An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
              $endgroup$
              – Reinderien
              11 hours ago











            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216199%2fgolf-game-boilerplate%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            7












            $begingroup$

            Some of this is nit-pickery, some is more fundamental:



            Import Order



            PEP-8 suggests an ordering to imports. No reason not to use it:




            Imports should be grouped in the following order:



            Standard library imports.
            Related third party imports.
            Local application/library specific imports.


            You should put a blank line between each group of imports.




            Code Organization: Constants



            You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.



            For example, you have a Ball class. Yet there are global constants named BALL_COLOR and BALL_OUTLINE_COLOR and BALL_RADIUS. Why is that? If they're related to your class, make them class constants.



            class Ball:
            BODY_COLOR = (255, 255, 255)
            OUTLINE_COLOR = (255, 0, 0)
            RADIUS = 10


            Code Organization: Types



            In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple or even two?



            import collections

            Size = collections.namedtuple('Size', 'width height')
            Position = collections.namedtuple('Position', 'x y')

            WINDOW_SIZE = Size(width=1500, height=800)
            START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)


            Code Organization: Functions



            You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main function, or some other-named function.



            def main():
            pg.init()
            pg.display.set_caption('Golf')
            ... etc ...


            You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window class or anything, but you might consider putting them into a Config class. (And using snake_case names.)



            Also, you have a lot of pygame boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:



            while still_playing:
            handle_events()
            update()
            render() # You call this "draw_window()" which is fine.


            Most of your logic, of course, will be in update(). In fact, since it mostly has to do with updating the position of the Ball object, it should mostly be in a call to ball.update_position(delay) (or some such name).



            You make use of a pair of temporaries x and y, but it seems like you could replace those with an old-position attribute on the Ball, or a second Ball object, or something.






            share|improve this answer









            $endgroup$


















              7












              $begingroup$

              Some of this is nit-pickery, some is more fundamental:



              Import Order



              PEP-8 suggests an ordering to imports. No reason not to use it:




              Imports should be grouped in the following order:



              Standard library imports.
              Related third party imports.
              Local application/library specific imports.


              You should put a blank line between each group of imports.




              Code Organization: Constants



              You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.



              For example, you have a Ball class. Yet there are global constants named BALL_COLOR and BALL_OUTLINE_COLOR and BALL_RADIUS. Why is that? If they're related to your class, make them class constants.



              class Ball:
              BODY_COLOR = (255, 255, 255)
              OUTLINE_COLOR = (255, 0, 0)
              RADIUS = 10


              Code Organization: Types



              In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple or even two?



              import collections

              Size = collections.namedtuple('Size', 'width height')
              Position = collections.namedtuple('Position', 'x y')

              WINDOW_SIZE = Size(width=1500, height=800)
              START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)


              Code Organization: Functions



              You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main function, or some other-named function.



              def main():
              pg.init()
              pg.display.set_caption('Golf')
              ... etc ...


              You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window class or anything, but you might consider putting them into a Config class. (And using snake_case names.)



              Also, you have a lot of pygame boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:



              while still_playing:
              handle_events()
              update()
              render() # You call this "draw_window()" which is fine.


              Most of your logic, of course, will be in update(). In fact, since it mostly has to do with updating the position of the Ball object, it should mostly be in a call to ball.update_position(delay) (or some such name).



              You make use of a pair of temporaries x and y, but it seems like you could replace those with an old-position attribute on the Ball, or a second Ball object, or something.






              share|improve this answer









              $endgroup$
















                7












                7








                7





                $begingroup$

                Some of this is nit-pickery, some is more fundamental:



                Import Order



                PEP-8 suggests an ordering to imports. No reason not to use it:




                Imports should be grouped in the following order:



                Standard library imports.
                Related third party imports.
                Local application/library specific imports.


                You should put a blank line between each group of imports.




                Code Organization: Constants



                You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.



                For example, you have a Ball class. Yet there are global constants named BALL_COLOR and BALL_OUTLINE_COLOR and BALL_RADIUS. Why is that? If they're related to your class, make them class constants.



                class Ball:
                BODY_COLOR = (255, 255, 255)
                OUTLINE_COLOR = (255, 0, 0)
                RADIUS = 10


                Code Organization: Types



                In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple or even two?



                import collections

                Size = collections.namedtuple('Size', 'width height')
                Position = collections.namedtuple('Position', 'x y')

                WINDOW_SIZE = Size(width=1500, height=800)
                START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)


                Code Organization: Functions



                You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main function, or some other-named function.



                def main():
                pg.init()
                pg.display.set_caption('Golf')
                ... etc ...


                You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window class or anything, but you might consider putting them into a Config class. (And using snake_case names.)



                Also, you have a lot of pygame boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:



                while still_playing:
                handle_events()
                update()
                render() # You call this "draw_window()" which is fine.


                Most of your logic, of course, will be in update(). In fact, since it mostly has to do with updating the position of the Ball object, it should mostly be in a call to ball.update_position(delay) (or some such name).



                You make use of a pair of temporaries x and y, but it seems like you could replace those with an old-position attribute on the Ball, or a second Ball object, or something.






                share|improve this answer









                $endgroup$



                Some of this is nit-pickery, some is more fundamental:



                Import Order



                PEP-8 suggests an ordering to imports. No reason not to use it:




                Imports should be grouped in the following order:



                Standard library imports.
                Related third party imports.
                Local application/library specific imports.


                You should put a blank line between each group of imports.




                Code Organization: Constants



                You have a bunch of "constants" defined. They're all-caps, which is good. They're declared together and at the top of the file, which is good. But they really shouldn't be global constants.



                For example, you have a Ball class. Yet there are global constants named BALL_COLOR and BALL_OUTLINE_COLOR and BALL_RADIUS. Why is that? If they're related to your class, make them class constants.



                class Ball:
                BODY_COLOR = (255, 255, 255)
                OUTLINE_COLOR = (255, 0, 0)
                RADIUS = 10


                Code Organization: Types



                In the same vein, you make a lot of use of tuples. But you just create them in-line and rely on convention to access them. Why not go ahead and use a collections.namedtuple or even two?



                import collections

                Size = collections.namedtuple('Size', 'width height')
                Position = collections.namedtuple('Position', 'x y')

                WINDOW_SIZE = Size(width=1500, height=800)
                START_POS = Position(x=0.5 * WINDOW_SIZE.width, y=0.99 * WINDOW_SIZE.height)


                Code Organization: Functions



                You have a lot of stuff at module scope. Sooner or later you'll want to either write a unit test, or run the debugger, or load your code into the command-line Python REPL. All of this is made easier if you move the module-scope statements into a main function, or some other-named function.



                def main():
                pg.init()
                pg.display.set_caption('Golf')
                ... etc ...


                You have a set of font/color variables that you create at module scope. There aren't currently enough drawing functions to create a Window class or anything, but you might consider putting them into a Config class. (And using snake_case names.)



                Also, you have a lot of pygame boilerplate mixed in with your game logic. I'd suggest separating the boilerplate into separate functions, something like:



                while still_playing:
                handle_events()
                update()
                render() # You call this "draw_window()" which is fine.


                Most of your logic, of course, will be in update(). In fact, since it mostly has to do with updating the position of the Ball object, it should mostly be in a call to ball.update_position(delay) (or some such name).



                You make use of a pair of temporaries x and y, but it seems like you could replace those with an old-position attribute on the Ball, or a second Ball object, or something.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 21 hours ago









                Austin HastingsAustin Hastings

                7,5071233




                7,5071233

























                    6












                    $begingroup$

                    Overall it isn't bad.



                    Direct imports for common symbols



                    Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.



                    from pg.font import SysFont
                    # ...
                    strokeFont = SysFont("monospace", 50)


                    snake_case



                    i.e. stroke_font for variables and function names. Also, Penalty should be lower-case because it isn't a class.



                    debug printing



                    This kind of thing:



                    print('     x-pos: %spx' % str(round(dx + x)))


                    can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.



                    Also: do you really need round? Could you instead go



                    print(f'    x-pos: {dx + x:.0f}px')


                    f-strings



                    As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the % operator.



                    Global clutter



                    It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.



                    Shadowing



                    Don't call something time. time is already a thing in Python.



                    Math



                    I kind of had to jump through some hoops to take advantage of atan2. I don't recommend doing this, but here's a one-liner alternative to your quadrant function:



                    return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1





                    share|improve this answer











                    $endgroup$













                    • $begingroup$
                      Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
                      $endgroup$
                      – Alex
                      15 hours ago










                    • $begingroup$
                      I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
                      $endgroup$
                      – alec935
                      15 hours ago










                    • $begingroup$
                      I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
                      $endgroup$
                      – Wombatz
                      11 hours ago










                    • $begingroup$
                      An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
                      $endgroup$
                      – Reinderien
                      11 hours ago
















                    6












                    $begingroup$

                    Overall it isn't bad.



                    Direct imports for common symbols



                    Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.



                    from pg.font import SysFont
                    # ...
                    strokeFont = SysFont("monospace", 50)


                    snake_case



                    i.e. stroke_font for variables and function names. Also, Penalty should be lower-case because it isn't a class.



                    debug printing



                    This kind of thing:



                    print('     x-pos: %spx' % str(round(dx + x)))


                    can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.



                    Also: do you really need round? Could you instead go



                    print(f'    x-pos: {dx + x:.0f}px')


                    f-strings



                    As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the % operator.



                    Global clutter



                    It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.



                    Shadowing



                    Don't call something time. time is already a thing in Python.



                    Math



                    I kind of had to jump through some hoops to take advantage of atan2. I don't recommend doing this, but here's a one-liner alternative to your quadrant function:



                    return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1





                    share|improve this answer











                    $endgroup$













                    • $begingroup$
                      Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
                      $endgroup$
                      – Alex
                      15 hours ago










                    • $begingroup$
                      I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
                      $endgroup$
                      – alec935
                      15 hours ago










                    • $begingroup$
                      I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
                      $endgroup$
                      – Wombatz
                      11 hours ago










                    • $begingroup$
                      An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
                      $endgroup$
                      – Reinderien
                      11 hours ago














                    6












                    6








                    6





                    $begingroup$

                    Overall it isn't bad.



                    Direct imports for common symbols



                    Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.



                    from pg.font import SysFont
                    # ...
                    strokeFont = SysFont("monospace", 50)


                    snake_case



                    i.e. stroke_font for variables and function names. Also, Penalty should be lower-case because it isn't a class.



                    debug printing



                    This kind of thing:



                    print('     x-pos: %spx' % str(round(dx + x)))


                    can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.



                    Also: do you really need round? Could you instead go



                    print(f'    x-pos: {dx + x:.0f}px')


                    f-strings



                    As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the % operator.



                    Global clutter



                    It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.



                    Shadowing



                    Don't call something time. time is already a thing in Python.



                    Math



                    I kind of had to jump through some hoops to take advantage of atan2. I don't recommend doing this, but here's a one-liner alternative to your quadrant function:



                    return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1





                    share|improve this answer











                    $endgroup$



                    Overall it isn't bad.



                    Direct imports for common symbols



                    Based on your discretion, certain often-used and unambiguous symbols can be imported without their module namespace, i.e.



                    from pg.font import SysFont
                    # ...
                    strokeFont = SysFont("monospace", 50)


                    snake_case



                    i.e. stroke_font for variables and function names. Also, Penalty should be lower-case because it isn't a class.



                    debug printing



                    This kind of thing:



                    print('     x-pos: %spx' % str(round(dx + x)))


                    can be improved in a few ways. Firstly, it looks like a debugging output and not actual game content, so typically you won't want to print this at all. That doesn't mean that you have to delete it, though - you can use actual Python logging at level debug to be able to select at the top level whether these statements are printed.



                    Also: do you really need round? Could you instead go



                    print(f'    x-pos: {dx + x:.0f}px')


                    f-strings



                    As in the previous example, you should consider using the new syntactical sugar of f-strings instead of the % operator.



                    Global clutter



                    It's tempting in Python to add a bunch of stuff (x, y, time, power, etc.) to the global scope. Don't give in! Put these into a game state object. Break up your global code into multiple subroutines, potentially in methods of the game state object.



                    Shadowing



                    Don't call something time. time is already a thing in Python.



                    Math



                    I kind of had to jump through some hoops to take advantage of atan2. I don't recommend doing this, but here's a one-liner alternative to your quadrant function:



                    return int(4 + 2/pi*atan2(y - ym, xm - x)) % 4 + 1






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited 21 hours ago

























                    answered 22 hours ago









                    ReinderienReinderien

                    4,565823




                    4,565823












                    • $begingroup$
                      Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
                      $endgroup$
                      – Alex
                      15 hours ago










                    • $begingroup$
                      I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
                      $endgroup$
                      – alec935
                      15 hours ago










                    • $begingroup$
                      I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
                      $endgroup$
                      – Wombatz
                      11 hours ago










                    • $begingroup$
                      An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
                      $endgroup$
                      – Reinderien
                      11 hours ago


















                    • $begingroup$
                      Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
                      $endgroup$
                      – Alex
                      15 hours ago










                    • $begingroup$
                      I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
                      $endgroup$
                      – alec935
                      15 hours ago










                    • $begingroup$
                      I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
                      $endgroup$
                      – Wombatz
                      11 hours ago










                    • $begingroup$
                      An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
                      $endgroup$
                      – Reinderien
                      11 hours ago
















                    $begingroup$
                    Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
                    $endgroup$
                    – Alex
                    15 hours ago




                    $begingroup$
                    Note: f-strings only work in Python 3. Using print(...) could be seen as indication that the OP works in Python 3, but the tags itself don't show what Python version is actually used.
                    $endgroup$
                    – Alex
                    15 hours ago












                    $begingroup$
                    I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
                    $endgroup$
                    – alec935
                    15 hours ago




                    $begingroup$
                    I'm using python 3. I use f-strings for sentences with many variables but the %s formatting is just by habit
                    $endgroup$
                    – alec935
                    15 hours ago












                    $begingroup$
                    I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
                    $endgroup$
                    – Wombatz
                    11 hours ago




                    $begingroup$
                    I would disagree on the "shadowing". If we didn't allow to use names that are also standard library modules we wouldn't be able to use these as well: code, warnings, cmd, email, signal, queue, symbol. And there is probably a pypi package for every convenient variable name. I understand that time is a very well known module, but where do we draw the line? My main point is, that OP doesn't even use the module so they don't shadow anything at all. If they did import that module i would totally agree with you.
                    $endgroup$
                    – Wombatz
                    11 hours ago












                    $begingroup$
                    An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
                    $endgroup$
                    – Reinderien
                    11 hours ago




                    $begingroup$
                    An easy place to draw the line is - do what PyCharm does. It has this check built-in. Obviously pip package names are not a problem unless they're in your requirements, but the more common Python library names should be avoided.
                    $endgroup$
                    – Reinderien
                    11 hours ago


















                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216199%2fgolf-game-boilerplate%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Færeyskur hestur Heimild | Tengill | Tilvísanir | LeiðsagnarvalRossið - síða um færeyska hrossið á færeyskuGott ár hjá færeyska hestinum

                    He _____ here since 1970 . Answer needed [closed]What does “since he was so high” mean?Meaning of “catch birds for”?How do I ensure “since” takes the meaning I want?“Who cares here” meaningWhat does “right round toward” mean?the time tense (had now been detected)What does the phrase “ring around the roses” mean here?Correct usage of “visited upon”Meaning of “foiled rail sabotage bid”It was the third time I had gone to Rome or It is the third time I had been to Rome

                    Slayer Innehåll Historia | Stil, komposition och lyrik | Bandets betydelse och framgångar | Sidoprojekt och samarbeten | Kontroverser | Medlemmar | Utmärkelser och nomineringar | Turnéer och festivaler | Diskografi | Referenser | Externa länkar | Navigeringsmenywww.slayer.net”Metal Massacre vol. 1””Metal Massacre vol. 3””Metal Massacre Volume III””Show No Mercy””Haunting the Chapel””Live Undead””Hell Awaits””Reign in Blood””Reign in Blood””Gold & Platinum – Reign in Blood””Golden Gods Awards Winners”originalet”Kerrang! Hall Of Fame””Slayer Looks Back On 37-Year Career In New Video Series: Part Two””South of Heaven””Gold & Platinum – South of Heaven””Seasons in the Abyss””Gold & Platinum - Seasons in the Abyss””Divine Intervention””Divine Intervention - Release group by Slayer””Gold & Platinum - Divine Intervention””Live Intrusion””Undisputed Attitude””Abolish Government/Superficial Love””Release “Slatanic Slaughter: A Tribute to Slayer” by Various Artists””Diabolus in Musica””Soundtrack to the Apocalypse””God Hates Us All””Systematic - Relationships””War at the Warfield””Gold & Platinum - War at the Warfield””Soundtrack to the Apocalypse””Gold & Platinum - Still Reigning””Metallica, Slayer, Iron Mauden Among Winners At Metal Hammer Awards””Eternal Pyre””Eternal Pyre - Slayer release group””Eternal Pyre””Metal Storm Awards 2006””Kerrang! Hall Of Fame””Slayer Wins 'Best Metal' Grammy Award””Slayer Guitarist Jeff Hanneman Dies””Bullet-For My Valentine booed at Metal Hammer Golden Gods Awards””Unholy Aliance””The End Of Slayer?””Slayer: We Could Thrash Out Two More Albums If We're Fast Enough...””'The Unholy Alliance: Chapter III' UK Dates Added”originalet”Megadeth And Slayer To Co-Headline 'Canadian Carnage' Trek”originalet”World Painted Blood””Release “World Painted Blood” by Slayer””Metallica Heading To Cinemas””Slayer, Megadeth To Join Forces For 'European Carnage' Tour - Dec. 18, 2010”originalet”Slayer's Hanneman Contracts Acute Infection; Band To Bring In Guest Guitarist””Cannibal Corpse's Pat O'Brien Will Step In As Slayer's Guest Guitarist”originalet”Slayer’s Jeff Hanneman Dead at 49””Dave Lombardo Says He Made Only $67,000 In 2011 While Touring With Slayer””Slayer: We Do Not Agree With Dave Lombardo's Substance Or Timeline Of Events””Slayer Welcomes Drummer Paul Bostaph Back To The Fold””Slayer Hope to Unveil Never-Before-Heard Jeff Hanneman Material on Next Album””Slayer Debut New Song 'Implode' During Surprise Golden Gods Appearance””Release group Repentless by Slayer””Repentless - Slayer - Credits””Slayer””Metal Storm Awards 2015””Slayer - to release comic book "Repentless #1"””Slayer To Release 'Repentless' 6.66" Vinyl Box Set””BREAKING NEWS: Slayer Announce Farewell Tour””Slayer Recruit Lamb of God, Anthrax, Behemoth + Testament for Final Tour””Slayer lägger ner efter 37 år””Slayer Announces Second North American Leg Of 'Final' Tour””Final World Tour””Slayer Announces Final European Tour With Lamb of God, Anthrax And Obituary””Slayer To Tour Europe With Lamb of God, Anthrax And Obituary””Slayer To Play 'Last French Show Ever' At Next Year's Hellfst””Slayer's Final World Tour Will Extend Into 2019””Death Angel's Rob Cavestany On Slayer's 'Farewell' Tour: 'Some Of Us Could See This Coming'””Testament Has No Plans To Retire Anytime Soon, Says Chuck Billy””Anthrax's Scott Ian On Slayer's 'Farewell' Tour Plans: 'I Was Surprised And I Wasn't Surprised'””Slayer””Slayer's Morbid Schlock””Review/Rock; For Slayer, the Mania Is the Message””Slayer - Biography””Slayer - Reign In Blood”originalet”Dave Lombardo””An exclusive oral history of Slayer”originalet”Exclusive! Interview With Slayer Guitarist Jeff Hanneman”originalet”Thinking Out Loud: Slayer's Kerry King on hair metal, Satan and being polite””Slayer Lyrics””Slayer - Biography””Most influential artists for extreme metal music””Slayer - Reign in Blood””Slayer guitarist Jeff Hanneman dies aged 49””Slatanic Slaughter: A Tribute to Slayer””Gateway to Hell: A Tribute to Slayer””Covered In Blood””Slayer: The Origins of Thrash in San Francisco, CA.””Why They Rule - #6 Slayer”originalet”Guitar World's 100 Greatest Heavy Metal Guitarists Of All Time”originalet”The fans have spoken: Slayer comes out on top in readers' polls”originalet”Tribute to Jeff Hanneman (1964-2013)””Lamb Of God Frontman: We Sound Like A Slayer Rip-Off””BEHEMOTH Frontman Pays Tribute To SLAYER's JEFF HANNEMAN””Slayer, Hatebreed Doing Double Duty On This Year's Ozzfest””System of a Down””Lacuna Coil’s Andrea Ferro Talks Influences, Skateboarding, Band Origins + More””Slayer - Reign in Blood””Into The Lungs of Hell””Slayer rules - en utställning om fans””Slayer and Their Fans Slashed Through a No-Holds-Barred Night at Gas Monkey””Home””Slayer””Gold & Platinum - The Big 4 Live from Sofia, Bulgaria””Exclusive! Interview With Slayer Guitarist Kerry King””2008-02-23: Wiltern, Los Angeles, CA, USA””Slayer's Kerry King To Perform With Megadeth Tonight! - Oct. 21, 2010”originalet”Dave Lombardo - Biography”Slayer Case DismissedArkiveradUltimate Classic Rock: Slayer guitarist Jeff Hanneman dead at 49.”Slayer: "We could never do any thing like Some Kind Of Monster..."””Cannibal Corpse'S Pat O'Brien Will Step In As Slayer'S Guest Guitarist | The Official Slayer Site”originalet”Slayer Wins 'Best Metal' Grammy Award””Slayer Guitarist Jeff Hanneman Dies””Kerrang! Awards 2006 Blog: Kerrang! Hall Of Fame””Kerrang! Awards 2013: Kerrang! Legend”originalet”Metallica, Slayer, Iron Maien Among Winners At Metal Hammer Awards””Metal Hammer Golden Gods Awards””Bullet For My Valentine Booed At Metal Hammer Golden Gods Awards””Metal Storm Awards 2006””Metal Storm Awards 2015””Slayer's Concert History””Slayer - Relationships””Slayer - Releases”Slayers officiella webbplatsSlayer på MusicBrainzOfficiell webbplatsSlayerSlayerr1373445760000 0001 1540 47353068615-5086262726cb13906545x(data)6033143kn20030215029