#!/usr/bin/env python3 """Implementazione minimale di Tetris da terminale con curses.""" import curses import random import time # Tetromini rappresentati come matrici di 0/1 SHAPES = [ [[1, 1, 1, 1]], # I [[1, 1], [1, 1]], # O [[0, 1, 0], [1, 1, 1]], # T [[0, 1, 1], [1, 1, 0]], # S [[1, 1, 0], [0, 1, 1]], # Z [[1, 0, 0], [1, 1, 1]], # J [[0, 0, 1], [1, 1, 1]], # L ] COLORS = [1, 2, 3, 4, 5, 6, 7] def new_piece(): """Restituisce un pezzo casuale (deep copy).""" return [row[:] for row in random.choice(SHAPES)] def rotate(piece): """Ruota il pezzo di 90 gradi in senso orario.""" return [list(row) for row in zip(*piece[::-1])] def collide(grid, piece, x, y): """Verifica se il pezzo collide con i bordi o con blocchi giĆ  presenti.""" for py, row in enumerate(piece): for px, cell in enumerate(row): if cell: nx, ny = x + px, y + py if nx < 0 or nx >= len(grid[0]) or ny >= len(grid): return True if ny >= 0 and grid[ny][nx]: return True return False def lock(grid, piece, x, y, color): """Blocca il pezzo nella griglia.""" for py, row in enumerate(piece): for px, cell in enumerate(row): if cell: grid[y + py][x + px] = color def clear_lines(grid): """Elimina le linee complete e ne conta quante.""" full = [i for i, row in enumerate(grid) if all(row)] for i in full: del grid[i] grid.insert(0, [0] * len(grid[0])) return len(full) def draw(stdscr, grid, piece, px, py, score): """Disegna la griglia, il pezzo attivo e il punteggio.""" stdscr.clear() h, w = len(grid), len(grid[0]) for y, row in enumerate(grid): for x, cell in enumerate(row): if cell: stdscr.addstr(y + 1, x * 2 + 1, "[]", curses.color_pair(cell)) else: stdscr.addstr(y + 1, x * 2 + 1, " .") for y, row in enumerate(piece): for x, cell in enumerate(row): if cell and py + y >= 0: stdscr.addstr(py + y + 1, (px + x) * 2 + 1, "[]", curses.color_pair(8)) stdscr.addstr(0, w * 2 + 4, f"Score: {score}") stdscr.refresh() def main(stdscr): curses.curs_set(0) stdscr.nodelay(True) stdscr.timeout(50) # Inizializza le coppie di colori for i in range(1, 8): curses.init_pair(i, i, curses.COLOR_BLACK) curses.init_pair(8, curses.COLOR_WHITE, curses.COLOR_WHITE) grid = [[0] * 10 for _ in range(20)] piece = new_piece() px, py = 3, 0 score = 0 fall_time = 0.5 last_fall = time.time() while True: key = stdscr.getch() if key == curses.KEY_LEFT: if not collide(grid, piece, px - 1, py): px -= 1 elif key == curses.KEY_RIGHT: if not collide(grid, piece, px + 1, py): px += 1 elif key == curses.KEY_DOWN: if not collide(grid, piece, px, py + 1): py += 1 elif key == ord(" "): rotated = rotate(piece) if not collide(grid, rotated, px, py): piece = rotated elif not collide(grid, rotated, px + 1, py): piece, px = rotated, px + 1 elif not collide(grid, rotated, px - 1, py): piece, px = rotated, px - 1 elif key == ord("q"): break now = time.time() if now - last_fall > fall_time: if collide(grid, piece, px, py + 1): lock(grid, piece, px, py, random.choice(COLORS)) score += clear_lines(grid) * 100 piece = new_piece() px, py = 3, 0 if collide(grid, piece, px, py): break else: py += 1 last_fall = now draw(stdscr, grid, piece, px, py, score) stdscr.nodelay(False) stdscr.addstr(len(grid) // 2, 0, "GAME OVER - Premi un tasto per uscire") stdscr.refresh() stdscr.getch() if __name__ == "__main__": curses.wrapper(main)