Strategi tetris


18

Tugas Anda adalah mengimplementasikan strategi Tetris yang seimbang dalam hal skor vs ukuran kode.

Dalam versi ini, permainan tetromino diputar dan dijatuhkan dari atas ke dalam kisi 20 baris dan 10 kolom. Saat jatuh, mereka tidak dapat diputar atau dipindahkan secara horizontal. Seperti biasa, potongan yang jatuh berhenti ketika mencapai bagian bawah kisi atau ketika gerakan ke bawah lebih lanjut akan menyebabkan tabrakan dengan kotak yang sudah ditempati.

Ketika ngaris horizontal mendapatkan diisi sepenuhnya, mereka runtuh secara bersamaan, grid diisi kembali dengan nbaris kosong di bagian atas, dan skor bertambah dengan 2 n -1 poin. Untuk n= 1,2,3,4, masing-masing 1,3,7,15 poin. Setelah garis hilang, beberapa blok mungkin tetap mengambang di udara (tidak ada " reaksi berantai gravitasi ").

Ketika tidak ada ruang yang tersedia untuk bagian saat ini untuk muncul di mana yang diinginkan, kotak dihapus, bagian saat ini diabaikan, dan permainan berlanjut dengan bagian berikutnya sebagai saat ini. Tidak ada penalti untuk itu.

Anda harus membaca aliran jenis potongan dan memutuskan cara memutarnya dan di mana menjatuhkannya. Lihatlah ke depan untuk potongan berikutnya (hanya satu) diperbolehkan: Anda dapat melihat potongan i+1sebelum menanggapi i, tetapi Anda harus memutuskan nasib isebelum melihat i+2. Tidak ada pandangan di depan tersedia di luar bagian terakhir dari input.

Jenis tetromino dan rotasinya dikodekan per tabel berikut:

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

Input adalah biner - urutan byte yang sisa ketika dibagi 7 harus ditafsirkan sebagai OIZJLST tetromino. Mereka akan muncul dengan probabilitas yang kira-kira sama (kecuali bahwa beberapa tipe pertama mungkin muncul sedikit lebih sering karena 256 tidak kelipatan 7, tetapi itu harus diabaikan). Input dapat dari stdin atau dari file bernama "i" atau diteruskan sebagai argumen. Anda dapat membaca semua input sekaligus, asalkan Anda pastikan untuk mematuhi pembatasan lihat-depan.

Output juga biner - urutan byte dengan panjang yang sama dengan input. Itu bisa stdout atau file bernama "o" atau hasil dari suatu fungsi. Setiap byte mengkodekan r*16 + x, di mana rrotasi yang diinginkan dan xmerupakan indeks berbasis kolom 0 di mana kuadrat paling kiri dari tetromino diputar harus pergi. Itu rdan xharus valid, yaitu 0 ≤ r ≤ 3dan 0 ≤ x ≤ 10-w, di mana wlebar potongan yang sesuai.

Program Anda harus deterministik - diberi input yang sama, harus menghasilkan output yang sama persis. Menggunakan PRNG tidak apa-apa asalkan itu adalah konst-seeded.

Skor total adalah skor dari gim dikurangi ukuran kode Anda dalam byte. Silakan gunakan file berikut (64kiB dari pseudo-random noise) sebagai masukan: https://gist.github.com/ngn/857bf2c99bfafc649b8eaa1e489e75e4/raw/880f29bd790638aa17f51229c105e726bce60235/i

Skrip python2 / python3 berikut membaca file "i" dan "o" dari direktori saat ini, mengulang permainan dan mencetak skor (harap ingat untuk mengurangi ukuran kode Anda dari skor):

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)

Begitu juga program C yang jauh lebih cepat tetapi dijamin hanya berfungsi di 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;}

Total skor tertinggi menang. Celah standar dilarang.


Ketika tidak ada ruang yang tersedia untuk bagian saat ini muncul di mana yang diinginkan Mari kita lihat apakah saya memahaminya dengan benar. Misalnya, jika kolom paling kiri terisi penuh dan program ingin menempatkan potongan berikutnya di sana, ini akan memaksa grid untuk dibersihkan, bahkan jika ada banyak ruang di tempat lain. Apakah itu benar?
Arnauld

@Arnauld ya, benar
ngn

Apakah boleh untuk mengoptimalkan file i ? Tantangan yang bagus, BTW!
Arnauld

Ya, itu berasal dari / dev / urandom saya, jadi saya tidak berharap akan ada pola yang bisa dieksploitasi di dalamnya. Terima kasih :)
ngn

1
Lebih tepatnya: apakah sah untuk menyimpan data pembantu dalam kode kami yang khusus untuk saya , seperti "menghapus 2 baris saat pindah # 147 daripada menunggu Tetris, jika tidak tumpukan akan menjadi terlalu tinggi"? (Ini sepertinya tidak bertentangan dengan 'jangan lihat bagian i + 2 dari file input'.)
Arnauld

Jawaban:


7

C, skor: 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];}}

Idenya adalah bahwa blok S, Z, O, saya menggunakan lokasi dan rotasi tetap:

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

Sisanya - J, L, T - dikemas ke dalam tiga kolom pertama dengan beberapa putaran siklik.

Versi tidak disatukan:

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

sederhana dan efisien - dilakukan dengan baik!
ngn
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.