Contest C Code Contested 2006. Tolong jelaskan sykes2.c

975

Bagaimana cara kerja program C ini?

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

Ini mengkompilasi sebagaimana adanya (diuji pada gcc 4.6.3). Ini mencetak waktu ketika dikompilasi. Di sistem saya:

    !!  !!!!!!              !!  !!!!!!              !!  !!!!!! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!! 
    !!      !!              !!      !!              !!  !!  !! 
    !!      !!              !!      !!              !!  !!  !! 
    !!  !!!!!!              !!      !!              !!  !!!!!!

Sumber: sykes2 - Jam dalam satu baris , petunjuk penulis sykes2

Beberapa petunjuk: Tidak ada kompilasi peringatan per default. Dikompilasi dengan -Wall, peringatan berikut dikirimkan:

sykes2.c:1:1: warning: return type defaults to int [-Wreturn-type]
sykes2.c: In function main’:
sykes2.c:1:14: warning: value computed is not used [-Wunused-value]
sykes2.c:1:1: warning: implicit declaration of function putchar [-Wimplicit-function-declaration]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: control reaches end of non-void function [-Wreturn-type]
dangkal
sumber
6
Debug: Menambahkan printf("%d", _);ke awal maincetakan: pastebin.com/HHhXAYdJ
corny
Integer, setiap variabel untyped default keint
drahnr
18
Sudahkah Anda membaca petunjuknya? ioccc.org/2006/sykes2/hint.text
nhahtdh
Jika Anda menjalankannya seperti ini, crash:./a.out $(seq 0 447)
SS Anne

Jawaban:

1819

Mari kita hapuskannya.

Indentasi:

main(_) {
    _^448 && main(-~_);
    putchar(--_%64
        ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
        : 10);
}

Memperkenalkan variabel untuk mengurai kekacauan ini:

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

Perhatikan bahwa -~i == i+1karena dua-pelengkap. Karena itu, kita punya

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));
    }
}

Sekarang, perhatikan bahwa a[b]itu sama denganb[a] , dan terapkan -~ == 1+perubahan itu lagi:

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));
    }
}

Mengubah rekursi menjadi loop dan menyelinap dengan sedikit penyederhanaan:

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

Ini menghasilkan satu karakter per iterasi. Setiap karakter ke-64, itu menghasilkan baris baru. Kalau tidak, ia menggunakan sepasang tabel data untuk mencari tahu apa yang akan dihasilkan, dan menempatkan karakter 32 (spasi) atau karakter 33 (a !). Tabel pertama ( ">'txiZ^(~z?") adalah satu set 10 bitmap yang menggambarkan penampilan setiap karakter, dan tabel kedua ( ";;;====~$::199") memilih bit yang sesuai untuk ditampilkan dari bitmap.

Tabel kedua

Mari kita mulai dengan memeriksa tabel kedua int shift = ";;;====~$::199"[(i*2&8) | (i/64)];,. i/64adalah nomor baris (6 hingga 0) dan i*2&88 iff iadalah 4, 5, 6 atau 7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8memilih digit oktal tinggi (untuk i%8= 0,1,4,5) atau digit oktal rendah (untuk i%8= 2,3,6,7) dari nilai tabel. Tabel shift akhirnya tampak seperti ini:

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

atau dalam bentuk tabel

00005577
11775577
11775577
11665577
22773377
22773377
44443377

Perhatikan bahwa penulis menggunakan terminator nol untuk dua entri tabel pertama (licik!).

Ini dirancang setelah tampilan tujuh segmen, dengan 7s sebagai blank. Jadi, entri dalam tabel pertama harus menentukan segmen yang menyala.

Tabel pertama

__TIME__adalah makro khusus yang didefinisikan oleh preprocessor. Itu mengembang ke konstanta string yang berisi waktu di mana preprocessor dijalankan, dalam bentuk "HH:MM:SS". Perhatikan bahwa itu berisi persis 8 karakter. Perhatikan bahwa 0-9 memiliki nilai ASCII 48 hingga 57 dan :memiliki nilai ASCII 58. Outputnya adalah 64 karakter per baris, sehingga menyisakan 8 karakter per karakter __TIME__.

7 - i/8%8dengan demikian indeks __TIME__yang saat ini sedang output ( 7-diperlukan karena kita beralih ike bawah). Jadi, tadalah karakter __TIME__sebagai keluaran.

aakhirnya sama dengan yang berikut dalam biner, tergantung pada input t:

0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000

Setiap angka adalah bitmap yang menggambarkan segmen yang menyala di layar tujuh segmen kami. Karena semua karakter 7-bit ASCII, bit tinggi selalu dihapus. Dengan demikian, 7dalam tabel segmen selalu dicetak sebagai kosong. Tabel kedua terlihat seperti ini dengan 7s sebagai blank:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

Jadi, misalnya, 4adalah 01101010(bit 1, 3, 5, dan 6 set), yang dicetak sebagai

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--

Untuk memperlihatkan bahwa kami benar-benar memahami kode, mari sesuaikan outputnya sedikit dengan tabel ini:

  00  
11  55
11  55
  66  
22  33
22  33
  44

Ini dikodekan sebagai "?;;?==? '::799\x07". Untuk tujuan artistik, kami akan menambahkan 64 ke beberapa karakter (karena hanya 6 bit rendah yang digunakan, ini tidak akan mempengaruhi output); ini memberi "?{{?}}?gg::799G"(perhatikan bahwa karakter ke-8 tidak digunakan, sehingga kita dapat benar-benar membuatnya menjadi apa pun yang kita inginkan). Menempatkan tabel baru kami di kode asli:

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

