Dato un elenco di mosse di Tetris, restituisce il numero di righe completate


37

Descrizione

Consideriamo una versione leggermente semplificata di Tetris in cui ogni mossa è composta da:

  • ruotando il pezzo in senso orario, da 0 a 3 volte
  • posizionando il pezzo su una data colonna
  • caduta veloce

L'obiettivo è determinare il numero di linee completate, dato un elenco di tali mosse di Tetris.

Le righe completate vengono rimosse man mano che i pezzi vengono rilasciati, seguendo le regole standard di Tetris.

Playfield

Il campo di gioco è largo 10 colonne. Non c'è Game Over e si presume che ci sia sempre abbastanza spazio e tempo per eseguire le azioni di cui sopra, indipendentemente dalla configurazione del campo di gioco. L'altezza del campo di gioco non ha molta importanza qui, ma puoi usare le 22 file standard come limite superiore.

Forme di tetromini

forme

Input Output

Ingresso

Un elenco separato da virgole di mosse di Tetris codificato con 3 caratteri. I primi due personaggi descrivono la forma di Tetromino da usare e l'ultimo descrive la posizione in cui è stato rilasciato.

  1. Tetromino: I, O, T, L, J, ZoS , nello stesso ordine come sopra.
  2. Numero di rotazioni in senso orario: 0a3
  3. Colonna: 0a 9. Questa è la colonna in cui si trova l'angolo in alto a sinistra del pezzo (contrassegnato da un xnell'immagine sopra) dopo la rotazione 1

Si presume che tutte le mosse nell'elenco fornito siano valide. Non è necessario controllare voci non valide come I07( Iforma orizzontale posizionata troppo a destra).

1 Sei libero di implementare un vero algoritmo di rotazione o di codificare tutte le forme diverse, purché xsi trovi nella colonna indicata dal terzo carattere della mossa.

Produzione

Numero di righe completate.

Esempio

esempio

O00,T24 genererà la prima posizione e O00,T24,S02,T01,L00,Z03,O07,L06,I05 genererà la seconda posizione.

Pertanto, la seguente sequenza genererà un Tetris e dovrebbe restituire 4:

O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19

Casi test

1) "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" -> 4
2) "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" -> 5
3) "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" -> 4
4) "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,
    I10,I10" -> 8
5) "O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02" -> 8

Pagina di prova

È possibile utilizzare questo JSFiddle per testare un elenco di mosse.


1
Su quale asse ruotano i pezzi?

1
@Arnauld Ti consiglio di dare un'occhiata al sistema di super rotazione e di modificare l'immagine un po 'di tempo. tetris.wikia.com/wiki/SRS

1
Quindi, possiamo trattarli come 25 (15 se non contate i duplicati) forme diverse, allora?

1
Le soluzioni possono accettare input come una matrice anziché una stringa separata da virgola?
Giordania,

1
Questa è la migliore domanda PCG che ho visto da molto tempo. Che bella idea! Migliore nel senso soggettivo di interessante e pratico e non troppo grande ma non troppo piccolo.
GreenAsJade,

Risposte:


5

PHP, 405 399 378 372 368 360 354 347 331 330 328 319 309 300 byte

(con la mappatura dei blocchi di Dave )

<?$f=[~0];L:for($y=0;$f[++$d+$y]|$s;$s>>=4)$y-=1022<$f[$d+$y]=$f[$d]|$s%16<<$x;$c-=$y;if(list($p,$r,$x)=$argv[++$z])for($s=I==$p?$r&1?4369:15:hexdec(decoct(O==$p?27:ord(base64_decode('M1ozWjqaF1kemR6ZPJMPyTnSJ0s')[ord($p)/4%5*4+$r])));;$d--)for($y=4;$y--;)if ($f[$d+$y]>>$x&$s>>$y*4&15)goto L;echo$c;

programma, prende mosse come argomenti separati, stampa il risultato

ripartizione per funzionare:

prende le mosse come matrice, restituisce il risultato

function t($a)
{
    $f=[~$z=0];             // init field $f (no need to init $z in golfed version)
    L:                      // jump label
                            // A: paint previous piece at line $d+1:
#   if($s)paint($f,$s,$d+1,$x);
    for($y=0;               // $y = working line offset and temporary score
        $f[++$d-$y]|$s;$s>>=4)// next field line; while field or piece have pixels ...
        $s>>=4)                 // next piece line
        $y+=1022<               // 3: decrease temp counter if updated line is full
            $f[$d-$y]=$f[$d]    // 2: use $y to copy over dropped lines
                |$s%16<<$x;     // 1: set pixels in working line
    $c+=$y;                         // add temp score to global score
