5
\$\begingroup\$

I am making a snake game and I'm almost done but I want to make my code more organized so people can understand so how to do it.

# -*- coding: utf-8 -*-
import pygame
import random
import time
import thread


##########################Functions#####################################################################################
def startBackground():
    gameDisplay.blit(introBackground, [0, 0])


def snake(block_size, snakeBody):
    if direction == "right":
        head = pygame.transform.rotate(img, 270)
    if direction == "left":
        head = pygame.transform.rotate(img, 90)
    if direction == "up":
        head = img
    if direction == "down":
        head = pygame.transform.rotate(img, 180)
    gameDisplay.blit(head, (snakeBody[-1][0], snakeBody[-1][1]))
    for XnY in snakeBody[:-1]:
        pygame.draw.rect(gameDisplay, green, [XnY[0], XnY[1], block_size, block_size])


def background():
    for x in range(display_width / Background.get_width() + 1):
        for y in range(display_height / Background.get_height() + 1):
            gameDisplay.blit(Background, (x * 100, y * 100))


def Apple(random_x_coordinates, random_y_coordinates):
    gameDisplay.blit(apple, (random_x_coordinates, random_y_coordinates))


def gameLoop():
    music("Sleep Away.mp3", -1)
    direction = "right"
    global FPS
    FPS = 30
    score = 0
    snakeBody = []
    snakeLength = 5
    random_Apple_x_coordinates = round(random.randrange(0, display_width - block_size) / 10.0) * 10.0
    random_Apple_y_coordinates = round(random.randrange(0, display_height - block_size) / 10.0) * 10.0
    lead_x = display_width / 2
    lead_x_change = 10
    lead_y = display_height / 2
    lead_y_change = 0
    gameExit = True
    gameOver = False
    speed = 10
    flag = 1
    flag2 = 0
    fail_music_play = True
    global direction
    while gameExit:
        while gameOver:
            gameDisplay.fill(white)
            if fail_music_play is True:
                music("fail.mp3")
                fail_music_play = False
            message_to_screen("Game Over !!!", red, -50)
            message_to_screen("your score is " + str(score), red, 0)
            message_to_screen("Press 'R' To Play again or 'Q' to Quit", red, 50)
            pygame.display.update()
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        gameExit = False
                        gameOver = False
                    if event.key == pygame.K_r:
                        gameLoop()
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                ####################################################################################################################################################################################
                # Screen Boundries
        if lead_x < 0 or lead_x >= display_width or lead_y >= display_height or lead_y < 0:
            gameOver = True
        ####################################################################################################################################################################################
        # Snake Movement
        lead_x += lead_x_change
        lead_y += lead_y_change
        ####################################################################################################################################################################################
        # Apple location and Creation
        Apple(random_Apple_x_coordinates, random_Apple_y_coordinates)
        ####################################################################################################################################################################################
        # Collision Detection
        for eachSegment in snakeBody[:-1]:
            if eachSegment == snakeHead:
                gameOver = True
        # Snake Body & Length
        # snakeBody = []
        # snakeLength = 1
        snakeHead = [lead_x, lead_y]
        snakeBody.append(snakeHead)
        if len(snakeBody) > snakeLength:
            del snakeBody[0]
        snake(block_size, snakeBody)
        clock.tick(FPS)
        ####################################################################################################################################################################################
        if ((random_Apple_x_coordinates <= lead_x < random_Apple_x_coordinates + apple_thickness) or (
                        random_Apple_x_coordinates < lead_x + block_size <= random_Apple_x_coordinates + apple_thickness)) and (
                    (random_Apple_y_coordinates <= lead_y < random_Apple_y_coordinates + apple_thickness) or (
                                random_Apple_y_coordinates < lead_y + block_size <= random_Apple_y_coordinates + apple_thickness)):
            random_Apple_x_coordinates = round(random.randrange(0, display_width - block_size))  # / 10.0) * 10.0
            random_Apple_y_coordinates = round(random.randrange(0, display_height - block_size))  # / 10.0) * 10.0
            gameDisplay.fill(purple, rect=[random_Apple_x_coordinates, random_Apple_y_coordinates, apple_thickness,
                                           apple_thickness])
            snakeLength += 1
            score += 1
            if score % 11 == 0:
                speed += 2
            ####################################################################################################################################################################################
        pygame.display.update()
        background()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                gameExit = False
                gameOver = False
            if event.type == pygame.KEYDOWN:
                global flag, flag2, direction
                if event.key == pygame.K_LEFT:
                    if not flag == 1:
                        direction = "left"
                        lead_x_change = -speed
                        lead_y_change = 0
                        flag2 = 0
                        flag = 1
                elif event.key == pygame.K_RIGHT:
                    if not flag == 1:
                        direction = "right"
                        lead_x_change = speed
                        lead_y_change = 0
                        flag2 = 0
                        flag = 1
                elif event.key == pygame.K_UP:
                    if not flag2 == 1:
                        direction = "up"
                        lead_y_change = -speed
                        lead_x_change = 0
                        flag2 = 1
                        flag = 0
                elif event.key == pygame.K_DOWN:
                    if not flag2 == 1:
                        direction = "down"
                        lead_y_change = speed
                        lead_x_change = 0
                        flag2 = 1
                        flag = 0
    pygame.quit()
    quit()


