Tetris Bloklarını Programlama (Kelimenin tam anlamıyla)


33

Oyunda Tetris , tuğla ya da 7 türleri vardır Tetr i minoes matematiksel şekilde bilinmektedir, zirkonyum tetraklorohidreks O minoes hepsi 4 kare segmentleri ile yapılır, çünkü:

Tetris tuğlaları

Yaklaşık şekillerine karşılık gelen I, J, L, O, S, T ve Z isimlerine sahiptir. 90 ° dönüşleri sayarak, toplam 19 benzersiz şekil vardır:

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

Meydan okuma

Bu 19 şeklin yapıldığı taban segmenti görevi gören dikdörtgen bir kod bloğu yazın. Bu kod şekillerden birine yerleştirildiğinde, bu şekille ilişkili tek büyük harf çıktısını alan bir program oluşturulmalıdır. Bu, 19 şekil için de geçerli olmalı.

19 şekilden bazılarında bulunan önde gelen boş alanlar tamamen boşluklarla doldurulur ( ). Sondaki boş alanlar hiçbir şeyle dolu değildir (bu nedenle programlar her zaman tam olarak dikdörtgen değildir).

Örnek

Bunun kod bloğunuz olduğunu varsayalım:

ABC
123

Daha sonra bloğun S Tetris parçasına yerleştirilmesi aşağıdakileri yapan bir program olacaktır S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Baştaki tüm boş alanların boşluk karakterleriyle dolu olduğuna ve hiçbir satırın izleyen boşluklara sahip olmadığına dikkat edin.)

Aynı fikir diğer 6 parçaya ve bunların rotasyonlarına da uygulanır.

notlar

  • 19 final programının tamamı aynı programlama dilinde yürütülecektir.
  • İstenirse, tüm programlara tek bir son satır ekleyebilir (yalnızca bazılarını veya tümünü değil).
  • Kod bloğunuz, satır sonlandırıcı olmayan karakterler (boşluklar dahil) içerebilir .
  • İsteğe bağlı takip eden bir yeni satırla birlikte mektubu stdout'a (veya dilinizin en yakın alternatifi) verin.

puanlama

Kod bloğu en küçük alana (genişlik kat yüksekliği) sahip olan gönderim kazanır. Bu esasen en kısa kod kazancı anlamına gelir, bu yüzden . Tiebreaker en yüksek oyu alan cevaba gidiyor .

ABC\n123Örnek parkı 3 x 2 = 6 değerine sahiptir.

Pasaj

Bir kod bloğu verildiğinde, bu snippet, 19 programın tümünü üretecektir:

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


Yani uzunluk-genişlik oranı 2 ila 3? Veya başka bir boyutta olabilir mi? Ayrıca, programın asgari olarak ne yapması gerekiyor? Boş programların sayılmadığını varsaymakla birlikte, hiçbir şey çıkaran programlar sayılmaz.
ASCIIThenANSI

@ASCIIThenANSI Herhangi bir genişlik ve yükseklik iyi. 2 * 3'ten büyük bir şeyin gerekli olacağını hayal ediyorum. Bloğun her biri için 19 ayrı tetromino şeklinden birine yerleştirilen 19 program vardır. Bu programlardan biri çalıştırıldığında, karşılık gelen tetris parça harfini çıkarır.
Calvin'in Hobileri

Vaov! Ne harika bir meydan okuma! Hangi dili kullandığımız önemli mi?
theonlygusti

@theonlygusti Bu sitedeki hemen hemen tüm sorular herhangi bir dile izin veriyor. Bu bir istisna değil.
Calvin'in Hobileri

@ Calvin'in Hobileri Evet, biliyorum; Sadece snippet'i JavaScript cevapları çalıştırmak için bir kontrol cihazı olarak yanlış yorumladım. Görünüşe göre sadece kod blokları düzenler.
theonlygusti

Yanıtlar:


16

<> <(Balık) - 12 * 32 = 384

