3
\$\begingroup\$

I am a self taught python beginner for about 3 months and recently I finished my own snake game project. As seen from the code, it is not very efficient nor tidy to look at so I want to know if there are any suggestions that can improve the reading of the code.

I posted a similar topic on stackoverflow asking how to tidy it and I was answered with adding functions with parameters and return values, but I am currently struggling to do that as I lack the understanding of connecting them into my main function.

But overall what do you think of the code? Because when I run it I thought it runs pretty well and it accomplishes exactly the way I want it to be.

from graphics import *
from threading import Timer
import keyboard, random, time

# configurations
width = 400
gridHeight = width
height = 470
timer = False
game = True
score = 0
bonus = 0
x = 70
y = 30
radius = 10
length = radius * 2
playerLength = 3
poisonLength = playerLength
i = 0
k = 0
pointRadius = 5
points = False
cherryPoints = False
key = "Right"
countDown = 0

# set coordinations
cX = 90
cY = 30
coordX = [10]
coordY = [10]
while coordX[len(coordX)-1] != width-10:
    cX+=20
    coordX.append(cX)
while coordY[len(coordY)-1] != 390:
    cY+=20
    coordY.append(cY)
randomX = random.choice(coordX)
randomY = random.choice(coordY)
cherryRandomX = random.choice(coordX)
cherryRandomY = random.choice(coordY)
poisonRandomX = random.choice(coordX)
poisonRandomY = random.choice(coordY)

# window set up
win = GraphWin("SNAKE", width, height, autoflush = False)
win.setBackground(color_rgb(15,15,15))

# grid
lineX = 20
while lineX < width:
    gridX = Line(Point(lineX,0),Point(lineX,gridHeight))
    gridX.setOutline(color_rgb(25,25,25))
    gridX.draw(win)
    lineX += 20
lineY = 20
while lineY <= gridHeight:
    gridX = Line(Point(0,lineY),Point(width,lineY))
    gridX.setOutline(color_rgb(25,25,25))
    gridX.draw(win)
    lineY += 20

# snake banner
UI = Rectangle(Point(0,400),Point(width,height))
UI.setFill(color_rgb(102,51,0))
UI.setOutline(color_rgb(102,51,0))
UI.draw(win)
snakeTitle = Text(Point(width/2,420),"SNAKE")
snakeTitle.setTextColor("green")
snakeTitle.setSize(20)
snakeTitle.draw(win)
scoreTitle = Text(Point(320,424),"SCORE")
scoreTitle.setTextColor("white")
scoreTitle.setSize(10)
scoreTitle.draw(win)
scoreUI = Text(Point(320,435),score)
scoreUI.setTextColor("white")
scoreUI.setSize(10)
scoreUI.draw(win)

# make player
player = {}
player[0] = Rectangle(Point(x-20-radius,y-radius), Point(x-20+radius, y+radius))
player[1] = Rectangle(Point(x-40-radius,y-radius), Point(x-40+radius, y+radius))
player[2] = Rectangle(Point(x-60-radius,y-radius), Point(x-60+radius, y+radius))

# make poison
poison = {}

