Tristan Marrec

Computer Graphics Programmer
Home
LinkedIn
Email 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

  1. The International Obfuscated C Code Contest - IOCCC. ioccc.org

  2. Fluid Simulation Internship - Tristan Marrec. /posts/fluid-simulation/

  3. Real-Time Fluid Dynamics for Games - Jos Stam. 2003. graphics.cs.cmu.edu/nsp/course/15-464/Spring11/papers/StamFluidforGames.pdf