#   paint($f);var_dump($c);
    if(list($p,$r,$x)=$a[$z++])// B: next piece: $p=name; $r=rotation, $x=column
#   {echo"<b>$z:$p$r-$x</b><br>";
        for(                // C: create base 16 value:
            $s=I==$p
                ? $r&1?4369:15  // I shape (rotated:0x1111, not rotated:0xf)
                : hexdec(decoct(    // 5: convert from base 8 to base 16
                    O==$p ? 27  // O shape (rotation irrelevant: 033)
                    : ord(          // 4: cast char to byte
                                    // 0: 4 bytes for each remaining tetromino
                        base64_decode('M1ozWjqaF1kemR6ZPJMPyTnSJ0s')[
                            ord($p)/4%5 // 1: map STZJL to 01234
                            *4      // 2: tetromino offset
                            +$r]    // 3: rotation offset
            )));;$d--
        )
            for($y=4;$y--;) // D: loop $y through piece lines
                if ($f[$d+$y]>>$x & $s>>$y*4 & 15) // if collision ...
                    goto L;         // goto Label: paint piece, tetris, next piece
#   }
    return$c;               // return score
}

come riferimento: la vecchia mappatura

            hexdec(decoct(          // 3. map from base 8 to base 16
                                    // 0. dword values - from high to low: rotation 3,2,1,0
                [0x991e991e,0xc90f933c,0x4b27d239,0x1b1b1b1b,0,0x5a335a33,0x59179a3a]
                [ord($m[0])/2%9]    // 1. map ZJLO.ST to 0123.56 -> fetch wanted tetromino
                >>8*$m[1]&255       // 2. the $m[1]th byte -> rotated piece
            ))

analisi

vedi la mia altra risposta PHP

voglio guardare?

rimuovere il #dalla sorgente della funzione e aggiungere questo:

function paint($f,$s=0,$d=0,$x=0){echo'<pre>';for($y=count($f)+5;$y--;$g=0)if(($t=(($z=$y-$d)==$z&3)*($s>>4*$z&15)<<$x)|$r=$f[$y]|0){$z=$t?" $r|$t=<b".(1022<($z=$t|$r)?' style=color:green':'').">$z":" <b>$r";for($b=1;$b<513;$b*=2)$z=($t&$b?'<b>'.($r&$b?2:1).'</b>':($r&$b?1:'.')).$z;printf("%02d $z</b>\n",$y);}echo'</pre>';}

alcuni passi del golf

Rev. 5: un grande salto (399- 21 = 378) è venuto semplicemente spostando il passaggio di colonna
da un ciclo separato per i due anelli esistenti.

Rev. 8: Il passaggio dall'array alla base 16 per il pezzo ($ s) non ha dato molto,
ma ha lasciato il posto a qualche altro golf.

Rev. 17: scricchiola i valori con base64_encode(pack('V*',<values>))
e usa l'indicizzazione dei byte invece di unpacksalvare 16 byte

Rev. 25-29: ispirato al codice di Dave: nuovo hashing (-2), nuovo design del loop (-9), goto (-10)
nessun pre-turno però; costerebbe 17 byte.

più potenziale

Con /2%9, ho potuto salvare 15 byte (solo 14 byte con /4%5)
inserendo i dati binari in un file be quindi indicizzandoli file(b)[0].
Lo voglio?

I personaggi UTF-8 costeranno molto per la trasformazione.

sull'hash

Ho usato ZJLO.ST /2%9 -> 0123.56; ma T.ZJLOS /3%7 -> 0.23456è buono.
un byte in più: O.STJLZ %13/2 -> 0.23456
e altri tre:OSTZJ.L %17%12%9 -> 01234.6

Non sono riuscito a trovare un hash breve (massimo 5 byte) che non lasci spazio;
ma Dave ha trovato STZJL /4%5 -> 01234, eliminando la O dalla lista. WTG!

btw: TIJSL.ZO (%12%8) -> 01234.67lascia spazio alla Iforma
(e a una finzione A, Mo Yforma). %28%8e %84%8, fare lo stesso (ma con Einvece di A).


