Tetris en Python con curses

Tetris en modo texto para terminal, escrito en Python con el m贸dulo curses de la biblioteca est谩ndar. Incluye las siete piezas con rotaci贸n, eliminaci贸n de l铆neas, puntuaci贸n, niveles progresivos y muestra la siguiente pieza. Funciona en Linux y macOS sin instalar nada; en Windows solo requiere windows-curses.
				"""
tetris.py 聴 Juego Tetris en modo texto

Versi贸n Python del cl谩sico Tetris de V铆ctor Barbero (2001).
Usa el m贸dulo curses de la biblioteca est谩ndar.

Autor: David Carrero Fernandez-Baillo
Web:   https://carrero.es

Requisitos: Python 3.8+, m贸dulo curses (incluido en la biblioteca est谩ndar)
  - Linux/macOS: sin dependencias adicionales
  - Windows:     pip install windows-curses
Controles: ? ? mover  ? rotar  ? bajar  Espacio ca铆da r谩pida  ESC salir
Ejecutar: python tetris.py

Versi贸n Pascal original: https://programacion.net/codigo/tetris_63
Versi贸n PHP:             https://programacion.net/codigo/tetris-php_1895
"""

import curses
import random
import time

FILAS = 20
COLS  = 10

FORMAS = [
    [(0,0),(0,1),(0,2),(0,3)],   # I
    [(0,0),(0,1),(1,0),(1,1)],   # O
    [(0,0),(0,1),(0,2),(1,1)],   # T
    [(0,1),(0,2),(1,0),(1,1)],   # S
    [(0,0),(0,1),(1,1),(1,2)],   # Z
    [(0,0),(1,0),(1,1),(1,2)],   # J
    [(0,2),(1,0),(1,1),(1,2)],   # L
]


def rotar(forma):
    rotada = [(dc, -dr) for dr, dc in forma]
    min_r  = min(r for r, _ in rotada)
    min_c  = min(c for _, c in rotada)
    return [(r - min_r, c - min_c) for r, c in rotada]


def valida(tablero, forma, pr, pc):
    for dr, dc in forma:
        r, c = pr + dr, pc + dc
        if r < 0 or r >= FILAS or c < 0 or c >= COLS or tablero[r][c]:
            return False
    return True


def colocar(tablero, forma, pr, pc, color):
    for dr, dc in forma:
        tablero[pr + dr][pc + dc] = color


def limpiar_lineas(tablero):
    nuevas = [fila for fila in tablero if not all(fila)]
    n = FILAS - len(nuevas)
    for _ in range(n):
        nuevas.insert(0, [0] * COLS)
    tablero[:] = nuevas
    return n


def nueva_pieza():
    idx = random.randrange(len(FORMAS))
    return list(FORMAS[idx]), idx + 1


def dibujar(ven, tablero, forma, pr, pc, color, sig_forma, sig_color, puntos, nivel, lineas):
    ven.erase()
    try:
        ven.addstr(0, 0,
                   f"? ? mover  ? rotar  ? bajar  Espacio: ca铆da  ESC: salir   "
                   f"Puntos:{puntos}  Nivel:{nivel}  Lineas:{lineas}",
                   curses.color_pair(8) | curses.A_BOLD)

        ven.addstr(FILAS + 1, 0, '+' + '--' * COLS + '+')
        for r in range(FILAS):
            ven.addch(r + 1, 0, '|')
            ven.addch(r + 1, COLS * 2 + 1, '|')

        for r in range(FILAS):
            for c in range(COLS):
                if tablero[r][c]:
                    ven.addstr(r + 1, c * 2 + 1, '[]',
                               curses.color_pair(tablero[r][c]) | curses.A_BOLD)
                else:
                    ven.addstr(r + 1, c * 2 + 1, '. ')

        for dr, dc in forma:
            r, c = pr + dr, pc + dc
            if 0 <= r < FILAS and 0 <= c < COLS:
                ven.addstr(r + 1, c * 2 + 1, '[]', curses.color_pair(color) | curses.A_BOLD)

        ven.addstr(2, COLS * 2 + 3, 'Sig:', curses.color_pair(8))
        for dr, dc in sig_forma:
            ven.addstr(3 + dr, COLS * 2 + 3 + dc * 2, '[]',
                       curses.color_pair(sig_color) | curses.A_BOLD)
    except curses.error:
        pass
    ven.refresh()


