오늘 받은 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 나 됩니다. )
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/ )