Bello; Mi piace il rilevamento combinato di pittura + linea, ed break 2è molto più pulito di quello che dovevo fare in C! Si potrebbe essere in grado di salvare alcuni byte utilizzando array_diff(set completato linee ad un valore fisso invece di utilizzare unsetquindi sostituire array_valuescon array_diff), ma non può dire dalla documentazione se questo potesse appiattire valori ripetuti (ad esempio array_diff ([1,2, 2,3], [1]) -> [2,2,3] o solo [2,3])
Dave

@Dave: array_diffnon rimuove i valori duplicati; e ho già il valore fisso (1023); ma non reindicizza l'array. Ottima idea, ma costerebbe un byte.
Tito,

wow, è stato un forte fruscio quando mi hai sorvolato! Sembra che ho un po 'di recupero da fare!
Dave,

Congratulazioni per aver raggiunto 300! Implementerò la tua ultima modifica suggerita (non avevo pensato di semplificare il controllo di rotazione ora che non mi serve /10dappertutto), ma altrimenti penso di aver finito. Sono sorpreso da quanto PHP e C si siano rivelati direttamente competitivi. È stato divertente - spero che l'OP accetti la tua risposta!
Dave,

@Dave & Titus - Vorrei poter accettare entrambe le risposte. Ragazzi avete fatto un ottimo lavoro. Complimenti comunque a Titus per aver raggiunto i 300. E penso che in realtà siano 299 poiché hai uno spazio inutile dopo a if.
Arnauld,

16

C, 401 392 383 378 374 351 335 324 320 318 316 305 byte

d[99]={'3Z3Z',983177049,513351321,1016270793,970073931,~0},*L=d+5,V,s;char c;main(x){B:for(c=0;c[++L]|V;V>>=4)c-=(L[c]=*L|(V&15)<<x)>1022;s-=c;if(scanf("%c%1d%d,",&c,&V,&x)>2)for(V=c^79?d[c/4%5]>>24-V*8:27,V=c^73?V/64%4<<8|V/8%8<<4|V%8:V&32?15:4369;;--L)for(c=4;c--;)if(L[c]>>x&V>>c*4&15)goto B;return s;}

Accetta input separati da virgole su stdin, restituisce il punteggio nello stato di uscita.