def main():
    global timer, scoreUI, score, bonus, playerLength, poisonLength, x, y, points, cherryPoints, randomX, randomY, cherryRandomX, cherryRandomY, poisonRandomX, poisonRandomY, key, countDown, k, game

    while(game==True):
        # score update
        scoreUI.undraw()
        scoreUI = Text(Point(320,435),score)
        scoreUI.setTextColor("white")
        scoreUI.setSize(10)
        scoreUI.draw(win)

        # generating new body blocks
        if len(player) < playerLength:
            i+=1
            player[i] = player[i-1].clone()

        # body following player
        player[0].undraw()
        for i in range(1,len(player)):
            player[len(player)-i].undraw()
            player[len(player)-i] = player[len(player)-i-1].clone()
            player[len(player)-i].draw(win)

        # update player's head coordinate
        player[0] = Rectangle(Point(x-radius,y-radius), Point(x+radius,y+radius))
        player[0].setFill("green")
        player[0].setWidth(2)
        player[0].draw(win)

        # player movement
        if keyboard.is_pressed("Up") and key != "Down":
            key = "Up"
        elif keyboard.is_pressed("Left") and key != "Right":
            key = "Left"
        elif keyboard.is_pressed("Down") and key != "Up":
            key = "Down"
        elif keyboard.is_pressed("Right") and key != "Left":
            key = "Right"
        if key == "Up":
            y -= length
        elif key == "Left":
            x -= length
        elif key == "Down":
            y += length
        elif key == "Right":
            x += length

        # point
        if points == False: # generates new point when eaten
            point = Rectangle(Point(randomX-pointRadius,randomY-pointRadius),Point(randomX+pointRadius,randomY+pointRadius))
            point.setFill("white")
            point.setWidth(2)
            point.draw(win)
            points = True
        if player[0].getCenter().getX() == point.getCenter().getX() and player[0].getCenter().getY() == point.getCenter().getY(): # when player eats the point
            point.undraw()
            playerLength += 1
            poisonLength += 1
            score += 200+bonus
            randomX = random.choice(coordX)
            randomY = random.choice(coordY)
            for i in range(len(player)):
                if (point.getCenter().getX() == player[i].getCenter().getX() and point.getCenter().getY() == player[i].getCenter().getY()) or (cherryPoints == True and cherryPoint.getCenter().getX() == point.getCenter().getX() and cherryPoint.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and cherry
                    randomX = random.choice(coordX)
                    randomY = random.choice(coordY)
            for i in range(len(poison)): # regenerate x and y coordinate if point shares the same coordinate to other array of poisons
                if point.getCenter().getX() == poison[i].getCenter().getX() and point.getCenter().getY() == poison[i].getCenter().getY():
                    cherryRandomX = random.choice(coordX)
                    cherryRandomY = random.choice(coordY)
            points = False

        # cherry
        if countDown == 150:
            countDown = 0
            if cherryPoints == False: # generates new cherry from countdown
                cherryPoint = Rectangle(Point(cherryRandomX-pointRadius,cherryRandomY-pointRadius),Point(cherryRandomX+pointRadius,cherryRandomY+pointRadius))
                cherryPoint.setFill(color_rgb(213,0,50))
                cherryPoint.setWidth(2)
                cherryPoint.draw(win)
                cherryPoints = True
        if cherryPoints == True:
            for i in range(2, 6): # cherry blinks between countdown 40 to 100
                if countDown == 20*i:
                    cherryPoint.undraw()
                elif countDown == 10+(20*i):
                    cherryPoint.draw(win)
            if countDown >= 100: # when countdown becomes 100, remove cherry and reset count down
                cherryPoints = False
                countDown = 0
                cherryRandomX = random.choice(coordX)
                cherryRandomY = random.choice(coordY)
        if cherryPoints==True and player[0].getCenter().getX() == cherryPoint.getCenter().getX() and player[0].getCenter().getY() == cherryPoint.getCenter().getY(): # when player eats the cherry
            cherryPoint.undraw()
            score += 500
            cherryRandomX = random.choice(coordX)
            cherryRandomY = random.choice(coordY)
            for i in range(len(player)):
                if (cherryPoint.getCenter().getX() == player[i].getCenter().getX() and cherryPoint.getCenter().getY() == player[i].getCenter().getY()) or (cherryPoint.getCenter().getX() == point.getCenter().getX() and cherryPoint.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point
                    cherryRandomX = random.choice(coordX)
                    cherryRandomY = random.choice(coordY)
            for i in range(len(poison)): # regenerate x and y coordinate if cherry shares the same coordinate to other array of poisons
                if cherryPoint.getCenter().getX() == poison[i].getCenter().getX() and cherryPoint.getCenter().getY() == poison[i].getCenter().getY():
                    cherryRandomX = random.choice(coordX)
                    cherryRandomY = random.choice(coordY)
            cherryPoints = False

        # poison
        if poisonLength % 5 == 0: # generates a poison block each time the player size reaches the multiple of 5
            poison[k] = Rectangle(Point(poisonRandomX-pointRadius,poisonRandomY-pointRadius),Point(poisonRandomX+pointRadius,poisonRandomY+pointRadius))
            poison[k].setFill("green")
            poison[k].setWidth(2)
            poison[k].draw(win)
            poisonRandomX = random.choice(coordX)
            poisonRandomY = random.choice(coordY)
            for i in range(len(player)):
                if (poison[k].getCenter().getX() == player[i].getCenter().getX() and poison[k].getCenter().getY() == player[i].getCenter().getY()) or (poison[k].getCenter().getX() == point.getCenter().getX() and poison[k].getCenter().getY() == point.getCenter().getY()) or (cherryPoints==True and poison[k].getCenter().getX() == cherryPoint.getCenter().getX() and poison[k].getCenter().getY() == cherryPoint.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point and cherry
                    poisonRandomX = random.choice(coordX)
                    poisonRandomY = random.choice(coordY)
            for i in range(len(poison)):
                if poison[k].getCenter().getX() == poison[i].getCenter().getX() and poison[k].getCenter().getY() == poison[i].getCenter().getY(): # regenerate x and y coordinate if new poison shares the same coordinate to other array of poisons
                    poisonRandomX = random.choice(coordX)
                    poisonRandomY = random.choice(coordY)
            bonus+=50
            k+=1
            poisonLength+=1

        # game over requirements
        for i in range(len(poison)): # if player touches poison
            if player[0].getCenter().getX() == poison[i].getCenter().getX() and player[0].getCenter().getY() == poison[i].getCenter().getY():
                game = False
        for i in range(2, len(player)): # if player touches its own body or reach out of window
            if (player[0].getCenter().getX() == player[i].getCenter().getX() and player[0].getCenter().getY() == player[i].getCenter().getY()) or x < 0 or x > width or y < 0 or y > gridHeight:
                game = False

        # FPS
        update(10)
        countDown += 1


    # GAME OVER
    gameOver = Text(Point(width/2,200), "GAME OVER")
    gameOver.setTextColor("red")
    gameOver.setSize(30)
    gameOver.draw(win)
    update()
    time.sleep(2)
    win.close()

main()
\$\endgroup\$
1
  • \$\begingroup\$ I'd suggest formatting your text so it's not just "big" paragraphs. Maybe split it with "What the code does" then "What you think could be improved", it'll be easier to read :) \$\endgroup\$
    – IEatBagels
    Aug 15, 2019 at 15:58

2 Answers 2

1
\$\begingroup\$
  • Docstrings: You should include a docstring at the beginning of every method, class and module you write. This will allow documentation to identify what your program is supposed to accomplish.
  • Wildcard Import Statements: from ... import * should be avoided. You can end up importing something that you don't use/need, which can cause name collisions and other issues. You should refer to this StackOverflow answer for more information. Figure out what you need and what you don't need from graphics.
  • Unused Imports: You don't use from threading import Timer. This creates a dependency that does not need to exist and makes the code more difficult to read.
  • Multiple Imports on one line: You have import keyboard, random, time. This does not comply with PEP-8s section on Imports. You should put each import on it's own line, with the exception of importing multiple specific things from a module, like so: from ... import ..., ....
  • Ordering Imports: You should order your imports like so:
    1. Standard library imports

    2. Related third party imports

    3. Local application/library specific imports

  • Variable/Operator Spacing: You should have a space before and after the =/+=/-=, etc, for variable assignment or other operators. Yes: i += 1 NO: i+=1.
  • Constant Variable Naming: Any variables that are constants in your program should be UPPERCASE.
  • Split code into methods: You have a lot of code that can be grouped into methods/classes. I don't have the time to do it for you, so I'll leave it up to you :).
  • Boolean Comparison (game==True): Having while game==True: is unnecessary. You can use the variable itself as the boolean expression. Instead, do while game:, which is easier to read and PEP-8 Compliant.
  • Global Variables: Global Variables are Bad! You'll have to implement this into your program without using globals on your own, again I don't have the time to rewrite everything.
  • Enumerate vs range(len(...)): Multiple times you have for i in range(len(...)):. You should use for index, value in enumerate(...) instead. This StackOverflow answer goes into detail about why you should make this change.
  • Use _ for unused loop variables: When you use enumerate, and don't need the value but only the index, use for index, _ in enumerate(...). The _ makes it clear that that variable should be ignored, and isn't useful.
  • Parameter '=' Spacing: When inserting variables like parameter_name=variable, there shouldn't be a space. Same when you have default parameters in method declarations.
  • Variable Naming snake_case: Variables should be snake_case, not camelCase.

Updated Code

"""
Module Docstring
A description of your program goes here
"""

import random
import time
import keyboard

from graphics import *

# configurations
WIDTH = 400
GRID_HEIGHT = WIDTH
HEIGHT = 470
TIMER = False
GAME = True
SCORE = 0
BONUS = 0
X = 70
Y = 30
RADIUS = 10
LENGTH = RADIUS * 2
PLAYER_LENGTH = 3
POISON_LENGTH = PLAYER_LENGTH
i = 0
k = 0
POINT_RADIUS = 5
POINTS = False
CHERRY_POINTS = False
KEY = "Right"
COUNTDOWN = 0

# set coordinations
C_X = 90
C_Y = 30
COORD_X = [10]
COORD_Y = [10]

while COORD_X[len(COORD_X) - 1] != (WIDTH - 10):
    C_X += 20
    COORD_X.append(C_X)

while COORD_Y[len(COORD_Y) - 1] != 390:
    C_Y += 20
    COORD_Y.append(C_Y)

RANDOM_X = random.choice(COORD_X)
RANDOM_Y = random.choice(COORD_Y)
CHERRY_RANDOM_X = random.choice(COORD_X)
CHERRY_RANDOM_Y = random.choice(COORD_Y)
POISON_RANDOM_X = random.choice(COORD_X)
POISON_RANDOM_Y = random.choice(COORD_Y)

#window setup
WINDOW = GraphWin("SNAKE", WIDTH, HEIGHT, autoflush=False)
WINDOW.setBackground(color_rgb(15, 15, 15))

# grid
LINE_X = 20
while LINE_X < WIDTH:
    GRID_X = Line(Point(LINE_X, 0), Point(LINE_X, GRID_HEIGHT))
    GRID_X.setOutline(color_rgb(25, 25, 25))
    GRID_X.draw(WINDOW)
    LINE_X += 20
LINE_Y = 20
while LINE_Y <= GRID_HEIGHT:
    GRID_X = Line(Point(0, LINE_Y), Point(WIDTH, LINE_Y))
    GRID_X.setOutline(color_rgb(25, 25, 25))
    GRID_X.draw(WINDOW)
    LINE_Y += 20

# snake banner
UI = Rectangle(Point(0, 400), Point(WIDTH, HEIGHT))
UI.setFill(color_rgb(102, 51, 0))
UI.setOutline(color_rgb(102, 51, 0))
UI.draw(WINDOW)
SNAKE_TITLE = Text(Point(WIDTH / 2, 420), "SNAKE")
SNAKE_TITLE.setTextColor("green")
SNAKE_TITLE.setSize(20)
SNAKE_TITLE.draw(WINDOW)
SCORE_TITLE = Text(Point(320, 424), "SCORE")
SCORE_TITLE.setTextColor("white")
SCORE_TITLE.setSize(10)
SCORE_TITLE.draw(WINDOW)
SCORE_UI = Text(Point(320, 435), SCORE)
SCORE_UI.setTextColor("white")
SCORE_UI.setSize(10)
SCORE_UI.draw(WINDOW)

# make player
PLAYER = {}
PLAYER[0] = Rectangle(Point(X - 20 - RADIUS, Y - RADIUS), Point(X - 20 + RADIUS, Y + RADIUS))
PLAYER[1] = Rectangle(Point(X - 40 - RADIUS, Y - RADIUS), Point(X - 40 + RADIUS, Y + RADIUS))
PLAYER[2] = Rectangle(Point(X - 60 - RADIUS, Y - RADIUS), Point(X - 60 + RADIUS, Y + RADIUS))

# make poison
POISON = {}

def main():    

    global TIMER, SCORE_UI, SCORE, BONUS, PLAYER_LENGTH, POISON_LENGTH, X, Y, POINTS, CHERRY_POINTS, RANDOM_X, RANDOM_Y, CHERRY_RANDOM_X, CHERRY_RANDOM_Y, POISON_RANDOM_X, POISON_RANDOM_Y, KEY, COUNTDOWN, k, GAME

    while GAME:
        # score update
        SCORE_UI.undraw()
        SCORE_UI = Text(Point(320, 435), SCORE)
        SCORE_UI.setTextColor("white")
        SCORE_UI.setSize(10)
        SCORE_UI.draw(WINDOW)

        # generating new body blocks
        if len(PLAYER) < PLAYER_LENGTH:
            i += 1
            PLAYER[i] = PLAYER[i - 1].clone()

        # body following player
        PLAYER[0].undraw()
        for i in range(1, len(PLAYER)):
            PLAYER[len(PLAYER) - i].undraw()
            PLAYER[len(PLAYER) - i] = PLAYER[len(PLAYER) - i - 1].clone()
            PLAYER[len(PLAYER) - i].draw(WINDOW)

        # update player's head coordinate
        PLAYER[0] = Rectangle(Point(X - RADIUS, Y - RADIUS), Point(X + RADIUS, Y + RADIUS))
        PLAYER[0].setFill("green")
        PLAYER[0].setWidth(2)
        PLAYER[0].draw(WINDOW)

        # player movement
        if keyboard.is_pressed("Up") and KEY != "Down":
            KEY = "Up"
        elif keyboard.is_pressed("Left") and KEY != "Right":
            KEY = "Left"
        elif keyboard.is_pressed("Down") and KEY != "Up":
            KEY = "Down"
        elif keyboard.is_pressed("Right") and KEY != "Left":
            KEY = "Right"
        if KEY == "Up":
            Y -= LENGTH
        elif KEY == "Left":
            X -= LENGTH
        elif KEY == "Down":
            Y += LENGTH
        elif KEY == "Right":
            X += LENGTH

        # point
        if not points: # generates new point when eaten
            point = Rectangle(Point(RANDOM_X - POINT_RADIUS, RANDOM_Y - POINT_RADIUS), Point(RANDOM_X + POINT_RADIUS, RANDOM_Y + POINT_RADIUS))
            point.setFill("white")
            point.setWidth(2)
            point.draw(WINDOW)
            points = True
        if PLAYER[0].getCenter().getX() == point.getCenter().getX() and PLAYER[0].getCenter().getY() == point.getCenter().getY(): # when player eats the point
            point.undraw()
            PLAYER_LENGTH += 1
            POISON_LENGTH += 1
            SCORE += 200 + BONUS
            RANDOM_X = random.choice(COORD_X)
            RANDOM_Y = random.choice(COORD_Y)
            for i, _ in enumerate(PLAYER):
                if (point.getCenter().getX() == PLAYER[i].getCenter().getX() and point.getCenter().getY() == PLAYER[i].getCenter().getY()) or (CHERRY_POINTS and cherry_point.getCenter().getX() == point.getCenter().getX() and cherry_point.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and cherry
                    RANDOM_X = random.choice(COORD_X)
                    RANDOM_Y = random.choice(COORD_Y)
            for i, _ in enumerate(POISON): # regenerate x and y coordinate if point shares the same coordinate to other array of poisons
                if point.getCenter().getX() == POISON[i].getCenter().getX() and point.getCenter().getY() == POISON[i].getCenter().getY():
                    CHERRY_RANDOM_X = random.choice(COORD_X)
                    CHERRY_RANDOM_Y = random.choice(COORD_Y)
            points = False

        # cherry
        if COUNTDOWN == 150:
            COUNTDOWN = 0
            if not CHERRY_POINTS: # generates new cherry from countdown
                cherry_point = Rectangle(Point(CHERRY_RANDOM_X - POINT_RADIUS, CHERRY_RANDOM_Y - POINT_RADIUS), Point(CHERRY_RANDOM_X + POINT_RADIUS, CHERRY_RANDOM_Y + POINT_RADIUS))
                cherry_point.setFill(color_rgb(213, 0, 50))
                cherry_point.setWidth(2)
                cherry_point.draw(WINDOW)
                CHERRY_POINTS = True
        if CHERRY_POINTS:
            for i in range(2, 6): # cherry blinks between countdown 40 to 100
                if COUNTDOWN == 20 * i:
                    cherry_point.undraw()
                elif COUNTDOWN == 10 + (20 * i):
                    cherry_point.draw(WINDOW)
            if COUNTDOWN >= 100: # when countdown becomes 100, remove cherry and reset count down
                CHERRY_POINTS = False
                COUNTDOWN = 0
                CHERRY_RANDOM_X = random.choice(COORD_X)
                CHERRY_RANDOM_Y = random.choice(COORD_Y)
        if CHERRY_POINTS and PLAYER[0].getCenter().getX() == cherry_point.getCenter().getX() and PLAYER[0].getCenter().getY() == cherry_point.getCenter().getY(): # when player eats the cherry
            cherry_point.undraw()
            SCORE += 500
            CHERRY_RANDOM_X = random.choice(COORD_X)
            CHERRY_RANDOM_Y = random.choice(COORD_Y)
            for i, _ in enumerate(PLAYER):
                if (cherry_point.getCenter().getX() == PLAYER[i].getCenter().getX() and cherry_point.getCenter().getY() == PLAYER[i].getCenter().getY()) or (cherry_point.getCenter().getX() == point.getCenter().getX() and cherry_point.getCenter().getY() == point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point
                    CHERRY_RANDOM_X = random.choice(COORD_X)
                    CHERRY_RANDOM_Y = random.choice(COORD_Y)
            for i, _ in enumerate(POISON): # regenerate x and y coordinate if cherry shares the same coordinate to other array of poisons
                if cherry_point.getCenter().getX() == POISON[i].getCenter().getX() and cherry_point.getCenter().getY() == POISON[i].getCenter().getY():
                    CHERRY_RANDOM_X = random.choice(COORD_X)
                    CHERRY_RANDOM_Y = random.choice(COORD_Y)
            CHERRY_POINTS = False

        # poison
        if POISON_LENGTH % 5 == 0: # generates a poison block each time the player size reaches the multiple of 5
            POISON[k] = Rectangle(Point(POISON_RANDOM_X - POINT_RADIUS, POISON_RANDOM_Y - POINT_RADIUS), Point(POISON_RANDOM_X + POINT_RADIUS, POISON_RANDOM_Y + POINT_RADIUS))
            POISON[k].setFill("green")
            POISON[k].setWidth(2)
            POISON[k].draw(WINDOW)
            POISON_RANDOM_X = random.choice(COORD_X)
            POISON_RANDOM_Y = random.choice(COORD_Y)
            for i, _ in enumerate(PLAYER):
                if POISON[k].getCenter().getX() == PLAYER[i].getCenter().getX() and POISON[k].getCenter().getY() == PLAYER[i].getCenter().getY() or (POISON[k].getCenter().getX() == point.getCenter().getX() and POISON[k].getCenter().getY() == point.getCenter().getY()) or (CHERRY_POINTS and POISON[k].getCenter().getX() == cherry_point.getCenter().getX() and POISON[k].getCenter().getY() == cherry_point.getCenter().getY()): # regenerate x and y coordinate if they share the same coordinate as player and point and cherry
                    POISON_RANDOM_X = random.choice(COORD_X)
                    POISON_RANDOM_Y = random.choice(COORD_Y)
            for i, _ in enumerate(POISON):
                if POISON[k].getCenter().getX() == POISON[i].getCenter().getX() and POISON[k].getCenter().getY() == POISON[i].getCenter().getY(): # regenerate x and y coordinate if new poison shares the same coordinate to other array of poisons
                    POISON_RANDOM_X = random.choice(COORD_X)
                    POISON_RANDOM_Y = random.choice(COORD_Y)
            BONUS += 50
            k += 1
            POISON_LENGTH += 1

        # game over requirements
        for i, _ in enumerate(POISON): # if player touches poison
            if PLAYER[0].getCenter().getX() == POISON[i].getCenter().getX() and PLAYER[0].getCenter().getY() == POISON[i].getCenter().getY():
                GAME = False
        for i in range(2, len(PLAYER)): # if player touches its own body or reach out of window
            if PLAYER[0].getCenter().getX() == PLAYER[i].getCenter().getX() and PLAYER[0].getCenter().getY() == PLAYER[i].getCenter().getY() or X < 0 or X > WIDTH or Y < 0 or Y > GRID_HEIGHT:
                GAME = False

        # FPS
        update(10)
        COUNTDOWN += 1


    # GAME OVER
    game_over = Text(Point(WIDTH / 2, 200), "GAME OVER")
    game_over.setTextColor("red")
    game_over.setSize(30)
    game_over.draw(WINDOW)
    update()
    time.sleep(2)
    WINDOW.close()

main()
\$\endgroup\$
1
\$\begingroup\$

random.randrange()

It looks like these are just used as an argument to random.choice() for picking random coordinates. random.randrange(start, stop, step) would work. This also might be a good place to create a function that returns a random coordinate.

X_START = 10
X_STEP = 90
X_LIMIT = width - 10

Y_START = 10
Y_STEP = 30
Y_LIMIT = 390

def random_coordinate():
    """return a random coordinate on the game grid."""

    x = random.rangrange(X_START, X_LIMIT, X_STEP)
    y = random.rangrange(Y_START, Y_LIMIT, Y_STEP)

    return x, y

Then when a random coordinate is needed in the program, call the function:

point_xy = random_coordinate()
cherry_xy = random_coordinate()
poison_xy = random_coordinate()

using range() to count down

Instead of using:

for i in range(1,len(player)):
    player[len(player)-i].undraw()
    ...

use:

for i in range(len(player)-1, 0, -1):
        player[i].undraw()
        ... 

functions

On stack overflow, they recommended using functions, because they can make your code easier to read, understand, debug, etc. The general rule of thumb (or guideline) it a unit of code should fit on one screen. That way it can be seen all at once without needing to scroll up and down. Your code has several comments that say what the next part of the code does. That may be a good hint that that chunk of code could be a separate function. ( comments starting with # <- are explanations for you, I wouldn't normally put them in )

LINEX_START = 20
LINEX_STEP = 20
LINEY_START = 20
LINEY_STEP = 20


def main_window():
    win = GraphWin("SNAKE", width, height, autoflush = False)
    win.setBackground(color_rgb(15,15,15))

    return win       # <- other functions will need win, so return it


def draw_grid(win):  # <- need 'win' to draw the lines so it gets passed in

    for lineX in range(LINEX_START, WIDTH, LINEX_STEP)
        gridX = Line(Point(lineX,0),Point(lineX, GRID_HEIGHT))
        gridX.setOutline(color_rgb(25,25,25))
        gridX.draw(win)

    for lineY <= GRIDHEIGHT:
        gridY = Line(Point(0,lineY),Point(WIDTH, lineY))
        gridY.setOutline(color_rgb(25,25,25))
        gridY.draw(win)

def draw_banner(win):
    UI = Rectangle(Point(0,400),Point(width,height))
    UI.setFill(color_rgb(102,51,0))
    UI.setOutline(color_rgb(102,51,0))
    UI.draw(win)

    snakeTitle = Text(Point(width/2,420),"SNAKE")
    snakeTitle.setTextColor("green")
    snakeTitle.setSize(20)
    snakeTitle.draw(win)

    scoreTitle = Text(Point(320,424),"SCORE")
    scoreTitle.setTextColor("white")
    scoreTitle.setSize(10)
    scoreTitle.draw(win)

def init_score(win):
    scoreUI = Text(Point(320,435),score)
    scoreUI.setTextColor("white")
    scoreUI.setSize(10)
    scoreUI.draw(win)

    return scoreUI

def make_player()
    player = [
        Rectangle(Point(x-20-radius,y-radius), Point(x-20+radius, y+radius)),
        Rectangle(Point(x-40-radius,y-radius), Point(x-40+radius, y+radius)),
        Rectangle(Point(x-60-radius,y-radius), Point(x-60+radius, y+radius)),
    ]

    return player

The same applies to main(). Chunks of related code can be put into their own function. This isn't tested, and doesn't include everything, but I hope it gives you some ideas.

def update_score(score_UI, score):
    scoreUI.undraw()
    scoreUI = Text(Point(320,435),score)
    scoreUI.setTextColor("white")
    scoreUI.setSize(10)
    scoreUI.draw(win)


def update_player(player, win):
    """
    Grows the length of the player's body by 1 square and moves the
    snake forward one space.  All the trailing parts of the body follow
    the part ahead of them.
    """

    # generating new body blocks
    if len(player) < MAXPLAYERLENGTH:
        player.append(player[-1].clone())

    # body following player
    player[0].undraw()
    for i in range(len(player)-1, 0, -1):
        player[i].undraw()
        player[i] = player[i-1].clone()
        player[i].draw(win)

    # update player's head coordinate
    player[0] = Rectangle(Point(x-radius,y-radius), Point(x+radius,y+radius))
    player[0].setFill("green")
    player[0].setWidth(2)
    player[0].draw(win)


def main():
    global timer
    score = 0
    bonus = 0
    ...

    point = random_coordinate()
    cherry = random_coordinate()
    poison = random_coordinate()

    win = main_window()    # <- save return value
    make_grid(win)         # <- to use it here
    show_banner(win)       # <- and here

    player = make_player()
    score_UI = init_score()

    game = True
    while game:
        update_score(score_UI, score)

        update_player(player)
        move_player(player)

        handle_points(points, player)
        handle_cherries(cherries, player)
        handle_poison(poison, player)

etcetera

good luck.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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