Tetris! Hauteurs finales (jour 3)

19

Défi tiré de mon concours de défi de code universitaire

C'est en fait le jour 0, mais le défi d'hier était trop facile et peut être une dupe d'une autre question ici.


Tetris est un jeu vidéo devenu populaire dans les années 80. Il consiste à placer une série de pièces de formes différentes qui tombent sur une planche, afin qu'elles s'adaptent de la manière la plus compacte possible.

Dans ce problème, nous supposerons une séquence de pièces qui tombent, chacune dans une certaine position et avec une certaine orientation qui ne peut pas être modifiée. Les pièces sont empilées à mesure qu'elles tombent et les rangées complètes ne sont pas éliminées (comme dans le jeu original). L'objectif est de déterminer la hauteur finale de chaque colonne de la planche après la chute de toutes les pièces.

Il y a un total de 7 pièces différentes, illustrées dans la figure:

formes

Défi

Étant donné une liste de pièces, affichez la hauteur de toutes les colonnes du tableau après la chute de toutes les pièces

Une pièce se compose de trois nombres: I, R et P. Le premier numéro, I, est l'identifiant de la pièce (un nombre entre 1 et 7, dans le même ordre que sur la figure). Le deuxième nombre, R, est la rotation de la pièce. Il peut prendre les valeurs 0, 90, 180 ou 270 et représente l'angle de rotation de la pièce dans le sens anti-horaire. Le troisième chiffre, P, indique la position de la pièce. Représente la colonne de gauche occupée par la pièce (cela peut être 1 ou 0 Index. Veuillez spécifier).

Exemple et cas de test (1 index)

  • Donné [[1, 0, 1], [4, 0, 1], [5, 90, 4]]

cas 1

  • Production [3, 3, 1, 3, 2]

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

cas # 2

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

  • [[3,0,1],[3,180,3]]Sortie donnée[1,1,4,4,4]

  • [[2,180,1],[2,0,3]]Sortie donnée[2,2,4,3,3]

Remarques

  • C'est du
  • La ligne / colonne peut être un index de 1 ou 0. Veuillez préciser.
  • Vous pouvez redéfinir les valeurs d'entrée (peut-être que vous voulez appeler la pièce 1 comme A, etc.). Dans ce cas, veuillez préciser

Des questions

  • Peut-on utiliser 4 valeurs distinctes au lieu d'un angle en degrés?: Oui

  • Sommes-nous censés gérer les "trous" si une pièce ne correspond pas exactement aux précédentes?: Oui

  • La hauteur ou la largeur de la planche est-elle délimitée? Non, ni la largeur ni la hauteur ne sont délimitées


Merci @Arnauld pour les images et les cas de test *. *

Luis felipe De jesus Munoz
la source
Peut I- Ril Pêtre saisi dans un ordre différent?
Neil
@Neil oui. Cela peut être dans n'importe quel ordre
Luis felipe De jesus Munoz
Si nous pouvons redéfinir les valeurs d'entrée, puis-je prendre l'ID de pièce comme une matrice qui représente la forme des pièces (sans rotation)?
Incarnation de l'ignorance
1
Je pense que nous ne pouvons pas entrer une matrice représentant la forme des pièces pour 2 raisons. L'entrée est clairement définie: 1,2,3 .. ou A, B, C .. Et une partie fondamentale de ce défi est de gérer cette contrainte.
AZTECCO
1
Serait-il correct d'inclure des 0 à la fin?
dana

Réponses:

10

JavaScript (Node.js) ,  286 284 270  266 octets

[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

Essayez-le en ligne! ou essayez une version améliorée qui affiche également la carte finale.

Encodage de forme

Toutes les pièces sont stockées sous la forme d'exactement 4 quartets (bits 4x4) avec les lignes triées dans l'ordre inverse et le pixel le plus à gauche mappé sur le bit le moins significatif. En d'autres termes, la représentation binaire de la forme est inversée verticalement et horizontalement.

Exemple:

exemple d'encodage de forme

Fonction de hachage et table de recherche

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

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

820

Ces entrées sont regroupées comme suit:

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

qui s'étend aux 82 grignotages suivants:

"717433667233ff47173333276611000000000000113213001112221112123333333311133233221211"

je"ff"

Les paramètres de la fonction de hachage ont été forcés par une méthode brute qui optimise les zéros de début et de fin. Le fait que la chaîne puisse être compressée un peu plus en utilisant 1e12les zéros du milieu et une conversion de base-16 en base-4 pour la partie droite est juste un effet secondaire bienvenu mais inattendu. :-)

