Memprogram Blok Tetris (Secara harfiah)

33

Dalam gim Tetris , ada 7 jenis batu bata atau Tetr i minoes , yang secara matematis dikenal sebagai tetr o minoes karena semuanya dibuat dengan 4 segmen persegi:

Batu bata tetris

Memiliki nama I, J, L, O, S, T, dan Z, yang sesuai dengan bentuk perkiraan mereka. Menghitung rotasi 90 °, total ada 19 bentuk unik:

I
I
I
I

IIII

 J
 J
JJ

JJJ
  J

JJ
J
J

J
JJJ

L
L
LL

  L
LLL

LL
 L
 L

LLL
L

OO
OO

 SS
SS

S
SS
 S

TTT
 T

T
TT
T

 T
TTT

 T
TT
 T

ZZ
 ZZ

 Z
ZZ
Z

Tantangan

Tulis blok kode segi empat yang bertindak sebagai segmen dasar dari 19 bentuk ini. Ketika kode ini disusun menjadi salah satu bentuk, sebuah program harus dibentuk yang menampilkan huruf besar tunggal yang terkait dengan bentuk itu. Ini harus bekerja untuk semua 19 bentuk.

Area kosong terkemuka yang hadir dalam beberapa 19 bentuk diisi seluruhnya dengan spasi ( ). Area kosong yang tertinggal tidak diisi dengan apa pun (sehingga program tidak selalu persis persegi panjang).

Contoh

Misalkan ini adalah blok kode Anda:

ABC
123

Maka salah satu susunan blok ke dalam potongan S Tetris akan menjadi program yang mencetak S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Perhatikan bahwa semua ruang kosong terdepan diisi dengan karakter spasi, dan tidak ada garis yang memiliki spasi tambahan.)

Ide yang sama berlaku untuk semua 6 bagian lainnya dan rotasi masing-masing.

Catatan

  • Semua 19 program final harus dijalankan dalam bahasa pemrograman yang sama.
  • Jika diinginkan, Anda dapat menambahkan satu baris baru untuk semua program (bukan hanya beberapa, semua atau tidak sama sekali).
  • Blok kode Anda dapat berisi karakter apa pun (termasuk spasi) yang bukan terminator baris .
  • Keluarkan surat ke stdout (atau alternatif terdekat bahasa Anda) dengan baris tambahan opsional.

Mencetak gol

Kiriman yang blok kodenya memiliki area terkecil (lebar kali tinggi) menang. Ini pada dasarnya berarti kode terpendek menang, itulah sebabnya ini ditandai dengan . Tiebreaker pergi ke jawaban terpilih tertinggi .

The ABC\n123contoh memiliki area 3 × 2 = 6.

Potongan

Diberikan blok kode, cuplikan ini akan menghasilkan semua 19 program:

<script>function drawShape(X,n,v){for(var t="",e=0;e<v.length;e++)for(var l=0;l<n.length;l++){for(var r=0;r<v[e].length;r++)t+="X"===v[e][r]?n[l]:X[l];t+="\n"}return t}function go(){var X=document.getElementById("input").value;if(0!=X.length){var n=X.replace(/./g," ").split("\n");X=X.split("\n");for(var v="I (v1):|I (v2):|J (v1):|J (v2):|J (v3):|J (v4):|L (v1):|L (v2):|L (v3):|L (v4):|O:|S (v1):|S (v2):|T (v1):|T (v2):|T (v3):|T (v4):|Z (v1):|Z (v2):".split("|"),t="X\nX\nX\nX|XXXX| X\n X\nXX|XXX\n  X|XX\nX\nX|X\nXXX|X\nX\nXX|  X\nXXX|XX\n X\n X|XXX\nX|XX\nXX| XX\nXX|X\nXX\n X|XXX\n X|X\nXX\nX| X\nXXX| X\nXX\n X|XX\n XX| X\nXX\nX".split("|"),e="",l=0;l<v.length;l++)e+=v[l]+"\n\n"+drawShape(n,X,t[l].split("\n"))+"\n";e=e.substring(0,e.length-2),document.getElementById("output").value=e}}</script><style>html *{font-family: monospace;}</style>Code Block:<br><textarea id='input' rows='8' cols='64'>ABC&#010;123</textarea><br><button type='button' onclick='go()'>Go</button><br><br>All 19 Programs:<br><textarea id='output' rows='24' cols='64'></textarea>

