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#
(this is in real-time)
How I made it#
“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);
}
}
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.
/%@@@&/
&@@@@@@
@@@@(
@@@ @@@@@&/ . @@@
@ & @@@. .
. * .%@@@@@@@@@@@@@@@@/ .@@@&
,@@@@@ #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@
(@@@@@@ ,@@@@@@@@@@@@@@@@@@@@@#. (@@@@@@@@@@@@@
@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%.@@@@@@@@
*@ ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ (
#@@ @@@@@@@@@@@@@@@@@@@@@@@@/ ./%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% @*
%@@@ @@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ (@@@@@@@@@@@@@ .@@@
@@@@ @@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@ @@@@@ /
@@@@ @@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@/ .@ @@
@@@@ @@@@@@ @@@@@@@@@@@@@@* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@ @@@ @@.
@@@@@ @@@@@@. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@, @@@@@@@@@@@@@@@@@@ @@@
@@@@@&@@@@@@@. /@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ *@@@@@@@@@@@@@@@@@@@@@ @@@@
@@@@@@@@@@@@@@@ ,@@@@@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@@@@@@@@@@@@@@@ @@@%
%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@& ,&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@#
@% @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/
@@@ ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ (@
.@@@@ , *(#%#(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ &@@@@@@@@@@@@@@@ &@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@% @@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@% .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/@@@@@@@ (@
@@@@@# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ &@@@@@@@@@@@. &@@%
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@
(@@@@@@@@@@@@@@@@%, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@
*@@@@@@@@@@@@@@@@@@@@# #@@@@@@@@@@@@@@@@@@@@@.
#*. &@@@@@@@@@@@@@@@@@@@@@@@@ @
/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@
@ (@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@
@@@ @@@@@@@@@@@@@@@@@&@@@@@@@@@@@@@@@@@@# @@@@# /@
@@@@@* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@/ ,@@@@@@@ @@@
.@@@@@@@@@@@@@@@@@@@@@, .@@@@@@@@@@@( @@@@/
@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@( @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
.@@@@@@@@@@@@@@@@@@@@@@&@@@@@@@@@@@
,**, @@@@@@@@@, @@#
#@@ @@@@@@@@@ @@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.@@@#
.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@.
.@@@(@@@@@@@@@@@@@@@@%*
@@@@@@@@@%#@@@@@@@@@@@%
@@@@@@@@@@@@@@@@@@@#
,@@@@@*,@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@#.
@@
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