kita mendapatkan

          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   

seperti yang kami harapkan. Ini tidak sekokoh aslinya, yang menjelaskan mengapa penulis memilih untuk menggunakan tabel yang dia lakukan.

nneonneo
sumber
2
@drahnr - Secara teknis ini adalah *(dereferensi) dan +: P
detly
18
@ АртёмЦарионов: Sekitar 30 menit, tapi saya telah kembali dan mengeditnya dengan adil. Saya banyak menggunakan C, dan saya telah melakukan beberapa deobfusi IOCCC untuk kepentingan pribadi sebelumnya (yang terakhir saya lakukan, hanya untuk kepentingan pribadi, adalah raytracer yang indah ini ). Jika Anda ingin bertanya bagaimana cara kerjanya, saya akan dengan senang hati
mematuhinya
5
@ АртёмЦарионов: sekitar satu hari IIRC (juga menghitung waktu yang dihabiskan untuk memahami geometri raytracer). Program itu juga sangat pintar, karena tidak menggunakan kata kunci .
nneonneo
178
C .. semua kekuatan bahasa majelis dikombinasikan dengan keterbacaan bahasa majelis
wim
6
Untuk informasi lebih lanjut dalam uraian ini, lihat “C yang dikaburkan dan Misteri Lain”, oleh Don Libes. Ini mengajarkan teknik C dengan menganalisis entri Kontes C yang dikaburkan.
Chris N
102

Mari format ini agar lebih mudah dibaca:

main(_){
  _^448&&main(-~_);
  putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}

Jadi, menjalankannya tanpa argumen, _ (argc secara konvensional) adalah 1. main()akan secara rekursif memanggil dirinya sendiri, melewati hasil -(~_)(bitwise negatif BUKAN _), jadi benar-benar akan 448 rekursi (Hanya kondisi di mana _^448 == 0).

Mengambil itu, itu akan mencetak 7 garis lebar 64 karakter (kondisi terner luar, dan 448/64 == 7). Jadi mari kita tulis ulang sedikit lebih bersih:

main(int argc) {
  if (argc^448) main(-(~argc));
  if (argc % 64) {
    putchar((32|-(~7[__TIME__-argc/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[argc*2&8|argc/64]/(argc&2?1:8)%8&1));
  } else putchar('\n');
}

Sekarang, 32adalah desimal untuk ruang ASCII. Itu bisa mencetak spasi atau '!' (33 adalah '!', Maka ' &1' pada akhirnya). Mari kita fokus pada gumpalan di tengah:

-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >>
     (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8

Seperti yang dikatakan poster lain, __TIME__adalah waktu kompilasi untuk program, dan merupakan string, jadi ada beberapa aritmatika string yang terjadi, serta mengambil keuntungan dari array subscript menjadi dua arah: a [b] sama dengan b [a] untuk array karakter.

7[__TIME__ - (argc/8)%8]

Ini akan memilih salah satu dari 8 karakter pertama di __TIME__. Ini kemudian diindeks menjadi [">'txiZ^(~z?"-48](0-9 karakter adalah 48-57 desimal). Karakter dalam string ini harus dipilih karena nilai ASCII mereka. Manipulasi kode ASCII karakter yang sama ini berlanjut melalui ekspresi, untuk menghasilkan pencetakan '' atau '!' tergantung pada lokasi dalam mesin terbang karakter.

chmeee
sumber
49

Menambahkan ke solusi lain, -~xsama dengan x+1karena ~xsama dengan (0xffffffff-x). Ini sama dengan (-1-x)komplemen 2s, begitu -~xjuga -(-1-x) = x+1.

Thomas Song
sumber
5
Menarik. Saya sudah tahu bahwa ~ x == -x - 1, tetapi saya tidak tahu alasan matematika di baliknya.
ApproachingDarknessFish
3
Ey, Cole, (-1-x) sama dengan (-x-1), Anda tidak perlu "memperbaikinya" !!
Thomas Song
7
Alasan yang sama mengapa jika seseorang adalah -1338, maka mereka BUKAN 1337.
Andrew Mao
4

Saya mengurangi modulo arithmetics sebanyak yang saya bisa dan menghilangkan rekursi

int pixelX, line, digit ;
for(line=6; line >= 0; line--){
  for (digit =0; digit<8; digit++){
    for(pixelX=7;pixelX > 0; pixelX--){ 
        putchar(' '| 1 + ">'txiZ^(~z?"["12:34:56"[digit]-'0'] >> 
          (";;;====~$::199"[pixel*2 & 8  | line] / (pixelX&2 ? 1 : 8) ) % 8 & 1);               
    }
  }
  putchar('\n');
}

Memperluasnya sedikit lagi:

int pixelX, line, digit, shift;
char shiftChar;
for(line=6; line >= 0; line--){
    for (digit =0; digit<8; digit++){
        for(pixelX=7;pixelX >= 0; pixelX--){ 
            shiftChar = ";;;====~$::199"[pixelX*2 & 8 | line];
            if (pixelX & 2)
                shift = shiftChar & 7;
            else
                shift = shiftChar >> 3;     
            putchar(' '| (">'txiZ^(~z?"["12:34:56"[digit]-'0'] + 1) >> shift & 1 );
        }

    }
    putchar('\n');
}
Lefteris E
sumber