Tetris en PHP CLI

Tetris en modo texto para ejecutar desde la terminal con PHP. Implementa las siete piezas cl谩sicas con rotaci贸n, eliminaci贸n de l铆neas, niveles de dificultad progresivos y puntuaci贸n. Usa secuencias ANSI para los colores y stty para el modo raw no bloqueante. Requiere PHP 8.0+ en Linux o macOS.
				 mover  ^ rotar  v bajar  Espacio caida rapida  ESC salir
 * Ejecutar:   php tetris.php
 *
 * Version Pascal original: https://programacion.net/codigo/tetris_63
 * Version Python:          https://programacion.net/codigo/tetris-python_1896
 */

declare(strict_types=1);

const FILAS  = 20;
const COLS   = 10;

define('ESC',   chr(27));
define('RESET', ESC . "[0m");
define('VERDE', ESC . "[1;32m");
define('COLORES', [
    '',
    ESC . "[1;36m",  // 1 I - cyan
    ESC . "[1;33m",  // 2 O - yellow
    ESC . "[1;35m",  // 3 T - magenta
    ESC . "[1;32m",  // 4 S - green
    ESC . "[1;31m",  // 5 Z - red
    ESC . "[1;34m",  // 6 J - blue
    ESC . "[1;37m",  // 7 L - white
]);
const FORMAS = [
    [[0,0],[0,1],[0,2],[0,3]],
    [[0,0],[0,1],[1,0],[1,1]],
    [[0,0],[0,1],[0,2],[1,1]],
    [[0,1],[0,2],[1,0],[1,1]],
    [[0,0],[0,1],[1,1],[1,2]],
    [[0,0],[1,0],[1,1],[1,2]],
    [[0,2],[1,0],[1,1],[1,2]],
];

function limpiar(): void  { echo ESC . "[2J" . ESC . "[H"; }
function cursor(bool $v): void { echo $v ? ESC . "[?25h" : ESC . "[?25l"; }

function rotar(array $f): array {
    $r    = array_map(fn($c) => [$c[1], -$c[0]], $f);
    $minR = min(array_column($r, 0));
    $minC = min(array_column($r, 1));
    return array_map(fn($c) => [$c[0] - $minR, $c[1] - $minC], $r);
}

function valida(array $board, array $f, int $pr, int $pc): bool {
    foreach ($f as [$dr, $dc]) {
        $r = $pr + $dr; $c = $pc + $dc;
        if ($r < 0 || $r >= FILAS || $c < 0 || $c >= COLS || $board[$r][$c]) return false;
    }
    return true;
}

function colocar(array &$board, array $f, int $pr, int $pc, int $col): void {
    foreach ($f as [$dr, $dc]) $board[$pr + $dr][$pc + $dc] = $col;
}

function limpiarLineas(array &$board): int {
    $nuevas = array_values(array_filter($board, fn($f) => in_array(0, $f, true)));
    $n      = FILAS - count($nuevas);
    $board  = array_merge(array_fill(0, $n, array_fill(0, COLS, 0)), $nuevas);
    return $n;
}

function dibujar(array $board, array $f, int $pr, int $pc, int $col,
                 array $sf, int $sc, int $puntos, int $nivel, int $lineas): void {
    limpiar();
    echo VERDE . "<- -> mover  ^ rotar  v bajar  Espacio: caida  ESC: salir   " .
         "Puntos:$puntos  Nivel:$nivel  Lineas:$lineas" . RESET . "n";

    $vista = $board;
    foreach ($f as [$dr, $dc]) {
        $r = $pr + $dr; $c = $pc + $dc;
        if ($r >= 0 && $r < FILAS && $c >= 0 && $c < COLS) $vista[$r][$c] = $col;
    }

    echo '+' . str_repeat('--', COLS) . "+n";
    foreach ($vista as $fila) {
        echo '|';
        foreach ($fila as $v) echo $v ? (COLORES[$v] . '[]' . RESET) : '. ';
        echo "|n";
    }
    echo '+' . str_repeat('--', COLS) . "+n";

    echo "n" . VERDE . "Sig:n" . RESET;
    $maxR = max(array_column($sf, 0));
    $maxC = max(array_column($sf, 1));
    for ($r = 0; $r <= $maxR; $r++) {
        for ($c = 0; $c <= $maxC; $c++) {
            $ok = false;
            foreach ($sf as [$dr, $dc]) if ($dr === $r && $dc === $c) { $ok = true; break; }
            echo $ok ? (COLORES[$sc] . '[]' . RESET) : '  ';
        }
        echo "n";
    }
}

function leerTecla(): string {
    $c = fread(STDIN, 4);
    return ($c === false || $c === '') ? '' : $c;
}

function nuevaPieza(): array {
    $idx = array_rand(FORMAS);
    return [FORMAS[$idx], $idx + 1];
}

register_shutdown_function(function(): void {
    system('stty sane');
    cursor(true);
    echo RESET . "n";
});
cursor(false);
system('stty -icanon -echo min 0 time 0');
stream_set_blocking(STDIN, false);

$board           = array_fill(0, FILAS, array_fill(0, COLS, 0));
[$forma, $color] = nuevaPieza();
[$sf,    $sc]    = nuevaPieza();
$pr = 0; $pc = (int)(COLS / 2) - 2;
$puntos = 0; $nivel = 1; $lineas = 0;
$ultimo = microtime(true);

while (true) {
    $retardo = max(0.1, 0.5 - ($nivel - 1) * 0.04);
    dibujar($board, $forma, $pr, $pc, $color, $sf, $sc, $puntos, $nivel, $lineas);
    usleep(50_000);

    $tecla = leerTecla();
    if ($tecla === ESC) { exit(0); }
    elseif ($tecla === ESC . "[D" && valida($board, $forma, $pr, $pc - 1)) $pc--;
    elseif ($tecla === ESC . "[C" && valida($board, $forma, $pr, $pc + 1)) $pc++;
    elseif ($tecla === ESC . "[A") {
        $rot = rotar($forma);
        if (valida($board, $rot, $pr, $pc)) $forma = $rot;
    }
    elseif ($tecla === ESC . "[B" && valida($board, $forma, $pr + 1, $pc)) $pr++;
    elseif ($tecla === " ") { while (valida($board, $forma, $pr + 1, $pc)) $pr++; }

    if (microtime(true) - $ultimo >= $retardo) {
        $ultimo = microtime(true);
        if (valida($board, $forma, $pr + 1, $pc)) {
            $pr++;
        } else {
            colocar($board, $forma, $pr, $pc, $color);
            $n = limpiarLineas($board);
            $lineas += $n;
            $puntos += [0, 100, 300, 500, 800][$n] * $nivel;
            $nivel   = intdiv($lineas, 10) + 1;
            [$forma, $color] = [$sf, $sc];
            [$sf, $sc]       = nuevaPieza();
            $pr = 0; $pc = (int)(COLS / 2) - 2;

            if (!valida($board, $forma, $pr, $pc)) {
                dibujar($board, $forma, $pr, $pc, $color, $sf, $sc, $puntos, $nivel, $lineas);
                echo COLORES[5] . "n  GAME OVER  Puntos: $puntos" . RESET . "n";
                sleep(3);
                exit(0);
            }
        }
    }
}

			
Descargar adjuntos
COMPARTE ESTE TUTORIAL

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

SIGUIENTE TUTORIAL