Daha zarif bir çözüm bulmayı planlıyordum, ama bir şekilde bununla sonuçlandım ki bu oldukça kaba bir güçtü:

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; 

Oldukça basit, kodu metin için 3x3 karede kontrol eder ve hangi tetrimino kodun şekline karşılık geldiğini görmek için sonuçları kullanır. Henüz golf oynamak için çok çaba sarf etmedim.

Burada kodu deneyin (bir tetrimino gibi biçimlendirmek için pasajı kullandıktan sonra)

Burada Z (v1) kod örneği


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

Yakın zamanda GNU'nun işlev özellikleri ve en ilginç constructorolanı, bu soruna daha önceki yaklaşımımda yaptığım şeyin daha dolambaçlı bir şekilde uygulanmasını sağlayan özellik.

Fikrin itişi, önceki ile aynıdır: Kodun hangi tetris bloğunun düzenlendiğini tanımlamak için bir dize oluşturun ve onu listede arayın. Bu, her biri dizeye bir karakter ekleyen işlevler çağırarak yapılır. Komplikasyon, fonksiyon sayısının değişkenlik gösterdiğini ve devam ettiğini gösteriyor.

Bir fonksiyonla tanımlamak, fonksiyonun girilmesinden attribute((constructor(x)))önce çalışmasını sağlar main(), isteğe bağlı xönceliklidir (düşük ise daha önce çalıştırıldığı anlamına gelir). Bu, makro, bazı bildirimler ve çağıran zinciri düşürmemize izin veren işlev işaretçilerine olan ihtiyacı ortadan kaldırır.

Kullanımı __LINE__öncelik öncelik düzeyleri beri 0-100 saklıdır, şüpheli olduğunu. Ancak, hatalarla sonuçlanmaz, sadece uyarılar ve golf oynarken bunlar çoktur, peki birkaç tane daha var mı?

Öncelikleri hiç kullanmamak için başka bir sütunun kesilmesine yardımcı olabilirdi, ancak yürütmenin sırası tanımlanmadı gibi görünüyor. (Bu durumda bunlar ters çevrilir, ancak diğer testler sonuçsuzdur.)

Burada L v2 örneği

Daha eski, daha taşınabilir, yaklaşım

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

En sevdiğim sorunlardan biri bu sitede çözdüm.

Her bloğun bir şekilde kendi koordinatlarını ilahi edeceğini düşünerek başladım. Satırlar kolaydır __LINE__ve yatay olarak bitişik blokların sayısı, bir değişmez dizenin uzunluğu kullanılarak bulunabilir:

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

Elde edilen dizginin uzunluğunu alın ve uygun bir sayıya bölün ve genişliğe sahip olursunuz. Ne yazık ki, bloktan önceki herhangi bir boş alan bu yöntemle görünmez. Boşluk sadece gibi şeyler, çok nadiren dizeleri dışında anlamı vardır çünkü hala şüpheli dizeleri, çözüm olacaktır a+++bvs. a+ ++b. Kısaca böyle bir şey düşündüm, ama yararlı bir şey bulamadım. Diğer bir olasılık, tanımlayıcıların blokların birleştiği yerde birbirine "yapıştırılmış" olmalarını sağlamak olabilirdi:

A  BA  B

Bu hala ilginç bir çözüm bulabilseydi şaşırmam.

Sadeliği rağmen, bu blok parçasına dayanan string çözümünü bulmam biraz zaman aldı:

s=//
"  \
";//

Parçanın yatay bir komşusu yoksa, ikinci satırdaki yeni satır ters eğik çizgiden kaçar, bir uzunluk 2 dizesi oluşturur. Bununla birlikte, bir komşusu varsa, ters eğik çizgi satırın başındaki tırnak işaretinden çıkacaktır. Bir sonraki bloğun 2 tanesi:

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

Bu, 5 uzunluğundaki "\" "dizesini oluşturur.

Daha da önemlisi, bu aynı zamanda bloktan önce boş alanın algılanmasını sağlar:

    s=//
    "  \
    ";//

