Líneas en el clon de Tetris

Estamos muy cerca ya de tener un clon de Tetris funcional, es decir, “algo” con lo que poder hacer líneas y que por lo tanto reproduzca la mecánica del juego. Recapitulando, hasta ahora hemos hecho los siguientes avances:

  1. Dibujar una pieza (básica) y programar la rotación: lo vimos en este artículo
  2. Programar la gravedad y el resto de movimientos: lo vimos en este otro artículo
  3. Colisiones de la pieza con otras piezas y el borde de la pantalla: fue el último artículo de la serie
  4. Haciendo líneas y retocando la mecánica anterior: el artículo de hoy
  5. Puntuaciones y niveles
  6. Borde, marcador, tabla de puntuaciones.
  7. Refinando los gráficos de las piezas

El artículo de hoy contempla muchas modificaciones al programa que tenemos construido, a saber:

  • Detectar que se ha producido una línea
  • Retirar dicha línea del área de juego
  • Generar piezas aleatoriamente, de diferentes colores.
  • Retoques gráficos al área de juego

Con esto podremos estar jugando indefinidamente y tendremos el juego listo para crear puntuaciones, niveles, y representar en la pantalla tanto la puntuación como la siguiente pieza, por ejemplo.

Os recomiendo qué debéis leer si llegáis a PItando en este momento, para poder aprovechar este artículo:

  1. La serie de Python, en general.
  2. La serie de Pygame, en particular.

El programa de partida para este artículo es el que viene de la semana anterior, que podéis ver en este cuadro (tendréis que desplegarlo):

import pygame

class Pieza:
    'Modela una pieza de Cuatris :)'
    def __init__(self, matriz, color, fondo, pantalla, lado_cuadrado):
        self.matriz = matriz
        self.color = color
        self.fondo = fondo
        self.pantalla = pantalla
        self.lado_cuadrado = lado_cuadrado
        # posición de la pieza
        self.x = 0
        self.y = 0
        self.calcula_dimensiones()

    def calcula_dimensiones(self):
        self.filas = len(self.matriz)
        self.columnas = len(self.matriz[0])
        
    def rotar90(self):
        'Rota la pieza en el sentido horario'
        self.matriz = list(zip(*self.matriz[::-1]))
        self.calcula_dimensiones()
        
    def rotarM90(self):
        'Rota la pieza en el sentido antihorario'
        self.matriz = list(zip(*self.matriz))[::-1]
        self.calcula_dimensiones()

    def __pinta_cuadrado(self, x, y, color):
        'dibuja un cuadrado en las coordenadas de malla especificadas'
        cuadrado = (x*self.lado_cuadrado,
                    y*self.lado_cuadrado,
                    self.lado_cuadrado,
                    self.lado_cuadrado)
        cuadrado_interno = (x*self.lado_cuadrado+8,
                            y*self.lado_cuadrado+8,
                            self.lado_cuadrado-16,
                            self.lado_cuadrado-16)
        pygame.draw.rect(self.pantalla,
                         (int(color[0]/2),
                          int(color[1]/2),
                          int(color[2]/2)),
                         cuadrado,
                         0)
        pygame.draw.rect(self.pantalla, color, cuadrado_interno, 0)

    def __borra_rectangulo(self, rectangulo):
        'borra el área especificada'
        pygame.draw.rect(self.pantalla, self.fondo, rectangulo, 0)

    def pinta(self, x_ini, y_ini):
        'pinta la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        # actualizamos la posición de la pieza
        self.x = x_ini
        self.y = y_ini
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, self.color)
                    
    def borra(self, x_ini, y_ini):
        'borra la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, self.fondo)

    # Movimiento de la pieza
    def mover(self, desplazamiento_x, desplazamiento_y):
        'mueve la pieza el desplazamiento indicado (sin pintarla), en coordenadas de malla'
        self.x = self.x + desplazamiento_x
        self.y = self.y + desplazamiento_y

    # Funciones que hacen uso del estado de la pieza
    def auto_pinta(self):
        'pintar la pieza en las propiedades autocontenidas'
        self.pinta(self.x, self.y)

    def auto_borra(self):
        'borrar la piezaden las propiedades autocontenidas'
        self.borra(self.x, self.y)

    def colisiona_con(self, resto):
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] == 1:
                    if resto.matriz[self.y + i][self.x + j] == 1:
                        return True
        return False

