Human-in-the-Loop extension for pi coding agent
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

141 lines
4.2 KiB

#!/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)