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 constructor
Attribut, 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, x
wobei 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+++b
vs. 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 F
wird eine Kennung erstellt, deren Name mit einem f beginnt und mit der Zeilennummer endet. A
tut 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