Yine, newline kaçar ve boş bloğun sola boşluğunun boşluğu, 6 uzunluğundaki "" dizgisine dahil edilir.

Toplamda, üst üste endişelenmemiz gereken yedi farklı blok konfigürasyonu vardır ve hepsi benzersiz uzunluklarda dizgiler yapar:

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

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

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

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

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

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

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

Nihai bloklar elbette bu kadar kısa olmayacaktır, ancak prensip blok büyüklüğünden bağımsız olarak aynıdır. Bu aynı zamanda genişlik tespiti için ayrı bir mekanizmanın gerekli olmadığına da sahip. Sonuç dizisine bu dizginin uzunluğuna karşılık gelen bir karakter eklenerek, 19 konfigürasyonun her biri, tüm bloklar çalıştırıldıktan sonra sadece uygun bir listeyle karşılaştırılması gereken benzersiz bir dizge verir.

Bu bir kez sıralandığında, bir sonraki büyük problem her bir blok satırı "nasıl ziyaret edeceğimiz" idi. C de, fonksiyonlar dışında yapılabileceklerle sınırlıyız. Ayrıca main()görünmemiz gerekiyor , ama sadece bir kez. İkincisi, bazıları tarafından kolayca elde edilir #define, ancak sonraki blokların kodunun içine alınmasını istiyorsak main(), son kapanış kıvrık braketinin ne zaman yerleştirileceğini bilme sorunu. Sonuçta, kaç blok satırın gerçekten kullanılacağını bilmiyoruz. Bu yüzden main()statik olmak zorundayız ve bir şekilde dinamik olması gerekiyor.

Diğer blok satırları kendi kendine yetecekse, işlevler olmalıdır, ancak her bir işlevin kendine özgü bir adın olduğundan ve aynı zamanda çağrılabilecek kadar öngörülebilir olduğundan emin olmamız gerekir main(). Ayrıca hangi işlevlerin çağrılacağını bilmek için bir mekanizmaya da ihtiyacımız var. Benzersiz isimler üretmek yardımcı makrolar tarafından çözülür:

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

Arama F, adı f ile başlayan ve satır numarasıyla biten bir tanımlayıcı oluşturacaktır. Aaynısını yapar, ancak çözüm işaretleyicisinin ikinci kısmı için kullanılan ve işlev işaretçileri olan bir ön ek ile. Bu tür dört göstericiyi ilan ediyoruz:

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

Bunlar global değişkenler olarak bildirildiğinden, uygun olarak NULL olarak ayarlanır. Daha sonra, her blok satır aşağıdaki kod parçasına sahip olacaktır:

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

Bu, önce bir işlevi bildirir, o işlevi işaret etmek için uygun işlev işaretçisini tanımlar (yalnızca bir kez globaller tanımlayabiliriz, ancak önceki bildirim NULL olarak başlatılsa bile) bir tanım olarak sayılmaz ve ardından fiili tanımlanır. işlevi. Bu main()NULL olmayan herhangi bir fonksiyon göstergesini çağırmanıza izin verir (a17 asla NULL olmayacaktır):

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

Bunu yapmak rdaha sonra dizgiler tablosunda aranan dizgeyi oluşturacaktır ve bulunursa uygun harf çıktısı alınacaktır .

Geriye kalan tek hile, belirsizlikten kaçınılması veya örtüşen dizgilerin birleştirilmesiyle eşleşecek dizelerin listesinin kısaltılmasıdır.

Burada L v2 örneği


6

x86 opcode (.com), 86 82 bayt

Tester:

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  

Kaynak:

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:

İnit AX = 0, SI = 100, BX = 0 Referansı olan win7dos'ta çalıştırın.


Desteklenen ortamların sayısını bir miktar azaltma konusunda rahatsanız, SI = 100h varsayabilir mov bx, 100hve başlangıçta bırakarak 3 bayttan tasarruf etmek için indeksleme için BX yerine bu kaydı kullanabilirsiniz .
gastropner

@gastropner Yapıldı ve farketmediğim bir noktayı düzeltti
l4m2
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.