Tetris-Strategie


18

Ihre Aufgabe ist es, eine Tetris-Strategie zu implementieren, die in Bezug auf Punktzahl und Codegröße ausgewogen ist.

In dieser Version des Spiels werden Tetrominos gedreht und von oben in ein Raster von 20 Zeilen und 10 Spalten abgelegt. Während des Fallens können sie nicht gedreht oder horizontal bewegt werden. Wie üblich stoppt ein fallengelassenes Teil, wenn es den unteren Rand des Gitters erreicht oder wenn eine weitere Abwärtsbewegung eine Kollision mit einem bereits belegten Feld verursachen würde.

Wenn nhorizontale Linien vollständig ausgefüllt sind, werden sie gleichzeitig ausgeblendet, das Raster wird oben mit nleeren Linien aufgefüllt und die Punktzahl wird um 2 n -1 Punkte erhöht . Für n= 1,2,3,4 sind das 1,3,7,15 Punkte. Nachdem die Linien verschwunden sind, können einige Blöcke in der Luft schweben (es gibt keine " Schwerkraftkettenreaktion ").

Wenn für das aktuelle Teil kein Platz mehr vorhanden ist, wird das Raster gelöscht, das aktuelle Teil ignoriert und das Spiel mit dem nächsten Teil als aktuell fortgesetzt. Dafür gibt es keine Strafe.

Sie sollten eine Reihe von Objekttypen lesen und entscheiden, wie sie gedreht und wo sie abgelegt werden sollen. Vorausschau für das nächste Stück (nur eines) ist zulässig: Sie können das Stück anschauen, i+1bevor Sie darauf antworten i, aber Sie müssen das Schicksal entschieden haben, ibevor Sie es anschauen i+2. Über den letzten Teil der Eingabe hinaus ist keine Vorausschau verfügbar.

Tetrominotypen und ihre Umdrehungen werden gemäß der folgenden Tabelle codiert:

        type 0    1    2    3    4    5    6
             O    I    Z    J    L    S    T
            ┌────┬────┬────┬────┬────┬────┬────┐
 rotation 0 │##  │#   │##  │ #  │#   │ ## │### │
            │##  │#   │ ## │ #  │#   │##  │ #  │
            │    │#   │    │##  │##  │    │    │
            │    │#   │    │    │    │    │    │
            ├────┼────┼────┼────┼────┼────┼────┤
          1 │##  │####│ #  │### │  # │#   │#   │
            │##  │    │##  │  # │### │##  │##  │
            │    │    │#   │    │    │ #  │#   │
            │    │    │    │    │    │    │    │
            ├────┼────┼────┼────┼────┼────┼────┤
          2 │##  │#   │##  │##  │##  │ ## │ #  │
            │##  │#   │ ## │#   │ #  │##  │### │
            │    │#   │    │#   │ #  │    │    │
            │    │#   │    │    │    │    │    │
            ├────┼────┼────┼────┼────┼────┼────┤
          3 │##  │####│ #  │#   │### │#   │ #  │
            │##  │    │##  │### │#   │##  │##  │
            │    │    │#   │    │    │ #  │ #  │
            │    │    │    │    │    │    │    │
            └────┴────┴────┴────┴────┴────┴────┘

Die Eingabe ist binär - eine Folge von Bytes, deren durch 7 geteilte Reste als die interpretiert werden sollten OIZJLST Tetromino . Sie werden mit ungefähr der gleichen Wahrscheinlichkeit auftreten (außer dass die ersten paar Typen etwas häufiger auftreten, weil 256 kein Vielfaches von 7 ist, aber das sollte vernachlässigbar sein). Die Eingabe kann von stdin oder aus einer Datei mit dem Namen "i" erfolgen oder als Argument übergeben werden. Sie können alle Eingaben auf einmal lesen, sofern Sie die Vorausschau-Einschränkung einhalten.

Die Ausgabe ist ebenfalls binär - eine Folge von Bytes mit der gleichen Länge wie die Eingabe. Dies kann stdout oder eine Datei mit dem Namen "o" oder das Ergebnis einer Funktion sein. Jedes Byte codiert r*16 + x, wobei rdie gewünschte Drehung und xder auf 0 basierende Index der Spalte ist, in die das am weitesten links stehende Quadrat des gedrehten Tetrominos gehen soll. Diese rund xmüssen gültig sein, dh 0 ≤ r ≤ 3und 0 ≤ x ≤ 10-w, wo wist die Breite des entsprechenden Stückes.

Ihr Programm muss deterministisch sein - bei gleicher Eingabe muss es genau die gleiche Ausgabe liefern. Die Verwendung eines PRNG ist in Ordnung, solange es konstituiert ist.

Die Gesamtpunktzahl ist die Punktzahl aus dem Spiel abzüglich der Größe Ihres Codes in Bytes. Verwenden Sie als Eingabe die folgende Datei (64 KB Pseudozufallsrauschen): https://gist.github.com/ngn/857bf2c99bfafc649b8eaa1e489e75e4/raw/880f29bd790638aa17f51229c105e726bce60235/i

Das folgende python2 / python3-Skript liest die Dateien "i" und "o" aus dem aktuellen Verzeichnis, wiederholt das Spiel und druckt die Partitur aus (bitte denken Sie daran, Ihre Codegröße von der Partitur zu subtrahieren):