def jugar(ven):
    curses.curs_set(0)
    ven.nodelay(True)
    curses.start_color()
    curses.init_pair(1, curses.COLOR_CYAN,    curses.COLOR_BLACK)  # I
    curses.init_pair(2, curses.COLOR_YELLOW,  curses.COLOR_BLACK)  # O
    curses.init_pair(3, curses.COLOR_MAGENTA, curses.COLOR_BLACK)  # T
    curses.init_pair(4, curses.COLOR_GREEN,   curses.COLOR_BLACK)  # S
    curses.init_pair(5, curses.COLOR_RED,     curses.COLOR_BLACK)  # Z
    curses.init_pair(6, curses.COLOR_BLUE,    curses.COLOR_BLACK)  # J
    curses.init_pair(7, curses.COLOR_WHITE,   curses.COLOR_BLACK)  # L
    curses.init_pair(8, curses.COLOR_GREEN,   curses.COLOR_BLACK)  # HUD

    tablero              = [[0] * COLS for _ in range(FILAS)]
    forma, color         = nueva_pieza()
    sig_forma, sig_color = nueva_pieza()
    pr, pc               = 0, COLS // 2 - 2
    puntos = 0
    nivel  = 1
    lineas = 0
    ultimo = time.time()

    while True:
        retardo = max(0.1, 0.5 - (nivel - 1) * 0.04)
        dibujar(ven, tablero, forma, pr, pc, color, sig_forma, sig_color, puntos, nivel, lineas)

        tecla = ven.getch()
        if tecla == 27:
            return
        elif tecla == curses.KEY_LEFT and valida(tablero, forma, pr, pc - 1):
            pc -= 1
        elif tecla == curses.KEY_RIGHT and valida(tablero, forma, pr, pc + 1):
            pc += 1
        elif tecla == curses.KEY_UP:
            rot = rotar(forma)
            if valida(tablero, rot, pr, pc):
                forma = rot
        elif tecla == curses.KEY_DOWN and valida(tablero, forma, pr + 1, pc):
            pr += 1
        elif tecla == ord(' '):
            while valida(tablero, forma, pr + 1, pc):
                pr += 1

        if time.time() - ultimo >= retardo:
            ultimo = time.time()
            if valida(tablero, forma, pr + 1, pc):
                pr += 1
            else:
                colocar(tablero, forma, pr, pc, color)
                n = limpiar_lineas(tablero)
                lineas += n
                puntos += [0, 100, 300, 500, 800][n] * nivel
                nivel   = lineas // 10 + 1
                forma, color         = sig_forma, sig_color
                sig_forma, sig_color = nueva_pieza()
                pr, pc               = 0, COLS // 2 - 2

                if not valida(tablero, forma, pr, pc):
                    dibujar(ven, tablero, forma, pr, pc, color,
                            sig_forma, sig_color, puntos, nivel, lineas)
                    ven.nodelay(False)
                    try:
                        ven.addstr(FILAS // 2, 2, f"GAME OVER  Puntos: {puntos}",
                                   curses.color_pair(5) | curses.A_BOLD)
                    except curses.error:
                        pass
                    ven.refresh()
                    ven.getch()
                    return

        time.sleep(0.02)


if __name__ == '__main__':
    curses.wrapper(jugar)

			
Descargar adjuntos
COMPARTE ESTE TUTORIAL

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
TUTORIAL ANTERIOR