Goals
- learn Python
- improve problem-solving skills
- improve my coding style
Hardware
This code runs on a Raspberry Pi 3 Model B (Raspbian OS) in combination with the Sense HAT. The 8x8 LED-Matrix on the Sense HAT is used for displaying the game-graphics, and the included joystick is used for steering the snake. While developing the game, the following online-tool has been used to emulate the hardware: https://trinket.io/sense-hat
Note: after clicking on the simulation, the arrow keys on the keyboard can be used for generating the joystick movement.
Code
from sense_hat import SenseHat
import time
import random
senseHat = SenseHat()
senseHat.low_light = True
GREEN = (0, 255, 0)
RED = (255, 0, 0)
START_DELAY = 3
MATRIX_MIN_VALUE = 0
MATRIX_MAX_VALUE = 7
MATRIX_SIZE = 8
while True:
# variables:
gameOverFlag = False
growSnakeFlag = False
generateRandomFoodFlag = False
snakeMovementDelay = 0.5
snakeMovementDelayDecrease = -0.02
# start delay:
time.sleep(START_DELAY)
# set default snake starting position (values are chosen by preference):
snakePosX = [3]
snakePosY = [6]
# generate random food position:
while True:
foodPosX = random.randint(0, 7)
foodPosY = random.randint(0, 7)
if foodPosX != snakePosX[0] or foodPosY != snakePosY[0]:
break
# set default snake starting direction (values are chosen by preference):
movementX = 0
movementY = -1
# -----------------------------------
# game loop
# -----------------------------------
while not gameOverFlag:
# check if snake eats food:
if foodPosX == snakePosX[0] and foodPosY == snakePosY[0]:
growSnakeFlag = True
generateRandomFoodFlag = True
snakeMovementDelay += snakeMovementDelayDecrease
# check if snake bites itself:
for i in range(1, len(snakePosX)):
if snakePosX[i] == snakePosX[0] and snakePosY[i] == snakePosY[0]:
gameOverFlag = True
# check if game-over:
if gameOverFlag:
break
# check joystick events:
events = senseHat.stick.get_events()
for event in events:
if event.direction == "left" and movementX != 1:
movementX = -1
movementY = 0
elif event.direction == "right" and movementX != -1:
movementX = 1
movementY = 0
elif event.direction == "up" and movementY != 1:
movementY = -1
movementX = 0
elif event.direction == "down" and movementY != -1:
movementY = 1
movementX = 0
# grow snake:
if growSnakeFlag:
growSnakeFlag = False
snakePosX.append(0)
snakePosY.append(0)
# move snake:
for i in range((len(snakePosX) - 1), 0, -1):
snakePosX[i] = snakePosX[i - 1]
snakePosY[i] = snakePosY[i - 1]
snakePosX[0] += movementX
snakePosY[0] += movementY
# check game borders:
if snakePosX[0] > MATRIX_MAX_VALUE:
snakePosX[0] -= MATRIX_SIZE
elif snakePosX[0] < MATRIX_MIN_VALUE:
snakePosX[0] += MATRIX_SIZE
if snakePosY[0] > MATRIX_MAX_VALUE:
snakePosY[0] -= MATRIX_SIZE
elif snakePosY[0] < MATRIX_MIN_VALUE:
snakePosY[0] += MATRIX_SIZE
# spawn random food:
if generateRandomFoodFlag:
generateRandomFoodFlag = False
retryFlag = True
while retryFlag:
foodPosX = random.randint(0, 7)
foodPosY = random.randint(0, 7)
retryFlag = False
for x, y in zip(snakePosX, snakePosY):
if x == foodPosX and y == foodPosY:
retryFlag = True
break
# update matrix:
senseHat.clear()
senseHat.set_pixel(foodPosX, foodPosY, RED)
for x, y in zip(snakePosX, snakePosY):
senseHat.set_pixel(x, y, GREEN)
# snake speed (game loop delay):
time.sleep(snakeMovementDelay)
Questions
- Would this example be better implemented in an object-oriented or procedural manner?
- Am I thinking too C/C++-like when implementing e.g. the
for
-loops? - Would code like this ever be accepted in an "professional environment"?
- What can be improved and learned?
1 Answer 1
Documentation
I recommend adding a header docstring summarizing the purpose of the code at the top of the file:
"""
Snake Game for Raspberry Pi Sense HAT
Add more details here, similar to the "Hardware" paragraph in your
question.
"""
Naming
The PEP 8 style guide recommends using snake_case (pun is incidental) for variable names and function names. For example,
snakeMovementDelay becomes snake_movement_delay
snakePosX[0] becomes snake_pos_x[0]
DRY
There is duplicate code for the X and Y calculations such as:
if snakePosX[0] > MATRIX_MAX_VALUE:
snakePosX[0] -= MATRIX_SIZE
elif snakePosX[0] < MATRIX_MIN_VALUE:
snakePosX[0] += MATRIX_SIZE
This can be factored out into a function such as:
def wrap_borders(position):
if position > MATRIX_MAX_VALUE:
position -= MATRIX_SIZE
elif position < MATRIX_MIN_VALUE:
position += MATRIX_SIZE
return position
Then called as:
snake_pos_x[0] = wrap_borders(snake_pos_x[0])
snake_pos_y[0] = wrap_borders(snake_pos_y[0])
Also, there is too much code in the outer while
loop. Look for other
opportunities to factor code out into functions.
Comments
Some of the comments are not needed because they merely repeat what the code does. For example:
# variables:
# start delay:
# grow snake:
You should elaborate on the specific meaning of the following comment:
(values are chosen by preference)
What is meant by "preference" here?
Simpler
I think the code is simpler and easier to read if you remove the "Flag" suffix from all variable names:
while not gameOverFlag:
is better as:
while not game_over:
Explore related questions
See similar questions with these tags.