Hobi Calvin
sumber
Jadi rasio panjang-lebar adalah 2 banding 3? Atau bisakah itu ukuran lain? Juga, apa yang harus dilakukan oleh program, minimal? Dengan asumsi program kosong tidak masuk hitungan, tetapi program yang menghasilkan apa-apa tidak berfungsi.
ASCIIThenANSI
@ ASCIIThenANSI Lebar dan tinggi apa pun baik-baik saja. Saya membayangkan sesuatu yang lebih besar dari 2 * 3 akan diperlukan. Ada 19 program, satu untuk setiap susunan blok menjadi salah satu dari 19 bentuk tetromino yang berbeda. Ketika salah satu dari program tersebut dijalankan, ia mengeluarkan huruf tetris yang sesuai.
Hobi Calvin
Wow! Tantangan yang luar biasa! Apakah penting bahasa apa yang kita gunakan?
theonlygusti
@theonlygusti Hampir semua pertanyaan di situs ini memungkinkan bahasa apa pun. Ini tidak terkecuali.
Hobi Calvin
@Hobbins Calvin Ya, saya tahu; Saya baru saja salah mengartikan cuplikan sebagai pengontrol untuk menjalankan JavaScript-jawaban. Ternyata itu hanya mengatur blok kode.
theonlygusti

Jawaban:

16

<> <(Ikan) - 12 * 32 = 384

Saya berencana untuk mencari solusi yang lebih elegan, tapi entah bagaimana saya berakhir dengan ini, yang sangat kasar:

c  0  g84*%\
c2*0  g84*%\
0  84*g84*%\
c  84*g84*%\
c2*84*g84*%\
0  88*g84*%\
c  88*g84*%\
?v         \
;>?v~~?vv   
"L" o;  >   
"S" o; >~?v 
"T" o;    > 
;  >~?v"L"o 
;     >"J"o 
?v         \
 >~?v~~?vv  
"I"  o;  >  
"J"  o; >   
    \~~?vv  
"T"  o;  >  
"Z"  o; >   
?v         \
 >?v"J"o;   
   >?v"Z"o; 
"L"o;>?!v   
"J"o;   >?v 
"T"o;     > 
?v?v"I"o;  >
   >"L"o;   
 >?v"T"o;   
   >?v"O"o; 
     >"S"o; 

Ini cukup sederhana, memeriksa kode dalam kotak 3x3 untuk teks dan menggunakan hasil untuk melihat tetrimino yang sesuai dengan bentuk kode. Saya belum mengambil banyak usaha untuk golf itu.

Coba kodenya di sini (setelah menggunakan snippet untuk membentuknya seperti tetrimino)

Contoh kode dalam bentuk Z (v1) di sini

Thijs ter Haar
sumber
14

C (gcc) , 26x20 = 520 25x19 = 475 23x17 = 391

#ifndef M            //
#define M(a,b)a##b   //
#define W(z,x)M(z,x) //
char*s,*S[]={"!!!!c",//
"8M !7! M8 878","77",//
"7!!MO887","788OM!!7"//
,"N7!78","7N87!"},r[5//
],*p=r;i=7;main(){for//
(;i--;)strstr(S[i],r)//
&&putchar("ITOJLSZ"[i//
]);}                 //
#endif               //
__attribute__((      //
constructor(__LINE__)//
))W(f,__LINE__)(){s= //
"                     \
";*p++=strlen(s)+12;}//

Saya baru-baru ini diberitahu tentang atribut fungsi GNU, dan yang paling menarik adalah constructoratributnya, yang memungkinkan untuk implementasi yang lebih singkat dari apa yang saya lakukan dengan cara yang lebih bundaran dalam pendekatan saya sebelumnya untuk masalah ini.

Dorongan ide sama dengan sebelumnya: Bangun string dan mencarinya dalam daftar untuk mengidentifikasi blok tetris mana kode diletakkan. Ini dilakukan dengan memanggil fungsi, masing-masing menambahkan karakter ke string. Komplikasi itu dan tetap bahwa jumlah fungsi bervariasi.

Menentukan fungsi dengan attribute((constructor(x)))membuatnya agar fungsi dijalankan sebelum main()dimasukkan, dengan opsional xmenjadi prioritas (lebih rendah berarti dijalankan sebelumnya). Ini menghilangkan kebutuhan untuk pointer fungsi, yang memungkinkan kita untuk menjatuhkan makro, beberapa deklarasi, dan rantai panggilan.

Menggunakan __LINE__untuk prioritas adalah rapuh, karena tingkat prioritas 0-100 dicadangkan. Namun, itu tidak menghasilkan kesalahan, hanya peringatan, dan itu banyak ketika bermain golf, jadi apa lagi?