a = [0] * 23 # grid (1square=1bit, 1row=1int, LSB is left, 3 empty rows on top)
#      O     I         Z       J       L       S       T        tetrominoes
t = [[[3,3],[1,1,1,1],[3,6],  [2,2,3],[1,1,3],[6,3],  [7,2]  ],
     [[3,3],[15],     [2,3,1],[7,4],  [4,7],  [1,3,2],[1,3,1]],
     [[3,3],[1,1,1,1],[3,6],  [3,1,1],[3,2,2],[6,3],  [2,7]  ],
     [[3,3],[15],     [2,3,1],[1,7],  [7,1],  [1,3,2],[2,3,2]]]
tw = [[2,1,3,2,2,3,3],[2,4,2,3,3,2,2],[2,1,3,2,2,3,3],[2,4,2,3,3,2,2]] # widths
th = [[2,4,2,3,3,2,2],[2,1,3,2,2,3,3],[2,4,2,3,3,2,2],[2,1,3,2,2,3,3]] # heights
score = 0
for p, rx in zip(bytearray(open('i', 'rb').read()),
                 bytearray(open('o', 'rb').read())):
    p %= 7; r = rx >> 4; x = rx & 15  # p:piece type, r:rotation, x:offset
    b = [u << x for u in t[r][p]]     # as a bit-matrix (list of ints)
    bw = tw[r][p]; bh = th[r][p]      # width and height
    y = 0                             # drop it
    while y <= 23 - bh and all((a[y + i] & b[i]) == 0 for i in range(bh)):
        y += 1
    y -= 1
    if y < 3:                         # no room?
        a = [0] * len(a)              # clear the grid and carry on
    else:
        for i in range(bh):           # add the piece to the grid
            a[y + i] |= b[i]
        n = 0
        for i in reversed(range(bh)): # collapse full lines
            if a[y + i] == (1 << 10) - 1:
                n += 1; del a[y + i]; a = [0] + a
        score += (1 << n) - 1
print(score)

Das gleiche gilt für das folgende, viel schnellere C-Programm, das jedoch garantiert nur unter Linux funktioniert:

#include<stdio.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/stat.h>
#define F(i,n,b...)for(i=0;i<n;i++){b;}
typedef int I;typedef char C;
I a[23],t[]={
51,4369,99,802,785,54,39,51,15,306,71,116,561,305,
51,4369,99,275,547,54,114,51,15,306,113,23,561,562};
C*th="2423322213223324233222132233";
I main(){
 struct stat h;stat("i",&h);I i,j,k,l=h.st_size,z=0;
 C*mi=mmap(0,l,1,1,open("i",0,0),0),*mo=mmap(0,l,1,1,open("o",0,0),0);
 F(k,l,
  I p=(mi[k]&255)%7,r=3&mo[k]>>4,q=r*7+p,x=mo[k]&15,y=0,h=th[q]-'0',b[4];
  F(i,h,b[i]=(t[q]>>(4*i)&15)<<x)
  while(y<=23-h){I u=0;F(i,h,u|=a[y+i]&b[i])if(u)break;y++;}
  if(--y<3){F(i,23,a[i]=0)continue;}
  F(i,h,a[y+i]|=b[i])
  I n=0;F(i,23,n+=a[i]==1023)
  if(n){j=23;F(i,20,a[j]=a[22-i];j-=a[j]!=1023)F(i,j,a[i]=0);z+=(1<<n)-1;})
 printf("%d\n",z);return 0;}

Die höchste Gesamtpunktzahl gewinnt. Standardlücken sind verboten.


Wenn für das aktuelle Stück kein Platz mehr zur Verfügung steht, schauen wir mal, ob ich das richtig verstehe. Wenn beispielsweise die Spalte ganz links ausgefüllt ist und das Programm das nächste Stück dort platzieren möchte, wird das Raster gelöscht, auch wenn an anderer Stelle ausreichend Platz vorhanden ist. Ist das korrekt?
Arnauld

@ Arnauld ja, richtig
ngn

Ist es in Ordnung, für die i- Datei zu optimieren ? Schöne Herausforderung, übrigens!
Arnauld

Ja, es kommt von meinem / dev / urandom, daher erwarte ich keine ausnutzbaren Muster darin. Danke :)
ngn

1
Genauer gesagt: Ist es zulässig, Hilfedaten in unserem Code zu speichern, der für i spezifisch ist , z. (Dies steht anscheinend nicht im Widerspruch zu "Schau nicht auf Teil i + 2 aus der Eingabedatei".)
Arnauld

Antworten:


7

C, Punktzahl: 4136 (4290 - 154 Bytes)

#include <stdio.h>
main(){int c,p=0,t[]={7,9,21,0,0,51,1,32,16,48,0,33,0,32,16,49};for(;(c=getchar())>=0;putchar(c)){c%=7;c=t[!c||!(10%c)?c:2*c+p++%4];}}

Die Idee ist, dass die Blöcke S, Z, O von mir festgelegte Positionen und Rotationen verwenden:

                  |
      s     z     |
      s s z z # # |
        s z   # # |
0 1 2 3 4 5 6 7 8 9

Der Rest - J, L, T - wird mit einer gewissen zyklischen Rotation in die ersten drei Säulen gepackt.

Ungolfed-Version:

#include <stdio.h>
int main() {
    int c,p=0,t[] = {7,9,21,51, 1,32,16,48, 16,48,0,33, 0,32,16,49 };
    while ((c=getchar())!=EOF) {
            switch(c%7) {
            case 0: c = t[0]; break;
            case 1: c = t[1]; break;
            case 2: c = t[2]; break;
            case 3: c = t[4+p++%4]; break;
            case 4: c = t[8+p++%4]; break;
            case 5: c = t[3]; break;
            case 6: c = t[12+p++%4]; break;
            }
            putchar(c);
    }
    return 0;
}

einfach und effizient - gut gemacht!
ngn
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.