def music(song_name, repeat=0):
    pygame.init()
    pygame.mixer.init()
    pygame.mixer.music.load(song_name)
    pygame.mixer.music.play(repeat)


####################  Images   #####################################################################################################################################################
img = pygame.image.load('SnakeHead.png')
Background = pygame.image.load("grass.png")
snakeIcon = pygame.image.load('snake.png')
apple = pygame.image.load('apple.png')
introBackground = pygame.image.load("startBackground.jpg")
####################  COLORS  #######################################################################################################################################################
white = (255, 255, 255)
black = (0, 0, 0)
bright_red = (255, 0, 0)
red = (155, 0, 0)
brown = (33, 20, 18)
purple = (150, 50, 150)
bright_purple = (255, 50, 255)
green = (20, 150, 20)
bright_green = (20, 255, 20)
blue = (30, 0, 150)
bright_blue = (0, 0, 255)
#####################################################################################################################################################################################
display_width = 800
display_height = 600
FPS = 30
score = 0
block_size = 20
apple_thickness = 20
direction = "right"
speed = 10
pause = False
#####################################################################################################################################################################################
rect_width = display_width / 4.0
rect_height = display_height / 12.0
####################################################################################################################################################################################
rect_x_corner = (display_width / 2) - rect_width / 2  # x-ccoordinate of head of the head of the rectangle
rect1_y_corner = (display_height * 1 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 1
rect2_y_corner = (display_height * 2 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 2
rect3_y_corner = (display_height * 3 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 3
rect4_y_corner = (display_height * 4 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 4
#####################################################################################################################################################################################
rect1_y_displace = -(display_height / 2) + rect1_y_corner + (rect_height / 2)
rect2_y_displace = -(display_height / 2) + rect2_y_corner + (rect_height / 2)
rect3_y_displace = -(display_height / 2) + rect3_y_corner + (rect_height / 2)
rect4_y_displace = -(display_height / 2) + rect4_y_corner + (rect_height / 2)
#####################################################################################################################################################################################
inactive_color1 = purple
inactive_color2 = green
inactive_color3 = red
inactive_color4 = blue
#####################################################################################################################################################################################
active_color1 = bright_purple
active_color2 = bright_green
active_color3 = bright_red
active_color4 = bright_blue
#####################################################################################################################################################################################
rect1_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)
rect2_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)
rect3_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)
rect4_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)


def Credits():
    game_credits = True
    gameDisplay.fill(white)
    while game_credits is True:
        for event in pygame.event.get():
            clock.tick(100)
            # def button(rect_x_corner, rect_y_corner, rect_width, rect_height, inactive_color, active_color, text_color, text="Random_Text", action=None):
            message_to_screen("The Game is made by :", green, -150, "freesasbold.ttf", 50)
            message_to_screen("Ahmed Tarek", red, -40)
            message_to_screen("omar Wael", black, 40)
            button((rect_x_corner + 200), (rect3_y_corner + 100), rect_width, rect_height, inactive_color3,
                   active_color3, black, "Back", "Back")
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
        pygame.display.update()


