tetris.py

Created by julien-bernon

Created on September 24, 2021

25.7 KB

Yet another Tetris, but with 3 modes : classic extended and 2 players 1P : left & right to move aside, down to go down , Up to rotate 2P : 3 & minus to move aside, Ans to go down, + to rotate

====================================================
Encore un autre tetris, mais avec 3 modes : le classique, une version étendue où la grille passe à 21 colonnes de large et surtout un mode 2 players en versus (chaque ligne complétée apparaît chez l’adversaire) pavé directionnel pour le P1 (haut pour tourner) 3,+,-,Ans pour le P2


from math import *
from kandinsky import *
from ion import *
from time import *
from random import randint


couleurs_defaut = (
    (64, 64, 64),
    (0, 255, 255),
    (255, 255, 0),
    (255, 0, 255),
    (255, 128, 0),
    (0, 0, 255),
    (255, 0, 0),
    (0, 255, 0),
    (128, 128, 128),
)


class Grille:
    def __init__(
        self,
        pos_x=0,
        pos_y=0,
        lignes=22,
        colonnes=10,
        unite=10,
        coul_ligne=(64, 64, 0),
        couleurs=couleurs_defaut,
        aleatoire=False,
    ):
        self.pos_x = pos_x
        self.pos_y = pos_y
        self.couleurs = couleurs

        self.lignes = lignes
        self.colonnes = colonnes
        self.unite = unite
        self.coul_ligne = self.couleurs[-1]
        if aleatoire:
            self.contenu = [
                [randint(0, len(self.couleurs) - 1) for i in range(self.colonnes)]
                for j in range(self.lignes)
            ]
        else:
            self.contenu = [
                [0 for i in range(self.colonnes)] for j in range(self.lignes)
            ]

    def print(self):
        # fill_rect(0,0,320,222,(0,0,0))
        for i in range(self.lignes):
            for j in range(self.colonnes):
                fill_rect(
                    self.pos_x + j * self.unite,
                    self.pos_y + i * self.unite,
                    self.unite,
                    self.unite,
                    self.coul_ligne,
                )
                fill_rect(
                    self.pos_x + j * self.unite + 1,
                    self.pos_y + i * self.unite + 1,
                    self.unite - 2,
                    self.unite - 2,
                    self.couleurs[self.contenu[i][j]],
                )

    def new_mos(self):  # crée une mosaïque aléatoire
        self.contenu = [
            [randint(0, len(self.couleurs) - 1) for i in range(self.colonnes)]
            for j in range(self.lignes)
        ]
        self.print()

    def is_complete(self, ligne):  # regarde si la ligne est complète
        for i in range(self.colonnes):
            if self.contenu[ligne][i] == 0:
                return False
        return True

    def rem(
        self, ligne
    ):  # efface la ligne indiquée puis rajoute une ligne a début (tout en haut)
        del self.contenu[ligne]
        self.contenu.insert(0, [0 for i in range(self.colonnes)])