class Recipiente(Pieza):
    def incorporar(self, otra_pieza):
        'incorpora la matriz de otra pieza a la de si mismo'
        for i in range(0,len(otra_pieza.matriz)):
            for j in range(0,len(otra_pieza.matriz[i])):
                if otra_pieza.matriz[i][j] == 1:
                    self.matriz[otra_pieza.y+i][otra_pieza.x+j] = otra_pieza.matriz[i][j]
                    

def siguiente_pieza():
    L = Pieza (matriz_L, MAGENTA, NEGRO, pantalla, SLOT)
    L.pinta(4, 0)
    return L


BLANCO = (255,255,255)
GRIS = (127,127,127)
NEGRO = (0, 0, 0)
MAGENTA = (200,0,200)
FPS = 60
SLOT = 40

# pantalla 12 x 22 y posición inicial de la pieza
ANCHO = 12*SLOT
ALTO = 22*SLOT
x_ini = 4
y_ini = 0

# inicializamos pygame
pygame.init()

# definición de la pantalla
pantalla = pygame.display.set_mode((ANCHO, ALTO))
pantalla.fill(NEGRO)

matriz_L = [(1, 0),
            (1, 0),
            (1, 1)]

matriz_Recipiente = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

recipiente = Recipiente (matriz_Recipiente, GRIS, NEGRO, pantalla, SLOT)
recipiente.pinta(0, 0)
L = siguiente_pieza()

pygame.display.update()

# reloj de control de refresco
clock = pygame.time.Clock()

# Evento de gravedad
GRAVEDAD = pygame.USEREVENT + 1
t_GRAVEDAD = 2000 #milisegundos
pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

continuar = True


while continuar: 
    # pausa hasta el siguiente "tick" de reloj
    clock.tick(FPS)

    # detección de evento QUIT (aspa)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            continuar = False
        else:
            # Procesar la gravedad
            if event.type == GRAVEDAD:
                L.auto_borra()
                L.mover(0, 1)
                if L.colisiona_con(recipiente):
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                    L.mover(0,-1)
                    L.auto_pinta()
                    # Incorporar pieza a "P"
                    recipiente.incorporar(L)
                    L = siguiente_pieza()
                else:
                    L.auto_pinta()
     
            
        
            if event.type == pygame.KEYDOWN:

                # El giro de las piezas puede provocar colisiones por el lado derecho
                if event.key == pygame.K_z:
                    L.auto_borra()
                    L.rotarM90()
                    if L.colisiona_con(recipiente):
                        L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_x:
                    L.auto_borra()
                    L.rotar90()
                    if L.colisiona_con(recipiente):
                        L.mover(-1,0)
                    L.auto_pinta()

                if event.key == pygame.K_LEFT:
                    L.auto_borra()
                    L.mover(-1,0)
                    if L.colisiona_con(recipiente):
                        # deshacer
                        L.mover(1, 0)
                    L.auto_pinta()

                if event.key == pygame.K_RIGHT:
                    L.auto_borra()
                    L.mover(1,0)
                    if L.colisiona_con(recipiente):
                        # deshacer
                        L.mover(-1, 0)
                    L.auto_pinta()

                # acelerar gravedad
                if event.key == pygame.K_DOWN:
                    t_GRAVEDAD = 70
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                else:
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
           
            pygame.display.update()

Detección de líneas

Para detectar que se ha producido una línea voy a aprovecharme de que, independientemente de lo que vemos por pantalla, tenemos un modelo por detrás que consta de matrices de ceros (“0”) y valores diferentes de cero (“1” por el momento). Además, tenemos todas esas matrices consolidadas en el objeto Recipiente, porque cuando detectamos una colisión por el lateral inferior, incorporamos la pieza que ha caído al propio recipiente.

Teniendo en cuenta eso, darnos cuenta de que hemos hecho una línea es tan sencillo como multiplicar todos los elementos de cada fila del objeto Recipiente y comprobar si el producto es cero (“0”). Si es cero, no hay línea. Si es distinto de cero, hay línea.

