I wrote Snake in Python 3 using Pygame and was wondering if anyone can do a code review of it? If this is the appropriate fourm to post such a thing?
Some things to mention:
1. I realize I could have used a dict in the place of my Segment class, but I decided to go with the class because it looked more clean to me.
2. I used recursion heavily, though I could have used a list instead. I decided to do it recursively for practice and fun (I don't use recursion often).
3. I don't have doc strings for any of my functions.
4. I probably could have used my get_all_snake_segment_locations function to avoid recursion.
5. I set fps to 10 to limit the speed of the game. Is this a bad way to do such a thing?
6. I attached an input manager I created and unit tests for my game for completeness. Though, I'm only asking the actual game to be reviewed, if you want to look at those you can. Also, note the unit tests are not complete yet for several functions I changed.
7. I really appreciate anyone who takes the time to give me feedback of any kind. This fourm has been a huge help to me and I'm grateful for everyone's insight!
import sys
import random
import itertools
import pygame
import inputmanager
class Segment:
def __init__(self, rect, direction=None, parent=None, child=None):
self.rect = rect
self.direction = direction
self.parent = parent
self.child = child
class Game:
def __init__(self):
pygame.init()
self.fps_clock = pygame.time.Clock()
self.fps = 10
self.window_size = (640, 480)
self.displaysurf = pygame.display.set_mode(self.window_size)
pygame.display.set_caption("Snake")
self.cell_size = (32, 32)
self.start_location = (320, 224)
self.head_segment = Segment(pygame.Rect(self.start_location, self.cell_size))
self.up = "up"
self.down = "down"
self.left = "left"
self.right = "right"
self.black = (0, 0, 0)
self.green = (0, 255, 0)
self.red = (255, 0, 0)
self.direction = None
self.extender = None
self.cell_locations = set(
itertools.product(
range(0, self.window_size[0], self.cell_size[0]),
range(0, self.window_size[1], self.cell_size[1])
)
)
def main(self):
while True:
self.get_input()
self.update()
self.render()
self.fps_clock.tick(self.fps)
def get_input(self):
inputmanager.InputManager.get_events()
inputmanager.InputManager.check_for_quit_event()
inputmanager.InputManager.update_keyboard_key_state()
inputmanager.InputManager.get_keyboard_input()
def update(self):
self.handle_input()
self.update_snake_direction(self.head_segment, self.direction)
self.move_snake(self.head_segment)
if self.extender is None:
self.add_extender_to_board()
if self.head_segment_collided_with_extender():
self.extend_snake()
if self.game_over():
self.refresh()
def handle_input(self):
if inputmanager.InputManager.quit:
self.terminate()
if (inputmanager.InputManager.keyboard[pygame.K_UP] == inputmanager.InputManager.pressed
and self.direction != self.down):
self.direction = self.up
elif (inputmanager.InputManager.keyboard[pygame.K_DOWN] == inputmanager.InputManager.pressed
and self.direction != self.up):
self.direction = self.down
elif (inputmanager.InputManager.keyboard[pygame.K_LEFT] == inputmanager.InputManager.pressed
and self.direction != self.right):
self.direction = self.left
elif (inputmanager.InputManager.keyboard[pygame.K_RIGHT] == inputmanager.InputManager.pressed
and self.direction != self.left):
self.direction = self.right
def terminate(self):
pygame.quit()
sys.exit()
def update_snake_direction(self, segment, parent_direction): ###TEST
if segment.child is not None:
self.update_snake_direction(segment.child, parent_direction)
if segment.parent is None:
segment.direction = parent_direction
else:
segment.direction = segment.parent.direction
def move_snake(self, segment):
self.move_segment(segment)
if segment.child is not None:
self.move_snake(segment.child)
def move_segment(self, segment):
if segment.direction == self.up:
segment.rect.move_ip(0, -self.cell_size[1])
elif segment.direction == self.down:
segment.rect.move_ip(0, self.cell_size[1])
elif segment.direction == self.left:
segment.rect.move_ip(-self.cell_size[0], 0)
elif segment.direction == self.right:
segment.rect.move_ip(self.cell_size[0], 0)
def add_extender_to_board(self):
snake_segments_locations = set(self.get_all_snake_segment_locations(self.head_segment))
location = random.choice(list(self.cell_locations-snake_segments_locations))
self.extender = pygame.Rect(location, self.cell_size)
def get_all_snake_segment_locations(self, segment):
yield segment.rect.topleft
if segment.child is not None:
yield from self.get_all_snake_segment_locations(segment.child)
def head_segment_collided_with_extender(self):
return self.head_segment.rect.colliderect(self.extender)
def extend_snake(self):
self.extender = None
self.add_segment_to_snake(self.head_segment)
def add_segment_to_snake(self, segment):
if segment.child is None:
if segment.direction == self.up:
topleft = (segment.rect.x, segment.rect.y+self.cell_size[1])
elif segment.direction == self.down:
topleft = (segment.rect.x, segment.rect.y-self.cell_size[1])
elif segment.direction == self.left:
topleft = (segment.rect.x+self.cell_size[0], segment.rect.y)
elif segment.direction == self.right:
topleft = (segment.rect.x-self.cell_size[0], segment.rect.y)
segment.child = Segment(
pygame.Rect(topleft, self.cell_size),
segment.direction,
segment
)
else:
self.add_segment_to_snake(segment.child)
def game_over(self):
return any([
self.head_segment_collided_with_self(self.head_segment),
self.head_segment_out_of_bounds()
])
def head_segment_collided_with_self(self, segment):
if segment.child is not None:
if self.head_segment.rect.colliderect(segment.child.rect):
return True
else:
return self.head_segment_collided_with_self(segment.child)
return False
def head_segment_out_of_bounds(self):
if (self.head_segment.rect.x < 0
or self.head_segment.rect.y < 0
or self.head_segment.rect.x >= self.window_size[0]
or self.head_segment.rect.y >= self.window_size[1]):
return True
return False
def refresh(self):
self.head_segment = Segment(pygame.Rect(self.start_location, self.cell_size))
self.direction = None
self.extender = None
def render(self):
self.displaysurf.fill(self.black)
self.draw_snake(self.head_segment)
if self.extender is not None:
self.draw_extender()
pygame.display.update()
def draw_snake(self, segment):
pygame.draw.rect(self.displaysurf, self.green, segment.rect)
if segment.child is not None:
self.draw_snake(segment.child)
def draw_extender(self):
pygame.draw.rect(self.displaysurf, self.red, self.extender)
if __name__ == "__main__":
game = Game()
game.main()
test_game.py
inputmanager.py
game.py
↧