From a272bf5829a21c7417284a29c8172933f673e751 Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Fri, 12 Nov 2021 19:43:35 +0100 Subject: Move Player definition outside the screen package Signed-off-by: Olivier Gayot --- cameltris/player.py | 219 ++++++++++++++++++++++++++++++++++++++++++ cameltris/screens/in_game.py | 221 ++----------------------------------------- 2 files changed, 225 insertions(+), 215 deletions(-) create mode 100644 cameltris/player.py (limited to 'cameltris') diff --git a/cameltris/player.py b/cameltris/player.py new file mode 100644 index 0000000..cea5f38 --- /dev/null +++ b/cameltris/player.py @@ -0,0 +1,219 @@ +import contextlib +import random +from typing import Optional + +import pygame + +from .color import Color +from .controller import Input, Controller +from .misc import Pause +from .piece import * +from .playfield import PlayField + + +class WouldCollide(Exception): + pass + + +class PlayerQuit(Exception): + pass + + +class Player: + def __init__(self, controller: Controller, starting_level: int): + self.controller = controller + self.playfield = PlayField() + + self.current_piece, self.current_piece_position = self.generate_piece() + self.next_piece, self.next_piece_position = self.generate_piece() + + self.level = self.starting_level = starting_level + self.score = 0 + + self.lines_burnt = 0 + + self.das = 0 + self.pressing_down_countdown: Optional[int] = None + + self.piece_drop_frames = 0 + + self.grid_canvas = pygame.Surface((500, 1000)) + self.piece_preview_canvas = pygame.Surface((200, 200)) + self.score_canvas = pygame.Surface((296, 50)) + self.level_canvas = pygame.Surface((296, 50)) + + def generate_piece(self) -> tuple[Piece, list[int]]: + # We may want to make this a function outside the class + piece = random.choice((TPiece, SPiece, IPiece, ZPiece, SquarePiece, LPiece, JPiece))() + + for row_id, row in enumerate(piece.elements): + if list(filter(lambda x: x is not None, row)): + break + + initial_y_position = -row_id + initial_x_position = (len(self.playfield.grid[0]) // 2) - (len(piece.elements[0]) // 2) + + return (piece, [initial_y_position, initial_x_position]) + + def lock_piece(self) -> None: + if self.has_collision(self.current_piece_position[0], self.current_piece_position[1]): + raise WouldCollide() + + for row_id, row in enumerate(self.current_piece.elements): + for col_id, element in enumerate(row): + if element is None: + continue + self.playfield.grid[row_id + self.current_piece_position[0]][col_id + self.current_piece_position[1]] = element + + count = self.playfield.burn_rows() + + if count == 1: + print("Single") + rate = 1. + elif count == 2: + print("Double") + rate = 2.5 + elif count == 3: + print("Triple") + rate = 7.5 + elif count == 4: + print("Tetris!") + rate = 30. + else: + rate = 0. + + self.lines_burnt += count + + self.score += int(self.level * 40 * rate) + + if self.lines_burnt >= self.level * 10: + self.level += 1 + self.current_piece, self.current_piece_position = self.next_piece, self.next_piece_position + self.next_piece, self.next_piece_position = self.generate_piece() + self.refresh_piece_preview_canvas() + + def has_collision(self, y: int, x: int) -> bool: + try: + for row_id, row in enumerate(self.current_piece.elements): + for col_id, element in enumerate(row): + if element is None: + continue + + if row_id + y < 0: + continue + + if col_id + x < 0: + return True + + if self.playfield.grid[row_id + y][col_id + x] is not None: + return True + + except IndexError: + return True + + return False + + def move_piece_down(self) -> None: + if not self.has_collision(self.current_piece_position[0] + 1, self.current_piece_position[1]): + self.current_piece_position[0] += 1 + else: + raise WouldCollide() + + def move_piece_up(self) -> None: + if not self.has_collision(self.current_piece_position[0] - 1, self.current_piece_position[1]): + self.current_piece_position[0] -= 1 + else: + raise WouldCollide() + + def move_piece_left(self) -> None: + if not self.has_collision(self.current_piece_position[0], self.current_piece_position[1] - 1): + self.current_piece_position[1] -= 1 + else: + raise WouldCollide() + + def move_piece_right(self) -> None: + if not self.has_collision(self.current_piece_position[0], self.current_piece_position[1] + 1): + self.current_piece_position[1] += 1 + else: + raise WouldCollide() + + def rotate_piece_counter_clockwise(self) -> None: + self.current_piece.rotate_counter_clockwise() + + if self.has_collision(self.current_piece_position[0], self.current_piece_position[1]): + self.current_piece.rotate_clockwise() + raise WouldCollide() + + def rotate_piece_clockwise(self) -> None: + self.current_piece.rotate_clockwise() + + if self.has_collision(self.current_piece_position[0], self.current_piece_position[1]): + self.current_piece.rotate_counter_clockwise() + raise WouldCollide() + + def handle_input_pressed(self, event: pygame.event.Event) -> None: + if self.controller.get_input_down(event) == Input.QUIT: + raise PlayerQuit() + + if self.controller.get_input_down(event) == Input.PAUSE: + raise Pause() + + with contextlib.suppress(WouldCollide): + if self.controller.get_input_down(event) == Input.MOVE_RIGHT: + self.move_piece_right() + self.das = 0 + if self.controller.get_input_down(event) == Input.MOVE_LEFT: + self.move_piece_left() + self.das = 0 + if self.controller.get_input_down(event) == Input.ROTATE_CLOCKWISE: + self.rotate_piece_clockwise() + if self.controller.get_input_down(event) == Input.ROTATE_COUNTER_CLOCKWISE: + self.rotate_piece_counter_clockwise() + if self.controller.get_input_down(event) == Input.MOVE_DOWN: + self.piece_drop_frames = 0 + self.pressing_down_countdown = 3 + try: + self.move_piece_down() + except WouldCollide: + self.lock_piece() + + def handle_input_released(self, event: pygame.event.Event) -> None: + if self.controller.get_input_up(event) == Input.MOVE_DOWN: + self.pressing_down_countdown = None + + def refresh_piece_preview_canvas(self) -> None: + self.piece_preview_canvas.fill(Color.black.value) + + non_empty_rows = list() + for row in self.next_piece.elements: + if any(map(lambda element: element is not None, row)): + non_empty_rows.append(row) + + non_empty_cols = set() + for row in self.next_piece.elements: + for col_id, element in enumerate(row): + if element is not None: + non_empty_cols.add(col_id) + + y_offset = (4 - len(non_empty_rows)) / 2 + x_offset = (4 - len(non_empty_cols)) / 2 + + # Display the next piece + for row_idx, row in enumerate(non_empty_rows): + for col_idx, element in enumerate(row): + if element is not None: + self.piece_preview_canvas.blit(element, ((col_idx + x_offset) * 50 + 1, (row_idx + y_offset) * 50 + 1)) + + def refresh_grid_canvas(self) -> None: + self.grid_canvas.fill(Color.black.value) + + for row_idx, row in enumerate(self.playfield.grid): + for col_idx, element in enumerate(row): + if element is not None: + self.grid_canvas.blit(element, (col_idx * 50 + 1, row_idx * 50 + 1)) + + # Display the current piece + for row_idx, row in enumerate(self.current_piece.elements): + for col_idx, element in enumerate(row): + if element is not None: + self.grid_canvas.blit(element, ((col_idx + self.current_piece_position[1]) * 50 + 1, (row_idx + self.current_piece_position[0]) * 50 + 1)) diff --git a/cameltris/screens/in_game.py b/cameltris/screens/in_game.py index 46450aa..bed8643 100644 --- a/cameltris/screens/in_game.py +++ b/cameltris/screens/in_game.py @@ -1,229 +1,17 @@ import contextlib from functools import partial -import random import sys -from typing import Callable, NoReturn, Optional +from typing import Callable, NoReturn import pygame from ..color import Color +from ..controller import Input, KeyboardController, JoystickController from .base_screen import Screen -from ..piece import * -from ..playfield import PlayField -from ..controller import Input, Controller, KeyboardController, JoystickController +from ..player import Player, WouldCollide from ..misc import Pause -class WouldCollide(Exception): - pass - - -class PlayerQuit(Exception): - pass - -right_pane_canvas = pygame.Surface((300, 1000)) -right_pane_canvas.fill((255, 255, 255)) - -class Player: - def __init__(self, controller: Controller, starting_level: int): - self.controller = controller - self.playfield = PlayField() - - self.current_piece, self.current_piece_position = self.generate_piece() - self.next_piece, self.next_piece_position = self.generate_piece() - - self.level = self.starting_level = starting_level - self.score = 0 - - self.lines_burnt = 0 - - self.das = 0 - self.pressing_down_countdown: Optional[int] = None - - self.piece_drop_frames = 0 - - self.grid_canvas = pygame.Surface((500, 1000)) - self.piece_preview_canvas = pygame.Surface((200, 200)) - self.score_canvas = pygame.Surface((296, 50)) - self.level_canvas = pygame.Surface((296, 50)) - - def generate_piece(self) -> tuple[Piece, list[int]]: - # We may want to make this a function outside the class - piece = random.choice((TPiece, SPiece, IPiece, ZPiece, SquarePiece, LPiece, JPiece))() - - for row_id, row in enumerate(piece.elements): - if list(filter(lambda x: x is not None, row)): - break - - initial_y_position = -row_id - initial_x_position = (len(self.playfield.grid[0]) // 2) - (len(piece.elements[0]) // 2) - - return (piece, [initial_y_position, initial_x_position]) - - def lock_piece(self) -> None: - if self.has_collision(self.current_piece_position[0], self.current_piece_position[1]): - raise WouldCollide() - - for row_id, row in enumerate(self.current_piece.elements): - for col_id, element in enumerate(row): - if element is None: - continue - self.playfield.grid[row_id + self.current_piece_position[0]][col_id + self.current_piece_position[1]] = element - - count = self.playfield.burn_rows() - - if count == 1: - print("Single") - rate = 1. - elif count == 2: - print("Double") - rate = 2.5 - elif count == 3: - print("Triple") - rate = 7.5 - elif count == 4: - print("Tetris!") - rate = 30. - else: - rate = 0. - - self.lines_burnt += count - - self.score += int(self.level * 40 * rate) - - if self.lines_burnt >= self.level * 10: - self.level += 1 - self.current_piece, self.current_piece_position = self.next_piece, self.next_piece_position - self.next_piece, self.next_piece_position = self.generate_piece() - self.refresh_piece_preview_canvas() - - def has_collision(self, y: int, x: int) -> bool: - try: - for row_id, row in enumerate(self.current_piece.elements): - for col_id, element in enumerate(row): - if element is None: - continue - - if row_id + y < 0: - continue - - if col_id + x < 0: - return True - - if self.playfield.grid[row_id + y][col_id + x] is not None: - return True - - except IndexError: - return True - - return False - - def move_piece_down(self) -> None: - if not self.has_collision(self.current_piece_position[0] + 1, self.current_piece_position[1]): - self.current_piece_position[0] += 1 - else: - raise WouldCollide() - - def move_piece_up(self) -> None: - if not self.has_collision(self.current_piece_position[0] - 1, self.current_piece_position[1]): - self.current_piece_position[0] -= 1 - else: - raise WouldCollide() - - def move_piece_left(self) -> None: - if not self.has_collision(self.current_piece_position[0], self.current_piece_position[1] - 1): - self.current_piece_position[1] -= 1 - else: - raise WouldCollide() - - def move_piece_right(self) -> None: - if not self.has_collision(self.current_piece_position[0], self.current_piece_position[1] + 1): - self.current_piece_position[1] += 1 - else: - raise WouldCollide() - - def rotate_piece_counter_clockwise(self) -> None: - self.current_piece.rotate_counter_clockwise() - - if self.has_collision(self.current_piece_position[0], self.current_piece_position[1]): - self.current_piece.rotate_clockwise() - raise WouldCollide() - - def rotate_piece_clockwise(self) -> None: - self.current_piece.rotate_clockwise() - - if self.has_collision(self.current_piece_position[0], self.current_piece_position[1]): - self.current_piece.rotate_counter_clockwise() - raise WouldCollide() - - def handle_input_pressed(self, event: pygame.event.Event) -> None: - if self.controller.get_input_down(event) == Input.QUIT: - raise PlayerQuit() - - if self.controller.get_input_down(event) == Input.PAUSE: - raise Pause() - - with contextlib.suppress(WouldCollide): - if self.controller.get_input_down(event) == Input.MOVE_RIGHT: - self.move_piece_right() - self.das = 0 - if self.controller.get_input_down(event) == Input.MOVE_LEFT: - self.move_piece_left() - self.das = 0 - if self.controller.get_input_down(event) == Input.ROTATE_CLOCKWISE: - self.rotate_piece_clockwise() - if self.controller.get_input_down(event) == Input.ROTATE_COUNTER_CLOCKWISE: - self.rotate_piece_counter_clockwise() - if self.controller.get_input_down(event) == Input.MOVE_DOWN: - self.piece_drop_frames = 0 - self.pressing_down_countdown = 3 - try: - self.move_piece_down() - except WouldCollide: - self.lock_piece() - - def handle_input_released(self, event: pygame.event.Event) -> None: - if self.controller.get_input_up(event) == Input.MOVE_DOWN: - self.pressing_down_countdown = None - - def refresh_piece_preview_canvas(self) -> None: - self.piece_preview_canvas.fill(Color.black.value) - - non_empty_rows = list() - for row in self.next_piece.elements: - if any(map(lambda element: element is not None, row)): - non_empty_rows.append(row) - - non_empty_cols = set() - for row in self.next_piece.elements: - for col_id, element in enumerate(row): - if element is not None: - non_empty_cols.add(col_id) - - y_offset = (4 - len(non_empty_rows)) / 2 - x_offset = (4 - len(non_empty_cols)) / 2 - - # Display the next piece - for row_idx, row in enumerate(non_empty_rows): - for col_idx, element in enumerate(row): - if element is not None: - self.piece_preview_canvas.blit(element, ((col_idx + x_offset) * 50 + 1, (row_idx + y_offset) * 50 + 1)) - - def refresh_grid_canvas(self) -> None: - self.grid_canvas.fill(Color.black.value) - - for row_idx, row in enumerate(self.playfield.grid): - for col_idx, element in enumerate(row): - if element is not None: - self.grid_canvas.blit(element, (col_idx * 50 + 1, row_idx * 50 + 1)) - - # Display the current piece - for row_idx, row in enumerate(self.current_piece.elements): - for col_idx, element in enumerate(row): - if element is not None: - self.grid_canvas.blit(element, ((col_idx + self.current_piece_position[1]) * 50 + 1, (row_idx + self.current_piece_position[0]) * 50 + 1)) - - def handle_input_pressed(instance, players: list[Player], event: pygame.event.Event) -> None: for player in players: if isinstance(player.controller, instance): @@ -238,6 +26,9 @@ def handle_input_released(instance, players: list[Player], event: pygame.event.E # Number of frames frames_per_gridcell = [48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1] +right_pane_canvas = pygame.Surface((300, 1000)) +right_pane_canvas.fill((255, 255, 255)) + class InGame(Screen): def __init__(self, players: list[Player], screen: pygame.surface.Surface): -- cgit v1.2.3