Ответ 1
Пусть де-обфускация.
Отступы:
main(_) {
_^448 && main(-~_);
putchar(--_%64
? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
: 10);
}
Представляем переменные, чтобы распутать этот беспорядок:
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
}
}
Обратите внимание, что -~i == i+1
из-за двойного дополнения. Поэтому имеем
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));
}
}
Теперь обратите внимание, что a[b]
совпадает с b[a]
и снова применяет изменение -~ == 1+
:
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));
}
}
Преобразование рекурсии в цикл и кратковременное упрощение:
// 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));
}
}
}
Это выводит один символ на итерацию. Каждый 64-й символ выводит новую строку. В противном случае он использует пару таблиц данных, чтобы выяснить, что выводить, и помещает либо символ 32 (пробел), либо символ 33 (a !
). Первая таблица (">'txiZ^(~z?"
) представляет собой набор из 10 растровых изображений, описывающих внешний вид каждого символа, а вторая таблица (";;;====~$::199"
) выбирает соответствующий бит для отображения из растрового изображения.
Вторая таблица
Начнем с изучения второй таблицы int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
. i/64
- номер строки (от 6 до 0) и i*2&8
равен 8, если f i
равно 4, 5, 6 или 7 mod 8.
if((i & 2) == 0) shift /= 8; shift = shift % 8
выбирает либо восьмеричную цифру (для i%8
= 0,1,4,5), либо нижнюю восьмеричную цифру (для i%8
= 2,3,6,7) значения таблицы. Таблица сдвига выглядит следующим образом:
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
или в табличной форме
00005577
11775577
11775577
11665577
22773377
22773377
44443377
Обратите внимание, что автор использовал нулевой ограничитель для первых двух записей таблицы (подлый!).
Это спроектировано после семисегментного дисплея с 7
как пробелы. Таким образом, записи в первой таблице должны определять загораемые сегменты.
Первая таблица
__TIME__
- это специальный макрос, определенный препроцессором. Он расширяется до строковой константы, содержащей время, в которое выполнялся препроцессор, в форме "HH:MM:SS"
. Обратите внимание, что он содержит ровно 8 символов. Обратите внимание, что 0-9 имеют значения ASCII с 48 по 57, а :
имеет значение ASCII 58. Выходной файл составляет 64 символа на строку, поэтому он оставляет 8 символов на символ __TIME__
.
7 - i/8%8
является, таким образом, индексом __TIME__
, который в настоящее время выводится (требуется 7-
, потому что мы итерируем i
вниз). Таким образом, t
выводится символ __TIME__
.
a
заканчивается в двоичном выражении следующим образом: в зависимости от входа t
:
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
Каждый номер представляет собой растровое изображение, описывающее сегменты, которые освещаются на нашем семисегментном дисплее. Так как символы все 7-бит ASCII, высокий бит всегда очищается. Таким образом, 7
в таблице сегментов всегда печатает как пробел. Вторая таблица выглядит так: 7
как пробелы:
000055
11 55
11 55
116655
22 33
22 33
444433
Итак, например, 4
- это 01101010
(набор бит 1, 3, 5 и 6), который печатается как
----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
Чтобы показать, что мы действительно понимаем код, пусть немного отрегулирует вывод этой таблицы:
00
11 55
11 55
66
22 33
22 33
44
Это кодируется как "?;;?==? '::799\x07"
. Для художественных целей мы добавим 64 к нескольким символам (поскольку используются только младшие 6 бит, это не повлияет на вывод); это дает "?{{?}}?gg::799G"
(обратите внимание, что 8-й символ не используется, поэтому мы можем сделать все, что захотим). Поместите нашу новую таблицу в исходный код:
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
получаем
!! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
как мы и ожидали. Это не так твердо, как оригинал, что объясняет, почему автор решил использовать таблицу, которую он сделал.