diff options
| author | Zoltan Szabatin <[email protected]> | 2025-03-02 19:56:28 -0800 |
|---|---|---|
| committer | Zoltan Szabatin <[email protected]> | 2025-03-02 19:56:28 -0800 |
| commit | 4ee5ce27a8309be7989e2126f39f9411c1178da2 (patch) | |
| tree | 7c3cd8874065ad6ed236c6f1385e7300e5074a69 /src | |
| parent | refactor: Create base game class for games (diff) | |
| download | splitscreen-duo-4ee5ce27a8309be7989e2126f39f9411c1178da2.tar.xz splitscreen-duo-4ee5ce27a8309be7989e2126f39f9411c1178da2.zip | |
feat: Add pong game
Diffstat (limited to 'src')
| -rw-r--r-- | src/splitscreen_duo/__init__.py | 24 | ||||
| -rw-r--r-- | src/splitscreen_duo/games/game_base.py | 2 | ||||
| -rw-r--r-- | src/splitscreen_duo/games/pong.py | 242 |
3 files changed, 267 insertions, 1 deletions
diff --git a/src/splitscreen_duo/__init__.py b/src/splitscreen_duo/__init__.py index 9fef2eb..9689db4 100644 --- a/src/splitscreen_duo/__init__.py +++ b/src/splitscreen_duo/__init__.py @@ -9,6 +9,7 @@ import json from .games import benchmark from .games.snake import Snake from .games.breakout import Breakout +from .games.pong import Pong def main() -> int: @@ -82,6 +83,19 @@ def main() -> int: pygame.quit() return 0 + elif message.get("value") == Game.PONG.value: + logger.info("received pong game selection from primary") + + pong_game = Pong(menu.screen, serial, INSTANCE) + game_result = pong_game.main_loop() + + 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") @@ -129,6 +143,16 @@ def main() -> int: pygame.quit() return 0 + elif serial_command.get("value") == Game.PONG.value: + logger.info("starting pong game") + + pong_game = Pong(menu.screen, serial, INSTANCE) + game_result = pong_game.main_loop() + + 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/game_base.py b/src/splitscreen_duo/games/game_base.py index ff45686..87cf65f 100644 --- a/src/splitscreen_duo/games/game_base.py +++ b/src/splitscreen_duo/games/game_base.py @@ -70,7 +70,7 @@ class GameBase: if self.waiting: self.screen.fill(self.BLACK) - text = self.font.render("waiting for opponent ...", True, self.WHITE) + text = self.font.render("Waiting for opponent ...", True, self.WHITE) self.screen.blit( text, diff --git a/src/splitscreen_duo/games/pong.py b/src/splitscreen_duo/games/pong.py new file mode 100644 index 0000000..aa3d8a3 --- /dev/null +++ b/src/splitscreen_duo/games/pong.py @@ -0,0 +1,242 @@ +import pygame +import random +from ..command import Command +from .game_base import GameBase +import logging + +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +PADDLE_WIDTH = 100 +PADDLE_HEIGHT = 10 +BALL_SIZE = 10 +PADDLE_SPEED = 10 +AI_SPEED = 4 +BALL_SPEED = 5 + +logger = logging.getLogger(__name__) + + +class Pong(GameBase): + def __init__(self, screen, serial, instance): + super().__init__(screen, serial, instance) + + self.screen_width = screen.get_width() + self.screen_height = screen.get_height() + self.player_score = 0 + self.ai_score = 0 + self.score_display_time = 0 + + self.reset() + + def reset(self): + self.player_paddle = [ + self.screen_width // 2 - PADDLE_WIDTH // 2, + self.screen_height - 40, + ] + self.ai_paddle = [ + self.screen_width // 2 - PADDLE_WIDTH // 2, + 40, + ] + self.ball = [ + self.screen_width // 2, + self.screen_height // 2, + ] + self.ball_dx = random.choice([-BALL_SPEED, BALL_SPEED]) + self.ball_dy = BALL_SPEED + + def move_player_paddle(self, dx): + self.player_paddle[0] += dx + + if self.player_paddle[0] < 0: + self.player_paddle[0] = 0 + if self.player_paddle[0] > self.screen_width - PADDLE_WIDTH: + self.player_paddle[0] = self.screen_width - PADDLE_WIDTH + + def move_ai_paddle(self): + if random.random() < 0.8: + if self.ai_paddle[0] + PADDLE_WIDTH // 2 < self.ball[0]: + self.ai_paddle[0] += AI_SPEED + elif self.ai_paddle[0] + PADDLE_WIDTH // 2 > self.ball[0]: + self.ai_paddle[0] -= AI_SPEED + + if self.ai_paddle[0] < 0: + self.ai_paddle[0] = 0 + if self.ai_paddle[0] > self.screen_width - PADDLE_WIDTH: + self.ai_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 + + player_rect = pygame.Rect( + self.player_paddle[0], + self.player_paddle[1], + PADDLE_WIDTH, + PADDLE_HEIGHT, + ) + ai_rect = pygame.Rect( + self.ai_paddle[0], + self.ai_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(player_rect) and self.ball_dy > 0: + self.ball_dy *= -1 + hit_pos = (self.ball[0] - self.player_paddle[0]) / PADDLE_WIDTH + self.ball_dx = BALL_SPEED * (hit_pos - 0.5) * 2 + elif ball_rect.colliderect(ai_rect) and self.ball_dy < 0: + self.ball_dy *= -1 + hit_pos = (self.ball[0] - self.ai_paddle[0]) / PADDLE_WIDTH + self.ball_dx = BALL_SPEED * (hit_pos - 0.5) * 2 + + if self.ball[1] <= 0: + self.player_score += 1 + + self.reset_ball() + elif self.ball[1] >= self.screen_height: + self.ai_score += 1 + + self.reset_ball() + + def reset_ball(self): + self.ball = [ + self.screen_width // 2, + self.screen_height // 2, + ] + self.ball_dx = random.choice([-BALL_SPEED, BALL_SPEED]) + self.ball_dy = random.choice([-BALL_SPEED, BALL_SPEED]) + + def check_game_over(self): + if self.player_score >= 5: + return "win" + + if self.ai_score >= 5: + return "lose" + + return None + + def update(self): + if self.score_display_time: + self.screen.fill(BLACK) + + result_text = "You Won!" if self.player_score >= 5 else "You Lost!" + result_display = self.font.render(result_text, True, WHITE) + player_score_text = self.font.render( + f"Your Score: {self.player_score}", True, WHITE + ) + ai_score_text = self.font.render(f"AI Score: {self.ai_score}", True, WHITE) + + self.screen.blit( + result_display, + ( + self.screen_width // 2 - result_display.get_width() // 2, + self.screen_height // 2 - 40, + ), + ) + self.screen.blit( + player_score_text, + ( + self.screen_width // 2 - player_score_text.get_width() // 2, + self.screen_height // 2 - 20, + ), + ) + self.screen.blit( + ai_score_text, + ( + self.screen_width // 2 - ai_score_text.get_width() // 2, + self.screen_height // 2 + 20, + ), + ) + pygame.display.flip() + + if pygame.time.get_ticks() - self.score_display_time > 3000: + logger.debug("score display timeout reached, exiting pong") + + self.is_running = False + + return None + + def main_loop(self): + clock = pygame.time.Clock() + + while self.is_running: + result = self.update() + + if result is not None: + return result + + if not self.score_display_time: + for event in pygame.event.get(): + result = self.handle_common_events(event) + + if result is not None: + return result + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT: + self.move_player_paddle(-PADDLE_SPEED) + elif event.key == pygame.K_RIGHT: + self.move_player_paddle(PADDLE_SPEED) + + keys = pygame.key.get_pressed() + + if keys[pygame.K_LEFT]: + self.move_player_paddle(-PADDLE_SPEED) + elif keys[pygame.K_RIGHT]: + self.move_player_paddle(PADDLE_SPEED) + + self.move_ai_paddle() + self.move_ball() + + game_over = self.check_game_over() + + if game_over: + self.score_display_time = pygame.time.get_ticks() + + logger.debug(f"game over: {game_over}, starting score display") + + continue + + self.screen.fill(BLACK) + pygame.draw.rect( + self.screen, + WHITE, + [ + self.player_paddle[0], + self.player_paddle[1], + PADDLE_WIDTH, + PADDLE_HEIGHT, + ], + ) + pygame.draw.rect( + self.screen, + WHITE, + [self.ai_paddle[0], self.ai_paddle[1], PADDLE_WIDTH, PADDLE_HEIGHT], + ) + pygame.draw.circle( + self.screen, + WHITE, + [int(self.ball[0]), int(self.ball[1])], + BALL_SIZE, + ) + + player_score_text = self.font.render( + f"Player: {self.player_score}", True, WHITE + ) + ai_score_text = self.font.render(f"AI: {self.ai_score}", True, WHITE) + + self.screen.blit(player_score_text, (10, self.screen_height - 40)) + self.screen.blit(ai_score_text, (10, 10)) + pygame.display.flip() + clock.tick(60) + + return None |