summaryrefslogtreecommitdiff
path: root/pycameltris
diff options
context:
space:
mode:
authorOlivier Gayot <olivier.gayot@sigexec.com>2021-11-12 13:32:37 +0100
committerOlivier Gayot <olivier.gayot@sigexec.com>2021-11-12 13:32:37 +0100
commitadf005bd29a395b653df1d990aa72694360a11dc (patch)
treed3cf034b95fc38c0e2d0217581d5e5f7f3689b43 /pycameltris
parent5bbc2d3008910bb9c1f9eb07d7dde5bda8aa52f4 (diff)
Rename the package cameltris and provide __main__.py
Signed-off-by: Olivier Gayot <olivier.gayot@sigexec.com>
Diffstat (limited to 'pycameltris')
-rw-r--r--pycameltris/controller.py105
-rw-r--r--pycameltris/misc.py6
-rw-r--r--pycameltris/piece.py128
-rw-r--r--pycameltris/screens/InGame.py326
-rw-r--r--pycameltris/screens/Pause.py39
-rw-r--r--pycameltris/screens/Screen.py12
6 files changed, 0 insertions, 616 deletions
diff --git a/pycameltris/controller.py b/pycameltris/controller.py
deleted file mode 100644
index 86f5bcb..0000000
--- a/pycameltris/controller.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import abc
-import enum
-
-import pygame
-
-
-class Input(enum.Enum):
- ROTATE_CLOCKWISE = 0
- ROTATE_COUNTER_CLOCKWISE = 1
- MOVE_LEFT = 2
- MOVE_RIGHT = 3
- MOVE_DOWN = 4
- PAUSE = 5
- QUIT = 6
-
-
-class Controller(abc.ABC):
- @abc.abstractmethod
- def is_pressed(self, input_: Input):
- pass
-
- @abc.abstractmethod
- def get_input_down(self, event):
- pass
-
- @abc.abstractmethod
- def get_input_up(self, event):
- pass
-
-
-class JoystickController(Controller):
-
- class PS3Controller(enum.Enum):
- CROSS = 0
- CIRCLE = 1
- TRIANGLE = 2
- SQUARE = 3
- START = 9
- DOWN = 14
- LEFT = 15
- RIGHT = 16
-
- def __init__(self, joystick):
- self.joystick = joystick
- self.mapping = {
- Input.ROTATE_CLOCKWISE: JoystickController.PS3Controller.CROSS,
- Input.ROTATE_COUNTER_CLOCKWISE: JoystickController.PS3Controller.CIRCLE,
- Input.MOVE_LEFT: JoystickController.PS3Controller.LEFT,
- Input.MOVE_RIGHT: JoystickController.PS3Controller.RIGHT,
- Input.MOVE_DOWN: JoystickController.PS3Controller.DOWN,
- Input.PAUSE: JoystickController.PS3Controller.START,
- }
- self.downevent = pygame.JOYBUTTONDOWN
- self.upevent = pygame.JOYBUTTONUP
-
- def is_pressed(self, input_: Input):
- return self.joystick.get_button(self.mapping[input_].value)
-
-
- def get_input_down(self, event):
- for key, value in self.mapping.items():
- if value.value == event.button:
- return key
-
- return None
-
- def get_input_up(self, event):
- for key, value in self.mapping.items():
- if value.value == event.button:
- return key
-
- return None
-
-
-class KeyboardController(Controller):
- def __init__(self, keyboard):
- self.keyboard = keyboard
- self.mapping = {
- Input.ROTATE_CLOCKWISE: pygame.K_f,
- Input.ROTATE_COUNTER_CLOCKWISE: pygame.K_d,
- Input.MOVE_LEFT: pygame.K_h,
- Input.MOVE_RIGHT: pygame.K_l,
- Input.MOVE_DOWN: pygame.K_j,
- Input.QUIT: pygame.K_q,
- Input.PAUSE: pygame.K_RETURN,
- }
- self.downevent = pygame.KEYDOWN
- self.upevent = pygame.KEYUP
-
- def is_pressed(self, input_: Input):
- return self.keyboard.get_pressed()[self.mapping[input_]]
-
- def get_input_down(self, event):
- for key, value in self.mapping.items():
- if value == event.key:
- return key
-
- return None
-
- def get_input_up(self, event):
- for key, value in self.mapping.items():
- if value == event.key:
- return key
-
- return None
diff --git a/pycameltris/misc.py b/pycameltris/misc.py
deleted file mode 100644
index 11c2358..0000000
--- a/pycameltris/misc.py
+++ /dev/null
@@ -1,6 +0,0 @@
-class Pause(Exception):
- pass
-
-class UnPause(Exception):
- pass
-
diff --git a/pycameltris/piece.py b/pycameltris/piece.py
deleted file mode 100644
index 2f3e7ce..0000000
--- a/pycameltris/piece.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import pygame
-
-
-square_template = pygame.Surface((48, 48))
-
-black = (0, 0, 0)
-white = (0xff, 0xff, 0xff)
-brown = (163, 75, 31)
-blue = (30, 34, 164)
-green = (30, 164, 59)
-red = (164, 30, 30)
-purple = (126, 30, 164)
-yellow = (164, 164, 30)
-cyan = (30, 164, 150)
-
-
-class Piece:
- def __init__(self):
- self.square = square_template.copy()
-
- def rotate_clockwise(self):
- self.elements = list(zip(*self.elements[::-1]))
-
- def rotate_counter_clockwise(self):
- self.rotate_clockwise()
- self.rotate_clockwise()
- self.rotate_clockwise()
-
-
-class ZPiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(blue)
- self.elements = (self.square, self.square, None), (None, self.square, self.square), (None, None, None)
- self.vertical = False
-
- def rotate_clockwise(self):
- self.rotate()
-
- def rotate_counter_clockwise(self):
- self.rotate()
-
- def rotate(self):
- if self.vertical:
- self.elements = (self.square, self.square, None), (None, self.square, self.square), (None, None, None)
- else:
- self.elements = (None, None, self.square), (None, self.square, self.square), (None, self.square, None)
-
- self.vertical = not self.vertical
-
-
-class SPiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(green)
- self.elements = (None, None, None), (None, self.square, self.square), (self.square, self.square, None)
- self.vertical = False
-
- def rotate_clockwise(self):
- self.rotate()
-
- def rotate_counter_clockwise(self):
- self.rotate()
-
- def rotate(self):
- if self.vertical:
- self.elements = (None, self.square, self.square), (self.square, self.square, None), (None, None, None)
- else:
- self.elements = (None, self.square, None), (None, self.square, self.square), (None, None, self.square)
-
- self.vertical = not self.vertical
-
-
-class SquarePiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(brown)
- self.elements = ((self.square, self.square), (self.square, self.square))
-
-
-class IPiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(red)
- self.elements = (None, None, None, None), (None, None, None, None), (self.square, self.square, self.square, self.square), (None, None, None, None)
- self.vertical = False
-
- def rotate_clockwise(self):
- self.rotate()
-
- def rotate_counter_clockwise(self):
- self.rotate()
-
- def rotate(self):
- if self.vertical:
- self.elements = (None, None, None, None), (None, None, None, None), (self.square, self.square, self.square, self.square), (None, None, None, None)
- else:
- self.elements = (None, None, self.square, None), (None, None, self.square, None), (None, None, self.square, None), (None, None, self.square, None)
-
- self.vertical = not self.vertical
-
-
-class LPiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(cyan)
- self.elements = (None, None, None), (self.square, self.square, self.square), (None, None, self.square)
-
-
-class JPiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(purple)
- self.elements = (None, None, None), (self.square, self.square, self.square), (self.square, None, None)
-
-
-class TPiece(Piece):
- def __init__(self):
- super().__init__()
-
- self.square.fill(yellow)
- self.elements = (None, None, None), (self.square, self.square, self.square), (None, self.square, None)
diff --git a/pycameltris/screens/InGame.py b/pycameltris/screens/InGame.py
deleted file mode 100644
index 93a1d47..0000000
--- a/pycameltris/screens/InGame.py
+++ /dev/null
@@ -1,326 +0,0 @@
-import contextlib
-from functools import partial
-import random
-import sys
-from typing import Callable, NoReturn, Optional
-
-import pygame
-
-from .Screen import Screen
-from ..piece import *
-from ..controller import Input, Controller, KeyboardController, JoystickController
-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.grid = [[None for _ in range(10)] for _ in range(20)]
-
- 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.grid[0]) // 2) - (len(piece.elements[0]) // 2)
-
- return (piece, [initial_y_position, initial_x_position])
-
- def burn_rows(self) -> int:
- rows_to_burn = list()
-
- for row in self.grid:
- if all(map(lambda element: element is not None, row)):
- rows_to_burn.append(row)
-
- for row in rows_to_burn:
- self.grid.insert(0, [None for _ in range(10)])
- self.grid.remove(row)
-
- return len(rows_to_burn)
-
- 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.grid[row_id + self.current_piece_position[0]][col_id + self.current_piece_position[1]] = element
-
- count = self.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.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(black)
-
- 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(black)
-
- for row_idx, row in enumerate(self.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):
- player.handle_input_pressed(event)
-
-
-def handle_input_released(instance, players: list[Player], event: pygame.event.Event) -> None:
- for player in players:
- if isinstance(player.controller, instance):
- player.handle_input_released(event)
-
-# 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]
-
-
-class InGame(Screen):
- def __init__(self, players: list[Player], screen: pygame.surface.Surface):
- self.players: list[Player] = players
- self.screen: pygame.surface.Surface = screen
- self.event_handler: dict[int, Callable[[pygame.event.Event], None]] = {}
-
- def exit(_) -> NoReturn:
- sys.exit()
-
- self.event_handler[pygame.QUIT] = exit
- self.event_handler[pygame.KEYDOWN] = partial(handle_input_pressed, KeyboardController, self.players)
- self.event_handler[pygame.KEYUP] = partial(handle_input_released, KeyboardController, self.players)
- self.event_handler[pygame.JOYBUTTONDOWN] = partial(handle_input_pressed, JoystickController, self.players)
- self.event_handler[pygame.JOYBUTTONUP] = partial(handle_input_released, JoystickController, self.players)
-
- def refresh_right_pane_canvas(self) -> None:
- for player in self.players:
- player.level_canvas.fill(black)
- player.score_canvas.fill(black)
-
- if pygame.font:
- score_font = pygame.font.Font(None, 56)
-
- player.score_canvas.blit(score_font.render(f"{player.score:08d}", True, white), (0, 0))
- player.level_canvas.blit(score_font.render(f"{player.level:08d}", True, white), (0, 0))
- right_pane_canvas.blit(player.score_canvas, (2, 10))
- right_pane_canvas.blit(player.level_canvas, (2, 70))
-
- right_pane_canvas.blit(player.piece_preview_canvas, (50, 200))
-
-
- def refresh(self) -> None:
- for player in self.players:
- player.refresh_grid_canvas()
- self.screen.blit(player.grid_canvas, (0, 0))
- self.refresh_right_pane_canvas()
- self.screen.blit(right_pane_canvas, (501, 0))
-
- def oneframe(self) -> None:
- for player in self.players:
- player.piece_drop_frames += 1
-
- try:
- for event in pygame.event.get():
- with contextlib.suppress(KeyError):
- self.event_handler[event.type](event)
- except Pause:
- pygame.event.clear()
- raise
-
- for player in self.players:
- player.das += 1
- if player.das == 16:
- with contextlib.suppress(WouldCollide):
- if player.controller.is_pressed(Input.MOVE_RIGHT):
- player.move_piece_right()
- if player.controller.is_pressed(Input.MOVE_LEFT):
- player.move_piece_left()
- player.das = 10
-
- if player.pressing_down_countdown == 0:
- try:
- player.move_piece_down()
- except WouldCollide:
- player.lock_piece()
- player.pressing_down_countdown = 2
- elif player.pressing_down_countdown is not None:
- player.pressing_down_countdown -= 1
-
- if player.piece_drop_frames >= frames_per_gridcell[player.level - 1]:
- player.piece_drop_frames = 0
- try:
- player.move_piece_down()
- except WouldCollide:
- player.lock_piece()
diff --git a/pycameltris/screens/Pause.py b/pycameltris/screens/Pause.py
deleted file mode 100644
index 766a45c..0000000
--- a/pycameltris/screens/Pause.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import contextlib
-import sys
-from typing import Callable, NoReturn
-
-import pygame
-
-from .Screen import Screen
-from ..controller import Controller, KeyboardController, JoystickController, Input
-from ..misc import UnPause
-
-
-def handle_input_pressed(controller: Controller, event: pygame.event.Event):
- if controller.get_input_down(event) == Input.PAUSE:
- raise UnPause()
-
-
-class Pause(Screen):
- def __init__(self, screen: pygame.surface.Surface):
- self.event_handler: dict[int, Callable[[pygame.event.Event], None]] = {}
-
- def exit(_) -> NoReturn:
- sys.exit()
-
- self.event_handler[pygame.QUIT] = exit
- self.event_handler[pygame.JOYBUTTONDOWN] = lambda evt: handle_input_pressed(JoystickController(evt.joy), evt)
- self.event_handler[pygame.KEYDOWN] = lambda evt: handle_input_pressed(KeyboardController(pygame.key), evt)
-
-
- def refresh(self) -> None:
- pass
-
- def oneframe(self) -> None:
- try:
- for event in pygame.event.get():
- with contextlib.suppress(KeyError):
- self.event_handler[event.type](event)
- except UnPause:
- pygame.event.clear()
- raise
diff --git a/pycameltris/screens/Screen.py b/pycameltris/screens/Screen.py
deleted file mode 100644
index 419c1d2..0000000
--- a/pycameltris/screens/Screen.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import abc
-
-
-class Screen(abc.ABC):
- @abc.abstractmethod
- def refresh(self) -> None:
- pass
-
- @abc.abstractmethod
- def oneframe(self) -> None:
- pass
-