Tan sencillo como eso. Luego, lo que tenemos que hacer una vez detectada la línea es retirarla del recipiente. Supongamos que hago línea en la fila 17, empezando a contar por arriba:

  • Copio la línea 16 en la 17,
  • Copio la línea 15 en la 16,
  • … copio la fila 0 en la 1 y, finalmente, pongo una nueva fila (sólo bordes de pantalla) en la fila 0.

Estas dos funciones, en el objeto Recipiente, son las que desarrollan este comportamiento:

    def quitar_fila(self, linea):
        'retira la fila indicada, poniendo una nueva fila en la parte de arriba de la pantalla'
        for fila in range(linea, 0, -1):
            self.matriz[fila] = self.matriz[fila - 1]
        self.matriz[0] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
        
    def comprobar_lineas(self, otra_pieza):
        'comprueba las líneas formadas por la nueva pieza'
        for fila in range(otra_pieza.y, otra_pieza.y + otra_pieza.filas):
            producto = 1;
            for valor in self.matriz[fila]:
                producto = producto * valor
                if producto == 0:
                    break
            if producto > 0:
                self.quitar_fila(fila)

Fijaos que sólo compruebo las potenciales filas en el rango que ocupa la pieza recién incorporada al Recipiente  (en este código, representado por el objeto self ), no en el Recipiente  entero.

Para repintar la pantalla, debemos repintar el Recipiente además de las piezas, cuando hayamos ejecutado todo esto. Al escribir las siguientes líneas en el bucle principal del juego, veremos que al repintar el Recipiente las piezas anteriormente depositadas se vuelven grises.

                if pieza.colisiona_con(recipiente):
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                    pieza.mover(0,-1)
                    # L.auto_pinta()
                    # Incorporar pieza a "P"
                    recipiente.auto_borra()
                    recipiente.incorporar(pieza)
                    recipiente.auto_pinta()
                    pieza = siguiente_pieza()
                else:
                    pieza.auto_pinta()

(Nota: he cambiado el nombre del objeto “L” por “pieza”, pero seguro que lo has notado)

Eso es por la forma en la que tenemos programada la gestión de colores (cada pieza tiene su color característico y el delRecipiente  es el gris), pero resolverlo es muy fácil.

Mejorando la gestión de colores

Para lograr un comportamiento normal en los colores de las piezas cuando éstas se depositan, lo que vamos a hacer hacer que las piezas, en vez de tener valores (“0” y “1”) en sus matrices, tengan valores mayores (“1”, “2”,…), representando cada uno de los valores un color diferente. Deshecharemos los parámetro color  de los constructores, porque ya no nos harán falta, y crearemos una lista de colores que, en cada posición (índices 1, 2, 3,…) almacene los valores RGB del color de cada pieza.

Así:

class Pieza:
    'Modela una pieza de Cuatris :)'
    def __init__(self, matriz, pantalla, lado_cuadrado):
        self.matriz = matriz
        # self.color = color
        # self.fondo = fondo
        self.pantalla = pantalla
        self.lado_cuadrado = lado_cuadrado
        # posición de la pieza
        self.x = 0
        self.y = 0
        self.calcula_dimensiones()

    def calcula_dimensiones(self):
        self.filas = len(self.matriz)
        self.columnas = len(self.matriz[0])
        
    def rotar90(self):
        'Rota la pieza en el sentido horario'
        self.matriz = list(zip(*self.matriz[::-1]))
        self.calcula_dimensiones()
        
    def rotarM90(self):
        'Rota la pieza en el sentido antihorario'
        self.matriz = list(zip(*self.matriz))[::-1]
        self.calcula_dimensiones()

    def __pinta_cuadrado(self, x, y, color):
        'dibuja un cuadrado en las coordenadas de malla especificadas'
        cuadrado = (x*self.lado_cuadrado,
                    y*self.lado_cuadrado,
                    self.lado_cuadrado,
                    self.lado_cuadrado)
        cuadrado_interno = (x*self.lado_cuadrado+8,
                            y*self.lado_cuadrado+8,
                            self.lado_cuadrado-16,
                            self.lado_cuadrado-16)
        pygame.draw.rect(self.pantalla,
                         (int(color[0]/2),
                          int(color[1]/2),
                          int(color[2]/2)),
                         cuadrado,
                         0)
        pygame.draw.rect(self.pantalla, color, cuadrado_interno, 0)

    def __borra_rectangulo(self, rectangulo):
        'borra el área especificada'
        pygame.draw.rect(self.pantalla, colores[0], rectangulo, 0)

    def pinta(self, x_ini, y_ini):
        'pinta la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        # actualizamos la posición de la pieza
        self.x = x_ini
        self.y = y_ini
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] != 0:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, colores[self.matriz[i][j]])
                    
    def borra(self, x_ini, y_ini):
        'borra la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] != 0:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, colores[0])

    # Movimiento de la pieza
    def mover(self, desplazamiento_x, desplazamiento_y):
        'mueve la pieza el desplazamiento indicado (sin pintarla), en coordenadas de malla'
        self.x = self.x + desplazamiento_x
        self.y = self.y + desplazamiento_y

    # Funciones que hacen uso del estado de la pieza
    def auto_pinta(self):
        'pintar la pieza en las propiedades autocontenidas'
        self.pinta(self.x, self.y)

    def auto_borra(self):
        'borrar la piezaden las propiedades autocontenidas'
        self.borra(self.x, self.y)

    def colisiona_con(self, resto):
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] != 0:
                    if resto.matriz[self.y + i][self.x + j] != 0:
                        return True
        return False

