from typing import List, Tuple from pygame import mixer import pygame import copy from entity.Entity import Entity from util.ConfigurationManager import ConfigurationManager ''' For information on the Tetris piece Tetromino go here: https://tetris.fandom.com/wiki/Tetromino ''' class Piece(Entity): GRAVITY = 300 # A move down every 300 ms def __init__(self, shape: Tuple, position: Tuple, color: str, inner_border_color: str, border_color: str): super().__init__(self.__get_points(shape, position), color, border_color) self.inner_border_color = inner_border_color self.center = self.__get_center(shape, position) self.piece_set_sound = mixer.Channel(2) self.previous_points = None self.previous_center = None def update(self, elapsed_time: int, well: Entity, stack: Entity) -> None: super().update(elapsed_time) tile_size = ConfigurationManager.configuration["engine"]["tile-size"] if self.elapsed_time >= Piece.GRAVITY: self.elapsed_time -= Piece.GRAVITY self.move((0, tile_size)) if well and self.collide(well): self.revert() if stack and self.collide(stack): self.revert() def draw(self, surface: pygame.Surface) -> None: super().draw(surface) tile_size = ConfigurationManager.configuration["engine"]["tile-size"] for square in self.points: if self.inner_border_color: vertex_one = (square[0][0] + (tile_size // 10), square[0][1] + (tile_size // 10)) vertex_two = (square[1][0] - (tile_size // 10), square[1][1] + (tile_size // 10)) vertex_three = (square[2][0] - (tile_size // 10), square[2][1] - (tile_size // 10)) vertex_four = (square[3][0] + (tile_size // 10), square[3][1] - (tile_size // 10)) new_square = (vertex_one, vertex_two, vertex_three, vertex_four) pygame.draw.polygon(surface, pygame.Color(self.inner_border_color), new_square, max(tile_size // 6, 1)) def move(self, vector: Tuple) -> None: # reset elapsed time if user moves down to stall gravity if vector[1] > 0: self.elapsed_time = 0 self.previous_points = copy.deepcopy(self.points) self.previous_center = copy.deepcopy(self.center) self.center[0] += vector[0] self.center[1] += vector[1] for sub_points in self.points: for point in sub_points: point[0] += vector[0] point[1] += vector[1] ''' For more information on a rotation of a piece go here: https://gamedev.stackexchange.com/questions/17974/how-to-rotate-blocks-in-tetris ''' def rotate(self) -> None: self.previous_points = copy.deepcopy(self.points) self.previous_center = copy.deepcopy(self.center) new_points = [] for square in self.points: for vertex in square: h = vertex[0] - self.center[0] k = vertex[1] - self.center[1] vertex[0] = (k * -1) + self.center[0] vertex[1] = h + self.center[1] new_points.append([square[-1]] + square[0:-1]) self.points = new_points def revert(self) -> None: if self.previous_points and self.previous_center: self.points = self.previous_points self.center = self.previous_center # TODO should be private def play_piece_set_sound(self) -> None: piece_set_sound_file = ConfigurationManager.configuration["sound"]["piece-set"] self.piece_set_sound.play(mixer.Sound(piece_set_sound_file)) def __get_points(self, shape: Tuple, position: Tuple) -> List: tile_size = ConfigurationManager.configuration["engine"]["tile-size"] points = [] for square in shape[:-1]: sub_points = [] for vertex in square: point = [vertex[0] * tile_size + position[0], vertex[1] * tile_size + position[1]] sub_points.append(point) points.append(sub_points) return points def __get_center(self, shape: Tuple, position: Tuple) -> List: tile_size = ConfigurationManager.configuration["engine"]["tile-size"] center = shape[-1] # cast to int and avoid exception from pygame return [int(center[0] * tile_size + position[0]), int(center[1] * tile_size + position[1])] # shape attributes I_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((2, 0), (3, 0), (3, 1), (2, 1)), ((3, 0), (4, 0), (4, 1), (3, 1)), (2, 0)) J_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((2, 0), (3, 0), (3, 1), (2, 1)), ((2, 1), (3, 1), (3, 2), (2, 2)), (1.5, 0.5)) L_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((2, 0), (3, 0), (3, 1), (2, 1)), ((0, 1), (1, 1), (1, 2), (0, 2)), (1.5, 0.5)) O_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((0, 1), (1, 1), (1, 2), (0, 2)), (1, 1)) S_SHAPE = (((0, 1), (1, 1), (1, 2), (0, 2)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((2, 0), (3, 0), (3, 1), (2, 1)), (1.5, 0.5)) T_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((2, 0), (3, 0), (3, 1), (2, 1)), (1.5, 0.5)) Z_SHAPE = (((0, 0), (1, 0), (1, 1), (0, 1)), ((1, 0), (2, 0), (2, 1), (1, 1)), ((1, 1), (2, 1), (2, 2), (1, 2)), ((2, 1), (3, 1), (3, 2), (2, 2)), (1.5, 0.5))