Gizli C Kodu Yarışması 2006. Lütfen sykes2.c'yi açıklayınız.


975

Bu C programı nasıl çalışır?

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

Olduğu gibi derlenir (üzerinde test edilmiştir gcc 4.6.3). Derlendiğinde zamanı yazdırır. Sistemimde:

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

Kaynak: sykes2 - Bir satırda bir saat , sykes2 yazar ipuçları

Bazı ipuçları: Varsayılan olarak derleme uyarısı yoktur. Derlendiğinde -Wall, aşağıdaki uyarılar gönderilir:

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]

6
Hata ayıklama: Baskıların printf("%d", _);başına ekleme main: pastebin.com/HHhXAYdJ
corny

Tamsayı, türlenmemiş her değişken varsayılan olarakint
drahnr

18
İpucunu okudun mu? ioccc.org/2006/sykes2/hint.text
nhahtdh


Eğer böyle çalıştırırsanız çöküyor:./a.out $(seq 0 447)
SS Anne

Yanıtlar:


1819

Bunu gizleyelim.

Hizalama:

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

Bu karışıklığı çözmek için değişkenlerle tanışın:

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İki tamamlayıcı nedeniyle unutmayın . Bu nedenle,

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

Şimdi, bununla a[b]aynıb[a] olduğuna dikkat edin ve -~ == 1+değişikliği tekrar uygulayın :

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

Özyinelemeyi bir döngüye dönüştürme ve biraz daha basit bir şekilde gizleme:

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

Bu, yineleme başına bir karakter çıkarır. Her 64 karakterde bir satırsonu çıkar. Aksi takdirde, neyin verileceğini anlamak için bir çift veri tablosu kullanır ve karakter 32 (boşluk) veya karakter 33 (a !) koyar . İlk tablo ( ">'txiZ^(~z?"), her karakterin görünümünü açıklayan 10 bitmap'lerden oluşan bir kümedir ve ikinci tablo ( ";;;====~$::199"), bitmapten görüntülemek için uygun biti seçer.

İkinci tablo

İkinci tabloyu inceleyerek başlayalım int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. i/64satır numarası (6 ila 0) ve i*2&88 iff i, 4, 5, 6 veya 7 mod 8'dir.

if((i & 2) == 0) shift /= 8; shift = shift % 8tablo değerinin yüksek sekizlik basamağını ( i%8= 0,1,4,5 için) veya düşük sekizli basamağını ( i%8= 2,3,6,7 için) seçer . Vardiya tablosu şöyle görünür:

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

veya tablo şeklinde

00005577
11775577
11775577
11665577
22773377
22773377
44443377

Yazarın ilk iki tablo girdisi için null sonlandırıcı kullandığını unutmayın (gizli!).

Bu, yedi segmentli bir ekrandan sonra, 7boşluklar olarak tasarlanmıştır. Bu nedenle, ilk tablodaki girdiler yanan segmentleri tanımlamalıdır.

İlk tablo

__TIME__önişlemci tarafından tanımlanan özel bir makrodur. Ön işlemcinin çalıştırıldığı zamanı içeren bir dize sabitine genişler "HH:MM:SS". Tam olarak 8 karakter içerdiğine dikkat edin. 0-9'un ASCII değerleri 48 ila 57 ve :ASCII değeri 58 olduğunu unutmayın. Çıktı satır başına 64 karakterdir, böylece karakter başına 8 karakter bırakır __TIME__.

7 - i/8%8dolayısıyla __TIME__, şu anda çıktısı alınmakta olan endeksidir ( aşağı doğru 7-tekrarladığımız için gereklidir i). Yani, çıktı olmanın tkarakteri __TIME__.

agirişe bağlı olarak ikili olarak aşağıdakine eşit olur t:

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

Her sayı, yedi bölümlü ekranımızda yanan bölümleri açıklayan bir bitmaptir . Karakterlerin hepsi 7 bit ASCII olduğundan, yüksek bit her zaman temizlenir. Böylece, 7segment tablosunda her zaman boş olarak yazdırılır. İkinci tablo 7boşluklar olarak s ile şöyle görünür :

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

Bu nedenle, örneğin, 4bir 01101010(bit 1, 3, 5, ve 6, set), baskılar olarak

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

Kodu gerçekten anladığımızı göstermek için, bu tabloyla çıktıyı biraz ayarlayalım:

  00  
