Tetris! Altezze finali (giorno 3)


19

Sfida Tratto dal mio concorso per la sfida del codice universitario

Questo è in realtà il Day 0, ma la sfida di ieri è stata troppo facile e può essere un inganno di un'altra domanda qui.


Tetris è un videogioco che è diventato popolare negli anni '80. Consiste nel posizionare una serie di pezzi con forme diverse che cadono su una tavola, in modo che si adattino nel modo più compatto possibile.

In questo problema assumeremo una sequenza di pezzi che cadono, ciascuno in una certa posizione e con un certo orientamento che non può essere modificato. I pezzi vengono accumulati mentre cadono e le file complete non vengono eliminate (come nel gioco originale). L'obiettivo è determinare l'altezza finale di ogni colonna del tabellone dopo che tutti i pezzi cadono.

Ci sono un totale di 7 pezzi diversi, mostrati in figura:

forme

Sfida

Dato un elenco di pezzi, genera l'altezza di tutte le colonne dal tabellone dopo che tutti i pezzi cadono

Un pezzo è composto da tre numeri: I, R e P. Il primo numero, I, è l'identificatore del pezzo (un numero compreso tra 1 e 7, nello stesso ordine della figura). Il secondo numero, R, è la rotazione del pezzo. Può assumere i valori 0, 90, 180 o 270 e rappresenta l'angolo di rotazione del pezzo in senso antiorario. Il terzo numero, P, indica la posizione del pezzo. Rappresenta la colonna a sinistra occupata dal pezzo (questo può essere 1 o 0 Index. Specificare).

Esempio e caso di test (1 indice)

  • Dato [[1, 0, 1], [4, 0, 1], [5, 90, 4]]

caso 1

  • Produzione [3, 3, 1, 3, 2]

  • Dato [[6, 270, 4], [1, 180, 5], [1, 90, 6], [7, 0, 4]]

caso n. 2

  • Produzione [0, 0, 0, 9, 9, 8, 3, 3]

  • Data [[3,0,1],[3,180,3]]uscita[1,1,4,4,4]

  • Data [[2,180,1],[2,0,3]]uscita[2,2,4,3,3]

Appunti

  • Questo è
  • Riga / colonna può essere 1 o 0 indice. Per favore specificare.
  • È possibile ridefinire i valori di input (forse si desidera chiamare il pezzo 1 come A, ecc.). In tal caso, specificare

Domande

  • Possiamo usare 4 valori distinti invece di un angolo in gradi ?:

  • Dovremmo gestire "buchi" se un pezzo non si adatta esattamente ai precedenti ?:

  • L'altezza o la larghezza della tavola sono limitate? No. Né la larghezza né l'altezza sono limitate


Grazie @Arnauld per le immagini e i casi di test *. *


Può I, Re Pessere inseriti in un ordine diverso?
Neil

@Neil si. Può essere in qualsiasi ordine
Luis felipe De jesus Munoz

Se possiamo ridefinire i valori di input, posso prendere l'id del pezzo come una matrice che rappresenta la forma dei pezzi (senza rotazione)?
Incarnazione dell'ignoranza il

1
Penso che non possiamo inserire una matrice che rappresenti la forma dei pezzi per 2 motivi. L'input è chiaramente definito: 1,2,3 .. o A, B, C .. E una parte fondamentale di questa sfida è gestire questo vincolo.
AZTECCO,

1
Sarebbe OK includere 0 finali?
dana,

Risposte:


10

JavaScript (Node.js) ,  286284 270  266 byte

[0..3]

a=>a.map(([p,r,x])=>(g=y=>y>3?g(+!Y--):b[Y+y]&(m[y]=('0x'+`717433667233ff4717333327661${1e12+0x5e7056a566ffff57efa65n.toString(4)}`[(p*2+r*56+y*99+13)%113])<<x)?m.map(v=>(g=x=>v&&g(x+1,H[x]=v&1?Y:~~H[x],v>>=1))(0,b[++Y]|=v)):g(y+1))(Y=a.length*4),m=[b=[-1]],H=[])&&H

Provalo online! oppure prova una versione avanzata che mostra anche la scheda finale.

Codifica della forma

