Strategia di Tetris


18

Il tuo compito è implementare una strategia Tetris bilanciata in termini di punteggio e dimensioni del codice.

In questa versione del gioco i tetromini vengono ruotati e rilasciati dall'alto in una griglia di 20 righe e 10 colonne. Durante la caduta, non possono essere ruotati o spostati in orizzontale. Come al solito, un pezzo lasciato cadere si ferma quando raggiunge il fondo della griglia o quando un ulteriore movimento verso il basso causerebbe la collisione con un quadrato già occupato.

Quando nle linee orizzontali vengono riempite completamente, collassano simultaneamente, la griglia viene riempita con nlinee vuote in alto e il punteggio viene incrementato di 2 n -1 punti. Per n= 1,2,3,4 corrispondono rispettivamente a 1,3,7,15 punti. Dopo che le linee scompaiono, alcuni blocchi potrebbero rimanere fluttuanti nell'aria (non c'è " reazione a catena di gravità ").

Quando non è disponibile spazio per far apparire il pezzo corrente dove desiderato, la griglia viene cancellata, il pezzo corrente viene ignorato e il gioco continua con il pezzo successivo come corrente. Non c'è penalità per quello.

Dovresti leggere un flusso di tipi di pezzi e decidere come ruotarli e dove lasciarli cadere. È consentito guardare avanti per il prossimo pezzo (solo uno): puoi guardare il pezzo i+1prima di rispondere i, ma devi aver deciso il destino di iprima di guardare i+2. Non è disponibile alcun aspetto oltre l'ultimo pezzo dell'input.

I tipi di tetromino e le loro rotazioni sono codificati secondo la seguente tabella:

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

L'input è binario - una sequenza di byte i cui resti quando divisi per 7 devono essere interpretati come i OIZJLSTtetromini. Si verificheranno approssimativamente con la stessa probabilità (tranne per il fatto che i primi pochi tipi potrebbero apparire leggermente più spesso a causa del 256 che non è un multiplo di 7, ma che dovrebbe essere trascurabile). L'input può provenire da stdin o da un file chiamato "i" o passato come argomento. Puoi leggere tutti gli input in una sola volta, a condizione di assicurarti di rispettare la restrizione look-ahead.

Anche l'output è binario: una sequenza di byte della stessa lunghezza dell'input. Può essere stdout o un file chiamato "o" o il risultato di una funzione. Ogni byte codifica r*16 + x, dove si rtrova la rotazione desiderata ed xè l'indice in base 0 della colonna dove dovrebbe andare il quadrato più a sinistra del tetromino ruotato. Quelli re xdevono essere validi, cioè 0 ≤ r ≤ 3e 0 ≤ x ≤ 10-w, dov'è wla larghezza del pezzo corrispondente.

Il programma deve essere deterministico - dato lo stesso input, deve produrre esattamente lo stesso output. L'uso di un PRNG è ok purché sia ​​seminato.

Il punteggio totale è il punteggio del gioco meno la dimensione del codice in byte. Utilizzare il seguente file (64 kB di rumore pseudo-casuale) come input: https://gist.github.com/ngn/857bf2c99bfafc649b8eaa1e489e75e4/raw/880f29bd790638aa17f51229c105e726bce60235/i

Il seguente script python2 / python3 legge i file "i" e "o" dalla directory corrente, riproduce il gioco e stampa il punteggio (ricordarsi di sottrarre la dimensione del codice dal punteggio):

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)

Così fa il seguente programma C molto più veloce ma è garantito che funzioni solo su Linux:

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

Il punteggio totale più alto vince. Sono vietate le scappatoie standard.


Quando non è disponibile spazio per la visualizzazione del pezzo corrente dove desiderato Vediamo se lo capisco correttamente. Ad esempio, se la colonna più a sinistra è interamente riempita e il programma desidera posizionare lì il pezzo successivo, questo costringerà la griglia a essere cancellata, anche se c'era molto spazio altrove. È corretto?
Arnauld,

@Arnauld sì, corretto
ngn

Va bene ottimizzare il file i ? Bella sfida, a proposito!
Arnauld,

Sì, viene dal mio / dev / urandom, quindi non mi aspetto che ci siano schemi sfruttabili in esso. Grazie :)
ngn

1
Più precisamente: è legale archiviare i dati di supporto nel nostro codice specifico per i , come "cancella 2 righe alla mossa # 147 invece di aspettare un Tetris, altrimenti lo stack andrà troppo in alto"? (Questo non sembra essere in conflitto con 'non guardare il pezzo i + 2 dal file di input'.)
Arnauld

Risposte:


7

C, punteggio: 4136 (4290 - 154 byte)

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

L'idea è che i blocchi S, Z, O, utilizzo posizioni e rotazioni fisse:

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

Il resto - J, L, T - sono impacchettati nelle prime tre colonne con una rotazione ciclica.

Versione non golfata:

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

semplice ed efficiente - ben fatto!
ngn,
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.