La definición de los colores y su matriz colores  es la siguiente:

BLANCO = (255,255,255)
GRIS = (127,127,127)
NEGRO = (0, 0, 0)
MAGENTA = (200,0,200)
VERDE = (0, 200, 0)
ROJO = (200, 0, 0)
AZUL = (0, 0, 200)
AMARILLO = (200, 200, 0)
MARRON = (0, 200, 200)
BLANCO = (200, 200, 200)

colores = [NEGRO, GRIS, MARRON, AZUL, BLANCO, VERDE, ROJO, MAGENTA, AMARILLO]

Ahora ya sólo queda generar las piezas, escoger la siguiente pieza de forma aleatoria, y proporcionaros el código completo.

Siguiente pieza

De acuerdo con las mejoras a los colores y la función siguiente_pieza , debemos hacer lo siguiente con ayuda de un módulo llamado random que proporciona una función para extraer un número entero de una sencuencia pseudo-aleatoria.

Lo primero, definir las piezas:

matriz_L = [(2, 0),
            (2, 0),
            (2, 2)]

matriz_J = [(0, 3),
            (0, 3),
            (3, 3)]

matriz_I = [(4, 0),
            (4, 0),
            (4, 0),
            (4, 0)]

matriz_S = [(0, 5, 5),
            (5, 5, 0)]

matriz_Z = [(6, 6, 0),
            (0, 6, 6)]

matriz_T = [(7, 7, 7),
            (0, 7, 0)]

matriz_O = [(8, 8),
            (8, 8)]

matrices = [0, matriz_Recipiente, matriz_L, matriz_J, matriz_I, matriz_S, matriz_Z, matriz_T, matriz_O]