Tutti i pezzi sono memorizzati esattamente come 4 nibble (4x4 bit) con le righe ordinate in ordine inverso e il pixel più a sinistra mappato sul bit meno significativo. In altre parole, la rappresentazione binaria della forma è speculare sia in verticale che in orizzontale.

Esempio:

esempio di codifica delle forme

Funzione hash e tabella di ricerca

p[0..6]r[0..3]y[0..3]n

n=(2p+56r+99y+13)mod113

820

Queste voci sono confezionate come:

`717433667233ff4717333327661${1e12+0x5e7056a566ffff57efa65n.toString(4)}`

che si espande ai seguenti 82 stuzzichini:

"717433667233ff47173333276611000000000000113213001112221112123333333311133233221211"

io"ff"

I parametri della funzione hash sono stati forzati brutalmente in modo da ottimizzare gli zeri iniziali e finali. Il fatto che la stringa possa essere compressa un po 'di più usando 1e12per gli zeri nel mezzo e una conversione da base-16 a base-4 per la parte giusta è solo un effetto collaterale gradito ma inaspettato. :-)

Ecco una dimostrazione del processo di disimballaggio per tutti i pezzi e tutte le rotazioni.

Commentate

a => a.map(([p, r, x]) => (     // for each piece p with rotation r and position x:
  g = y =>                      //   g = recursive function taking y
    y > 3 ?                     //   if y is greater than 3:
      g(+!Y--)                  //     reset y to 0, decrement Y and try again
    :                           //   else:
      b[Y + y] & (              //     test if we have a collision of the board with
        m[y] =                  //     the y-th row m[y] of the current piece
          ('0x' + `717...`[     //     which is extracted from a lookup table
            (p * 2 + r * 56 +   //     using the hash function described in the
             y * 99 + 13) % 113 //     previous paragraph
          ]) << x               //     and shifted to the left according to x
      ) ?                       //     if we have a collision:
        m.map(v => (            //       we iterate again on the piece rows stored in m[]
          g = x =>              //         g = recursive function taking x
            v &&                //         if v is not equal to 0:
            g(                  //           do a recursive call:
              x + 1,            //             increment x
              H[x] =            //             update the height at x:
                v & 1 ?         //               if this bit is set:
                  Y             //                 set it to Y
                :               //               else:
                  ~~H[x],       //                 leave it unchanged or force it to 0
                                //                 if it was still undefined
              v >>= 1           //             shift v to the right
            )                   //           end of recursive call
          )(0,                  //         initial call to g with x = 0
               b[++Y] |= v)     //         increment Y and copy the piece row to the board
        )                       //     end of map()
      :                         //   else (no collision):
        g(y + 1)                //     do a recursive call to test the next row
  )(Y = a.length * 4),          //   initial call to g with y = Y = 4 * the number of pieces
                                //   (assuming the worst case: piled vertical I pieces)
  m = [b = [-1]], H = []        //   initialize m[], b[] and H[]
                                //   we set a full line at the bottom of b[]
) && H                          // end of map(); return H[]

3
Bel lavoro imballare / disimballare i pezzi. È davvero impressionante :)
dana,

7

C (clang) , 253 239 221 212 byte

t(*a,*b,c){char*z="VP\225TBUIUVAaUZ@AWVDeTf@EVWhU😎EQV😀RTYT😉UU";for(size_t*p,f,n,y,i;c--;b++){f=1<<(8-*b)/3;p=z+*b++*8+*b++%f*2;f=n=*p;for(y=i=0;i<=f%4;y=fmax(y,a[*b+i++]+n%4))n/=4;for(;i--;a[*b+i]=y+n%4)n/=4;}}

Provalo online!

ps In realtà la dimensione del codice è di 221 byte (ma 212 caratteri) a causa dei caratteri UNICODE codificati in UTF-8. Ma tio.run lo considera come un codice di 212 byte ...

La dimensione del codice sul mio computer è di 209 caratteri (218 byte). Ma non ho potuto sostituire il \225carattere visibile in tio.run 😞

Codice Ungolfed

// a - output array (must be zeroed), b - array of block info, c - number of blocks

// Figure codes: 2->0, 3->1, 6->2, 1->3, 5->4, 7->5, 4->6 (0,1 are L-figures, 2 is is T-figure, 3 is a line 1x4; 4,5 are zigzags; 6 is a cube 2x2)
// Vertical and horizontal positions are zero-indexed, angles = 0..3

