Tetris-Bausteine ​​programmieren (wörtlich)


33

Im Spiel Tetris gibt es 7 Arten von Ziegeln oder Tetr i Minoes , die als mathematisch bekannt sind tetr o Minoes , weil sie alle gemacht mit 4 quadratischen Segmenten sind:

Tetris-Steine

Sie haben die Namen I, J, L, O, S, T und Z, die ihren ungefähren Formen entsprechen. Bei 90 ° -Drehungen gibt es insgesamt 19 einzigartige Formen:

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

Herausforderung

Schreiben Sie einen rechteckigen Codeblock, der als Basissegment fungiert, aus dem diese 19 Formen bestehen. Wenn dieser Code in einer der Formen angeordnet ist, sollte ein Programm erstellt werden, das den einzelnen Großbuchstaben ausgibt, der dieser Form zugeordnet ist. Dies muss für alle 19 Formen funktionieren.

Die führenden leeren Bereiche in einigen der 19 Formen sind vollständig mit Leerzeichen ausgefüllt ( ). Die nachgestellten leeren Bereiche werden mit nichts gefüllt (daher sind die Programme nicht immer genau rechteckig).

Beispiel

Angenommen, dies wäre Ihr Codeblock:

ABC
123

Dann wäre jede Anordnung des Blocks in dem S-Tetris-Stück ein Programm, das Folgendes ausgibt S:

   ABCABC
   123123
ABCABC
123123

ABC
123
ABCABC
123123
   ABC
   123

(Beachten Sie, dass alle führenden Leerzeichen mit Leerzeichen gefüllt sind und dass keine Zeilen nachgestellte Leerzeichen enthalten.)

Die gleiche Idee gilt für alle 6 anderen Teile und ihre jeweiligen Rotationen.

Anmerkungen

  • Alle 19 endgültigen Programme müssen in derselben Programmiersprache ausgeführt werden.
  • Falls gewünscht, können Sie allen Programmen (nicht nur einigen, allen oder keinen) eine einzige nachgestellte Zeile hinzufügen .
  • Ihr Codeblock kann beliebige Zeichen (einschließlich Leerzeichen) enthalten, die keine Zeilenabschlusszeichen sind .
  • Geben Sie den Brief mit einem optionalen Zeilenumbruch an stdout (oder an die nächstgelegene Alternative Ihrer Sprache) aus.

Wertung

Die Einreichung, deren Codeblock die kleinste Fläche (Breite mal Höhe) hat, gewinnt. Dies bedeutet im Wesentlichen, dass der kürzeste Code gewinnt, weshalb dies als . Tiebreaker geht zu der am höchsten bewerteten Antwort.

Das ABC\n123Beispiel hat die Fläche 3 × 2 = 6.

Ausschnitt

Mit diesem Codeblock werden alle 19 Programme generiert:

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


Das Längen-Breiten-Verhältnis beträgt also 2 zu 3? Oder kann es eine andere Größe sein? Was muss das Programm mindestens tun? Angenommen, leere Programme zählen nicht, aber Programme, die nichts ausgeben, tun dies.
ASCIIThenANSI

@ASCIIThenANSI Jede Breite und Höhe sind in Ordnung. Ich stelle mir vor, dass etwas größer als 2 * 3 notwendig sein wird. Es gibt 19 Programme, eines für jede Anordnung des Blocks in eine der 19 verschiedenen Tetromino-Formen. Wenn eines dieser Programme ausgeführt wird, wird der entsprechende Tetris-Stückbuchstabe ausgegeben.
Calvins Hobbys

Wow! Was für eine großartige Herausforderung! Ist es wichtig, welche Sprache wir verwenden?
Theonlygusti

@theonlygusti Fast alle Fragen auf dieser Website lassen jede Sprache zu. Dies ist keine Ausnahme.
Calvins Hobbys

@ Calvin'sHobbies Ja, ich weiß; Ich habe das Snippet nur als Controller für das Ausführen von JavaScript-Antworten falsch interpretiert. Anscheinend ordnet es nur Codeblöcke.
Theonlygusti

Antworten:


16

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

Ich hatte vor, eine elegantere Lösung zu finden, aber irgendwie kam ich zu dem Ergebnis, das ziemlich brachial ist:

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; 