Richiede chardi essere firmato (che è l'impostazione predefinita per GCC) e '3Z3Z'deve essere interpretato come 861549402 (come nel caso di GCC su macchine little endian).


Esempio di utilizzo:

echo "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" | ./tetris; echo "$?";

Spiegazione di alto livello:

Tutte le forme tranne la linea possono adattarsi a una griglia 3x3 con un angolo mancante:

6 7 -
3 4 5
0 1 2

Ciò significa che è facile memorizzarli in un byte ciascuno. Per esempio:

- - -         0 0 -
- # -   -->   0 1 0   -->   0b00010111
# # #         1 1 1

(allineamo ogni pezzo nella parte inferiore sinistra della scatola per facilitarne la caduta)

Dato che otteniamo almeno 4 byte in un int, ciò significa che possiamo memorizzare tutte e 4 le rotazioni di ogni pezzo in un singolo numero intero, con un caso speciale per la linea. Possiamo anche adattare ogni riga della griglia di gioco in un int (richiede solo 10 bit) e il pezzo attualmente in calo in un lungo (4 righe = 40 bit).


Abbattersi:

d[99]={             // General memory
  '3Z3Z',           //  Shape of "S" piece (multi-char literal, value = 861549402)
  983177049,        //  Shape of "T" piece
  513351321,        //  Shape of "Z" piece
  1016270793,       //  Shape of "J" piece
  970073931,        //  Shape of "L" piece
  ~0                //  Bottom of game grid (solid row)
},                  //  Rest of array stores lines in the game
*L=d+5,             // Working line pointer (start >= base of grid)
V,                  // Current piece after expansion (also stores rotation)
s;                  // Current score (global to initialise as 0)
char c;             // Input shape identifier (also counts deleted lines)
main(x){            // "x" used to store x location
  B:                                // Loop label; jumps here when piece hits floor
  for(c=0;c[++L]|V;V>>=4)           // Paint latest piece & delete completed lines
    c-=(L[c]=*L|(V&15)<<x)>1022;
  s-=c;                             // Increment score
  if(scanf("%c%1d%d,",&c,&V,&x)>2)  // Load next command
    for(V=c^79?                     // Identify piece & rotation
          d[c/4%5]>>24-V*8          //  Not "O": load from data
        :27,                        //  Else "O": always 27
        V=c^73?                     // If not "I" (line):
          V/64%4<<8|V/8%8<<4|V%8    //  expand shape to 16-bits
        :                           // Else we are a line:
          V&32?                     //  "I" coincides with "J"; use this to check
               15:4369;             //  horizontal/vertical
        ;--L)                       // Loop from top-to-bottom of grid
      for(c=4;c--;)                 //  Loop through rows of piece
        if(L[c]>>x&V>>c*4&15)       //   If collides with existing piece:
          goto B;                   //    Exit loops
  return s;                         // Return score in exit status
}

-4, -1 grazie a @Titus e -23, -11 con ispirazione dalla loro risposta


Ben fatto! Potresti semplicemente fare a s+=(d[A-x]=d[A])meno di usare x?
Arnauld,

Sfortunatamente xè necessario tenere traccia del numero di righe da comprimere nel passaggio corrente (ogni riga Aviene impostata sul valore della riga A-xman mano che il ciclo avanza)
Dave

D'oh! Colpa mia. Ci scusiamo per un suggerimento così sciocco. :)
Arnauld

1
@Titus è un abuso dell'indicizzazione dell'array di C. In parole povere, 1[a]e a[1]fai la stessa cosa (o più precisamente, si a[b]traduce in *(a+b)). È abusato in questo modo per evitare parentesi. In questo caso, 1[*v]== (*v)[1], ovvero la seconda lettera del comando, ovvero la rotazione.
Dave,

1
Riesci a sbarazzarti del Isegnaposto? In tal caso, prova /2%9come hash anziché %12. %12%8altrimenti.
Tito,

2

Rubino, 474 443 428 379 + 48 = 427 byte

-1 grazie a @Titus

Questo può sicuramente essere giocato a golf di più.

Legge un dizionario binario di pezzi (vedi sotto) da STDIN o un nome file e accetta un elenco di mosse come argomento, ad es $ cat pieces | ruby script.rb O00,T24,S02,....

q=$*.pop
z=$<.read.unpack('S*')
b=c=g=i=0
x=10
u=1023
r=->n{n&2**x-1>0?n:r[n>>x]}
y=->n{n>0?[n&u]+y[n>>x]:[]}
l=->c,n{z.find{|m|m>>x==c<<2|n.to_i}||l[c,n-2]}
q.scan(/(\w)(\d)(\d)/){n=l["IOTLJSZ".index($1),$2.to_i]
v=(n&1|(n&6)<<9|(n&56)<<17|(n&960)<<24)<<$3.to_i
(y[b].size+1).times{t=b<<40
t&v>0&&break
g=t|v
v<<=x}
b=0
a=y[r[g]]
a.grep_v(u){|o|b|=o<<x*i
i+=1}
c+=a.count u}
p c

Dati binari del pezzo (formato xxd)

0000000: c003 4b04 d810 d814 b820 9c24 d029 5a2c  ..K...... .$.)Z,
0000010: 7830 9634 e039 ca3c 3841 d444 c849 4e4c  x0.4.9.<8A.D.INL
0000020: 9861 9869 5c64 5c6c f050 f058 9a54 9a5c  .a.i\d\l.P.X.T.\

Guardalo su repl.it (con argomenti codificati, dizionario): https://repl.it/Cqft/2

Ungolfed e spiegazione

# Move list from ARGV
q = $*.pop

# Read piece dictionary; pieces are 16-bit integers: 3 for letter,
# 2 for rotation, 10 for shape (and 1 wasted)
z = $<.read.unpack('S*')

# Empty board and various counters
b = c = g = i = 0
x = 10 # Magic numbers
u = 1023

# A function to remove empty lines
r = ->n{ n & 2**x - 1 > 0 ? n : r[n >> x] }

# A function to split the board into lines
y = ->n{ n > 0 ? [n & u] + y[n >> x] : [] }

# A function to lookup a piece by letter and rotation index
l = ->c,n{ z.find {|m| m >> x == c << 2 | n.to_i } || l[c, n-2] }