La matriz de piezas la usaremos para escoger la pieza usando un número aleatorio entre 2 y 8. Realmente, las dos primeras posiciones están ahí “de relleno” para que los bucles de escoger color y pieza sean coherente (la pieza nº 8, que es el cuadrado, tiene el color nº 8 que es el amarillo.

Esta es la función de generar la siguiente pieza:

def siguiente_pieza():
    n_pieza = random.randint(2, 8)
    pieza = Pieza (matrices[n_pieza], pantalla, SLOT)
    pieza.pinta(4, 0)
    return pieza

Y esta es la sentencia para generar el recipiente: recipiente = Recipiente (matriz_Recipiente, pantalla, SLOT)

Conclusión

Estas son todas las mejoras y ampliaciones de calado que corresponden a este artículo. Podéis enviarme vuestros comentarios, dudas y sugerencias a través del formulario de contacto y la dirección de correo que allí se encuentra, o de las redes sociales: @pitandonet en Twitter y PItandonet en facebook.

Os dejo aquí el código del programa completo (que tendréis que desplegar para verlo) y un Vine con el resultado que podréis ver, examinar y (por supuesto) mejorar.

import pygame
import random

class Pieza:
    'Modela una pieza de Cuatris :)'
    def __init__(self, matriz, pantalla, lado_cuadrado):
        self.matriz = matriz
        # self.color = color
        # self.fondo = fondo
        self.pantalla = pantalla
        self.lado_cuadrado = lado_cuadrado
        # posición de la pieza
        self.x = 0
        self.y = 0
        self.calcula_dimensiones()

    def calcula_dimensiones(self):
        self.filas = len(self.matriz)
        self.columnas = len(self.matriz[0])
        
    def rotar90(self):
        'Rota la pieza en el sentido horario'
        self.matriz = list(zip(*self.matriz[::-1]))
        self.calcula_dimensiones()
        
    def rotarM90(self):
        'Rota la pieza en el sentido antihorario'
        self.matriz = list(zip(*self.matriz))[::-1]
        self.calcula_dimensiones()

    def __pinta_cuadrado(self, x, y, color):
        'dibuja un cuadrado en las coordenadas de malla especificadas'
        cuadrado = (x*self.lado_cuadrado,
                    y*self.lado_cuadrado,
                    self.lado_cuadrado,
                    self.lado_cuadrado)
        cuadrado_interno = (x*self.lado_cuadrado+8,
                            y*self.lado_cuadrado+8,
                            self.lado_cuadrado-16,
                            self.lado_cuadrado-16)
        pygame.draw.rect(self.pantalla,
                         (int(color[0]/2),
                          int(color[1]/2),
                          int(color[2]/2)),
                         cuadrado,
                         0)
        pygame.draw.rect(self.pantalla, color, cuadrado_interno, 0)

    def __borra_rectangulo(self, rectangulo):
        'borra el área especificada'
        pygame.draw.rect(self.pantalla, colores[0], rectangulo, 0)

    def pinta(self, x_ini, y_ini):
        'pinta la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        # actualizamos la posición de la pieza
        self.x = x_ini
        self.y = y_ini
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] != 0:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, colores[self.matriz[i][j]])
                    
    def borra(self, x_ini, y_ini):
        'borra la pieza especificada a partir de las coordenadas de malla x_ini, y_ini'
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] != 0:
                    self.__pinta_cuadrado(x_ini+j, y_ini+i, colores[0])

    # Movimiento de la pieza
    def mover(self, desplazamiento_x, desplazamiento_y):
        'mueve la pieza el desplazamiento indicado (sin pintarla), en coordenadas de malla'
        self.x = self.x + desplazamiento_x
        self.y = self.y + desplazamiento_y

    # Funciones que hacen uso del estado de la pieza
    def auto_pinta(self):
        'pintar la pieza en las propiedades autocontenidas'
        self.pinta(self.x, self.y)

    def auto_borra(self):
        'borrar la piezaden las propiedades autocontenidas'
        self.borra(self.x, self.y)

    def colisiona_con(self, resto):
        for i in range(0,len(self.matriz)):
            for j in range(0,len(self.matriz[i])):
                if self.matriz[i][j] != 0:
                    if resto.matriz[self.y + i][self.x + j] != 0:
                        return True
        return False

class Recipiente(Pieza):

    def __quitar_fila(self, linea):
        'retira la fila indicada, poniendo una nueva fila en la parte de arriba de la pantalla'
        for fila in range(linea, 0, -1):
            self.matriz[fila] = self.matriz[fila - 1]
        self.matriz[0] = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
        
    def comprobar_lineas(self, otra_pieza):
        'comprueba las líneas formadas por la nueva pieza'
        for fila in range(otra_pieza.y, otra_pieza.y + otra_pieza.filas):
            producto = 1;
            for valor in self.matriz[fila]:
                producto = producto * valor
                if producto == 0:
                    break
            if producto > 0:
                self.__quitar_fila(fila)
        
    def incorporar(self, otra_pieza):
        'incorpora la matriz de otra pieza a la de si mismo'
        for i in range(0,len(otra_pieza.matriz)):
            for j in range(0,len(otra_pieza.matriz[i])):
                if otra_pieza.matriz[i][j] != 0:
                    self.matriz[otra_pieza.y+i][otra_pieza.x+j] = otra_pieza.matriz[i][j]
        self.comprobar_lineas(otra_pieza)
                    

