commit
e76accd1ee
7 changed files with 4399 additions and 0 deletions
@ -0,0 +1,7 @@ |
|||||||
|
node_modules/ |
||||||
|
__pycache__/ |
||||||
|
*.pyc |
||||||
|
*.log |
||||||
|
.DS_Store |
||||||
|
*.tmp |
||||||
|
/tmp/ |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
{ |
||||||
|
"name": "pi-hitl-programming", |
||||||
|
"version": "0.1.0", |
||||||
|
"description": "Human-in-the-Loop extension for pi coding agent, focused on programming", |
||||||
|
"type": "module", |
||||||
|
"main": "./src/index.ts", |
||||||
|
"scripts": { |
||||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@earendil-works/pi-coding-agent": "^0.79.6", |
||||||
|
"@earendil-works/pi-ai": "^0.79.6", |
||||||
|
"@earendil-works/pi-tui": "^0.79.6" |
||||||
|
}, |
||||||
|
"pi": { |
||||||
|
"extensions": [ |
||||||
|
"./src/index.ts" |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,141 @@ |
|||||||
|
#!/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) |
||||||
Loading…
Reference in new issue