diff options
Diffstat (limited to 'src/splitscreen_duo')
| -rw-r--r-- | src/splitscreen_duo/__init__.py | 23 | ||||
| -rw-r--r-- | src/splitscreen_duo/games/breakout.py | 243 |
2 files changed, 265 insertions, 1 deletions
diff --git a/src/splitscreen_duo/__init__.py b/src/splitscreen_duo/__init__.py index 8233caf..581fe81 100644 --- a/src/splitscreen_duo/__init__.py +++ b/src/splitscreen_duo/__init__.py @@ -6,7 +6,7 @@ import logging import pygame from .serial import Serial import json -from .games import benchmark, snake +from .games import benchmark, snake, breakout def main() -> int: @@ -66,6 +66,18 @@ def main() -> int: pygame.quit() return 0 + elif message.get("value") == Game.BREAKOUT.value: + logger.info("received breakout game selection from primary") + + game_result = breakout.main_loop(menu.screen, serial, INSTANCE) + + if ( + game_result + and game_result.get("command") == Command.QUIT.value + ): + pygame.quit() + + return 0 except json.JSONDecodeError: logger.error("failed to decode serial message") @@ -102,6 +114,15 @@ def main() -> int: pygame.quit() return 0 + elif serial_command.get("value") == Game.BREAKOUT.value: + logger.info("starting breakout game") + + game_result = breakout.main_loop(menu.screen, serial, INSTANCE) + + if game_result and game_result.get("command") == Command.QUIT.value: + pygame.quit() + + return 0 menu.draw_menu() diff --git a/src/splitscreen_duo/games/breakout.py b/src/splitscreen_duo/games/breakout.py new file mode 100644 index 0000000..bb42fa8 --- /dev/null +++ b/src/splitscreen_duo/games/breakout.py @@ -0,0 +1,243 @@ +import pygame +import random +from ..command import Command +import logging +import json + +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +RED = (255, 0, 0) +BLUE = (0, 0, 255) +PADDLE_WIDTH = 100 +PADDLE_HEIGHT = 10 +BALL_SIZE = 10 +BRICK_WIDTH = 80 +BRICK_HEIGHT = 30 +BRICK_ROWS = 5 +BRICK_COLS = 10 +SPEED = 5 + +logger = logging.getLogger(__name__) + + +class Breakout: + def __init__(self, screen_width, screen_height): + self.screen_width = screen_width + self.screen_height = screen_height + + self.reset() + + def reset(self): + self.paddle = [ + self.screen_width // 2 - PADDLE_WIDTH // 2, + self.screen_height - 40, + ] + self.ball = [self.screen_width // 2, self.screen_height // 2] + self.ball_dx = SPEED + self.ball_dy = -SPEED + self.bricks = [] + + for row in range(BRICK_ROWS): + for col in range(BRICK_COLS): + self.bricks.append( + [ + col * (BRICK_WIDTH + 5) + 5, + row * (BRICK_HEIGHT + 5) + 5, + BRICK_WIDTH, + BRICK_HEIGHT, + ] + ) + + self.score = 0 + + def move_paddle(self, dx): + self.paddle[0] += dx + + if self.paddle[0] < 0: + self.paddle[0] = 0 + if self.paddle[0] > self.screen_width - PADDLE_WIDTH: + self.paddle[0] = self.screen_width - PADDLE_WIDTH + + def move_ball(self): + self.ball[0] += self.ball_dx + self.ball[1] += self.ball_dy + + if self.ball[0] <= BALL_SIZE or self.ball[0] >= self.screen_width - BALL_SIZE: + self.ball_dx *= -1 + if self.ball[1] <= BALL_SIZE: + self.ball_dy *= -1 + + paddle_rect = pygame.Rect( + self.paddle[0], self.paddle[1], PADDLE_WIDTH, PADDLE_HEIGHT + ) + ball_rect = pygame.Rect( + self.ball[0] - BALL_SIZE, + self.ball[1] - BALL_SIZE, + BALL_SIZE * 2, + BALL_SIZE * 2, + ) + + if ball_rect.colliderect(paddle_rect) and self.ball_dy > 0: + self.ball_dy *= -1 + hit_pos = (self.ball[0] - self.paddle[0]) / PADDLE_WIDTH + self.ball_dx = SPEED * (hit_pos - 0.5) * 2 + + for brick in self.bricks[:]: + brick_rect = pygame.Rect(brick[0], brick[1], brick[2], brick[3]) + + if ball_rect.colliderect(brick_rect): + self.bricks.remove(brick) + + self.score += 1 + self.ball_dy *= -1 + + logger.debug(f"brick hit, score: {self.score}") + + break + + def check_game_over(self): + if self.ball[1] >= self.screen_height: + return "lose" + if not self.bricks: + return "win" + + return None + + +def main_loop(screen, serial, instance): + clock = pygame.time.Clock() + breakout = Breakout(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_LEFT: + breakout.move_paddle(-20) + elif event.key == pygame.K_RIGHT: + breakout.move_paddle(20) + elif event.key == pygame.K_ESCAPE: + return None + + breakout.paddle[0] = pygame.mouse.get_pos()[0] - PADDLE_WIDTH // 2 + + breakout.move_paddle(0) + + 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) + + breakout.move_ball() + + game_over = breakout.check_game_over() + + if game_over: + my_score = breakout.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) + pygame.draw.rect( + screen, + BLUE, + [breakout.paddle[0], breakout.paddle[1], PADDLE_WIDTH, PADDLE_HEIGHT], + ) + pygame.draw.circle( + screen, WHITE, [int(breakout.ball[0]), int(breakout.ball[1])], BALL_SIZE + ) + + for brick in breakout.bricks: + pygame.draw.rect(screen, RED, brick) + + score_text = font.render(f"Score: {breakout.score}", True, WHITE) + + screen.blit(score_text, (10, 10)) + pygame.display.flip() + clock.tick(60) + + return None |