11  55
11  55
  66  
22  33
22  33
  44

Bu olarak kodlanır "?;;?==? '::799\x07". Sanatsal amaçlar için, birkaç karaktere 64 ekleyeceğiz (sadece düşük 6 bit kullanıldığından, bu çıktıyı etkilemez); bu verir "?{{?}}?gg::799G"(8. karakterin kullanılmadığına dikkat edin, böylece aslında istediğimizi yapabiliriz). Yeni masamızı orijinal koda koyarak:

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

aldık

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

tam beklediğimiz gibi. Orijinal kadar sağlam görünmüyor, bu da yazarın neden yaptığı masayı kullanmayı seçtiğini açıklıyor.


2
@drahnr - Teknik olarak hem bir *(dereference) hem de bir +: P
detly

18
@ АртёмЦарионов: Yaklaşık 30 dakika, ama geri geliyorum ve bunu biraz düzenliyorum. C'yi çok kullanıyorum ve daha önce kişisel ilgi için birkaç IOCCC deobfüzyonu yaptım (son yaptığım, sadece kişisel ilgi için, bu güzel ışın izleyiciydi ). Nasıl çalıştığını sormak isterseniz, mecbur olmaktan memnuniyet duyarım;)
nneonneo

5
@ АртёмЦарионов: yaklaşık bir gün IIRC (ayrıca raytracer geometrisini anlamak için harcanan zamanı sayar). Bu program da çok zekidir, çünkü anahtar kelime kullanmaz .
nneonneo

178
C. montaj dilinin okunabilirliği ile birleştirilen montaj dilinin tüm gücü
wim

6
Bu bağlamda daha fazla bilgi için Don Libes tarafından yazılan “Gizli C ve Diğer Gizemler” e bakınız. Gizli C Yarışması girişlerini analiz ederek C tekniklerini öğretir.
Chris N

102

Bunu daha kolay okumak için biçimlendirelim:

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

Yani, argüman olmadan çalıştırmak, _ (geleneksel olarak argc) 'dir 1. main()özyinelemeli olarak kendisini -(~_)(negatif bitsel DEĞİL _) sonucunu geçerek kendisini çağıracaktır , bu yüzden gerçekten 448 özyinelemelere gidecektir (Sadece koşul nerede _^448 == 0).

Bunu alarak, 7 64 karakter genişliğinde çizgi (dış üçlü koşul ve 448/64 == 7) yazdıracaktır . Şimdi biraz daha temiz yazalım:

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

Şimdi, 32ASCII alanı için ondalık. Bir boşluk veya bir '!' (33 '!', Dolayısıyla sonunda ' &1'). Ortadaki lekeye odaklanalım:

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

Başka bir afişin söylediği gibi, __TIME__program için derleme zamanıdır ve bir dizedir, bu nedenle bir dizi alt dizinin çift yönlü olmasının avantajlarından yararlanmanın yanı sıra bazı dize aritmetiği devam eder: a [b], b [a] ile aynıdır karakter dizileri için.

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

Bu, içindeki ilk 8 karakterden birini seçecektir __TIME__. Bu daha sonra dizine eklenir [">'txiZ^(~z?"-48](0-9 karakter 48-57 ondalık sayıdır). Bu dizedeki karakterlerin ASCII değerleri için seçilmiş olması gerekir. Aynı karakter ASCII kod manipülasyonu, bir '' veya '!' karakterin glifindeki konuma bağlı olarak.


49

Diğer çözümler ekleme, -~xeşittir x+1, çünkü ~xeşdeğerdir (0xffffffff-x). Bu eşittir (-1-x)yüzden, 2s tamamlayıcı -~xolduğunu -(-1-x) = x+1.


5
İlginç. Bir süredir biliyorum ~ x == -x - 1, ama arkasındaki matematiksel akıl yürütmeyi bilmiyordum.
ApproachingDarknessFish

3
Ey, Cole, (-1-x) (-x-1) ile aynıdır, "düzeltmeniz" gerekmez!
Thomas Song

7
Birisi -1338 ise, o zaman 1337 DEĞİLDİR
Andrew Mao

4

Modulo aritmetiği olabildiğince gizledim ve tekrarlamayı kaldırdım

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

Biraz daha genişletmek:

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');
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.