class Piece:
    briques = [0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0, 
    0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0, 
    0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0, 
    0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0, 
    0,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0, 
    0,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0, 
    0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0]
    # couleurs=((0,0,0),(255,0,0),(0,255,0),(0,0,255),(255,255,0),(255,0,255),(0,255,255),(255,128,0),(128,128,128))
    def __init__(self, race, grille, pos=(3, -3)):
        self.actuelle = [
            [self.briques[race * 16 + 4 * i + j] for j in range(4)] for i in range(4)
        ]
        self.future = [i for i in self.actuelle]
        self.grille = grille
        self.pos = pos
        self.race = race
        self.couleur = self.grille.couleurs[race + 1]

    def rotate(self):
        self.future = rotate(self.actuelle)
        self.efface()
        for i in range(4):
            for j in range(4):
                if self.future[i][j]:
                    if (
                        j + self.pos[0] < 0
                        or j + self.pos[0] >= self.grille.colonnes
                        or i + self.pos[1] >= self.grille.lignes
                    ):
                        self.print()
                        return False
                    elif self.grille.contenu[i + self.pos[1]][j + self.pos[0]] != 0:
                        self.print()
                        return False
        self.actuelle = [[self.future[i][j] for j in range(4)] for i in range(4)]
        self.print()
        return True

    def anti_rotate(self):
        self.future = anti_rotate(self.actuelle)
        self.efface()
        for i in range(4):
            for j in range(4):
                if self.future[i][j]:
                    if (
                        j + self.pos[0] < 0
                        or j + self.pos[0] >= self.grille.colonnes
                        or i + self.pos[1] >= self.grille.lignes
                    ):
                        self.print()
                        return False
                    elif self.grille.contenu[i + self.pos[1]][j + self.pos[0]] != 0:
                        self.print()
                        return False
        self.actuelle = [[self.future[i][j] for j in range(4)] for i in range(4)]
        self.print()
        return True

    def bas(self):
        self.efface()
        for i in range(4):
            for j in range(4):
                if self.actuelle[i][j]:
                    if (
                        0 <= self.pos[1] + i + 1 < self.grille.lignes
                        and 0 <= self.pos[0] + j < self.grille.colonnes
                    ):
                        if (
                            self.grille.contenu[self.pos[1] + i + 1][self.pos[0] + j]
                            != 0
                        ):
                            self.print()
                            return False
                    elif self.pos[1] + i + 1 == self.grille.lignes:
                        self.print()
                        return False
        self.pos = (self.pos[0], self.pos[1] + 1)
        self.print()
        return True

    def gauche(self):
        self.efface()
        for i in range(4):
            for j in range(4):
                if self.actuelle[i][j]:
                    if (
                        self.pos[0] + j - 1 < 0
                        or self.pos[0] + j - 1 >= self.grille.colonnes
                    ):
                        self.print()
                        return False
                    else:
                        if self.pos[1] + i < 0:
                            pass
                        elif (
                            self.grille.contenu[self.pos[1] + i][self.pos[0] + j - 1]
                            != 0
                        ):
                            self.print()
                            return False
        self.pos = (self.pos[0] - 1, self.pos[1])
        self.print()
        return True

    def droite(self):
        self.efface()
        for i in range(4):
            for j in range(4):
                if self.actuelle[i][j]:
                    if (
                        self.pos[0] + j + 1 < 0
                        or self.pos[0] + j + 1 >= self.grille.colonnes
                    ):
                        self.print()
                        return False
                    else:
                        if self.pos[1] + i < 0:
                            pass
                        elif (
                            self.grille.contenu[self.pos[1] + i][self.pos[0] + j + 1]
                            != 0
                        ):
                            self.print()
                            return False
        self.pos = (self.pos[0] + 1, self.pos[1])
        self.print()
        return True

    def colle(self):
        self.efface()
        for i in range(4):
            for j in range(4):
                try:
                    if (
                        self.pos[0] + j < self.grille.colonnes
                        and self.pos[0] + j >= 0
                        and self.pos[1] + i < self.grille.lignes
                        and self.pos[1] + i >= 0
                        and self.actuelle[i][j]
                    ):
                        self.grille.contenu[self.pos[1] + i][self.pos[0] + j] = (
                            self.race + 1
                        )
                except:
                    pass
        self.grille.print()

    def print(self):
        for i in range(4):
            for j in range(4):
                if self.actuelle[i][j]:
                    fill_rect(
                        self.grille.pos_x + (j + self.pos[0]) * self.grille.unite,
                        self.grille.pos_y + (i + self.pos[1]) * self.grille.unite,
                        self.grille.unite,
                        self.grille.unite,
                        self.couleur,
                    )

    def efface(self):
        for i in range(4):
            for j in range(4):
                if self.actuelle[i][j]:
                    fill_rect(
                        self.grille.pos_x + (j + self.pos[0]) * self.grille.unite,
                        self.grille.pos_y + (i + self.pos[1]) * self.grille.unite,
                        self.grille.unite,
                        self.grille.unite,
                        self.grille.coul_ligne,
                    )
                    fill_rect(
                        self.grille.pos_x + (j + self.pos[0]) * self.grille.unite + 1,
                        self.grille.pos_y + (i + self.pos[1]) * self.grille.unite + 1,
                        self.grille.unite - 2,
                        self.grille.unite - 2,
                        self.grille.couleurs[0],
                    )

    def efface_tot(self):
        fill_rect(
            self.grille.pos_x + self.pos[0] * self.grille.unite,
            self.grille.pos_y + self.pos[1] * self.grille.unite,
            4 * self.grille.unite,
            4 * self.grille.unite,
            self.grille.couleurs[0],
        )