Es ist ziemlich einfach, es überprüft den Code in einem 3x3-Quadrat auf Text und verwendet die Ergebnisse, um festzustellen, welches Tetrimino der Form des Codes entspricht. Ich habe mir noch nicht viel Mühe gegeben, um Golf zu spielen.

Probieren Sie den Code hier aus (nachdem Sie das Snippet verwendet haben, um es wie ein Tetrimino zu formen)

Beispielcode in Form Z (v1) hier


14

C (gcc) , 26 × 20 = 520, 25 × 19 = 475, 23 × 17 = 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;}//

Kürzlich wurde ich über die Funktionsattribute von GNU informiert, und am interessantesten über das constructorAttribut, das eine knappe Implementierung dessen ermöglicht, was ich in meiner früheren Herangehensweise an dieses Problem auf rundere Weise getan habe.

Der Grundgedanke ist derselbe wie zuvor: Erstellen Sie eine Zeichenfolge und suchen Sie danach in einer Liste, um zu ermitteln, als welcher Tetris-Block der Code ausgelegt ist. Dies geschieht durch Aufrufen von Funktionen, von denen jede der Zeichenfolge ein Zeichen hinzufügt. Die Komplikation war und ist, dass die Anzahl der Funktionen variiert.

Wenn Sie eine Funktion mit definieren, attribute((constructor(x)))wird sie so main()eingegeben, dass die Funktion ausgeführt wird, bevor sie eingegeben wird, xwobei die Priorität optional ist (niedriger bedeutet, dass sie früher ausgeführt wird). Dadurch werden keine Funktionszeiger mehr benötigt, sodass wir ein Makro, einige Deklarationen und die Aufrufkette löschen können.

Die Verwendung __LINE__für die Priorität ist fraglich, da die Prioritätsstufen 0-100 reserviert sind. Dies führt jedoch nicht zu Fehlern, sondern nur zu Warnungen. Diese sind beim Golfen in Hülle und Fülle vorhanden. Was gibt es also noch mehr?

Es hätte geholfen, eine andere Spalte davon abzuhalten, Prioritäten überhaupt nicht zu verwenden, aber die Ausführungsreihenfolge scheint nicht definiert zu sein. (In diesem Fall sind sie umgekehrt, andere Tests sind jedoch nicht schlüssig.)

Beispiel für L v2 hier

Älterer, portablerer Ansatz

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

Eines meiner Lieblingsprobleme, das ich auf dieser Site gelöst habe.

Ich begann damit, dass jeder Block irgendwie seine eigenen Koordinaten erraten würde. Mit Zeilen ist es einfach __LINE__, und die Anzahl horizontal benachbarter Blöcke lässt sich mit der Länge eines String-Literals wie folgt ermitteln:

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

Nehmen Sie die Länge der resultierenden Zeichenfolge und dividieren Sie durch eine richtige Zahl und Sie haben die Breite. Leider ist jeder leere Raum vor dem Block durch diese Methode unsichtbar. Ich immer noch im Verdacht Strings wäre die Lösung, da Leerzeichen nur außerhalb von Strings ist sehr selten Sinn, in Dingen wie a+++bvs. a+ ++b. Ich habe kurz über so etwas nachgedacht, konnte mir aber nichts Sinnvolles einfallen lassen. Eine andere Möglichkeit wäre gewesen, Identifikatoren dort "zusammenkleben" zu lassen, wo sich Blöcke treffen:

A  BA  B

Es würde mich nicht wundern, wenn dies noch zu einer interessanten Lösung führen könnte.

Trotz seiner Einfachheit habe ich einige Zeit gebraucht, um die String-Lösung zu finden, die auf diesem Blockfragment basiert:

s=//
"  \
";//

Wenn das Fragment keine horizontalen Nachbarn hat, wird die neue Zeile in der zweiten Zeile durch den Backslash maskiert, wodurch eine Zeichenfolge der Länge 2 erstellt wird. Wenn es jedoch einen Nachbarn hat, wird der Backslash stattdessen am Zeilenanfang durch das Anführungszeichen maskiert 2 des nächsten Blocks:

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

Dadurch wird die Zeichenfolge "\" "mit der Länge 5 erstellt.

Dies ermöglicht insbesondere auch die Erkennung von Leerstellen vor dem Block:

    s=//
    "  \
    ";//