Itu akan membantu mencukur kolom lain untuk tidak menggunakan prioritas sama sekali, tetapi urutan eksekusi tampaknya tidak didefinisikan. (Mereka terbalik dalam kasus ini, tetapi tes lain tidak dapat disimpulkan.)

Contoh L v2 di sini

Pendekatan yang lebih tua, lebih portabel,

#ifndef M              //
#define M(a,b) a##b    //
#define W(z,x)M(z,x)   //
#define F W(f,__LINE__)//
#define A W(a,__LINE__)//
char r[5],*S[]={"####k"//
,";<<US##;",";##SU<<;",//
";;",";T<;#","<S #;# S"//
"< <;<","T;#;<"},*s,*p=//
r;i;typedef(*T)();T a17//
,a36,a55,a74;main(){for//
(a17(),a36&&a36(),a55&&//
a55(),a74&&a74();i--;) //
strstr(S[i],r)&&putchar//
("ILJOZTS"[i]);}i=7;   //
#endif                 //
F();T A=F;F(){s=       //
"                       \
";*p++=strlen(s)+12;}  //

Salah satu masalah favorit saya yang saya pecahkan di situs ini.

Saya mulai dengan mencari tahu setiap blok akan ilahi koordinat sendiri entah bagaimana. Barisnya mudah __LINE__, dan jumlah blok yang berdekatan secara horizontal dapat ditemukan dengan menggunakan panjang string literal, seperti:

char*s=//char*s=//
"       ""       "
;        ;        

Ambil panjang string yang dihasilkan dan dan bagi dengan jumlah yang tepat dan Anda memiliki lebar. Sayangnya, ruang kosong sebelum blok tidak terlihat oleh metode ini. Saya masih menduga string akan menjadi solusi, karena spasi hanya memiliki makna luar string sangat jarang, dalam hal-hal seperti a+++bvs. a+ ++b. Saya secara singkat mempertimbangkan sesuatu seperti itu, tetapi tidak dapat menemukan sesuatu yang berguna. Kemungkinan lain adalah membiarkan pengidentifikasi "direkatkan" bersama-sama ketika blok bertemu:

A  BA  B

Saya tidak akan terkejut jika ini masih bisa menjadi solusi yang menarik.

Terlepas dari kesederhanaannya, butuh beberapa waktu untuk menemukan solusi string, yang didasarkan pada fragmen blok ini:

s=//
"  \
";//

Jika fragmen tidak memiliki tetangga horizontal, baris baru pada baris kedua diloloskan oleh garis miring terbalik, membuat string dengan panjang 2. Jika, bagaimanapun, memiliki tetangga, garis miring terbalik akan lepas dari tanda queri di awal baris 2 dari blok selanjutnya:

s=//s=//
"  \"  \
";//";//

Ini akan membuat string "\" "dengan panjang 5.

Lebih penting lagi, ini juga memungkinkan deteksi ruang kosong sebelum blok:

    s=//
    "  \
    ";//

Lagi-lagi, baris baru diloloskan, dan spasi putih dari blok kosong di sebelah kiri termasuk dalam string "" panjang 6 yang dihasilkan.

Total ada tujuh konfigurasi blok yang berbeda pada satu baris yang perlu kita khawatirkan, dan semuanya membuat string dengan panjang yang unik:

2 "  "
---
s=//
"  \
";//

5 "  \"  "
---
s=//s=//
"  \"  \
";//";//

6 "      "
---
    s=//
    "  \
    ";//

9 "  \"      "
----
    s=//s=//
    "  \"  \
    ";//";//

10 "          "
---
        s=//
        "  \
        ";//

8 "  \"  \"  "
---
s=//s=//s=//
"  \"  \"  \
";//";//";//

11 "  \"  \"  \"  "
----
s=//s=//s=//s=//
"  \"  \"  \"  \
";//";//";//";//

Blok akhir tentu saja tidak memiliki panjang yang pendek, tetapi prinsipnya sama terlepas dari ukuran blok. Ini juga memiliki bonus yang tidak diperlukan mekanisme terpisah untuk mendeteksi lebar. Dengan menambahkan karakter yang sesuai dengan panjang string ini ke string hasil, masing-masing dari 19 konfigurasi menghasilkan string yang unik, yang hanya perlu dibandingkan dengan daftar yang sesuai setelah semua blok dijalankan.