t(*a,*b,c)
{
  char*z="VP\225TBUIUVAaUZ@AWVDeTf@EVWhU😎EQV😀RTYT😉UU";  // UTF-8
//char*z="VP\225TBUIUVAaUZ@AWVDeTf@EVW\1hU😎\26EQV😀RTYT😉UU";  // 3 bytes longer (use it if you can't copy previous string correctly) :)
  // Blocks
  for(size_t*p,f,n,y,i;c--;b++){
    f=1<<(8-*b)/3;  // number of figure variants
    p=z+*b++*8+*b++%f*2;
    // Get top base line position (y)
    f=n=*p;  // figure width, TBLs and HATs
    for(y=i=0;i<=f%4;
      y=fmax(y,a[*b+i++]+n%4))
      n/=4;
    // Add heights (HATs)
    for(;i--;
      a[*b+i]=y+n%4)
      n/=4;
  }
}  // 215 chars (224 bytes)

Descrizione

Troviamo la linea di base superiore ( TBL ) di ogni figura e descriviamola come un numero di celle sotto TBL per ogni posizione orizzontale. Descriviamo anche il numero di celle (altezza) sopra TBL ( HAT ).

Per esempio:

                       ________ ________
_ [] _____ HAT = 1,0,0 [] [] [] HAT = 0,0,0 ___ [] [] _ ​​HAT = 0,1,1 [] [] [] HAT = 0,0,0
 [] [] [] TBL = 1,1,1 [] TBL = 2,1,1 [] [] TBL = 1,1,0 [] TBL = 1,2,1

Descriviamo TBL e HAT per ogni figura e ciascun angolo di rotazione:

Larghezza TBLs CAPPELLI
----- ------- -------
L-figure:
  3 1 1 1 1 0 0 // 0 °
  2 1 1 0 2 // 90 °
  3 1 1 2 0 0 0 // 180 °
  2 3 1 0 0 // 270 °

  3 1 1 1 0 0 1 // 0 °
  2 1 3 0 0 // 90 °
  3 2 1 1 0 0 0 // 180 °
  2 1 1 2 0 // 270 °

T-figura:
  3 1 1 1 0 1 0 // 0 °
  2 1 2 0 1 // 90 °
  3 1 2 1 0 0 0 // 180 °
  2 2 1 1 0 // 270 °

Linea:
  4 1 1 1 1 0 0 0 0 // 0 °, 180 °
  1 4 0 // 90 °, 270 °

zig-zag:
  3 1 1 0 0 1 1 // 0 °, 180 °
  2 1 2 1 0 // 90 °, 270 °

  3 0 1 1 1 1 0 // 0 °, 180 °
  2 2 1 0 1 // 90 °, 270 °

Cubo:
  2 2 2 0 0 // qualsiasi angolo

Ora dovremmo codificare questi numeri come sequenze di 2 bit e inserirli in un array (sostituendolo 4 0con 3 1un angolo di 90 ° della "linea" per adattarsi a 2 bit - il risultato sarà lo stesso e ridurre le larghezze di 1).

Codificheremo in ordine: larghezza (in 2 LSB), TBL , HAT (indietro per loop indietro). Ad esempio 2 2 1 1 0 per 270 °, angolo T-somma può essere codificato come 1 0 1 2 1(ultimo 1 è larghezza-1 ): 0b0100011001 = 281.

aggiornato il 12.02:

a) Ho convertito un array in una stringa e ho salvato 18 caratteri (puoi vedere il precedente codice di 239 byte ) :))

b) Maggiore ottimizzazione, il codice viene ridotto di 9 caratteri.
Questo è il mio ultimo tentativo (penso di sì, lol!) 😀


1
Puoi colpire usando <s> ... </s>.
Jonathan Frech,


Oh, fico. Grazie. Lol :))
Jin X

Wow! Tetris di basso livello 🤔
Rustem B.

TBL è il numero di celle di figura sotto la linea più alta che ha solo spazio libero o blocchi di celle al di sotto e al di sopra di esso (senza spazio libero e quindi celle). TBL + HAT = altezza della figura (su ogni posizione orizzontale). Anche TBL> 0 e HAT> 0.
Jin X