def text_objects(text, color, text_style="freesansbold.ttf", text_size=40):
    font = pygame.font.Font(text_style, text_size)  # change None to Font Style and change 25 to change font size
    textSurface = font.render(text, True, color)
    return textSurface, textSurface.get_rect()


def message_to_screen(msg, color, y_displace, text_style="freesansbold.ttf", text_size=30):
    textSurface, textRectangle = text_objects(msg, color)
    textRectangle.center = (display_width / 2), (display_height / 2) + y_displace
    gameDisplay.blit(textSurface, textRectangle)


def message_to_rect(msg, color, rect_x_center, rect_y_center, text_style=None, text_size=25):
    textSurface, textRectangle = text_objects(msg, color, text_style, text_size)
    textRectangle.center = rect_x_center, rect_y_center
    gameDisplay.blit(textSurface, textRectangle)


####################################################################################################################################################################################
def button(rect_x_corner, rect_y_corner, rect_width, rect_height, inactive_color, active_color, text_color,
           text="Random_Text", action=None):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    rect_x_center = rect_x_corner + (rect_width / 2)
    rect_y_center = rect_y_corner + (rect_height / 2)
    gameDisplay.fill(inactive_color, rect=[rect_x_corner, rect_y_corner, rect_width, rect_height])
    if rect_x_corner + rect_width > mouse[0] > rect_x_corner and rect_y_corner + rect_height > mouse[1] > rect_y_corner:
        gameDisplay.fill(active_color, rect=[rect_x_corner, rect_y_corner, rect_width, rect_height])
        message_to_rect(text, text_color, rect_x_center, rect_y_center)
        pygame.display.update()
        if click[0] == 1 and action is not None:
            if action == "Play":
                gameLoop()
            if action == "Credits":
                Credits()
            if action == "Quit":
                pygame.quit()
                quit()
            if action == "Back":
                startBackground()
                pygame.display.update()
                game_intro()
    else:
        gameDisplay.fill(inactive_color, rect=[rect_x_corner, rect_y_corner, rect_width, rect_height])
        message_to_rect(text, text_color, rect_x_center, rect_y_center)


####################################################################################################################################################################################
def game_intro():
    game = True
    while game:
        # User Can Only Quit the game OR Use the button Function to Escape the menu, using the buttons!
        for event in pygame.event.get():
            button(rect_x_corner, rect1_y_corner, rect_width, rect_height, inactive_color1, active_color1, black,
                   "Play", "Play")
            button(rect_x_corner, rect2_y_corner, rect_width, rect_height, inactive_color2, active_color2, black,
                   "Options", "Options")
            button(rect_x_corner, rect3_y_corner, rect_width, rect_height, inactive_color3, active_color3, black,
                   "Credits", "Credits")
            button(rect_x_corner, rect4_y_corner, rect_width, rect_height, inactive_color4, active_color4, black,
                   "Quit", "Quit")
            if event.type == pygame.QUIT:
                game = False
                pygame.quit()
                quit()
            pygame.display.update()


####################################################################################################################################################################################
pygame.init()
####################################################################################################################################################################################
pygame.display.set_caption("Snake 🐍")
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
pygame.display.set_icon(snakeIcon)
####################################################################################################################################################################################
startBackground()
pygame.display.update()
game = True
clock.tick(30)
game_intro()

I would appreciate it if you could help me because this is my project and I have to give it to the instructor tomorrow.

\$\endgroup\$
  • \$\begingroup\$ any one can help please \$\endgroup\$ – omar wael Sep 2 '16 at 18:34
1
\$\begingroup\$

Your code overall is pretty structured, but here are some improvement suggestions. The acronym for them is called PBS:

1. 'P'ut DocStrings in all of your functions

I cannot stress enough how important it is to document large projects, such as yours. Six to seven months from now, you'll be looking at your code thinking: 'what the heck is this?'.

To give an exmaple of what I mean, take your function startBackground().It creates the intro screen to your game, So this is the DocString I would add:

def startBackground():
    '''
       Blits the intro screen of the game.
    '''
    gameDisplay.blit(introBackground, [0, 0])

While the DocString above may seem trivial, it will certainly matter for some of your large functions, such as gameLoop() or snake().

When creating your DocStrings, just remember that your trying to summarize exactly what your function does. you could even add a description of the arguments your function takes.

2. 'B'e consistent

