# Pong in Python using Pygame

Hi I have written the following code in Python. Any improvements on my code will be appreciated as this is my first shot at pygame!

There are some problems in terms of functionality, to state briefly ball sometimes gets stuck within a paddle, which I am continuing to resolve

import pygame, sys
import random
from math import degrees, radians, sin, cos

pygame.init()

# Create a screen
win = pygame.display.set_mode((800,700))
win_info = pygame.display.Info()
win_w, win_h = win_info.current_w, win_info.current_h
pygame.display.set_caption("Pong")

speed = 10
running = True
fps = 30

#############################################################################

class border(pygame.sprite.Sprite):
width = win_w
height = 10

def __init__(self,width, height, colour):
pygame.sprite.Sprite.__init__(self)

self.image = pygame.Surface((width,height))
self.image.fill(colour)

self.rect = self.image.get_rect()

def draw(self):
win.blit(self.image, self.rect)

#############################################################################

"""left arg refer to top left corner coordinate of paddle
"""

def __init__(self,width, height, speed, colour):
pygame.sprite.Sprite.__init__(self)

self.width = width
self.height = height

self.image = pygame.Surface([width, height])
self.image.fill(colour)

self.speed = speed
self.vel_x = 0
self.vel_y = 0

# Size of rectangle
self.rect = self.image.get_rect()

def move(self, x, y):
self.rect.move_ip(x,y)

def moveTo(self, x, y):
self.rect.topleft = (x,y)

def draw(self):
win.blit(self.image, (self.rect.x,self.rect.y))

#############################################################################

class ball(pygame.sprite.Sprite):
""" Facing takes 1 or -1 to determine direction of movement
"""
pygame.sprite.Sprite.__init__(self)

self.rect = self.image.get_rect()
# Replace facing when finalising implementation of ball
self.facing = -1
self.factor = factor
self.vel_x, self.vel_y = self.randomMovement(factor)

def move(self, x,y):
self.rect.move_ip(x,y)

def moveTo(self, x, y):
self.rect.center = (x,y)

self.vel_x = self.vel_x * -1

def draw(self):
win.blit(self.image, (self.rect))

def randomMovement(self,factor=None):
#unit vector
if factor == None:
# Used for all calls after ball instance is initialised
factor = self.factor
rand_y = random.randrange(-1,2,2)
y = sin(angle)
x = cos(angle)
""" How to get over the use of int and rounding errors. Using int the line before causes the vector to be 0,0 as the numbers after 0 are truncated"""
self.vel_x, self.vel_y = (int(x*factor),int(y*factor)*rand_y)
#print(x, y)
return self.vel_x, self.vel_y

#############################################################################

# Main Game Loop
def main():
white = (255,255,255)
border_height = 10

# Set X and Y of left pad

# Set X and Y of right pad

top_border = border(win_w, border_height, white)
top_border.rect.topleft = (0, 0)
bottom_border = border(win_w, border_height, white)
bottom_border.rect.topleft = (0, win_h-border_height)
gameBall = ball(10, 10, white)
gameBall.rect.center = (int(win_w/2), int(win_h/2))

redrawGameWin(to_draw)

backGround = pygame.Surface((win_w,win_h))
backGround.fill(white)

clock = pygame.time.Clock()

# Game Loop
while running:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.display.quit()
pygame.quit()
sys.exit()

keys = pygame.key.get_pressed()
# Left pad moving up with top boundary collision
if keys[pygame.K_s] and pad_left.rect.y > 0:
else:

# Left pad moving down with bottom boundary collision
if keys[pygame.K_x]:
else:

if not keys[pygame.K_s] and not keys[pygame.K_x]:

# Right pad moving up with top boundary collision
if keys[pygame.K_k]:
else:

# Right pad moving down with bottom boundary collision
if keys[pygame.K_m]:
else:

if not keys[pygame.K_k] and not keys[pygame.K_m]:

# Ball bouncing off top border
if gameBall.rect.colliderect(top_border.rect) or gameBall.rect.colliderect(bottom_border.rect):
gameBall.vel_y = gameBall.vel_y*-1

# Ball bouncing off right pad

# Ball bouncing off left pad

# Ball moving by velocity vector
gameBall.move(gameBall.vel_x, gameBall.vel_y)

# Ball moves off screem
if gameBall.rect.left >= win_w or gameBall.rect.right <=0:
gameBall.randomMovement()
gameBall.moveTo(int(win_w/2), int(win_h/2))

redrawGameWin(to_draw)

#############################################################################

def redrawGameWin(shapes):
#global dirty_rects
win.fill((0,0,0))
for shape in shapes:
shape.draw()

pygame.display.flip()

#############################################################################

if __name__ == "__main__":
main()


• Welcome to Code Review! Are you interested in adding features to your code or in reviewing the existing code? Please read the help center before answering that. – Mast Mar 16 at 10:21
• @Mast Reviewing the existing code! And trying to address the problems I have outlined – KOOTS HOOTS Mar 17 at 8:00
• Reviewing the existing code is on topic for here but addressing that list of problems is not. You should likely edit the question to indicate that there are some functional problems but they will be addressed separately on stack overflow. – Reinderien Mar 17 at 13:02
• @Reinderien Will do that now! – KOOTS HOOTS Mar 18 at 14:09

## Global code

This stuff:

pygame.init()

# Create a screen
win = pygame.display.set_mode((800,700))
win_info = pygame.display.Info()
win_w, win_h = win_info.current_w, win_info.current_h
pygame.display.set_caption("Pong")


should be moved into one or more functions. These:

pad_colour = (255,255,255) #White colour

speed = 10
running = True
fps = 30


can probably stay in global scope as ALL_CAPS constants.

## Class names

border should be Border since it's a class name. The same goes for Paddle.

## Statics

class border(pygame.sprite.Sprite):
width = win_w
height = 10

def __init__(self,width, height, colour):


I don't think that the first width/height variables do what you think they do. Currently they can be deleted. If you want them to take effect as defaults, then you'd actually write

class border(pygame.sprite.Sprite):
def __init__(self, colour, width=win_w, height=10):


## Method names

should be snake_case, i.e.

random_movement, collide_pad, etc.

## Don't repeat yourself

The code to check the left/right pad is very repetitive. Think about what is shared and what differs, and factor out the differing stuff. So far as I can see, the differences include the references to the key constants K_s, some distance calculations, etc.