5

Lisp comune, 634 byte

(let((w(make-hash-table))(r 0))(defun z(c)(or(gethash c w)0))(defun x(c v)(setf r(max r c))(setf(gethash c w)v))(defun m(s)(dolist(c s)(apply(lambda(n u p)(let*((i(let*((j'(2 2 2))(k'(3 3))(l'(2 3))(m'(3 2))(o(case n(1(list'(1 1 1 1)'(4)))(2(list j k'(1 1 2)'(3 1)))(3(list j'(1 3)'(2 1 1)k))(4(list'(2 2)))(5(list'(2 2 1)l))(6(list j l'(1 2 1)m))(7(list'(1 2 2)m)))))(setf(cdr(last o))o)))(o(nth(+ u 2)i))(b(nth u i))(s(length o))(d 0)(h 0))(dotimes(i s)(let*((w(nth i b))(g(z(+ i p)))(m(+ g w)))(when(> m d)(setf d m)(setf h(- g(-(apply'max b)w))))))(dotimes(i s)(x(-(+ s p)i 1)(+(nth i o)h)))))c))(dotimes(i r)(print(z (+ i 1))))))

verboso

(defun circular (list)
  (setf (cdr (last list)) list))

(defun get-piece (piece-number)
  (circular (case piece-number
              (1 (list '(1 1 1 1)
                       '(4)))
              (2 (list '(2 2 2)
                       '(3 3)
                       '(1 1 2)
                       '(3 1)))
              (3 (list '(2 2 2)
                       '(1 3)
                       '(2 1 1)
                       '(3 3)))
              (4 (list '(2 2)))
              (5 (list '(2 2 1)
                       '(2 3)))
              (6 (list '(2 2 2)
                       '(2 3)
                       '(1 2 1)
                       '(3 2)))
              (7 (list '(1 2 2)
                       '(3 2))))))

(let ((world (make-hash-table))
      (rightmost-column 0))
  (defun get-world-column (column)
    (or (gethash column world) 0))

  (defun set-world-column (column value)
    (setf rightmost-column (max rightmost-column column))
    (setf (gethash column world) value))

  (defun drop-piece (piece-number rotation position)
    (let* ((piece (get-piece piece-number))
           (top (nth (+ rotation 2) piece))
           (bottom (nth rotation piece))
           (size (length top))
           (max-combined-height 0)
           (contact-height 0))
      (dotimes (i size)
        (let* ((down-distance (nth i bottom))
               (height (get-world-column (+ i position)))
               (combined-height (+ height down-distance)))
          (when (> combined-height max-combined-height)
            (setf max-combined-height combined-height)
            (setf contact-height
                  (- height
                     (- (apply #'max bottom)
                        down-distance))))))
      (dotimes (i size)
        (set-world-column (- (+ size position) i 1)
                          (+ (nth i top) contact-height)))))

  (defun drop-pieces (pieces)
    (dolist (piece pieces)
      (apply #'drop-piece piece)))

  (defun print-world ()
    (loop for i from 1 to rightmost-column
          do (print (get-world-column i)))))

(defun play-tetris (pieces)
  (drop-pieces pieces)
  (print-world))

Provalo

I pezzi sono liste circolari di liste di numeri. Questi sotto-elenchi rappresentano ciascuno un lato della forma, i numeri indicano la distanza dal lato opposto. Sono da sinistra a destra quando quel lato è in basso, da destra a sinistra quando in alto, dall'alto verso il basso quando a sinistra e dal basso verso l'alto quando a destra. Queste scelte di progettazione eliminano la necessità di scrivere codice per la rotazione. Sfortunatamente, la mancanza del codice di rotazione non sembra aver compensato le lunghe rappresentazioni delle forme, o la logica un po 'complicata che ho usato per calcolare le nuove altezze delle colonne.

La rotazione è un numero intero non negativo. 0 = 0 gradi, 1 = 90 gradi, 2 = 180 gradi, 4 = 270 gradi


5

C # (compilatore interattivo Visual C #) , 308 byte

a=>{var o=new int[a.Max(x=>x.Item3+4)];foreach(var(i,r,p)in a){var b="\"4TqzŒª!\0\0HSš	Ó\0$\n\0!“A“š š@";int m=0,n=b[i],t=0,u=n/8+r%(n%8),v=b[u*=2]<<8|b[u-1];for(;t<v/8%8;m=m>n?m:n)n=o[p+t]+v%8-(n=(u=v>>6+3*t++)/2&1)-(n&u);for(;t-->0;)o[p+t]=m-(n=(u=v>>6+3*t)/4&1)-(n&u);}return o;}

Provalo online!

OK - È stata una follia ... Ho presentato una risposta che utilizzava tecniche di code-golf ordinarie. Ma quando ho visto ciò che gli altri stavano inviando, mi sono reso conto che c'era un modo migliore.

Ogni (shape, rotation)tupla è codificata in una stringa C # letterale con i duplicati rimossi. Il processo di codifica acquisisce ciascuna di queste configurazioni in 2 byte.

L'altezza del negozio a 3 bit più bassa e la larghezza del negozio successiva a 3 bit. Poiché ciascuno di questi valori non è mai superiore a 4, possono essere letti direttamente dai 3 bit senza alcuna conversione. Ecco alcuni esempi:

  W   H
010 010 (2x2)
010 011 (2x3)
001 100 (1x4)
011 010 (3x2)
100 001 (4x1)

Successivamente, ogni colonna viene memorizzata in 3 bit. La cosa più utile per me da memorizzare era il numero di quadrati mancanti nella parte superiore e inferiore della colonna.

// missing squares per column

+------ 0 top / 0 bottom
|+----- 0 top / 1 bottom
||+---- 0 top / 1 bottom
|||
HHH (L-Shape)         HH (Jagged-Shape)
H                    HH
                     |||
1 top / 0 bottom ----+||
0 top / 0 bottom -----+|
0 top / 1 bottom ------+

Non ci sono mai più di 2 quadrati mancanti nella parte superiore o inferiore e non c'è mai più di 1 quadrato mancante allo stesso tempo. Dato questo insieme di vincoli, ho trovato la seguente codifica:

// column encoding of missing squares per column

000: none missing
100: 1 missing on top
101: 2 missing on top
010: 1 missing on bottom
011: 2 missing on bottom
110: 1 missing on top and bottom

Poiché dobbiamo tenere conto al massimo di 3 colonne con quadrati mancanti sopra o sotto, possiamo codificare ogni (shape, rotation)tupla in 15 bit.

 C3  C2  C1   W   H
000 000 000 010 010 - 2x2 with no missing squares
000 000 000 100 001 - 4x1 with no missing squares
100 000 100 011 010 - 3x2 with missings square on top of columns 1 and 3
000 110 000 010 011 - 2x3 with missing squares on top and bottom of column 2

Infine, sono state rimosse forme duplicate. L'esempio seguente mostra come più (shape,rotation)tuple possono produrre output duplicati per la stessa forma a rotazioni diverse:

// Square
HH  (0, 90, 180, 270)
HH
#-------------------------------#
// ZigZag
HH  (0, 180)    H  (90, 270)
 HH            HH
               H
#-------------------------------#
// T
 H  (0)        HHH  (180)
HHH             H

 H  (90)       H    (270)
HH             HH
 H             H

Tutti gli output univoci vengono determinati e salvati in a byte[]e convertiti in una stringa C # letterale. Per cercare rapidamente dove si basa una forma Ie R, i primi 7 byte dell'array sono costituiti da una chiave di ricerca codificata.

Di seguito è riportato un link al programma che ho usato per comprimere i pezzi.

Provalo online!

Meno codice golf e commentato:

// a: input list of (i,r,p) tuples
a=>{
  // create an output array that 4 more than
  // the largest position. this may result
  // in some trailing 0's
  var o=new int[a.Max(x=>x.Item3+4)];

  // iterate over each (i,r,p) tuple
  foreach(var(i,r,p)in a){
    // escaped string
    var b="\"4Tqzª!\0\0HS   Ó\0$\n\0!A @";
    // declare several variables that will be used later
    int m=0,n=b[i],t=0,
      // u is the decoded index into b for the current (i,r) pair
      u=n/8+r%(n%8),
      // convert 2 bytes from b into an encoded (shape,rotation) pair
      v=b[u*=2]<<8|b[u-1];
    // iterate over the columns, determining the top of the current
    // piece. The formula here is:
    //   piece_top = max(column_height + shape_height - shape_space_bottom)
    for(;t<v/8%8;m=m>n?m:n)
      n=o[p+t]+v%8-(n=(u=v>>6+3*t++)/2&1)-(n&u);
    // iterate over the columns again, saving the the new height
    // in each column. The formula here is:
    //   new_column_height = piece_top - shape_space_top
    for(;t-->0;)
      o[p+t]=m-(n=(u=v>>6+3*t)/4&1)-(n&u);
  }
  return o;
}

4

Carbone , 98 byte

Fθ«≔§⪪§⪪”)¶∧↷"e«↨U∧0%3;D∧⁼~h⊟⁵h/₂dΦ↗(EF”2⊟ι1⊟ιη≔⊟ιζW‹Lυ⁺ζLη⊞υ⁰≔⌈Eη⁻§υ⁺ζλ﹪Iκ³εFLη§≔υ⁺ζκ⁺ε⊕÷I§ηκ³»Iυ

Provalo online! Il collegamento è alla versione dettagliata del codice. Prende l'input come una matrice di valori [P, R, I], dove I è compreso tra 0 e 6, R è compreso tra 0 e 3 e P è anche indicizzato 0. Spiegazione:

Fθ«

Passa sopra i pezzi di input.

≔§⪪§⪪”)¶∧↷"e«↨U∧0%3;D∧⁼~h⊟⁵h/₂dΦ↗(EF”2⊟ι1⊟ιη

Estrai la descrizione del pezzo attuale e la rotazione. (Vedi sotto.)

≔⊟ιζ

Estrarre la posizione.

W‹Lυ⁺ζLη⊞υ⁰

Assicurarsi che vi sia spazio orizzontale sufficiente per posizionare il pezzo.

≔⌈Eη⁻§υ⁺ζλ﹪Iκ³ε

Assicurarsi che vi sia spazio verticale sufficiente per posizionare il pezzo.

FLη§≔υ⁺ζκ⁺ε⊕÷I§ηκ³

Calcola le nuove altezze delle colonne interessate.

»Iυ

Quando tutti i pezzi sono stati elaborati, visualizza l'elenco finale delle altezze di colonna su righe separate.

La stringa compressa rappresenta la stringa originale 00001923001061443168200318613441602332034173203014614341642430137. Qui gli 2s sono Iseparatori e gli 1s sono Rseparatori. I pezzi quindi decodificano come segue:

P\R  0    1    2    3
0    0000 9
1    300  06   443  68
2    003  86   344  60
4    33
5    034  73
6    030  46   434  64
7    430  37

I Rvalori mancanti vengono automaticamente riempiti ciclicamente da Carbone. Ogni cifra viene quindi mappata su due valori, sporgenza e altezza totale, secondo la seguente tabella:

\ O H
0 0 1
3 0 2
4 1 2
6 0 3
7 1 3
8 2 3
9 0 4

La sporgenza e l'altezza totale si riferiscono alle altezze delle colonne come segue: Dato un pezzo che vogliamo posizionare in una data posizione e, potrebbe essere possibile posizionare il pezzo anche se una delle colonne è più alta di e. La quantità di spazio libero è data dallo sbalzo. La nuova altezza della colonna dopo aver posizionato il pezzo è semplicemente la posizione posizionata più l'altezza totale.

Esempio: supponiamo di iniziare posizionando un 5pezzo nella colonna 1. Poiché non c'è nient'altro, il pezzo viene quindi posizionato nella posizione 0 e le colonne 1 e 3 ora hanno l'altezza 1 mentre la colonna 2 ha l'altezza 2. Vogliamo quindi posizionare un 6pezzo con 1rotazione nella colonna 0. Qui possiamo effettivamente posizionare questo pezzo in posizione 0; sebbene la colonna 1 abbia un'altezza di 1, il pezzo ha una sporgenza di 1 e quindi c'è abbastanza spazio per posizionarlo. La colonna 0 finisce con un'altezza di 2 e la colonna 1 finisce con un'altezza di 3.

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.