Entschärfen wir sie.
Einrücken:
main(_) {
_^448 && main(-~_);
putchar(--_%64
? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
: 10);
}
Einführung von Variablen, um dieses Durcheinander zu entwirren:
main(int i) {
if(i^448)
main(-~i);
if(--i % 64) {
char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
} else {
putchar(10); // newline
}
}
Beachten Sie, dass -~i == i+1
wegen des Zweierkomplements. Daher haben wir
main(int i) {
if(i != 448)
main(i+1);
i--;
if(i % 64 == 0) {
putchar('\n');
} else {
char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
}
}
Beachten Sie nun, dass a[b]
ist dasselbe wie b[a]
und wenden Sie die -~ == 1+
wieder ändern:
main(int i) {
if(i != 448)
main(i+1);
i--;
if(i % 64 == 0) {
putchar('\n');
} else {
char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
}
}
Die Rekursion in eine Schleife umwandeln und ein wenig mehr Vereinfachung einbauen:
// please don't pass any command-line arguments
main() {
int i;
for(i=447; i>=0; i--) {
if(i % 64 == 0) {
putchar('\n');
} else {
char t = __TIME__[7 - i/8%8];
char a = ">'txiZ^(~z?"[t - 48] + 1;
int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
if((i & 2) == 0)
shift /= 8;
shift = shift % 8;
char b = a >> shift;
putchar(32 | (b & 1));
}
}
}
Dabei wird pro Iteration ein Zeichen ausgegeben. Nach jedem 64. Zeichen wird ein Zeilenumbruch ausgegeben. Andernfalls werden zwei Datentabellen verwendet, um herauszufinden, was ausgegeben werden soll, und es wird entweder Zeichen 32 (ein Leerzeichen) oder Zeichen 33 (ein !
). Die erste Tabelle ( ">'txiZ^(~z?"
) ist ein Satz von 10 Bitmaps, die das Aussehen der einzelnen Zeichen beschreiben, und die zweite Tabelle ( ";;;====~$::199"
) wählt das entsprechende Bit in der Bitmap aus, das angezeigt werden soll.
Die zweite Tabelle
Betrachten wir zunächst die zweite Tabelle, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
. i/64
ist die Zeilennummer (6 bis 0) und i*2&8
ist 8, wenn i
ist 4, 5, 6 oder 7 mod 8.
if((i & 2) == 0) shift /= 8; shift = shift % 8
wählt entweder die hohe Oktalziffer (für i%8
= 0,1,4,5) oder die niedrige Oktalziffer (für i%8
= 2,3,6,7) des Tabellenwertes. Die Verschiebungstabelle sieht am Ende wie folgt aus:
row col val
6 6-7 0
6 4-5 0
6 2-3 5
6 0-1 7
5 6-7 1
5 4-5 7
5 2-3 5
5 0-1 7
4 6-7 1
4 4-5 7
4 2-3 5
4 0-1 7
3 6-7 1
3 4-5 6
3 2-3 5
3 0-1 7
2 6-7 2
2 4-5 7
2 2-3 3
2 0-1 7
1 6-7 2
1 4-5 7
1 2-3 3
1 0-1 7
0 6-7 4
0 4-5 4
0 2-3 3
0 0-1 7
oder in tabellarischer Form
00005577
11775577
11775577
11665577
22773377
22773377
44443377
Beachten Sie, dass der Autor für die ersten beiden Tabelleneinträge den Nullterminator verwendet hat (raffiniert!).
Diese ist nach dem Vorbild einer Sieben-Segment-Anzeige gestaltet, mit 7
s als Leerzeichen. Die Einträge in der ersten Tabelle müssen also die Segmente definieren, die beleuchtet werden.
Die erste Tabelle
__TIME__
ist ein spezielles, vom Präprozessor definiertes Makro. Es expandiert zu einer String-Konstante, die die Zeit enthält, zu der der Präprozessor ausgeführt wurde, und zwar in der Form "HH:MM:SS"
. Beachten Sie, dass er genau 8 Zeichen enthält. Beachten Sie, dass 0-9 die ASCII-Werte 48 bis 57 und :
hat den ASCII-Wert 58. Die Ausgabe umfasst 64 Zeichen pro Zeile, so dass 8 Zeichen pro Zeichen von __TIME__
.
7 - i/8%8
ist somit der Index von __TIME__
die gerade ausgegeben wird (die 7-
wird benötigt, weil wir eine Iteration durchführen i
nach unten). Also, t
ist der Charakter von __TIME__
ausgegeben werden.
a
ergibt je nach Eingabe den folgenden Binärwert t
:
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
Jede Zahl ist eine Bitmap die die Segmente beschreiben, die in unserer Sieben-Segment-Anzeige aufleuchten. Da es sich bei den Zeichen um 7-Bit-ASCII-Zeichen handelt, wird das High-Bit immer gelöscht. Somit, 7
in der Segmenttabelle wird immer als Leerzeichen gedruckt. Die zweite Tabelle sieht so aus, mit dem 7
s als Leerzeichen:
000055
11 55
11 55
116655
22 33
22 33
444433
So zum Beispiel, 4
es 01101010
(die Bits 1, 3, 5 und 6 sind gesetzt), die wie folgt ausgedruckt wird
----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
Um zu zeigen, dass wir den Code wirklich verstehen, passen wir die Ausgabe mit dieser Tabelle ein wenig an:
00
11 55
11 55
66
22 33
22 33
44
Dies wird verschlüsselt als "?;;?==? '::799\x07"
. Zu künstlerischen Zwecken fügen wir zu einigen der Zeichen 64 hinzu (da nur die niedrigen 6 Bits verwendet werden, wirkt sich dies nicht auf die Ausgabe aus); dies ergibt "?{{?}}?gg::799G"
(Beachten Sie, dass das 8. Zeichen unbenutzt ist, so dass wir es eigentlich beliebig gestalten können). Einfügen unserer neuen Tabelle in den ursprünglichen Code:
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
erhalten wir
!! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
wie wir es erwartet hatten. Es sieht nicht so solide aus wie das Original, was erklärt, warum der Autor sich für die Tabelle entschieden hat, die er verwendet hat.