Setelah ini disortir, masalah besar berikutnya adalah bagaimana "mengunjungi" setiap baris blok. Dalam C, kami sangat terbatas pada apa yang bisa dilakukan di luar fungsi. Kita juga perlu main()muncul, tetapi hanya sekali. Yang terakhir mudah dicapai oleh beberapa #defines, tetapi jika kita ingin kode blok berikutnya berada di dalam main(), masalah bagaimana mengetahui kapan harus menempatkan braket keriting penutupan akhir. Lagi pula, kita tidak tahu berapa banyak baris balok yang akan digunakan. Jadi kita perlu memiliki main()statis dan entah bagaimana sisanya menjadi dinamis.

Jika baris-blok lainnya harus mandiri, mereka harus fungsi, tetapi kita perlu memastikan setiap fungsi memiliki nama yang unik, sementara juga cukup dapat diprediksi untuk dapat dipanggil dari main(). Kita juga membutuhkan mekanisme untuk mengetahui fungsi mana yang sebenarnya dipanggil. Membuat nama-nama unik diselesaikan oleh makro pembantu:

#define M(a,b) a##b     //
#define W(z,x)M(z,x)    //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //

Memanggil Fakan membuat pengenal yang namanya dimulai dengan f dan diakhiri dengan nomor baris. Amelakukan hal yang sama tetapi dengan awalan sebagai, yang digunakan untuk bagian kedua dari solusi, yang merupakan pointer fungsi. Kami mendeklarasikan empat petunjuk tersebut:

typedef(*T)();T a17,a36,a55,a74;

Karena ini dinyatakan sebagai variabel global, mereka dengan mudah diatur ke NULL. Nantinya, setiap blok-baris akan memiliki bagian kode berikut:

F();T A=F;F()

Ini pertama-tama akan mendeklarasikan suatu fungsi, mendefinisikan pointer fungsi yang sesuai untuk menunjuk ke fungsi itu (kita hanya dapat mendefinisikan global sekali, tetapi deklarasi sebelumnya tidak dihitung sebagai definisi, bahkan jika itu diinisialisasi ke NULL), dan kemudian mendefinisikan aktual fungsi. Ini memungkinkan Anda main()memanggil fungsi apa saja yang bukan NULL (a17 tidak akan pernah menjadi NULL):

a17(),a36&&a36(),a55&&a55(),a74&&a74()

Melakukannya akan membangun string r, yang kemudian dicari dalam tabel string dan jika ditemukan, huruf yang sesuai adalah output.

Satu-satunya trik yang tersisa adalah bahwa daftar string yang cocok dengan itu dipersingkat setiap kali ambiguitas dapat dihindari, atau string yang tumpang tindih dapat digabungkan.

Contoh L v2 di sini

gastropner
sumber
6

opcode x86 (.com), 86 82 byte

Penguji:

org 100h
macro e {
db $F6,$04,$DF,$78,$13,$75,$08,$00,$C0,$40,$83,$C6,$52,$EB,$F1,$88
db $C2,$00,$D0,$00,$D0,$46,$EB,$E8,$05,$02,$40,$73,$ED,$E8,$26,$00
db $50,$08,$43,$4D,$2C,$0C,$1C,$15,$A5,$14,$10,$13,$3F,$27,$20,$0F
db $51,$1D,$29,$49,$49,$4A,$4A,$4A,$4A,$4C,$4C,$4C,$4C,$4F,$53,$53
db $54,$54,$54,$54,$5A,$5A,$5F,$AE,$75,$FD,$8A,$55,$12,$B4,$02,$CD
db $21,$C3
}

macro n { db 82 dup $20 }

macro s { db 10 }

n
e
s
n
e
s
e
e  

Sumber:

BOF:    ;mov bx, 100h
p:      test [si], byte $DF
        js _a ; exist
        jnz _b ; newline
_z:     add al, al
        inc ax
q:      add si, EOF-BOF
        jmp p
_b:     mov dl, al
        add al, dl
        add al, dl
        inc si
        jmp p
_a:     add ax, 4002h
        jnc q
        call y
        db 80,8,67,77,44,12,28,21,165,20,16,19,63,39,32,15,81,29,41
        db 'IIJJJJLLLLOSSTTTTZZ'
y:      pop di
        scasb
        jnz y+1
        mov dl,[di+18]
        mov ah,2
        int $21
        ret
EOF:

Jalankan di win7dos di mana init AX = 0, SI = 100, BX = 0 Referensi

l4m2
sumber
Jika Anda merasa nyaman mengurangi jumlah lingkungan yang didukung, Anda dapat mengasumsikan SI = 100j dan menggunakan register itu alih-alih BX untuk pengindeksan, untuk menghemat 3 byte dengan menjatuhkannya mov bx, 100hdi awal.
gastropner
@gastropner Selesai dan memperbaiki titik di mana saya tidak melihat
l4m2