class Jeu:
    def __init__(self, level=0, score=0, couleurs=couleurs_defaut):
        self.grille = Grille()
        self.score = score
        self.level = level
        self.etat = 0
        self.couleurs = couleurs

    def play(self, mode=0):
        fill_rect(0, 0, 320, 240, self.couleurs[0])
        if mode == 0:
            self.grille = Grille()
            texte("TETRIS", 110, 10, 5, self.couleurs[-1])
            texte("Next", 110, 80, 3, self.couleurs[-1])
            texte("Level", 110, 150, 2, self.couleurs[-1])
            texte(str(self.level), 178, 150, 2, self.couleurs[-1])
            texte("Score", 110, 180, 2, self.couleurs[-1])
            texte(str(self.score), 178, 180, 2, self.couleurs[-1])
            self.pos_suivante = (20, 7)
        elif mode == 1:
            self.grille = Grille(0, 0, 22, 20)
            texte("TETRIS", 210, 10, 3, self.couleurs[-1])
            texte("Next", 240, 40, 2, self.couleurs[-1])
            texte("Level", 240, 100, 2, self.couleurs[-1])
            texte(str(self.level), 240, 120, 2, self.couleurs[-1])
            texte("Score:", 240, 170, 2, self.couleurs[-1])
            texte(str(self.score), 240, 190, 2, self.couleurs[-1])
            self.pos_suivante = (24, 6)
        else:  # mode==2, mode 2 joueurs
            self.grille = Grille()
            self.grille2 = Grille(220, 0, 22, 10)
            self.pos_suivante = (11, 0)
            choix = randint(0, 6)
            self.piece2 = Piece(choix, self.grille2)
            choix = randint(0, 6)
            self.suivante2 = Piece(choix, self.grille2, (-6, 16))
            self.suivante2.efface_tot()
            self.suivante2.print()
            self.grille2.print()
            new2 = True
        lignes_tot = 0
        choix = randint(0, 6)
        self.piece = Piece(choix, self.grille)
        new = True
        choix = randint(0, 6)
        self.suivante = Piece(choix, self.grille, self.pos_suivante)
        self.suivante.efface_tot()
        self.suivante.print()
        self.grille.print()
        temps = monotonic()

        while True:
            # delta est le temps entre 2 avancées successives
            # semble correspondre au timing original
            if self.level < 9:
                delta = -0.0742 * self.level + 0.899
            else:
                delta = -0.012 + 0.278
            while monotonic() - temps < delta:
                deplacement = False

                if keydown(KEY_LEFT):
                    deplacement = deplacement or self.piece.gauche()
                if keydown(KEY_RIGHT):
                    deplacement = deplacement or self.piece.droite()
                if keydown(KEY_DOWN):
                    descend = self.piece.bas()
                    deplacement = deplacement or descend
                    if descend:
                        new = False
                if keydown(KEY_UP):
                    deplacement = deplacement or self.piece.rotate()

                if mode == 2:
                    if keydown(KEY_THREE):
                        deplacement = deplacement or self.piece2.gauche()
                    if keydown(KEY_MINUS):
                        deplacement = deplacement or self.piece2.droite()
                    if keydown(KEY_ANS):
                        descend = self.piece2.bas()
                        deplacement = deplacement or descend
                        if descend:
                            new2 = False
                    if keydown(KEY_PLUS):
                        deplacement = deplacement or self.piece2.rotate()
                if deplacement:
                    if self.level < 5:
                        sleep(0.05)
                    elif self.level < 10:
                        sleep(0.04)
                    elif self.level < 15:
                        sleep(0.03)
                    else:
                        sleep(0.02)
            test = self.piece.bas()

            temps = monotonic()
            if (not test) and new:
                break

            if new:
                new = False

            if not test:
                self.piece.colle()
                self.score += 1
                nb_lignes = 0
                for i in range(self.grille.lignes):
                    if self.grille.is_complete(i):
                        nb_lignes += 1
                        self.grille.rem(i)
                        self.grille.print()
                if nb_lignes == 1:
                    self.score += 40 * (1 + self.level)
                elif nb_lignes == 2:
                    self.score += 100 * (1 + self.level)
                elif nb_lignes == 3:
                    self.score += 300 * (1 + self.level)
                elif nb_lignes == 4:
                    self.score += 1200 * (1 + self.level)
                lignes_tot += nb_lignes
                if lignes_tot >= self.level * 10 + 10:
                    self.level += 1
                if mode == 2:  # on ajoute les lignes au joueur 2
                    for i in range(nb_lignes):
                        self.grille2.contenu.append(
                            [randint(0, 7) for j in range(self.grille2.colonnes)]
                        )
                        del self.grille2.contenu[0]
                        self.grille2.print()
                self.piece = Piece(self.suivante.race, self.grille)
                choix = randint(0, 6)
                new = True
                self.suivante.efface_tot()

                if mode == 0:
                    fill_rect(178, 150, 100, 60, self.couleurs[0])
                    texte(str(self.level), 178, 150, 2, self.couleurs[-1])
                    texte(str(self.score), 178, 180, 2, self.couleurs[-1])
                    self.suivante = Piece(choix, self.grille, (20, 7))
                elif mode == 1:
                    fill_rect(240, 130, 100, 30, self.couleurs[0])
                    texte(str(self.level), 240, 120, 2, self.couleurs[-1])
                    fill_rect(240, 190, 100, 30, self.couleurs[0])
                    texte(str(self.score), 240, 190, 2, self.couleurs[-1])
                    self.suivante = Piece(choix, self.grille, (24, 6))
                else:

                    self.suivante = Piece(choix, self.grille, (11, 0))
                self.suivante.print()
            if mode == 2:
                if new2:
                    new2 = False
                test2 = self.piece2.bas()
                if (not test2) and new2:
                    break
                if not test2:
                    self.piece2.colle()
                    self.score += 1
                    nb_lignes = 0
                    for i in range(self.grille2.lignes):
                        if self.grille2.is_complete(i):
                            nb_lignes += 1
                            self.grille2.rem(i)
                            self.grille2.print()
                    if nb_lignes == 1:
                        self.score += 40 * (1 + self.level)
                    elif nb_lignes == 2:
                        self.score += 100 * (1 + self.level)
                    elif nb_lignes == 3:
                        self.score += 300 * (1 + self.level)
                    elif nb_lignes == 4:
                        self.score += 1200 * (1 + self.level)
                    lignes_tot += nb_lignes
                    if lignes_tot >= self.level * 10 + 10:
                        self.level += 1
                    # on ajoute les lignes au joueur 1
                    for i in range(nb_lignes):
                        self.grille.contenu.append(
                            [randint(0, 7) for j in range(self.grille.colonnes)]
                        )
                        del self.grille.contenu[0]
                        self.grille.print()
                    self.piece2 = Piece(self.suivante2.race, self.grille2)
                    choix = randint(0, 6)
                    new2 = True
                    self.suivante2.efface_tot()
                    self.suivante2 = Piece(choix, self.grille2, (-6, 16))
                    self.suivante2.print()
        self.GameOver()

    def GameOver(self):
        Grille(0, 0, 22, 32, 10, (64, 64, 0), couleurs_defaut, True).print()

        texte_bg("GAME", 90, 10, 140, (255, 255, 255), 5, (64, 64, 64), 10)
        texte_bg("OVER", 90, 70, 140, (255, 255, 255), 5, (64, 64, 64), 10)

        texte_bg("Level", 90, 150, 68, (255, 255, 255), 2, (64, 64, 64), 4)
        texte_bg(str(self.level), 158, 150, 68, (255, 255, 255), 2, (64, 64, 64), 4)
        texte_bg("Score", 90, 180, 68, (255, 255, 255), 2, (64, 64, 64), 4)
        texte_bg(str(self.score), 158, 180, 68, (255, 255, 255), 2, (64, 64, 64), 4)

    def Intro(self):
        Grille(0, 0, 22, 32, 10, (64, 64, 0), couleurs_defaut, True).print()
        texte_bg("TETRIS", 60, 0, 200, (255, 255, 255), 5, (64, 64, 64), 10)
        texte_bg("Classic", 70, 80, 180, (255, 255, 255), 3, (64, 64, 64), 10)
        texte_bg("Extended", 70, 130, 180, (255, 255, 255), 3, (64, 64, 64), 10)
        texte_bg("2 Players", 70, 180, 180, (255, 255, 255), 3, (64, 64, 64), 10)
        cadre = Rectangle_Clignotant(70, 80, 180, 44, 5)
        while not keydown(KEY_OK):
            cadre.print()
            if keydown(KEY_DOWN):
                cadre.efface()
                cadre.y = 80 + (cadre.y - 80 + 50) % 150
                print(str(cadre.y))
            elif keydown(KEY_UP):
                cadre.efface()
                cadre.y = 80 + (cadre.y - 80 - 50) % 150
                print(str(cadre.y))
            sleep(0.2)
        print((cadre.y - 80) // 50)
        self.play((cadre.y - 80) // 50)


class Rectangle_Clignotant:
    def __init__(
        self,
        x,
        y,
        width,
        height,
        thickness=10,
        couleurs=couleurs_defaut[0:-1],
        bg_color=(couleurs_defaut[0]),
    ):
        self.x, self.y, self.width, self.height, self.thickness, self.couleurs, self.bg_color = (
            x,
            y,
            width,
            height,
            thickness,
            couleurs,
            bg_color,
        )
        self.choix_coul = 0

    def print(self):
        fill_rect(
            self.x, self.y, self.width, self.thickness, self.couleurs[self.choix_coul]
        )
        fill_rect(
            self.x,
            self.y + self.height - self.thickness,
            self.width,
            self.thickness,
            self.couleurs[self.choix_coul],
        )
        fill_rect(
            self.x, self.y, self.thickness, self.height, self.couleurs[self.choix_coul]
        )
        fill_rect(
            self.x + self.width - self.thickness,
            self.y,
            self.thickness,
            self.height,
            self.couleurs[self.choix_coul],
        )
        self.choix_coul = (self.choix_coul + 1) % len(self.couleurs)

    def efface(self):
        fill_rect(self.x, self.y, self.width, self.thickness, self.bg_color)
        fill_rect(
            self.x,
            self.y + self.height - self.thickness,
            self.width,
            self.thickness,
            self.bg_color,
        )
        fill_rect(self.x, self.y, self.thickness, self.height, self.bg_color)
        fill_rect(
            self.x + self.width - self.thickness,
            self.y,
            self.thickness,
            self.height,
            self.bg_color,
        )


def rotate(matrice):
    return [
        [matrice[j][-1 - i] for j in range(len(matrice[i]))]
        for i in range(len(matrice))
    ]


def anti_rotate(matrice):
    return [
        [matrice[-1 - j][i] for j in range(len(matrice[i]))]
        for i in range(len(matrice))
    ]


lettres = {
    "A": "0111010001100011000111111100011000100000",
    "B": "1111010001100011111010001100011111000000",
    "C": "0111010001100001000010000100010111000000",
    "D": "1111010010100011000110001100101111000000",
    "E": "1111110000100001110010000100001111100000",
    "F": "1111110000100001110010000100001000000000",
    "G": "0111110000100001001110001100010111100000",
    "H": "1000110001100011111110001100011000100000",
    "I": "0111000100001000010000100001000111000000",
    "J": "0111100010000100001000010100100110000000",
    "K": "1000110010101001110010010100101000100000",
    "L": "0100001000010000100001000010000111100000",
    "M": "1000111011101011000110001100011000100000",
    "N": "1000111001101011001110001100011000100000",
    "O": "0111010001100011000110001100010111000000",
    "P": "1111010001100011111010000100001000000000",
    "Q": "0111010001100011000110101100100110100000",
    "R": "1111010001100011111010010100011000100000",
    "S": "0111010001100000111000001100010111000000",
    "T": "1111100100001000010000100001000010000000",
    "U": "1000110001100011000110001100010111000000",
    "V": "1000110001100011000101010010100010000000",
    "W": "1000110001100011000110101110111000100000",
    "X": "1000110001010100010001010100011000100000",
    "Y": "1000110001100010101000100001000010000000",
    "Z": "1111100001000100010001000100001111100000",
    "a": "0000001110000010111110001100010111100000",
    "b": "1000010000111101000110001100011111000000",
    "c": "0000000000011101000110000100000111100000",
    "d": "0000100001011111000110001100010111100000",
    "e": "0000001110100011111110000100000111100000",
    "f": "0000000111010000100011110010000100000000",
    "g": "0000001111100011000110001011110000101110",
    "h": "1000010000100001011011001100011000100000",
    "i": "0000000100000000110000100001000111000000",
    "j": "0000000100000000010000100001000010011000",
    "k": "0000001000010000100101010011010100100000",
    "l": "0100001000010000100001000001000001100000",
    "m": "0000000000010101010110101101011010100000",
    "n": "0000000000101101100110001100011000100000",
    "o": "0000000000011101000110001100010111000000",
    "p": "0000000110010010100101110010000100001000",
    "q": "0000000000001100100101001001110000100001",
    "r": "0000000000001110110001000010000100000000",
    "s": "0000000000001110100000111000010111000000",
    "t": "0000001000011100100001000010000011000000",
    "u": "0000000000000001000110001100110110100000",
    "v": "0000000000000001000110001010100010000000",
    "w": "0000000000000001000110101101010101000000",
    "x": "0000010001010100010000100010101000100000",
    "y": "0000001001010010100100110000100110000010",
    "z": "0000000000011110000100110010000111100000",
    "0": "0111010001101011010110101100010111000000",
    "1": "0010011100001000010000100001001111100000",
    "2": "0111010001000010001000100010011111100000",
    "3": "0111010001000010011000001100010111000000",
    "4": "0010001010100101001011111000100001000000",
    "5": "1111110000111100000100001100011111000000",
    "6": "0111010001100001111010001100010111000000",
    "7": "1111110001000010001000100010000100000000",
    "8": "0111010001100010111010001100010111000000",
    "9": "0111010001100010111100001010010011000000",
    " ": "0000000000000000000000000000000000000000",
    ".": "0000000000000000000000000001100011000000",
    ",": "0000000000000000000000110001100001000100",
    ":": "0000000000000000010000000000000010000000",
}


for i in lettres.keys():
    conversion = []
    for j in lettres[i]:
        if j == "0":
            conversion.append(False)
        else:
            conversion.append(True)
    lettres[i] = conversion


def main():
    posx, posy = 0, 0
    dispos = sorted(list(lettres.keys()))
    for k in range(1, 4):
        for i in range(len(dispos)):
            for j in range(len(lettres[dispos[i]])):
                x = posx + (k * 6 * i) % 320 + k * (j % 5)
                y = posy + 9 * k * ((k * 9 * i) // 320) + k * (j // 5)
                if lettres[dispos[i]][j]:
                    for l in range(k):
                        for m in range(k):
                            set_pixel(x + l, y + m, (0, 0, 0))
        posy = y + 9 * k


def texte(chaine, posx=0, posy=0, taille=1, color=(0, 0, 0)):
    for i in range(len(chaine)):
        carac = chaine[i]
        if not carac in lettres.keys():
            carac = " "
        for j in range(len(lettres[carac])):
            x = posx + (taille * 6 * i) % 320 + taille * (j % 5)
            y = posy + taille * (j // 5)
            if lettres[carac][j]:
                for k in range(taille):
                    for l in range(taille):
                        set_pixel(x + k, y + l, color)


def texte_bg(
    chaine,
    posx=0,
    posy=0,
    width=320,
    color=(0, 0, 0),
    taille=1,
    bg_color=(255, 255, 255),
    padding=10,
):
    fill_rect(
        posx,
        posy,
        width,
        9 * (taille + (taille * 6 * len(chaine)) // width) + 2 * padding,
        bg_color,
    )
    texte(chaine, posx + padding, posy + padding, taille, color)


a = Jeu()
a.Intro()