Wiederum wird die neue Zeile maskiert und das Leerzeichen des leeren Blocks links in die resultierende Zeichenfolge "" der Länge 6 eingefügt.

Insgesamt gibt es sieben verschiedene Konfigurationen von Blöcken in einer Reihe, über die wir uns Gedanken machen müssen, und alle bilden Zeichenfolgen mit einer einzigartigen Länge:

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

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

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

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

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

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

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

Die endgültigen Blöcke werden natürlich nicht so kurz sein, aber das Prinzip ist das gleiche, unabhängig von der Blockgröße. Dies hat auch den Vorteil, dass ein separater Mechanismus zum Erfassen der Breite nicht erforderlich ist. Durch Hinzufügen eines Zeichens, das der Länge dieser Zeichenfolge entspricht, zu einer Ergebniszeichenfolge ergibt jede der 19 Konfigurationen eine eindeutige Zeichenfolge, die erst dann mit einer geeigneten Liste verglichen werden muss, wenn alle Blöcke ausgeführt wurden.

Sobald dies geklärt war, bestand das nächste große Problem darin, jede Reihe von Blöcken zu "besuchen". In C beschränken wir uns sehr auf das, was außerhalb von Funktionen möglich ist. Wir müssen main()auch erscheinen, aber nur einmal. Letzteres ist für manche leicht zu erreichen #define, aber wenn der Code nachfolgender Blöcke innerhalb von sein soll main(), stellt sich die Frage, wann die letzte schließende geschweifte Klammer zu setzen ist. Schließlich wissen wir nicht, wie viele Blockreihen tatsächlich verwendet werden. Wir müssen also main()statisch und den Rest irgendwie dynamisch sein.

Wenn die anderen Blockzeilen in sich geschlossen sein sollen, müssen sie Funktionen sein, aber wir müssen sicherstellen, dass jede Funktion einen eindeutigen Namen hat und gleichzeitig vorhersehbar genug ist, um von dort aufgerufen werden zu können main(). Wir brauchen auch einen Mechanismus, um zu wissen, welche Funktionen tatsächlich aufgerufen werden müssen. Das Generieren eindeutiger Namen wird durch Hilfsmakros gelöst:

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

Durch Anrufen Fwird eine Kennung erstellt, deren Name mit einem f beginnt und mit der Zeilennummer endet. Atut das gleiche, aber mit einem als Präfix, das für den zweiten Teil der Lösung verwendet wird, der Funktionszeiger ist. Wir deklarieren vier solche Zeiger:

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

Da diese als globale Variablen deklariert sind, werden sie zweckmäßigerweise auf NULL gesetzt. Später hat jede Blockzeile den folgenden Code:

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

Dies deklariert zuerst eine Funktion, definiert den entsprechenden Funktionszeiger, der auf diese Funktion verweist (wir können nur einmal Globale definieren, aber die frühere Deklaration zählte nicht als Definition, auch wenn sie auf NULL initialisiert wurde) und definiert dann die tatsächliche Funktion. Auf diese Weise können Sie main()jeden Funktionszeiger aufrufen, der nicht NULL ist (a17 wird niemals NULL sein):

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

Auf diese Weise wird die Zeichenfolge erstellt r, die dann in der Tabelle der Zeichenfolgen gesucht wird. Wenn sie gefunden wird, wird der entsprechende Buchstabe ausgegeben.

Der einzige verbleibende Trick besteht darin, dass die Liste der zu vergleichenden Zeichenfolgen immer dann gekürzt wurde, wenn Mehrdeutigkeiten vermieden oder überlappende Zeichenfolgen zusammengeführt werden konnten.

Beispiel für L v2 hier


6

x86-Opcode (.com), 86 82 Byte

Prüfer:

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  

Quelle:

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:

Führen Sie in win7dos wo init AX = 0, SI = 100, BX = 0 Referenzen


Wenn Sie die Anzahl der unterstützten Umgebungen etwas reduzieren möchten, können Sie SI = 100h annehmen und dieses Register anstelle von BX für die Indizierung verwenden, um 3 Bytes zu sparen, indem Sie es mov bx, 100ham Anfang löschen .
Gastropner

@gastropner Erledigt und einen Punkt behoben, an dem ich nichts bemerkt habe
l4m2
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.