I've noticed a few places where you switch between naming conventions.

Take here for example

def Apple(random_x_coordinates, random_y_coordinates):
    gameDisplay.blit(apple, (random_x_coordinates, random_y_coordinates))

All your other function start with lower case, so change this function to

def apple(random_x_coordinates, random_y_coordinates):
    gameDisplay.blit(apple, (random_x_coordinates, random_y_coordinates))

Also, there are certain common naming conventions for python. Mr.Lott, already has a nice answer on Stack Overflow that cites documentation from the PEP 8 styling guide for python. The documentation that he cites is concerning naming conventions. It reads:

Function names should be lowercase, with words separated by underscores as necessary to improve readability. mixedCase is allowed only in contexts where that's already the prevailing style. [For Variables] Use the function naming rules: lowercase with words separated by underscores as necessary to improve readability.

The most important thing to remember about naming convention for any programming language is pick one style, and stick to it. Nothing makes code more un-readable than mixed naming conventions.

3. 'S'tructure you code

Your code structure is a bit sloppy. There are lines down at the bottem that should be near the top. In fact I would move all of this:

####################  Images   #####################################################################################################################################################
img = pygame.image.load('SnakeHead.png')
Background = pygame.image.load("grass.png")
snakeIcon = pygame.image.load('snake.png')
apple = pygame.image.load('apple.png')
introBackground = pygame.image.load("startBackground.jpg")
####################  COLORS  #######################################################################################################################################################
white = (255, 255, 255)
black = (0, 0, 0)
bright_red = (255, 0, 0)
red = (155, 0, 0)
brown = (33, 20, 18)
purple = (150, 50, 150)
bright_purple = (255, 50, 255)
green = (20, 150, 20)
bright_green = (20, 255, 20)
blue = (30, 0, 150)
bright_blue = (0, 0, 255)
#####################################################################################################################################################################################
display_width = 800
display_height = 600
FPS = 30
score = 0
block_size = 20
apple_thickness = 20
direction = "right"
speed = 10
pause = False
#####################################################################################################################################################################################
rect_width = display_width / 4.0
rect_height = display_height / 12.0
####################################################################################################################################################################################
rect_x_corner = (display_width / 2) - rect_width / 2  # x-ccoordinate of head of the head of the rectangle
rect1_y_corner = (display_height * 1 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 1
rect2_y_corner = (display_height * 2 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 2
rect3_y_corner = (display_height * 3 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 3
rect4_y_corner = (display_height * 4 / 5.0) - (rect_height / 2)  # y-ccoordinate of head of the head of rectangle 4
#####################################################################################################################################################################################
rect1_y_displace = -(display_height / 2) + rect1_y_corner + (rect_height / 2)
rect2_y_displace = -(display_height / 2) + rect2_y_corner + (rect_height / 2)
rect3_y_displace = -(display_height / 2) + rect3_y_corner + (rect_height / 2)
rect4_y_displace = -(display_height / 2) + rect4_y_corner + (rect_height / 2)
#####################################################################################################################################################################################
inactive_color1 = purple
inactive_color2 = green
inactive_color3 = red
inactive_color4 = blue
#####################################################################################################################################################################################
active_color1 = bright_purple
active_color2 = bright_green
active_color3 = bright_red
active_color4 = bright_blue
#####################################################################################################################################################################################
rect1_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)
rect2_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)
rect3_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)
rect4_x_displace = - (display_width / 2) + rect_x_corner + (rect_width / 2)

To the top of your code, right after your imports. The goal is to get your program to flow seamlessly down until the last line. Take this code snippet for example:

def print_number(sumation):
    print(sumation)

num1 = int(input("Enter number 1: "))
num2 = int(input("Enter number 2: "))

def add_number(a, b):
    return a + b

sumation = add_number(num1, num2)
print_number(sumation)

While the code above works, it does not flow. This is a better way to write the above:

num1 = int(input("Enter number 1: "))
num2 = int(input("Enter number 2: "))

def add_number():
    return num1 + num2

def print_number():
    print(add_number())

print_number()

Just think of your code as if it were a book. You wouldn't show how the story ends before introducing he characters would you?

That is what PBS stands for. While PBS does not get into very specfic details, it provides a basic checklist for your program, and a good starting point to more specific optimization.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.