Tristan MarrecComputer Graphics Programmer | Home | ||
contact@tmarrec.dev | Updated |
---|
3D ASCII Fluid Simulation in the Terminal -
Inspired by the IOCCC and my fluid simulation internship, I created this funny looking code. It’s a 3D fluid simulation that mimics a tornado, all rendered in terminal in ASCII art with ray marching. I started by manually obfuscating the code to minimize the number of characters, then used a custom python script to convert it to ASCII art.
Code available as ZIP download.
#define _(x)for(R=0;x>R;++R) float x, T=240 ,B,j= .53,f,y,O[4426112], Z,w, I, n, N,i ,p , E,g= 0; char P[60737];int d=80,X=79,Y=64,l=40, L= 551368, t,m,a,R,r,e,C=6400,c=15168,J =949,K=12,k= 0x1fbb4000;float*W(int t,int m,int a,int T){ return&O[(t + 82*m+6724 *a)+(c+T*L)];}float b(float x,float y,float n){x=x*x+y*y+n*n;int s=k+(* ( int*)&x>>1);return*(float*)&s; }void H(float x,float y,float n, float*k,float*h,float*c) {E=b(x,y,n);*k=x/E;*h=y/E;*c=n/ E;}void s(int x, int S){ _(L)O[c+x*L+R]+=.4*O[ c+S*L+R];}float*D(int o){return &O[(t *X+m)*3+o];} float z( float k){return 0>k? -k:k;}void o(int x,int y){_(e){r=R%C;t= r%d+1;m=r/d+1;a=R /C+1;*W (t ,m,a ,x)=*W( t,m,a,y);}} void A(float*z){*z=*z<.5?.5:*z>85?85:*z;}void u(int i, int y,int u , int v ,int w) {_(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+1;float E=t-d*.4**W(t,m,a,u),J=m- d*.4**W(t,m, a,v) ,e= a-d* .4**W(t ,m,a,w);A(&E);A(&J);A(&e);float R=E-(int)E,j=J-(int)J,l=1-j, p=e-(int)e,c=1-p;*W(t ,m,a ,i)=(1-R)*(l*c **W(E,J,e,y)+j*c* *W(E,1+J,e,y)+l*p**W(E,J,e+1 ,y)+j*p**W(E,1+J,1+e,y) )+R*( l*c**W(E+1,J,e,y) +j*c**W(1+E,1+J,e,y)+l*p**W(1+ E,J,1+e,y)+j*p**W(1+E,J+1,e+ 1,y) ) ;}}int puts(const char*);void v(int x,int i,int k,int b,int c){*W(t,m,a,x)-=40*(*W(t+k,m+b ,a+c ,i )-*W(t-k,m-b,a-c,i));}void q(int u,int T,int w,int i){_(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+ 1;*W (t,m,a,i)=-1./3*((*W(1+t,m,a,u)-*W(t-1,m,a,u))/d+(*W(t,m+1,a,T)-*W(t,m-1,a,T) )/ d+(*W ( t,m,1+a,w)-*W(t,m,a-1,w))/d);}_(e){r=R %C;t=r%d+1;m=r/d+ 1;a=R/ C+1;v(u,i,1,0,0);v(T,i,0,1,0);v(w,i,0,0,1 );}}char h[]= " (\\tjxucXUCQOmqdkaoM&%$\033[38;5;141m+"; int main(){e=C*d;m=c+ L*8;P[J*Y] ='\0';_(m)O[R]=0;for(t=0;t<Y;++t){P[t*J+X*K ]='\n';for(m=0;m<X;){H(m ++-40,t-32,-145,&w,&Z,&I);*D(0)=w;*D(1)=Z;*D(2 )= I;}} for(;;){_(L)O[c+3*L+R]=O[c+4*L+R]=O[c+5*L+R]=O[c+7*L +R]=0;_(e){r =R%C ;t=r%d+1;m=r/d+1;a=R/C+1;x=t-l;y=a-l;E=x*x+y*y;r=k+(*(int*)&E>>1);p=*(float*) &r;if (m<13&m>10){if (p<2){*W(t,m,a,4)=1.5;N=g+(m/d);E=N*N;w=N*N;N=N-(E*N)/6+(E* E*N)/120;i=1-w/2+(w*w)/24;*W(t,m,a,3)=N/2+.0275;*W(t,m ,a,5)=i*.7-.35;}}if(m<2&p <3)*W(t,m,a,7)=.1;}s(0, 3);s(1,4);s (2,5);o(5,2);o(4,1);o(3,0 ) ;q(3,4,5,1);u(1,4,3,4,5);u(0,3,3,4,5);u(2 ,5,3 , 4,5); q(0,1,2,4);s(6,7);o(7,6);u(6,7,0,1,2 );x=T* .9998+B*.02;B=.9998*B-T*.02;T=x;H(- T,0,-B,&I,&w,& Z );g+= .1;g=g>3.14 ?-g:g;H(Z,0,-I,&N,&i ,&p);_( 5056){r= R%5056;t=r%Y;m=r/Y;E=*D(0); float v=*D (1),g =*D(2),V=E*N+v*(i*Z -w*p)+g*-I,s=E*i +v*(p* I-Z* N)+g*-w;E=E*p+v*(N*w-I*i)+g*-Z;f=0;int q=t* J+m*K+8;for(a=0;a<K;++a)P[q+a-8]=h[35+a];while(f<d*5 ){x=z(T+V*f)-l,y=z(s*f)-l,n=z(B+E*f)-l;float k=y>n?y:n,c=x>k?x:k,R=(c<0?c:0)+b(x<0 ?0:x,y<0?0:y,n>0?n:0);if(R<.01){x=T+f *V+l;y=f*s+l;n=B+f*E+l; f=1;a=0;for(; (a++<1)|(d>x& y<d& n<d& x>0&0<y& n>0); ) {f*=1-*W(n+1,1+y,x+1,6)*j;x=x+V*j;y=y+s*j;n=n+E *j;}a=(1-f)*35;a=0>a?0:a>34?34:a; int c=240+.44*a;P[q]=(c-200 )/10+48;P[q+1]=c%10+48 ;P[q+3]=h[a];break;}f+=R ;}}puts(P);}}
Video result
How I made it
1) “Readable” code
I started with clean C code, a basic implementation of Jos Stam’s famous Real-Time Fluid Dynamics for Games. I then obfuscated it bit by bit, trying as much as possible to reduce the number of characters. This is the final stage of what I call my “readable” code.
Used to this ugly code, I don’t find it difficult to work with (this is the third time I’ve rewritten it in another language, from C++ to JS to C).
#define _(x)for(R=0;R<x;++R) float x,T=240,B,j=.53,f,y,O[4426112],Z,w,I,n,N,i,p,E,g=0; char P[60737]; int d=80,X=79,Y=64,l=40,L=551368,t,m,a,R,r,e,C=6400,c=15168,J=949,K=12,k=0x1fbb4000;; float*W(int t,int m,int a,int T){return&O[(t+82*m+6724*a)+(c+T*L)];} float b(float x,float y,float n){x=x*x+y*y+n*n;int s=k+(*(int*)&x>>1);return*(float*)&s;} void H(float x,float y,float n,float*k,float*h,float*c){E=b(x,y,n);*k=x/E;*h=y/E;*c=n/E;} void s(int x,int S){_(L)O[c+x*L+R]+=.4*O[c+S*L+R];} float*D(int o){return &O[(t*X+m)*3+o];}; float z(float k){return k<0?-k:k;} void o(int x,int y){ _(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+1;*W(t,m,a,x)=*W(t,m,a,y);} } void A(float*z){*z=*z<.5?.5:*z>85?85:*z;} void u(int i,int y,int u,int v,int w){ _(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+1; float E=t-d*.4**W(t,m,a,u),J=m-d*.4**W(t,m,a,v),e=a-d*.4**W(t,m,a,w); A(&E);A(&J);A(&e); float R=E-(int)E,j=J-(int)J,l=1-j,p=e-(int)e,c=1-p; *W(t,m,a,i)=(1-R)*(l*c**W(E,J,e,y)+j*c**W(E,1+J,e,y)+l*p**W(E,J,e+1,y)+j*p**W(E,1+J,1+e,y))+R*(l*c**W(E+1,J,e,y)+j*c**W(1+E,1+J,e,y)+l*p**W(1+E,J,1+e,y)+j*p**W(1+E,J+1,e+1,y)); } } int puts(const char*); void v(int x,int i,int k,int b,int c){*W(t,m,a,x)-=40*(*W(t+k,m+b,a+c,i)-*W(t-k,m-b,a-c,i));} void q(int u,int T,int w,int i){ _(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+1; *W(t,m,a,i)=-1./3*((*W(t+1,m,a,u)-*W(t-1,m,a,u))/d+(*W(t,m+1,a,T)-*W(t,m-1,a,T))/d+(*W(t,m,a+1,w)-*W(t,m,a-1,w))/d); } _(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+1; v(u,i,1,0,0);v(T,i,0,1,0);v(w,i,0,0,1); } } char h[]=" (\\tjxucXUCQOmqdkaoM&%$\033[38;5;241m+"; int main(){ e=C*d; m=c+L*8; P[J*Y]='\0'; _(m)O[R]=0; for(t=0;t<Y;++t){ P[t*J+X*K]='\n'; for(m=0;m<X;++m){ H(m-40,t-32,-145,&w,&Z,&I); *D(0)=w;*D(1)=Z;*D(2)=I; } } for(;;){ _(L)O[c+3*L+R]=O[c+4*L+R]=O[c+5*L+R]=O[c+7*L+R]=0; _(e){r=R%C;t=r%d+1;m=r/d+1;a=R/C+1; x=t-l;y=a-l; E=x*x+y*y;r=k+(*(int*)&E>>1); p=*(float*)&r; if (m<13&&m>10){ if (p<2){ *W(t,m,a,4)=1.5; N=g+(m/d);E=N*N;w=N*N; N=N-(E*N)/6+(E*E*N)/120; i=1-w/2+(w*w)/24; *W(t,m,a,3)=N/2+.0275; *W(t,m,a,5)=i*.7-.35; } } if(m<2&&p<3)*W(t,m,a,7)=.1; } s(0,3);s(1,4);s(2,5); o(3,0);o(4,1);o(5,2); q(3,4,5,1);u(0,3,3,4,5);u(1,4,3,4,5);u(2,5,3,4,5); q(0,1,2,4);s(6,7);o(7,6);u(6,7,0,1,2); x=T*.9998+B*.02;B=B*.9998-T*.02;T=x; H(-T,0,-B,&I,&w,&Z);g+=.1;g=g>3.14?-g:g;H(Z,0,-I,&N,&i,&p); _(5056){r=R%5056;t=r%Y;m=r/Y; E=*D(0); float v=*D(1),g=*D(2),V=E*N+v*(i*Z-w*p)+g*-I,s=E*i+v*(p*I-Z*N)+g*-w; E=E*p+v*(N*w-I*i)+g*-Z; f=0; int q=t*J+m*K+8; for(a=0;a<K;++a)P[q+a-8]=h[35+a]; while(f<d*5){ x=z(T+V*f)-l,y=z(s*f)-l,n=z(B+E*f)-l; float k=y>n?y:n,c=x>k?x:k,R=(c<0?c:0)+b(x<0?0:x,y<0?0:y,n>0?n:0); if(R<.01){ x=T+f*V+l;y=f*s+l;n=B+f*E+l;f=1; for(a=0;a<1||x<d&&y<d&n<d&x>0&&y>0&&n>0;++a){ f*=1-*W(n+1,y+1,x+1,6)*j; x=x+V*j;y=y+s*j;n=n+E*j; } a=(1-f)*35; a=a<0?0:a>34?34:a; int c=240+.44*a; P[q]=(c-200)/10+48; P[q+1]=c%10+48; P[q+3]=h[a]; break; } f+=R; } } puts(P); } }
2) Ascii template
I then used an ASCII image generator to obtain this image, which is my basic template for my code. I generated several different sizes until I found one that matched my code perfectly.
/%@@@&/ &@@@@@@ @@@@( @@@ @@@@@&/ . @@@ @ & @@@. . . * .%@@@@@@@@@@@@@@@@/ .@@@& ,@@@@@ #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@ (@@@@@@ ,@@@@@@@@@@@@@@@@@@@@@#. (@@@@@@@@@@@@@ @@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%.@@@@@@@@ *@ ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ( #@@ @@@@@@@@@@@@@@@@@@@@@@@@/ ./%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% @* %@@@ @@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ (@@@@@@@@@@@@@ .@@@ @@@@ @@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@ @@@@@ / @@@@ @@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@/ .@ @@ @@@@ @@@@@@ @@@@@@@@@@@@@@* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@ @@@ @@. @@@@@ @@@@@@. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@, @@@@@@@@@@@@@@@@@@ @@@ @@@@@&@@@@@@@. /@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@@ @@@@ @@@@@@@@@@@@@@@ ,@@@@@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@@@@@@@@@@@@@@@ @@@% %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@& ,&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@# @% @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ @@@ ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ (@ .@@@@ , *(#%#(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ &@@@@@@@@@@@@@@@ &@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@% @@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@% .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/@@@@@@@ (@ @@@@@# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ &@@@@@@@@@@@. &@@% @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@ (@@@@@@@@@@@@@@@@%, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@# #@@@@@@@@@@@@@@@@@@@@@. #*. &@@@@@@@@@@@@@@@@@@@@@@@@ @ / @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@ @ (@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@ @@@ @@@@@@@@@@@@@@@@@&@@@@@@@@@@@@@@@@@@# @@@@# /@ @@@@@* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ ,@@@@@@@ @@@ .@@@@@@@@@@@@@@@@@@@@@, .@@@@@@@@@@@( @@@@/ @@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@( @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ /@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@@@@@@@@@@@@&@@@@@@@@@@@ ,**, @@@@@@@@@, @@# #@@ @@@@@@@@@ @@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.@@@# .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@. .@@@(@@@@@@@@@@@@@@@@%* @@@@@@@@@%#@@@@@@@@@@@% @@@@@@@@@@@@@@@@@@@# ,@@@@@*,@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@#. @@
3) From code to ASCII art
Finally, I wrote a horrible Python script that took my readable code, the ASCII template and merged them to create the ASCII code, replacing it character by character. This was a bit tricky as keywords and numbers can’t be separated by spaces or line breaks in C.
Note: There was a final mini-step on the ASCII code by hand to correct some errors that the script couldn’t handle and to make the look as pleasing as possible.
# This code is AWFUL but (kind-of) works :) import re originalArrays = [] wholeString = '' with open('template.txt', 'r') as f: wholeString = f.read() wholeString = re.sub("[^ \n]", "@", wholeString) lines = wholeString.split('\n') for line in lines: original = re.findall("[^. ][^. ]*", line) if (original): originalArrays.append(original) spaceAvailable = 0 jointArrays = sum(originalArrays, []) arrays = [] for s in jointArrays: arr = list(s) for i in range(len(arr)): arr[i] = '☺' arrstring = ''.join(arr) arr = list(arrstring) arrays.append((list(s),arr)) wholeCode = '' with open('clear.c', 'r') as fe: wholeCode = fe.read() wholeCode = wholeCode.replace('\n','') wholeCode = wholeCode.replace('\t','') wholeCode = wholeCode.replace('#include <stdlib.h>#include <stdio.h>','') wholeCode = wholeCode.replace('#define _(x)for(R=0;x>R;++R){r=R%C;t=r%S+1;m=r/S+1;a=1+R/C;','') keywords=['main','#define','float','void','const','int','long','||','*=','551368','15168','4426112','6724','40','240','35','34','48','10','&&','return','unsigned','sizeof','char','malloc','for','while','break','puts','if',"'\\n'",'++','.53','5056','.5','1./3','.1','80','148','128','0x1fbb4000','256','.44','.156','.128','.031','.004','120','24','156','.9998','.02','3.14','.001','256','69','68','.4','/s/t'] for a in arrays: old = a[0] i = 0 while(i < len(a[1])): needBreak = False if (a[1][i] == '☺'): for k in keywords: size = len(k) if wholeCode[:size] == k: availableSpace = a[1][i:].count('☺') if (size > availableSpace): needBreak = True break if needBreak: break a[1][i] = wholeCode[:1] wholeCode = wholeCode[1:] i += 1 for a in arrays: old = ''.join(a[0]) new = ''.join(a[1]) wholeString = wholeString.replace(old,new,1) #wholeString = wholeString.replace('☺☺☺☺', '/**/') wholeString = wholeString.replace('☺', ' ') wholeString = re.sub("[ ]*\n", "\n", wholeString) #wholeString = re.sub("^(( )+)\1", "\t", wholeString) f = open('ascii.c', 'w') f.write("#define _(x)for(R=0;x>R;++R){r=R%C;t=r%S+1;m=r/S+1;a=1+R/C;\n") f.write(wholeString) f.close() if (len(wholeCode) > 0): print("NOT ENOUGH SPACE FOR CODE!!!!") print(wholeCode)
References
- The International Obfuscated C Code Contest - IOCCC. ioccc.org
- Fluid Simulation Internship - Tristan Marrec. /posts/fluid-simulation/
- Real-Time Fluid Dynamics for Games - Jos Stam. 2003. graphics.cs.cmu.edu/nsp/course/15-464/Spring11/papers/StamFluidforGames.pdf