Hi ha unes peces que van caient i cal anar apilant peces de la forma més compacta possible, per això tenim la possibilitat de desplaçar-les o fer-les rodar. Hi ha un fons musical que fa sonar el següent fitxer de so.
El programa és el següent:
import board import displayio import time import random import funcions import keypad from adafruit_bitmap_font import bitmap_font from adafruit_display_text import label import audiocore import audiomixer import audioio import digitalio import neopixel
# Creem un objecte per a la pantalla
pant = board.DISPLAY
# Tamany dels blocs de Tetris
tamany_blocs = 6
# Número de columnes i files del taulell
columnes = 10
files = 20
# Creem una imatge amb dotze colors
imatge = displayio.Bitmap(160, 128, 12)
# Creem una paleta de dotze colors
paleta = displayio.Palette(12)
# Omplim la paleta
paleta[0] = 0x000000 # Negre per al fons
paleta[1] = 0xFFFFFF # Blanc
paleta[2] = 0x9999FF # Blau per la part externa
paleta[3] = 0xFF0000 # Vermell
paleta[4] = 0x00FF00 # Verd
paleta[5] = 0xC0C000 # Or vell
paleta[6] = 0xFFFF00 # Groc
paleta[7] = 0xFF00FF # Magenta
paleta[8] = 0x00FFFF # Cian
paleta[9] = 0xFFA500 # Taronja
paleta[10] = 0x8000FF # Violat
paleta[11] = 0xFF8040 # Marró
# Utilitzem aquesta llista per escollir un color aleatori que li posarem a cada peca
colors_peca = [1, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# Creem un mosaic vinculat a la imatge
tile_grid = displayio.TileGrid(imatge, pixel_shader=paleta)
# Creem un grup on allotjar el mosaic
grup = displayio.Group()
grup.append(tile_grid)
# Mostrem el grup a la pantalla
pant.root_group = grup
# Creem variables que permetin situar el taulell en qualsevol posició
x0 = 20
y0 = 0
# Noms dels botons
Noms_botons = (
"B",
"A",
"START",
"SELECT",
"Dreta",
"Avall",
"Amunt",
"Esquerra"
)
# Objecte per a llegir els botons
pad = keypad.ShiftRegisterKeys(
clock=board.BUTTON_CLOCK,
data=board.BUTTON_OUT,
latch=board.BUTTON_LATCH,
key_count=8,
value_when_pressed=True,
)
# Inicializar temporizadors
temps_caiguda = time.monotonic()
interval_caiguda = 0.5 # Interval de temps per a la caiguda de les peces
factor_reduccio = 0.98 # Redueix l'interval un 5% cada vegada que una peça cau
interval_caiguda_minim = 0.1 # Límita l'interval de caiguda mínim a 0.1 segons
temps_botons = time.monotonic()
interval_botons = 0.05 # Interval de temps per a comprobar els botons
files_completades = 0
puntuacio = 0
# Creem les peces
punt = funcions.crear_matriu(1,1,1)
doble_punt = funcions.crear_matriu(1,2,1)
triple_punt = funcions.crear_matriu(1,3,1)
escala = funcions.crear_matriu(2,2,1)
# es necessari colocar 0 en les posicions on no hi ha peça
escala[0][1] = 0
quadrat = funcions.crear_matriu(2,2,1)
ele = funcions.crear_matriu(2,3,1)
ele[0][1] = 0
ele[0][2] = 0
ele2 = funcions.crear_matriu(2,3,1)
ele2[0][0] = 0
ele2[0][1] = 0
escala_punt = funcions.crear_matriu(2,3,1)
escala_punt[0][2] = 0
escala_punt[1][0] = 0
escala_punt2 = funcions.crear_matriu(2,3,1)
escala_punt2[0][0] = 0
escala_punt2[1][2] = 0
recta = funcions.crear_matriu(1,4,1)
te = funcions.crear_matriu(2,3,1)
te[1][0] = 0
te[1][2] = 0
# Afegim les peces en una llista per a escollir una aleatoria posteriorment
peces = [punt, doble_punt, triple_punt, escala, quadrat, ele, ele2, escala_punt, escala_punt2, recta, te]
# Definir la peça actual
peca_actual = random.choice(peces) # Selecciona una peça aleatoria
x_peca = x0 + 3 * tamany_blocs # Col·loquem la peça al centre horitzontal del taulell
y_peca = y0 # Comencem a la part superior
# Font per escriure a la pantalla
font = bitmap_font.load_font("/font/ib8x8u.bdf")
# Creem la matriu 20x10 del taulell
matriu_taulell = funcions.crear_matriu(20, 10, 0)
# Es crea una variable per a la peça guardada
peca_guardada = None # Inicialment no hi ha cap peça
# Definim una región per a mostrar la peça guardada
x_guardada = 130 # Coordenada X del recuadre
y_guardada = 10 # Coordenada Y del recuadre
# Configura el altaveu
altaveu = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
altaveu.direction = digitalio.Direction.OUTPUT
altaveu.value = True
# Configura el mezclador d'audio
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1, bits_per_sample=16, samples_signed=True)
audio = audioio.AudioOut(board.A0)
audio.play(mixer)
neopixel_d8 = neopixel.NeoPixel(board.D8, 1) # 1 LED connectat al pin D8
neopixel_d8.brightness = 0.5 # ajustem la brillantor
neopixel_d8.fill((0, 0, 0)) # Ens assegurem de que comença apagat
# Si posem aquestes funcions en el fitxer 'funcions' hi han problemes
# Dibuixar la quadrícula inicial una sola vegada
def dibuixar_quadrícula(x0, y0, columnes, files):
# Afegim el contorn
for x in range(x0, columnes * tamany_blocs + x0):
for y in range(files * tamany_blocs, 128):
imatge[x, y] = 2
for y in range(0, 128):
for x in range(x0 - 7, x0):
imatge[x, y] = 2
for x in range(columnes * tamany_blocs + x0, columnes * tamany_blocs + 8 + x0):
imatge[x, y] = 2
def esborrar_peca(matriu_peca,imatge, x_peca, y_peca):
for i in range(len(matriu_peca)): # Files de la peça
for j in range(len(matriu_peca[i])): # Columnes de la peça
valor = matriu_peca[i][j]
if valor > 0: # Només dibuixar si el valor és més gran que 0
for x in range(x_peca + j * tamany_blocs, x_peca + (j + 1) * tamany_blocs):
for y in range(y_peca + i * tamany_blocs, y_peca + (i + 1) * tamany_blocs):
imatge[x, y] = 0 # Assignem el valor de la peça
def dibuixar_peca(matriu_peca, imatge, x_peca, y_peca):
# Dibuixem la peça en la seva posició actual
for i in range(len(matriu_peca)): # Files de la peça
for j in range(len(matriu_peca[i])): # Columnes de la peça
valor = matriu_peca[i][j]
if valor > 0: # Només dibuixar si el valor és més gran que 0
for x in range(x_peca + j * tamany_blocs, x_peca + (j + 1) * tamany_blocs):
for y in range(y_peca + i * tamany_blocs, y_peca + (i + 1) * tamany_blocs):
imatge[x, y] = valor # Assignem el valor de la peça
# Funció per a redibuixar tot el taulell després d'eliminar una fila
def redibuixar_taulell(matriu_taulell):
for i in range(files): # Recorrem les files
for j in range(columnes): # Recorrem les columnes
valor = matriu_taulell[i][j]
# Redibuixar cada bloc en la seva posició corresponent
for x in range(x0 + j * tamany_blocs, x0 + (j + 1) * tamany_blocs):
for y in range(y0 + i * tamany_blocs, y0 + (i + 1) * tamany_blocs):
imatge[x, y] = valor # Asignar el valor del tablero al gráfico
# Funció per a comprobar si una fila está plena
def comprovar_fila_completa(matriu_taulell):
global files_completades, puntuacio
# Recorrem cada fila del taulell
for i in range(files): # 'files' es el número total de files en el taulell
if all(matriu_taulell[i][j] != 0 for j in range(columnes)): # Si la fila está plena
eliminar_fila(matriu_taulell, i) # Eliminar la fila i-èsima
files_completades += 1
if files_completades > 0:
puntuacio = files_completades * 100 # Incrementa la puntuació
mostrar_puntuacio(puntuacio) # Mostra la puntuació actualizada
neopixel_d8.fill((0, 255, 0)) # Encendre NeoPixel D8 (color verd com a premi)
time.sleep(0.5) # Mantenir encès 0.5 segons
neopixel_d8.fill((0, 0, 0)) # Apagar
return True # Indicar que s'ha eliminat una fila
return False # No s'ha eliminat cap fila
# Funció per a eliminar una fila completa i baixar les superiors
def eliminar_fila(matriu_taulell, fila):
# Moure totes les files superiors una posición cap a baix
for i in range(fila, 0, -1):
matriu_taulell[i] = matriu_taulell[i - 1][:] # Copiar la fila de sobre a la fila actual
# Buidar la fila superior (posar zeros a la fila 0)
matriu_taulell[0] = [0] * columnes
# Actualizar la representació visual del taulell
redibuixar_taulell(matriu_taulell)
def pantalla_inici():
# Neteja la pantalla abans de posar-hi text
imatge.fill(0)
# Crea el text inicial i el coloca a la pantalla
req_text = label.Label(font, text="Premeu START", color=0xFFFF00)
req_text.x = 30
req_text.y = 64
# Crea un grup per mostrar el missatge
grup_inici = displayio.Group()
grup_inici.append(req_text)
pant.root_group = grup_inici
# Bucle de espera per a que el jugador presioni `START`
while True:
boto = pad.events.get()
# Verifica si el botó `START` (key_number 2) ha sigut presionat
if boto and boto.pressed and boto.key_number == 2:
pant.root_group = grup # fem que aparegui el grup per a que el joc funcioni
break # Sortim del bucle
# Dibuixem la peça guardada si existeix
if peca_guardada is not None:
for i in range(len(peca_guardada)):
for j in range(len(peca_guardada[i])):
valor = peca_guardada[i][j]
if valor > 0:
for x in range(x_guardada + j * tamany_blocs, x_guardada + (j + 1) * tamany_blocs):
for y in range(y_guardada + i * tamany_blocs, y_guardada + (i + 1) * tamany_blocs):
imatge[x, y] = valor
def pantalla_final():
apagar_musica()
imatge.fill(0) # Neteja la pantalla
req_text = label.Label(font, text="Final del Joc", color=0xFFFF00)
req_text.x = 30
req_text.y = 64
pant.root_group = req_text
# Esperem fins que es presioni el botó START
while True:
boto = pad.events.get()
if boto and boto.pressed and boto.key_number == 2:
break # Sortim del bucle
# Inicializar variables del juego, d'altra forma el programa no funciona correctament
def reiniciar():
global peca_actual, x_peca, y_peca, matriu_taulell, peca_guardada, interval_caiguda, puntuacio, files_completades
grup.remove(puntuacio_text)
puntuacio == 0
files_completades = 0
interval_caiguda = 0.5
peca_guardada = None
dibuixar_peca_guardada()
matriu_taulell = funcions.crear_matriu(files, columnes, 0) # Reinicia el taulell
peca_actual = random.choice(peces) # Genera una peca nova
funcions.cambiar_valor_peca(peca_actual,random.choice(colors_peca)) # Fem que la peca tingui un color aleatori
x_peca, y_peca = x0 + 3 * tamany_blocs, y0
while True:
pantalla_inici() # Mostrar la pantalla d'inici
encender_musica("musica_tetris.wav",volumen=1)
break
# Funció per a dibuixar la peça guardada al recuadre
def dibuixar_peca_guardada():
# Neteja l'àrea de la peça guardada
for x in range(x_guardada, x_guardada + 4 * tamany_blocs):
for y in range(y_guardada, y_guardada + 4 * tamany_blocs):
imatge[x, y] = 0 # Fons negre
# Dibuixa la peça guardada si existeix
if peca_guardada is not None:
for i in range(len(peca_guardada)):
for j in range(len(peca_guardada[i])):
valor = peca_guardada[i][j]
if valor > 0:
for x in range(x_guardada + j * tamany_blocs, x_guardada + (j + 1) * tamany_blocs):
for y in range(y_guardada + i * tamany_blocs, y_guardada + (i + 1) * tamany_blocs):
imatge[x, y] = valor
else:
for x in range(x_guardada, x_guardada + 4 * tamany_blocs):
for y in range(y_guardada, y_guardada + 4 * tamany_blocs):
imatge[x, y] = 0
def validar_posicio(peca, matriu_taulell, x, y, tamany_blocs):
global x0, y0
for i in range(len(peca)): # Files de la peça
for j in range(len(peca[0])): # Columnes de la peça
if peca[i][j] != 0: # Verifiquem les cel·les ocupades per la peça
# Coordenades absolutes del bloc
bloque_x = x + j * tamany_blocs
bloque_y = y + i * tamany_blocs
# Convertim a index del taulell
col = (bloque_x - x0) // tamany_blocs
fila = (bloque_y - y0) // tamany_blocs
# Verifiquem límits del taulell
if bloque_x < x0 or bloque_x >= x0+len(matriu_taulell[0])*tamany_blocs or bloque_y < y0 or bloque_y >= y0+len(matriu_taulell)*tamany_blocs:
return False
# Verifiquem colisió a la matriu del taulell
if matriu_taulell[fila][col] != 0:
return False
# Si tots els blocs son válids
return True
def mostrar_puntuacio(puntuacio):
global puntuacio_text
if puntuacio == 0:
# Text de la puntuació
puntuacio_text = label.Label(font,text=f"Punts\n {puntuacio}",color=0xFFFFFF)
puntuacio_text.x = 100
puntuacio_text.y = 100
elif puntuacio >= 100 and puntuacio <= 9999:
grup.remove(puntuacio_text)
# Text de la puntuació
puntuacio_text = label.Label(font,text=f"Punts\n {puntuacio}",color=0xFFFFFF)
puntuacio_text.x = 100
puntuacio_text.y = 100
else:
grup.remove(puntuacio_text)
# Text de la puntuació
puntuacio_text = label.Label(font,text=f"Punts\n{puntuacio}",color=0xFFFFFF)
puntuacio_text.x = 100
puntuacio_text.y = 100
# Afegim el text al grup
grup.append(puntuacio_text)
# Mostrem el grup a la pantalla
pant.root_group = grup
# Funció per a encendre la música
def encender_musica(nom_fitxer, volumen=1):
fitxer = open(nom_fitxer, "rb")
wave = audiocore.WaveFile(fitxer)
mixer.voice[0].play(wave, loop=True) # Reprodueix en bucle
mixer.voice[0].level = volumen # Ajusta el volumen
# Funció per apagar la música
def apagar_musica():
mixer.voice[0].stop()
# Iniciem el joc amb la pantalla d'inici fora del bucle
pantalla_inici()
encender_musica("musica_tetris.wav",volumen=1)
# Dibuixem la quadrícula un cop inicia el joc
dibuixar_quadrícula(x0, y0, columnes, files)
mostrar_puntuacio(puntuacio)
while True:
temps_actual = time.monotonic()
# Comprobem si la següent peça es pot colocar d'altra forma el joc acaba
if funcions.detectar_colisio(peca_actual, matriu_taulell, x_peca, y_peca) and y_peca == y0:
pantalla_final()
reiniciar()
puntuacio == 0
dibuixar_quadrícula(x0, y0, columnes, files)
mostrar_puntuacio(0)
# Comprobem l'entrada dels botons
if temps_actual - temps_botons > interval_botons:
boto = pad.events.get()
if boto:
if boto.pressed and boto.key_number == 6: # Botó "Amunt" per a rotar
esborrar_peca(peca_actual, imatge, x_peca, y_peca)
peca_rotada = funcions.rotate(peca_actual)
if not funcions.detectar_colisio(peca_rotada, matriu_taulell, x_peca, y_peca) and not funcions.detectar_limits(peca_rotada, x_peca, y_peca):
peca_actual = peca_rotada
dibuixar_peca(peca_actual, imatge, x_peca, y_peca) # Dibuixar la peça després de rotarla
# Moviment cap a la dreta
elif boto.pressed and boto.key_number == 4:
if not funcions.detectar_colisio(peca_actual, matriu_taulell, x_peca + tamany_blocs, y_peca) and not funcions.detectar_limits(peca_actual, x_peca + tamany_blocs, y_peca):
esborrar_peca(peca_actual, imatge, x_peca, y_peca)
x_peca += tamany_blocs
dibuixar_peca(peca_actual, imatge, x_peca, y_peca) # Dibuixar la peça després de mourela
# Moviment cap a l'esquerra
elif boto.pressed and boto.key_number == 7:
if not funcions.detectar_colisio(peca_actual, matriu_taulell, x_peca - tamany_blocs, y_peca) and not funcions.detectar_limits(peca_actual, x_peca - tamany_blocs, y_peca):
esborrar_peca(peca_actual, imatge, x_peca, y_peca)
x_peca -= tamany_blocs
dibuixar_peca(peca_actual, imatge, x_peca, y_peca) # Dibuixar la peça després de mourela
# Moviment cap avall (caiguda ràpida)
elif boto.pressed and boto.key_number == 5: # Botó per a fer caure la peça rápidament
# Fer que la peça caigui fins que hi hagi una colisió
while not funcions.detectar_colisio(peca_actual, matriu_taulell, x_peca, y_peca + tamany_blocs):
esborrar_peca(peca_actual, imatge, x_peca, y_peca) # Borra la peça de la posició anterior
y_peca += tamany_blocs # Moure la peça cap avall
dibuixar_peca(peca_actual, imatge, x_peca, y_peca) # Dibuixa la peça en la nova posició
# Fixar la peça al taulell quan col·lisiona
funcions.fixar_peca(peca_actual, matriu_taulell, x_peca, y_peca)
while comprovar_fila_completa(matriu_taulell): # Comprovar si alguna fila està completa
# Si s'ha eliminat una fila, continuar comprovant en bucle fins que no hi hagi més files plenes
pass
# Generar una nova peça
peca_actual = random.choice(peces)
funcions.cambiar_valor_peca(peca_actual,random.choice(colors_peca))
x_peca, y_peca = x0 + 3 * tamany_blocs, y0 # Posició inicial de la nova peça
dibuixar_peca(peca_actual, imatge, x_peca, y_peca) # Dibuixar la nova pieça
interval_caiguda = max(interval_caiguda * factor_reduccio, interval_caiguda_minim)
elif boto.pressed and boto.key_number == 1: # Botó per a guardar/intercambiar la peça
if peca_guardada is None: # Si no hi ha una peça guardada
esborrar_peca(peca_actual, imatge, x_peca, y_peca) # Esborrem la peça de la pantalla
# Guardem la peça actual
peca_guardada = peca_actual
# Generem una nova peça
peca_actual = random.choice(peces)
funcions.cambiar_valor_peca(peca_actual, random.choice(colors_peca))
x_peca, y_peca = x0 + 3 * tamany_blocs, y0 # Reiniciem la posició de la nova peça
else: # Si hi ha una peça guardada
# Validar si la pieza guardada puede colocarse
if validar_posicio(peca_guardada, matriu_taulell, x0 + 3, y0, tamany_blocs):
# Cambia x0 y y0 según las coordenadas iniciales
esborrar_peca(peca_actual, imatge, x_peca, y_peca) # Esborrem la peça de la pantalla
# Intercambiem la peça actual amb la guardada
peca_actual, peca_guardada = peca_guardada, peca_actual
dibuixar_peca_guardada()
temps_botons = temps_actual # Actualizar el temps de control dels botons
# Controlar la caiguda de les peces
if temps_actual - temps_caiguda > interval_caiguda:
if funcions.detectar_colisio(peca_actual, matriu_taulell, x_peca, y_peca + tamany_blocs):
funcions.fixar_peca(peca_actual, matriu_taulell, x_peca, y_peca)
while comprovar_fila_completa(matriu_taulell): # Comprobar si alguna fila está completa
pass # Eliminar totes les files plenes
# Redibuixar tot el taulell després de gestionar les files
redibuixar_taulell(matriu_taulell)
# Redueix l'interval de caiguda per augmentar la dificultat
interval_caiguda = max(interval_caiguda * factor_reduccio, interval_caiguda_minim)
# Generar una nova peça
peca_actual = random.choice(peces)
funcions.cambiar_valor_peca(peca_actual,random.choice(colors_peca))
x_peca, y_peca = x0 + 3 * tamany_blocs, y0 # Posició inicial de la nova peça
else:
esborrar_peca(peca_actual, imatge, x_peca, y_peca)
y_peca += tamany_blocs
dibuixar_peca(peca_actual, imatge, x_peca, y_peca) # Dibuixar la peça després de moure
temps_caiguda = temps_actual # Actualizar el temps de caiguda
El fitxer funcions.py té el següent contingut:
tamany_blocs = 6 columnes = 10 files = 20 x0 = 20 y0 = 0
# Funció que crea una matriu
def crear_matriu(files, columnes, valor):
matriu = []
for i in range(files):
files = [valor] * columnes
matriu.append(files)
return matriu
# Creem el taulell
matriu_taulell = crear_matriu(20, 10, 0)
# Creem les peces
punt = crear_matriu(1,1,1)
doble_punt = crear_matriu(1,2,1)
triple_punt = crear_matriu(1,3,1)
escala = crear_matriu(2,2,1)
# es necessari colocar 0 en les posicions on no hi ha peça
escala[0][1] = 0
quadrat = crear_matriu(2,2,1)
ele = crear_matriu(2,3,1)
ele[0][1] = 0
ele[0][2] = 0
ele2 = crear_matriu(2,3,1)
ele2[0][0] = 0
ele2[0][1] = 0
escala_punt = crear_matriu(2,3,1)
escala_punt[0][2] = 0
escala_punt[1][0] = 0
escala_punt2 = crear_matriu(2,3,1)
escala_punt2[0][0] = 0
escala_punt2[1][2] = 0
recta = crear_matriu(1,4,1)
te = crear_matriu(2,3,1)
te[1][0] = 0
te[1][2] = 0
def detectar_colisio(matriu_peca, matriu_taulell, x_peca, y_peca):
for i in range(len(matriu_peca)): # Files de la peça
for j in range(len(matriu_peca[i])): # Columnes de la peça
if matriu_peca[i][j] > 0: # Només ens preocupen els blocs visibles
# Coordenades de la peça al taulell
x_tau = (x_peca - x0) // tamany_blocs + j
y_tau = (y_peca - y0) // tamany_blocs + i
# Comprovar si la peça toca el terra o una altra peça
if y_tau >= files or not (0 <= x_tau < columnes) or matriu_taulell[y_tau][x_tau] > 0:
return True
return False
def fixar_peca(matriu_peca, matriu_taulell, x_peca, y_peca):
for i in range(len(matriu_peca)): # Files de la peça
for j in range(len(matriu_peca[i])): # Columnes de la peça
if matriu_peca[i][j] > 0: # Només blocs visibles
# Coordenades de la peça al taulell
x_tau = (x_peca - x0) // tamany_blocs + j
y_tau = (y_peca - y0) // tamany_blocs + i
# Assignar la peça al taulell
if y_tau < files and x_tau < columnes:
matriu_taulell[y_tau][x_tau] = matriu_peca[i][j]
def rotate(piece):
return [list(row) for row in zip(*piece[::-1])]
def detectar_limits(peca_rotada, x_peca, y_peca):
for i in range(len(peca_rotada)):
for j in range(len(peca_rotada[i])):
if peca_rotada[i][j] > 0:
# Comprovar si la peça surt dels límits del taulell
if x_peca + j * tamany_blocs < x0 or x_peca + (j + 1) * tamany_blocs > x0 + columnes * tamany_blocs:
return True
return False
def cambiar_valor_peca(matriu_peca, nou_valor):
for i in range(len(matriu_peca)):
for j in range(len(matriu_peca[i])):
if matriu_peca[i][j] != 0: # Només cambia el valor on hi ha blocs de la peça
matriu_peca[i][j] = nou_valor

Esta obra de Oriol Boix está licenciada bajo una licencia no importada Creative Commons Reconocimiento-NoComercial-SinObraDerivada 3.0.