def siguiente_pieza():
    n_pieza = random.randint(2, 8)
    pieza = Pieza (matrices[n_pieza], pantalla, SLOT)
    pieza.pinta(4, 0)
    return pieza


BLANCO = (255,255,255)
GRIS = (127,127,127)
NEGRO = (0, 0, 0)
MAGENTA = (200,0,200)
VERDE = (0, 200, 0)
ROJO = (200, 0, 0)
AZUL = (0, 0, 200)
AMARILLO = (200, 200, 0)
MARRON = (0, 200, 200)
BLANCO = (200, 200, 200)
FPS = 60
SLOT = 40

colores = [NEGRO, GRIS, MARRON, AZUL, BLANCO, VERDE, ROJO, MAGENTA, AMARILLO]

# pantalla 12 x 22 y posición inicial de la pieza
ANCHO = 12*SLOT
ALTO = 22*SLOT
x_ini = 4
y_ini = 0

# inicializamos pygame
pygame.init()

# definición de la pantalla
pantalla = pygame.display.set_mode((ANCHO, ALTO))
pantalla.fill(NEGRO)

matriz_Recipiente = [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

matriz_L = [(2, 0),
            (2, 0),
            (2, 2)]

matriz_J = [(0, 3),
            (0, 3),
            (3, 3)]

matriz_I = [(4, 0),
            (4, 0),
            (4, 0),
            (4, 0)]

matriz_S = [(0, 5, 5),
            (5, 5, 0)]

matriz_Z = [(6, 6, 0),
            (0, 6, 6)]

matriz_T = [(7, 7, 7),
            (0, 7, 0)]

matriz_O = [(8, 8),
            (8, 8)]

matrices = [0, matriz_Recipiente, matriz_L, matriz_J, matriz_I, matriz_S, matriz_Z, matriz_T, matriz_O]

recipiente = Recipiente (matriz_Recipiente, pantalla, SLOT)
recipiente.pinta(0, 0)
pieza = siguiente_pieza()

pygame.display.update()

# reloj de control de refresco
clock = pygame.time.Clock()

# Evento de gravedad
GRAVEDAD = pygame.USEREVENT + 1
t_GRAVEDAD = 2000 #milisegundos
pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

continuar = True


while continuar: 
    # pausa hasta el siguiente "tick" de reloj
    clock.tick(FPS)

    # detección de evento QUIT (aspa)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            continuar = False
        else:
            # Procesar la gravedad
            if event.type == GRAVEDAD:
                pieza.auto_borra()
                pieza.mover(0, 1)
                if pieza.colisiona_con(recipiente):
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                    pieza.mover(0,-1)
                    # L.auto_pinta()
                    # Incorporar pieza a "P"
                    recipiente.auto_borra()
                    recipiente.incorporar(pieza)
                    recipiente.auto_pinta()
                    pieza = siguiente_pieza()
                else:
                    pieza.auto_pinta()
     
            
        
            if event.type == pygame.KEYDOWN:

                # El giro de las piezas puede provocar colisiones por el lado derecho
                if event.key == pygame.K_z:
                    pieza.auto_borra()
                    pieza.rotarM90()
                    if pieza.colisiona_con(recipiente):
                        pieza.mover(-1,0)
                    pieza.auto_pinta()

                if event.key == pygame.K_x:
                    pieza.auto_borra()
                    pieza.rotar90()
                    if pieza.colisiona_con(recipiente):
                        pieza.mover(-1,0)
                    pieza.auto_pinta()

                if event.key == pygame.K_LEFT:
                    pieza.auto_borra()
                    pieza.mover(-1,0)
                    if pieza.colisiona_con(recipiente):
                        # deshacer
                        pieza.mover(1, 0)
                    pieza.auto_pinta()

                if event.key == pygame.K_RIGHT:
                    pieza.auto_borra()
                    pieza.mover(1,0)
                    if pieza.colisiona_con(recipiente):
                        # deshacer
                        pieza.mover(-1, 0)
                    pieza.auto_pinta()

                # acelerar gravedad
                if event.key == pygame.K_DOWN:
                    t_GRAVEDAD = 70
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)
                else:
                    t_GRAVEDAD = 2000
                    pygame.time.set_timer(GRAVEDAD, t_GRAVEDAD)

            
            pygame.display.update()

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *