• All Objects
    • Life
    • Technic
    • entry
  • Tags
  • github
  • docker
  • go
  • git
  • wsl2
Life

난해한 Tic-Tac-Toe

2020. 6. 19. 23:56

오늘 받은 GitHub 뉴스레터에 Tic-Tac-Toe 애플리케이션이 트렌드에 올라왔길래, 뭐가 대단해서 올라온 거지? 한 줄에 구현하기라도 한 건가? 싶어서 코드를 봤는데... 

#include <stdio.h> 

#define N(a)       "%"#a"$hhn"
#define O(a,b)     "%10$"#a"d"N(b)
#define U          "%10$.*37$d"
#define G(a)       "%"#a"$s"
#define H(a,b)     G(a)G(b)
#define T(a)       a a 
#define s(a)       T(a)T(a)
#define A(a)       s(a)T(a)a
#define n(a)       A(a)a
#define D(a)       n(a)A(a)
#define C(a)       D(a)a
#define R          C(C(N(12)G(12)))
#define o(a,b,c)   C(H(a,a))D(G(a))C(H(b,b)G(b))n(G(b))O(32,c)R
#define SS         O(78,55)R "\n\033[2J\n%26$s";
#define E(a,b,c,d) H(a,b)G(c)O(253,11)R G(11)O(255,11)R H(11,d)N(d)O(253,35)R
#define S(a,b)     O(254,11)H(a,b)N(68)R G(68)O(255,68)N(12)H(12,68)G(67)N(67)

char* fmt = O(10,39)N(40)N(41)N(42)N(43)N(66)N(69)N(24)O(22,65)O(5,70)O(8,44)N(
            45)N(46)N    (47)N(48)N(    49)N( 50)N(     51)N(52)N(53    )O( 28,
            54)O(5,        55) O(2,    56)O(3,57)O(      4,58 )O(13,    73)O(4,
            71 )N(   72)O   (20,59    )N(60)N(61)N(       62)N (63)N    (64)R R
            E(1,2,   3,13   )E(4,    5,6,13)E(7,8,9        ,13)E(1,4    ,7,13)E
            (2,5,8,        13)E(    3,6,9,13)E(1,5,         9,13)E(3    ,5,7,13
            )E(14,15,    16,23)    E(17,18,19,23)E(          20, 21,    22,23)E
            (14,17,20,23)E(15,    18,21,23)E(16,19,    22     ,23)E(    14, 18,
            22,23)E(16,18,20,    23)R U O(255 ,38)R    G (     38)O(    255,36)
            R H(13,23)O(255,    11)R H(11,36) O(254    ,36)     R G(    36 ) O(
            255,36)R S(1,14    )S(2,15)S(3, 16)S(4,    17 )S     (5,    18)S(6,
            19)S(7,20)S(8,    21)S(9    ,22)H(13,23    )H(36,     67    )N(11)R
            G(11)""O(255,    25 )R        s(C(G(11)    ))n (G(          11) )G(
            11)N(54)R C(    "aa")   s(A(   G(25)))T    (G(25))N         (69)R o
            (14,1,26)o(    15, 2,   27)o   (16,3,28    )o( 17,4,        29)o(18
            ,5,30)o(19    ,6,31)o(        20,7,32)o    (21,8,33)o       (22 ,9,
            34)n(C(U)    )N( 68)R H(    36,13)G(23)    N(11)R C(D(      G(11)))
            D(G(11))G(68)N(68)R G(68)O(49,35)R H(13,23)G(67)N(11)R C(H(11,11)G(
            11))A(G(11))C(H(36,36)G(36))s(G(36))O(32,58)R C(D(G(36)))A(G(36))SS

#define arg d+6,d+8,d+10,d+12,d+14,d+16,d+18,d+20,d+22,0,d+46,d+52,d+48,d+24,d\
            +26,d+28,d+30,d+32,d+34,d+36,d+38,d+40,d+50,(scanf(d+126,d+4),d+(6\
            -2)+18*(1-d[2]%2)+d[4]*2),d,d+66,d+68,d+70, d+78,d+80,d+82,d+90,d+\
            92,d+94,d+97,d+54,d[2],d+2,d+71,d+77,d+83,d+89,d+95,d+72,d+73,d+74\
            ,d+75,d+76,d+84,d+85,d+86,d+87,d+88,d+100,d+101,d+96,d+102,d+99,d+\
            67,d+69,d+79,d+81,d+91,d+93,d+98,d+103,d+58,d+60,d+98,d+126,d+127,\
            d+128,d+129

char d[538] = {1,0,10,0,10};

int main() {
    while(*d) printf(fmt, arg);
}

이건.. 뭐 무슨 내용인지 읽기는 커녕, 이게 돌아가는 코드라고는 전혀 생각조차 안 하게 되고, %N을 그린 아스키 그림인 건가 싶기도 한 난해한 코드였습니다. 심지어 컴파일하고 실행하는 법까지 적혀있는 거 보니, 그제야 이게 실행되는 코드라고? 의문이 들 정도였으니까요. ( 그런데도 스타가 무려 1,4k 나 됩니다. ) 

https://github.com/carlini/printf-tac-toe#usage

README를 좀 더 읽고 나서야, 무슨 내용인지 이해가 갔는데, 위의 코드는 "국제 난독화 C 코드 콘테스트"(The International Obfuscated C Code Contest : IOCCC ) 참여를 위해 만든 코드라고 합니다. 올해 IOCCC는 5월 15일까지 소스코드 제출 마감이었고, 올해가 27번째 행사라고 합니다. 아마 위의 코드는 올해 대회에 참가한 후 GitHub에 푸시된 걸로 보입니다. 

 

IOCCC는 다음과 같이 소개되어있습니다. 

- 규칙안에서 가장 난해한 C 프로그램을 작성합니다.
- 반어적인 방식으로, 프로그래밍 스타일의 중요성을 보여줍니다. 
- 평소에 사용하지 않는 코드로 컴파일러를 힘들게 합니다. 
- C언의 미묘한 부분을 묘사합니다. 
- 열악한 코드에 안전한 포럼을 제공합니다. 

평소에 잘 사용하지 않는 방식으로 프로그램을 만들어 프로그래밍 스타일이 얼마나 중요한 것인지 역설적으로 보여주는 것이 목적인 대회인 것 같습니다. 올해 수상자는 아직 발표되지 않은 것 같아서, 다른 코드는 어떤가 하고 하나를 열어 봤는데.. 

#define j(n,r,i) A*n=R(r[i].a);//
#define E X("syntax error\n",1)//
 #define     d(n) A*n(A*x,A*y)//
  #define    k(d,v) D(d);e v;//
   #define  F X("fail\n",-1)//
     #define h(n) A*n(A*x)//
      #include <stdlib.h>//
    #define     u(n) n[0].l//
   #include        <stdio.h>//
    #define       w union A//
      #define   e return//
					             #define N 5//
					         #define q c=getc(p)//
					    #define o(i,v) [i]={[1].p=v},//
					  #define m(i,I,n) [i]={{I},[1].l=n},//
					#define g(n) A*r=C(&(A){.l=n});r[1].a=//
