class BreakoutGame: def __init__(self, config): self.board = BulletinBoard(config.get_board_width(), config.get_board_height()) self.wall = BrickWall(self.board.get_width(), self.board.get_height() * 0.3, config.get_color_matrix(), config.get_color_map(), config.outline_bricks()) paddle_width = self.board.get_width() // 8 paddle_height = self.board.get_height() // 40 self.paddle = Rectangle(paddle_width, paddle_height, "black", filled=True) self.board.pin(self.wall, 0, 0.1 * self.board.get_height()) self.board.pin(self.paddle, self.board.get_width() / 2, 0.9 * self.board.get_height()) self.reset_ball() self.message_box = TextBox("", "Helvetica Neue", 20, "#0000FF") self.dy = config.get_initial_y_velocity() self.dx = random.uniform(config.get_min_x_velocity(), config.get_max_x_velocity()) self.dx = random.choice([-1, 1]) * self.dx self.lives_left = config.get_num_balls() self.time_step = config.get_time_step() self.game_in_progress = False self.game_over = False def start(self): self.board.listen_for("mousemove", self.mousemove_action) self.timer = self.board.call_every(self.step, self.time_step) self.board.listen_for("click", self.click_action) def mousemove_action(self, x, y): paddle_x = min(self.board.get_width() - self.paddle.get_width(), x) self.board.unpin(self.paddle) self.board.pin(self.paddle, paddle_x, 0.9 * self.board.get_height()) def display_message(self, msg): self.board.unpin(self.message_box) self.message_box = TextBox(msg, "Helvetica Neue", 20, "#0000FF") self.board.pin(self.message_box, self.board.get_width() // 2, 10) def reset_ball(self): ball_size = self.board.get_width() // 25 self.ball = Circle(ball_size, "black", filled=True) self.board.pin(self.ball, self.board.get_width() / 2, self.board.get_height() / 2) def click_action(self, x, y): if not self.game_over: self.game_in_progress = True def check_for_bounce(self): (x, y) = self.ball.get_center() radius = self.ball.get_radius() if x - radius <= 0 or x + radius >= self.board.get_width(): self.dx = -self.dx if y - radius <= 0: self.dy = -self.dy def element_at(self, x, y): result = self.wall.shatter(x, y - (0.1 * self.board.get_height())) if not result: return self.board.element_at(x, y) else: return self.wall def check_for_collision(self): def get_colliding_objects(): (x, y) = self.ball.get_center() radius = self.ball.get_radius() corners = [(x - radius, y - radius), (x + radius, y - radius), (x - radius, y + radius), (x + radius, y + radius)] colliders = set() for (corner_x, corner_y) in corners: element = self.element_at(corner_x, corner_y) if element != None: colliders.add(element) return colliders for collider in get_colliding_objects(): if collider == self.paddle: self.dy = -abs(self.dy) elif collider == self.wall: self.dy = -self.dy def check_for_game_end(self): if self.wall.all_shattered(): self.game_in_progress = False self.game_over = True self.display_message("YOU WIN!") else: (x, y) = self.ball.get_center() radius = self.ball.get_radius() if y - radius >= self.board.get_height(): self.board.unpin(self.ball) self.lives_left -= 1 self.game_in_progress = False if self.lives_left <= 0: self.game_over = True self.display_message("GAME OVER") else: self.reset_ball() def step(self): if self.game_in_progress and not self.game_over: self.ball.move(self.dx, self.dy) self.check_for_bounce() self.check_for_collision() self.check_for_game_end()