diff options
Diffstat (limited to 'src/splitscreen_duo/games/snake.py')
| -rw-r--r-- | src/splitscreen_duo/games/snake.py | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/splitscreen_duo/games/snake.py b/src/splitscreen_duo/games/snake.py new file mode 100644 index 0000000..64c6edd --- /dev/null +++ b/src/splitscreen_duo/games/snake.py @@ -0,0 +1,229 @@ +import pygame +import random +from ..command import Command +import logging +import json + +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +RED = (255, 0, 0) +GREEN = (0, 255, 0) +BLOCK_SIZE = 20 +SPEED = 15 + +logger = logging.getLogger(__name__) + + +class Snake: + def __init__(self, screen_width, screen_height): + self.screen_width = screen_width + self.screen_height = screen_height + + self.reset() + + def reset(self): + self.body = [ + [ + self.screen_width // 2 - self.screen_width // 2 % BLOCK_SIZE, + self.screen_height // 2 - self.screen_height // 2 % BLOCK_SIZE, + ] + ] + self.direction = "RIGHT" + self.change_to = self.direction + self.score = 0 + self.food = self.spawn_food() + + def spawn_food(self): + while True: + food = [ + random.randrange(0, self.screen_width // BLOCK_SIZE) * BLOCK_SIZE, + random.randrange(0, self.screen_height // BLOCK_SIZE) * BLOCK_SIZE, + ] + + if food not in self.body: + return food + + def move(self): + if self.change_to == "UP" and self.direction != "DOWN": + self.direction = "UP" + if self.change_to == "DOWN" and self.direction != "UP": + self.direction = "DOWN" + if self.change_to == "LEFT" and self.direction != "RIGHT": + self.direction = "LEFT" + if self.change_to == "RIGHT" and self.direction != "LEFT": + self.direction = "RIGHT" + + head = [self.body[0][0], self.body[0][1]] + + if self.direction == "UP": + head[1] -= BLOCK_SIZE + if self.direction == "DOWN": + head[1] += BLOCK_SIZE + if self.direction == "LEFT": + head[0] -= BLOCK_SIZE + if self.direction == "RIGHT": + head[0] += BLOCK_SIZE + + self.body.insert(0, head) + + if head[0] == self.food[0] and head[1] == self.food[1]: + logger.debug( + f"snake ate food at {self.food}, score increased to {self.score + 1}" + ) + + self.score += 1 + self.food = self.spawn_food() + else: + self.body.pop() + + def check_collision(self): + head = self.body[0] + + if ( + head[0] < 0 + or head[0] >= self.screen_width + or head[1] < 0 + or head[1] >= self.screen_height + or head in self.body[1:] + ): + return True + + return False + + +def main_loop(screen, serial, instance): + clock = pygame.time.Clock() + snake = Snake(screen.get_width(), screen.get_height()) + font = pygame.font.Font(None, 36) + is_running = True + opponent_dead = False + my_score = 0 + opponent_score = 0 + waiting = False + score_display_time = 0 + + while is_running: + if waiting: + screen.fill(BLACK) + + text = font.render("Waiting for opponent...", True, WHITE) + + screen.blit( + text, + ( + screen.get_width() // 2 - text.get_width() // 2, + screen.get_height() // 2, + ), + ) + pygame.display.flip() + + if serial.in_waiting() > 0: + data = serial.readline().decode("utf-8").strip() + message = json.loads(data) + + if message.get("command") == Command.SCORE.value: + opponent_score = message.get("value", 0) + waiting = False + score_display_time = pygame.time.get_ticks() + elif message.get("command") == Command.QUIT.value: + return { + "command": Command.QUIT.value, + "action": None, + "value": None, + } + + elif score_display_time: + screen.fill(BLACK) + + my_text = font.render(f"Your Score: {my_score}", True, WHITE) + opp_text = font.render( + f"Your Opponent's Score: {opponent_score}", True, WHITE + ) + + screen.blit( + my_text, + ( + screen.get_width() // 2 - my_text.get_width() // 2, + screen.get_height() // 2 - 20, + ), + ) + screen.blit( + opp_text, + ( + screen.get_width() // 2 - opp_text.get_width() // 2, + screen.get_height() // 2 + 20, + ), + ) + pygame.display.flip() + + if pygame.time.get_ticks() - score_display_time > 3000: + return None + + else: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return { + "command": Command.QUIT.value, + "action": None, + "value": None, + } + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_UP: + snake.change_to = "UP" + elif event.key == pygame.K_DOWN: + snake.change_to = "DOWN" + elif event.key == pygame.K_LEFT: + snake.change_to = "LEFT" + elif event.key == pygame.K_RIGHT: + snake.change_to = "RIGHT" + elif event.key == pygame.K_ESCAPE: + return None + + if serial.in_waiting() > 0: + data = serial.readline().decode("utf-8").strip() + message = json.loads(data) + + if message.get("command") == Command.SCORE.value: + opponent_dead = True + opponent_score = message.get("value", 0) + + snake.move() + + if snake.check_collision(): + my_score = snake.score + + serial.write( + json.dumps( + { + "command": Command.SCORE.value, + "action": None, + "value": my_score, + } + ).encode("utf-8") + ) + + if opponent_dead: + score_display_time = pygame.time.get_ticks() + else: + waiting = True + + continue + + screen.fill(BLACK) + + for segment in snake.body: + pygame.draw.rect( + screen, GREEN, [segment[0], segment[1], BLOCK_SIZE, BLOCK_SIZE] + ) + + pygame.draw.rect( + screen, RED, [snake.food[0], snake.food[1], BLOCK_SIZE, BLOCK_SIZE] + ) + + score_text = font.render(f"Score: {snake.score}", True, WHITE) + + screen.blit(score_text, (10, 10)) + pygame.display.flip() + clock.tick(SPEED) + + return None |