#define s(x,y,i,q,n,o) if(x[3].a){if(u(x)==rT||u(x)==rC){i;if(q)k(y,x)}g(n)y;o;r[3].a=x;e r;}//
//
void X(char*f,int r){ fputs(f,stderr);exit(r);}typedef w{w* (*t) (w*x);w* (*l) (w*x,w*y);w*a;long long p;}A;h(R) {x[N-1].p++;e x;}h(Ch) ;h(Y) ;h(fA) ;d(fD) ;d(fT) ;d(fW) ;d(fCh) ;d(rA2) ;d(fA2) ;d(rY2) ;d(Y2) ;d(rS2) ;d(fS22) ;d(rD) ;d(rE) ;d(rT) ;d(rC) ;void D(A*f) {if(f&&!--f[N-1].p) {if(u(f)!=fW&&u(f)!=fCh) D(f[1].a);if(u(f)!=fT) D(f[2].a);if(u(f)!=rE&&u(f)!=rT&&u(f)!=rC) D(f[3].a);free(f);}}int ch=EOF,c;FILE*p;A T[256][2];void U(void) {do{if(c==EOF) e;if(T[c][0].t) e;if(!T[c][1].p) E;if(T[c][1].p==4) for(/**??/
/
q;c!='\n'&&c!=EOF;q);q;}while(1);}A*I(void){if(c==EOF)E;A*f=T[c];if
(f[0].t!=Ch){q;U();}e f[0].t(&f[1]);}h(C){A*r=malloc(sizeof(A[N]));
if(!r) X("memory error\n",-2); r[0]=*x; for(int i=1;i<N;i++)r[i]=(A
){0};e R(r); }int   main(int x,const char**y){if(x!=2)F;p=fopen(y[1
],"rb");if(!p)F;q;   U();while(c!=EOF){A*f=I();while(f[3].a){if(u(f
)==rT){f=f[1].u(a)(   R(f[1].a),f);}else if(u(f)==rC){T[96][0].t=Y;
j(r,f,1) D(f); f=r;}   else{g(fT)f;r[2].p=ftell(p)+(c==EOF);f=u(f)(
R(f),r);}}D(f);}e 0;}   h(Ch){A*r=C(x);q;if(c==EOF)E;r[1].p=c;q;U()
;e r;}h(NL){A*r=C(x);r   [1].p='\n';e r;}d(rT){j(r,x,2)D(y);k(x,r)}
d(fD0){g(fD)y;k(x,r)}d(   fA1){s(y,x,,1,fA2,)e u(x)(x,y);}d(fA2){j(
a,x,1)j(b,x,3)k(x,fA1(a,   u(b)(b,y)))}d(rA1){s(y,x,,0,rA2,)if(u(y)
==fD0){g(fD)R(x[2].a);D(y   );k(x,r)}A*r=fA(x[2].a);k(x,fA1(y,r))}d
(rA2){j(f,x,1)j(a,x,3)k(x,   rA1(f,u(a)(a,y)))}h(fA){R(x);if(x[0].t
!=fA)e x;e rA1(x,fA(x[1].a)   );}d(fD1){s(x,y,,1,rD,)e u(x)(x,y);}d
(rD){j(a,x,3)j(b,x,1)k(x,fD1   (u(a)(a,y),b))}d(fD){A*a=fA(x[1].a);
k(x,fD1(a,y))}h(Yt){(void)x;A   *r=C(&(A){.t=fA});r[1].a=I();r[2].a
=I();e r;}h(YE){(void)x;k(I(),   I())}d(Y1){s(y,x,,1,Y2,)e u(x)(x,y
);}d(Y2){j(a,x,1)j(b,x,3)k(x,Y1   (a,u(b)(b,y)))}h(rY1){if(x[3].a){
if(u(x)==rT)e x;if(u(x)==rC){k(I   (),x)}A*r=C(&(A){.l=rY2});r[3].a
=x;e r;}if(u(x)!=fD0)e Y1(x,I()     );D(x);T[96][0].t=Yt;g(fD)I();T
[96][0].t=Y;e r;} d(rY2) {j(a,   x   ,3)k(x,rY1(u(a) (a,y)))}h(Y){(
void)x;e rY1(I());}d(fK1){j(r   ,x,   1)D(y);k(x,r)}d(fK0){g(fK1)y;
k(x,r)}d(fS21){s(y,x,,1,fS22   ,)e u   (x)(x,y);}d(fS22){j(a,x,1)j(
b,x,3)k(x,fS21(a,u(b)(b,y))   )}A*rS1   (A*x,A*y,A*z){s(y,x,D(z),1,
rS2,r[2].a=z)if(u(y)==fD0)   {g(fD)C(&   (A){.t=fA});r[1].a[1].a=R(
x[2].a);r[1].a[2].a=z;k(x   ,r)}j(r,x,2   )k(x,fS21(y,u(r)(r,z)))}d
(rS2){j(a,x,3)j(b,x,2)j(   f,x,1)k(x,rS1   (f,u(a)(a,y),b))}d(fS2){
j(a,x,1) R(y);e rS1(x,u   (a)(a,y),y);}d(   fS1){g(fS2)R(x[1].a);r[
2].a=y;k(x,r)}d(fS0){g   (fS1)y; k(x,r)}d(   fI){k(x,y)}d(fV){k(y,x
)}d(fW) {putc(x[1].p,   stdout);k(x,y)}d(fR   ){ch=getc(stdin);A*r=
C(&(A){.l=ch!=EOF?fI   :fV});k(x,u(y)(y,r))}   d(fCh){A*r=C(&(A){.l
=ch==x[1].p?fI:fV})   ;k(x,u(y) (y,r))}d(fP){   A*r=C(&(A) {.l=ch!=
EOF?fW:fV}); if(ch   !=EOF)r[1].p=ch;k(x,u(y)(   y,r))}d(rE){if(y[3
].a==y)e u(y)(y,x   );j(r,x,1)k(x,u(r)(r,y))}d(   fE){g(rE)y;r[3].a
=r;k(x,r)} d(fT)   {g(rT) R(x[1].a); r[2].a=y;r[   3].a=r; if(fseek
(p, x[2]. p -1,   SEEK_SET))F;q;U();k(x,r)}d(rC){   (void)x;e y;}d(
fC){g(rC)y;r[3   ].a=r; T[96][0].t=YE; k(x,r)}A T[   256][2]={m(46,
Ch,fW)m(63,Ch   ,fCh)o(32,1)m(64,C,fR)[96]={{Y}},o(   9,2)m(99,C,fE
)o(10,3)m(100,C,fD0)m(101,C,fC)o(35,4)m(105,C,fI)m(107,C,fK0)o(11,5
)o(13,6)m(114,NL,fW)m(115,C,fS0)m(118,C,fV)o(12,7)m(124,C,fP)};/*/;
int main(){printf("```s``." "3`.2. `.d`" "``k.rik" "`d`.y`" "``."//
"l``c.n.o.gr```s`.n`.e`.c.h```.r`.u```k.td.y. i`.c`.e`.tk\n");}/**/

http://ioccc.org/2019/adamovsky/prog.c 

 

맨 처음 봤던 코드는 양반이었구나 싶습니다. 1984년에 처음 열린 대회의 우승 작은 이 정도까지는 아니었던 걸 보면, 해가 거듭될수록 점점 어려운 코드가 양산되고 있는 것 같습니다. 저 정도면 도대체 디버깅은 어떻게 하는 건지 궁금할 정도입니다. 모두 저렇게 코딩한다면, 음.. 잘 만든 프로그램 하나로 평생직장 얻는 건 쉬울 거 같습니다. 그리고 난독화 솔루션 업체는 다른 일을 찾아봐야 할지도 모르겠네요. 

 

IOCCC를 만든사람은 Landon Curt Noll ( http://www.isthe.com/chongo/ )과 Larry Bassel( https://www.linkedin.com/in/larry-bassel-37b157103 ) 인데, 한분은 리눅스 커널 엔지니어이고 한분은 시스코의 보안 아키텍쳐입니다. 그리고 두 분 다 상당히 유쾌하신 것 같습니다. Larry는 자기소개에 "진짜 엔지니어는 윈도우를 사용하지 않기 때문에.. "라고 해놓으시고 Landon은 홈페이지가 초창기 웹페이지의 모습을 가지고 있으면서 ( 내 홈페이지는 만드는 중이 아니에요. 그냥 끝마치지 않은 겁니다. )라고 소개하고 있습니다. 

유쾌한 두 엔지니어의 얘기를 찾아보다보니, IOCCC가 왜 탄생했고, 아직까지 계속되는 이유를 알 거 같았습니다. 그리고 시덥지 않은 대회지만, 저렇게 함으로써 생각하는 방식을 바꿔보고 다른 아이디어를 만들어나가는 계기가 될 수도 있을 거 같네요. ( 그런데 저 위에 코드는 아무리 생각해도 심한 거 같습니다. ) 

 

 

출처 : printf-tac-toe ( https://github.com/carlini/printf-tac-toe )

        IOCCC ( http://ioccc.org/ )

반응형
copyright 2020. noname

티스토리툴바