aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/splitscreen_duo/__init__.py23
-rw-r--r--src/splitscreen_duo/games/breakout.py243
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