Voici une démonstration du processus de déballage de toutes les pièces et de toutes les rotations.

Commenté

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[]
Arnauld
la source
3
Bon travail d'emballage / déballage des pièces. C'est vraiment impressionnant :)
dana
7

C (clang) , 253 239 221 212 octets

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;}}

Essayez-le en ligne!

ps En fait, la taille du code est de 221 octets (mais 212 caractères) en raison des caractères UNICODE codés en UTF-8. Mais tio.run le traite comme un code de 212 octets ...

La taille du code sur mon ordinateur est de 209 caractères (218 octets). Mais je ne pouvais pas le remplacer \225par un caractère visible dans tio.run 😞

Code non golfé

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

La description

Trouvons la ligne de base supérieure ( TBL ) de chaque figure et décrivons-la comme un nombre de cellules sous TBL pour chaque position horizontale. Décrivons également le nombre de cellules (hauteur) au-dessus de TBL ( HAT ).

Par exemple:

                       ________ ________
_ [] _____ 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

Décrivons les TBL et les HAT pour chaque figure et chaque angle de rotation:

Largeur TBLs HATs
----- ------- -------
Chiffres en L:
  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 °

Chiffre en T:
  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 °

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

Zigzags:
  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 °

Cube:
  2 2 2 0 0 // tout angle

Maintenant, nous devons coder ces nombres sous forme de séquences de 2 bits et les placer dans un tableau (en remplaçant 4 0par 3 1un angle de 90 ° de "ligne" pour tenir sur 2 bits - le résultat sera le même et diminuera les largeurs de 1).

Nous allons encoder dans l'ordre: largeur (en 2 LSB), TBL , HAT (en arrière pour la boucle en arrière). Par exemple , 2 2 1 1 0 pour 270 ° Angle de T-chiffre est codé sous la forme 1 0 1 2 1(dernier 1 est la largeur-1 ): 0b0100011001 = 281.

mise à jour 12.02:

a) J'ai converti un tableau en chaîne et enregistré 18 caractères (vous pouvez voir le code précédent de 239 octets ) :))

b) Plus d'optimisation, le code est réduit de 9 caractères.
Ceci est ma dernière tentative (je pense que oui, lol!) 😀

Jin X
la source
1
Vous pouvez frapper en utilisant <s> ... </s>.
Jonathan Frech
1
243 octets .
Jonathan Frech
Oh cool. Merci. Lol :))
Jin X
Hou la la! Tetris de bas niveau Rust
Rustem B.
TBL est le nombre de cellules de la figure sous la ligne la plus élevée qui n'a que de l'espace libre ou des blocs de cellules en dessous et au-dessus (pas d'espace libre puis des cellules). TBL + HAT = hauteur de la figure (sur chaque position horizontale). TBL> 0 et HAT> 0 aussi.
Jin X
5

Lisp commun, 634 octets

(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))))))

Verbeux

(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))

Essaye-le

Les pièces sont des listes circulaires de listes de nombres. Ces sous-listes représentent chacune un côté de la forme, les nombres indiquant à quelle distance ils se trouvent du côté opposé. Ils sont de gauche à droite lorsque ce côté est en bas, de droite à gauche en haut, de haut en bas à gauche et de bas en haut à droite. Ces choix de conception éliminent la nécessité d'écrire du code pour la rotation. Malheureusement, l'absence de code de rotation ne semble pas avoir compensé les longues représentations de forme ou la logique quelque peu compliquée que j'ai utilisée pour calculer les nouvelles hauteurs de colonne.

La rotation est un entier non négatif. 0 = 0 degrés, 1 = 90 degrés, 2 = 180 degrés, 4 = 270 degrés

Charlim
la source
5

C # (compilateur interactif Visual C #) , 308 octets

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;}

Essayez-le en ligne!

OK - C'était de la folie ... J'ai soumis une réponse qui utilisait des techniques de golf de code banales. Mais quand j'ai vu ce que les autres soumettaient, j'ai réalisé qu'il y avait une meilleure façon.