# Read the move list
q.scan(/(\w)(\d)(\d)/) {
  # Look up the piece
  n = l["IOTLJSZ".index($1), $2.to_i]

  # Convert the 10-bit piece to a 40-bit piece (4 rows of 10 columns)
  v = (n & 1 |
        (n & 6) << 9 |
        (n & 56) << 17 |
        (n & 960) << 24
      ) << $3.to_i # Shift by the appropriate number of columns

  # Drop the piece onto the board
  (y[b].size + 1).times {
    t = b << 40
    t & v > 0 && break
    g = t | v
    v <<= x
  }

  # Clear completed rows
  b = 0
  a = y[r[g]]
  a.grep_v(u) {|o|
    b |= o << x * i
    i += 1
  }

  c += a.count u # Count cleared rows
}
p c

1 byte: m >> 10potrebbe esserem >> x
Tito

@Titus Buon occhio. Grazie!
Giordania,

Non è necessario richiedere esplicitamente \ds nell'espressione regolare: /(\w)(\d)(\d)//(\w)(.)(.)/
manatwork

2

PHP, 454 435 427 420 414 byte

campi bit per pezzi e mappa; ma nessun caso speciale per la Iforma come il golf di Dave.

<?$t=[I=>[15,4369],O=>[51],T=>[114,562,39,305],L=>[113,802,71,275],J=>[116,547,23,785],Z=>[54,561],S=>[99,306]];foreach($argv as$z=>$m)if($z){$s=$t[$m[0]][$m[1]%count($t[$m[0]])];for($d=$i=0;$i<4;$i++)for($k=0;$k<4;$k++)if($s>>4*$k&1<<$i){for($y=0;$y++<count($f);)if($f[$y-1]&1<<$m[2]+$i)$d=max($d,$y-$k);$k=3;}for($k=$d;$s;$k++,$s>>=4)if(1022<$f[$k]|=$s%16<<$m[2]){$c++;unset($f[$k]);}$f=array_values($f);}echo$c;

accetta argomenti dalla riga di comando, stampa il risultato

non golfato come funzione

accetta argomenti come matrice, restituisce risultato

function t($a)
{
    // bitwise description of the stones and rotations
    $t=[I=>[15,4369],O=>[51],T=>[114,562,39,305],L=>[113,802,71,275],J=>[116,547,23,785],Z=>[54,561],S=>[99,306]];
    foreach($a as$m)
    {
        $s=$t[$m[0]][$m[1]%count($t[$m[0]])];   // $s=stone
        // find dropping space
        for($d=$i=0;$i<4;$i++)
            // a) lowest pixel of stone in column i
            for($k=0;$k<4;$k++)
                if($s>>4*$k&1<<$i)
                {
                    // b) top pixel of field in column x+i 
                    for($y=0;$y++<count($f);)
                        if($f[$y-1]&1<<$m[2]+$i)$d=max($d,$y-$k);
                    $k=3; // one byte shorter than `break;`
                }
        // do drop
        for($k=$d;$s;$k++,$s>>=4)
            if(1022<$f[$k]|=$s%16<<$m[2])   // add block pixels to line pixels ... if full,
            {$c++;unset($f[$k]);}           // tetris
        $f=array_values($f);
    }
    return$c;
}

test (sulla funzione)

$data=[
    "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19"=>4,
    "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" => 5,
    "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" => 4,
    "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,I10,I10" => 8,
    // additional example for the two last tetrominoes:
    'O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02' => 8,
];
function out($a){if(is_object($a)){foreach($a as$v)$r[]=$v;return'{'.implode(',',$r).'}';}if(!is_array($a))return$a;$r=[];foreach($a as$v)$r[]=out($v);return'['.join(',',$r).']';}
function cmp($a,$b){if(is_numeric($a)&&is_numeric($b))return 1e-2<abs($a-$b);if(is_array($a)&&is_array($b)&&count($a)==count($b)){foreach($a as $v){$w = array_shift($b);if(cmp($v,$w))return true;}return false;}return strcmp($a,$b);}
function test($x,$e,$y){static $h='<table border=1><tr><th>input</th><th>output</th><th>expected</th><th>ok?</th></tr>';echo"$h<tr><td>",out($x),'</td><td>',out($y),'</td><td>',out($e),'</td><td>',cmp($e,$y)?'N':'Y',"</td></tr>";$h='';}
foreach($data as $v=>$e)
{
    $x=explode(',',$v);
    test($x,$e,t($x));
}

427? Sei su!
Giordania,

@Jordan: e quelli 427 includono il <?sovraccarico :)
Tito
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.