Désobscurcissons-le.
Indentation:
main(_) {
_^448 && main(-~_);
putchar(--_%64
? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
: 10);
}
Présentation de variables pour démêler ce gâchis:
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
}
}
Notez cela à -~i == i+1
cause du complément à deux. Par conséquent, nous avons
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));
}
}
Maintenant, notez que a[b]
c'est la même choseb[a]
et appliquez à -~ == 1+
nouveau la modification:
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));
}
}
Convertir la récursivité en boucle et se faufiler dans un peu plus de simplification:
// 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));
}
}
}
Cela génère un caractère par itération. Chaque 64ème caractère, il sort une nouvelle ligne. Sinon, il utilise une paire de tableaux de données pour déterminer ce qu'il faut sortir et met soit le caractère 32 (un espace) soit le caractère 33 (a !
). Le premier tableau ( ">'txiZ^(~z?"
) est un ensemble de 10 bitmaps décrivant l'apparence de chaque caractère, et le second tableau ( ";;;====~$::199"
) sélectionne le bit approprié à afficher dans le bitmap.
Le deuxième tableau
Commençons par examiner la deuxième table, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
. i/64
est le numéro de ligne (6 à 0) et i*2&8
est 8 ssi i
4, 5, 6 ou 7 mod 8.
if((i & 2) == 0) shift /= 8; shift = shift % 8
sélectionne soit le chiffre octal haut (pour i%8
= 0,1,4,5), soit le chiffre octal bas (pour i%8
= 2,3,6,7) de la valeur du tableau. La table de décalage finit par ressembler à ceci:
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
ou sous forme de tableau
00005577
11775577
11775577
11665577
22773377
22773377
44443377
Notez que l'auteur a utilisé le terminateur nul pour les deux premières entrées de table (sournois!).
Ceci est conçu après un affichage à sept segments, avec 7
s comme blancs. Ainsi, les entrées du premier tableau doivent définir les segments qui s'allument.
Le premier tableau
__TIME__
est une macro spéciale définie par le préprocesseur. Il se développe en une constante de chaîne contenant l'heure à laquelle le préprocesseur a été exécuté, sous la forme "HH:MM:SS"
. Notez qu'il contient exactement 8 caractères. Notez que 0-9 ont des valeurs ASCII 48 à 57 et :
une valeur ASCII 58. La sortie est de 64 caractères par ligne, ce qui laisse 8 caractères par caractère de __TIME__
.
7 - i/8%8
est donc l'indice de ce __TIME__
qui est actuellement en cours de sortie (le 7-
est nécessaire parce que nous itérons i
vers le bas). Donc, t
c'est le caractère d' __TIME__
être sorti.
a
finit par égaler ce qui suit en binaire, selon l'entrée t
:
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
Chaque numéro est un bitmap décrivant les segments qui sont allumés dans notre affichage à sept segments. Comme les caractères sont tous en ASCII 7 bits, le bit haut est toujours effacé. Ainsi, 7
dans la table des segments, l'impression est toujours vide. Le deuxième tableau ressemble à ceci avec le 7
s en blanc:
000055
11 55
11 55
116655
22 33
22 33
444433
Ainsi, par exemple, 4
est 01101010
(bits 1, 3, 5 et 6 définis), qui s'imprime comme
----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
Pour montrer que nous comprenons vraiment le code, ajustons un peu la sortie avec ce tableau:
00
11 55
11 55
66
22 33
22 33
44
Ceci est codé comme "?;;?==? '::799\x07"
. À des fins artistiques, nous ajouterons 64 à quelques-uns des caractères (puisque seuls les 6 bits les plus faibles sont utilisés, cela n'affectera pas la sortie); cela donne "?{{?}}?gg::799G"
(notez que le 8ème caractère n'est pas utilisé, nous pouvons donc en faire ce que nous voulons). Mettre notre nouvelle table dans le code d'origine:
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
on a
!! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
comme nous nous y attendions. Ce n'est pas aussi solide que l'original, ce qui explique pourquoi l'auteur a choisi d'utiliser le tableau qu'il a fait.
printf("%d", _);
au début desmain
impressions: pastebin.com/HHhXAYdJ