Chaque (shape, rotation) tuple est codé dans un littéral de chaîne C # avec les doublons supprimés. Le processus de codage capture chacune de ces configurations sur 2 octets.

La hauteur de mémoire la plus basse sur 3 bits et la largeur de la mémoire suivante sur 3 bits. Étant donné que chacune de ces valeurs n'est jamais supérieure à 4, elles peuvent être lues directement à partir des 3 bits sans aucune conversion. Voici quelques exemples:

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

Ensuite, chaque colonne est stockée sur 3 bits. La chose la plus utile pour moi de stocker était le nombre de carrés manquants en haut et en bas de la colonne.

// 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 ------+

Il n'y a jamais plus de 2 carrés manquants en haut ou en bas, et il n'y a jamais plus d'un carré manquant des deux en même temps. Compte tenu de cet ensemble de contraintes, j'ai trouvé le codage suivant:

// 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

Étant donné que nous devons tenir compte d'au plus 3 colonnes avec des carrés manquants au-dessus ou au-dessous, nous pouvons coder chaque (shape, rotation)tuple en 15 bits.

 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

Enfin, les formes en double ont été supprimées. L'exemple suivant montre comment plusieurs (shape,rotation)tuples peuvent produire des sorties en double pour la même forme à différentes rotations:

// 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

Toutes les sorties uniques sont déterminées et enregistrées dans un byte[]et converties en un littéral de chaîne C #. Afin de rechercher rapidement où une forme est basée sur Iet R, les 7 premiers octets du tableau consistent en une clé de recherche codée.

Ci-dessous, un lien vers le programme que j'ai utilisé pour compresser les morceaux.

Essayez-le en ligne!

Code moins joué et commenté:

// 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;
}
dana
la source
4

Fusain , 98 octets

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

Essayez-le en ligne! Le lien est vers la version détaillée du code. Prend l'entrée comme un tableau de valeurs [P, R, I], où I est de 0 à 6, R est de 0 o 3 et P est également indexé 0. Explication:

Fθ«

Faites une boucle sur les pièces d'entrée.

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

Extrayez la description de la pièce et de la rotation en cours. (Voir ci-dessous.)

≔⊟ιζ

Extraire la position.

W‹Lυ⁺ζLη⊞υ⁰

Assurez-vous qu'il y a suffisamment d'espace horizontal pour placer la pièce.

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

Assurez-vous qu'il y a suffisamment d'espace vertical pour placer la pièce.

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

Calculez les nouvelles hauteurs des colonnes affectées.

»Iυ

Lorsque toutes les pièces ont été traitées, affichez la liste finale des hauteurs de colonne sur des lignes distinctes.

La chaîne compressée représente la chaîne d'origine 00001923001061443168200318613441602332034173203014614341642430137. Ici, les 2s sont des Iséparateurs et les 1s sont des Rséparateurs. Les morceaux se décodent donc comme suit:

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

Les Rvaleurs manquantes sont automatiquement remplies de manière cyclique par Charcoal. Chaque chiffre correspond ensuite à deux valeurs, le porte-à-faux et la hauteur totale, selon le tableau suivant:

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

Le porte-à-faux et la hauteur totale se rapportent aux hauteurs de colonne comme suit: Étant donné une pièce que nous voulons placer à une position donnée e, il peut être possible de placer la pièce même si l'une des colonnes est plus haute quee . La quantité d'espace disponible est donnée par le surplomb. La nouvelle hauteur de la colonne après avoir placé la pièce est simplement la position placée plus la hauteur totale.

Exemple: Supposons que nous commençons par placer un 5morceau dans la colonne 1. Puisqu'il n'y a rien d'autre encore, le morceau est donc placé à la position 0 et les colonnes 1 et 3 ont maintenant la hauteur 1 tandis que la colonne 2 a la hauteur 2. Nous voulons ensuite placer un 6morceau avec 1rotation dans la colonne 0. Ici, nous pouvons réellement placer cette pièce à la position 0; bien que la colonne 1 ait une hauteur de 1, la pièce a un surplomb de 1, et donc il y a assez d'espace pour la placer. La colonne 0 se termine par une hauteur de 2 et la colonne 1 se termine par une hauteur de 3.

Neil
la source