Created
December 20, 2016 10:23
-
-
Save rongjiecomputer/099e9c6aac3a299bfc3ae98334f10a60 to your computer and use it in GitHub Desktop.
Ascii Fluid Simulation (Windows)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this de-obfuscated version by Davide Della Casa | |
// original obfuscated version by Yusuke Endoh | |
// modified for Windows (Mingw-w64) | |
// formatted in Chromium C/C++ style | |
// compile with gcc ascii-win.c -o ascii -O3 -s -flto | |
// usage and original repo see https://github.com/davidedc/Ascii-fluid-simulation-deobfuscated | |
#include <stdio.h> | |
#include <complex.h> | |
#include <math.h> | |
#include <windows.h> | |
#define _POSITION +0 | |
#define _WALLFLAG +1 | |
#define _DENSITY +2 | |
#define _FORCE +3 | |
#define _VELOCITY +4 | |
#define _NEXTPARTICLE +5 | |
#define PARTICLE 5 * | |
#define NEXTSCREENROW 80 + | |
#define CONSOLE_WIDTH 80 | |
#define CONSOLE_HEIGHT 24 | |
double complex particles[CONSOLE_WIDTH * CONSOLE_HEIGHT * 2 * 5], | |
sandboxAreaScan = 0 /* 0 is top-left */, particlesDistance, | |
particlesInteraction; | |
int x, y, screenBufferIndex, totalOfParticles; | |
int gravity = 1, pressure = 4, viscosity = 7; | |
char screenBuffer[CONSOLE_WIDTH * CONSOLE_HEIGHT + 1]; | |
int main() { | |
CONSOLE_SCREEN_BUFFER_INFO info; | |
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); | |
{ | |
GetConsoleScreenBufferInfo(hStdout, &info); | |
short R = info.srWindow.Bottom - info.srWindow.Top + 1; | |
short C = info.srWindow.Right - info.srWindow.Left + 1; | |
DWORD written; | |
COORD curPos = {0, 0}; | |
FillConsoleOutputCharacter(hStdout, ' ', R * C, curPos, &written); | |
SetConsoleCursorPosition(hStdout, curPos); | |
} | |
// read the input file to initialise the particles. | |
// # stands for "wall", i.e. unmovable particles (very high density) | |
// any other non-space character represents normal particles. | |
int particlesCounter = 0; | |
while ((x = getc(stdin)) != EOF) { | |
switch (x) { | |
case '\n': | |
// next row, going down the real part increases | |
// rewind the complex part too so particle is at the left | |
sandboxAreaScan = creal(sandboxAreaScan) + 2 + _Complex_I; | |
break; | |
case ' ': | |
break; | |
case '#': | |
// The character # represents �wall particle� (a particle with fixed | |
// position), | |
// and any other non-space characters represent free particles. | |
// A wall sets the flag on 2 particles side by side. | |
particles[PARTICLE particlesCounter _WALLFLAG] = | |
particles[PARTICLE particlesCounter _NEXTPARTICLE _WALLFLAG] = 1; | |
default: | |
// Each non-empty character sets the position of two | |
// particles one below the other (real part is rows) | |
// i.e. each cell in the input file corresponds to 1x2 particle spaces, | |
// and each character sets two particles | |
// one on top of each other. | |
// It's as if the input map maps to a space that has twice the height, | |
// as if the vertical | |
// resolution was higher than the horizontal one. | |
// This is corrected later, see "y scale correction" comment. | |
// I think this is because of gravity simulation, the vertical | |
// resolution has to be | |
// higher, or conversely you can get away with simulating a lot less of | |
// what goes on in the | |
// horizontal axis. | |
particles[PARTICLE particlesCounter _POSITION] = sandboxAreaScan; | |
particles[PARTICLE particlesCounter _NEXTPARTICLE _POSITION] = | |
sandboxAreaScan + 1; | |
// we just added two particles | |
totalOfParticles = particlesCounter += 2; | |
} | |
// next column, going to the right the complex part decreases | |
sandboxAreaScan = sandboxAreaScan - _Complex_I; | |
} | |
while (1) { | |
int particlesCursor, particlesCursor2; | |
// Iterate over every pair of particles to calculate the densities | |
for (particlesCursor = 0; particlesCursor < totalOfParticles; | |
particlesCursor++) { | |
// density of "wall" particles is high, other particles will bounce off | |
// them. | |
particles[PARTICLE particlesCursor _DENSITY] = | |
particles[PARTICLE particlesCursor _WALLFLAG] * 9; | |
for (particlesCursor2 = 0; particlesCursor2 < totalOfParticles; | |
particlesCursor2++) { | |
particlesDistance = particles[PARTICLE particlesCursor _POSITION] - | |
particles[PARTICLE particlesCursor2 _POSITION]; | |
particlesInteraction = cabs(particlesDistance) / 2 - 1; | |
// this line here with the alternative test | |
// works much better visually but breaks simmetry with the | |
// next block | |
// if (round(creal(particlesInteraction)) < 1){ | |
// density is updated only if particles are close enough | |
if (floor(1.0 - creal(particlesInteraction)) > 0) { | |
particles[PARTICLE particlesCursor _DENSITY] += | |
particlesInteraction * particlesInteraction; | |
} | |
} | |
} | |
// Iterate over every pair of particles to calculate the forces | |
for (particlesCursor = 0; particlesCursor < totalOfParticles; | |
particlesCursor++) { | |
particles[PARTICLE particlesCursor _FORCE] = gravity; | |
for (particlesCursor2 = 0; particlesCursor2 < totalOfParticles; | |
particlesCursor2++) { | |
particlesDistance = particles[PARTICLE particlesCursor _POSITION] - | |
particles[PARTICLE particlesCursor2 _POSITION]; | |
particlesInteraction = cabs(particlesDistance) / 2 - 1; | |
// force is updated only if particles are close enough | |
if (floor(1.0 - creal(particlesInteraction)) > 0) { | |
particles[PARTICLE particlesCursor _FORCE] += | |
particlesInteraction * | |
(particlesDistance * | |
(3 - particles[PARTICLE particlesCursor _DENSITY] - | |
particles[PARTICLE particlesCursor2 _DENSITY]) * | |
pressure + | |
particles[PARTICLE particlesCursor _VELOCITY] * viscosity - | |
particles[PARTICLE particlesCursor2 _VELOCITY] * viscosity) / | |
particles[PARTICLE particlesCursor _DENSITY]; | |
} | |
} | |
} | |
// empty the buffer | |
for (screenBufferIndex = 0; | |
screenBufferIndex < CONSOLE_WIDTH * CONSOLE_HEIGHT; | |
screenBufferIndex++) { | |
screenBuffer[screenBufferIndex] = 0; | |
} | |
for (particlesCursor = 0; particlesCursor < totalOfParticles; | |
particlesCursor++) { | |
if (!particles[PARTICLE particlesCursor _WALLFLAG]) { | |
// This is the newtonian mechanics part: knowing the force vector acting | |
// on each | |
// particle, we accelerate the particle (see the change in velocity). | |
// In turn, velocity changes the position at each tick. | |
// Position is the integral of velocity, velocity is the integral of | |
// acceleration and | |
// acceleration is proportional to the force. | |
// force affects velocity | |
if (cabs(particles[PARTICLE particlesCursor _FORCE]) < 4.2) | |
particles[PARTICLE particlesCursor _VELOCITY] += | |
particles[PARTICLE particlesCursor _FORCE] / 10; | |
else | |
particles[PARTICLE particlesCursor _VELOCITY] += | |
particles[PARTICLE particlesCursor _FORCE] / 11; | |
// velocity affects position | |
particles[PARTICLE particlesCursor _POSITION] += | |
particles[PARTICLE particlesCursor _VELOCITY]; | |
} | |
// given the position of the particle, determine the screen buffer | |
// position that it's going to be in. | |
x = particles[PARTICLE particlesCursor _POSITION] * _Complex_I; | |
// y scale correction, since each cell of the input map has | |
// "2" rows in the particle space. | |
y = particles[PARTICLE particlesCursor _POSITION] / 2; | |
screenBufferIndex = x + CONSOLE_WIDTH * y; | |
// if the particle is on screen, update | |
// four buffer cells around it | |
// in a manner of a "gradient", | |
// the representation of 1 particle will be like this: | |
// | |
// 8 4 | |
// 2 1 | |
// | |
// which after the lookup that puts chars on the | |
// screen will look like: | |
// | |
// ,. | |
// `' | |
// | |
// With this mechanism, each particle creates | |
// a gradient over a small area (four screen locations). | |
// As the gradients of several particles "mix", | |
// (because the bits are flipped | |
// independently), | |
// a character will be chosen such that | |
// it gives an idea of what's going on under it. | |
// You can see how corners can only have values of 8,4,2,1 | |
// which will have suitably "pointy" characters. | |
// A "long vertical edge" (imagine two particles above another) | |
// would be like this: | |
// | |
// 8 4 | |
// 10 5 | |
// 2 1 | |
// | |
// and hence 5 and 10 are both vertical bars. | |
// Same for horizontal edges (two particles aside each other) | |
// | |
// 8 12 4 | |
// 2 3 1 | |
// | |
// and hence 3 and 12 are both horizontal dashes. | |
// ... and so on for the other combinations such as | |
// particles placed diagonally, where the diagonal bars | |
// are used, and places where four particles are present, | |
// in which case the highest number is reached, 15, which | |
// maps into the blackest character of the sequence, '#' | |
if (y >= 0 && y < CONSOLE_HEIGHT - 1 && x >= 0 && x < CONSOLE_WIDTH - 1) { | |
screenBuffer[screenBufferIndex] |= 8; // set 4th bit to 1 | |
screenBuffer[screenBufferIndex + 1] |= 4; // set 3rd bit to 1 | |
// now the cell in row below | |
screenBuffer[NEXTSCREENROW screenBufferIndex] |= 2; // set 2nd bit to 1 | |
screenBuffer[NEXTSCREENROW screenBufferIndex + 1] |= | |
1; // set 1st bit to 1 | |
} | |
} | |
// Update the screen buffer | |
for (screenBufferIndex = 0; | |
screenBufferIndex < CONSOLE_WIDTH * CONSOLE_HEIGHT; | |
screenBufferIndex++) { | |
if (screenBufferIndex % CONSOLE_WIDTH == CONSOLE_WIDTH - 1) | |
screenBuffer[screenBufferIndex] = '\n'; | |
else | |
// the string below contains 16 characters, which is for all | |
// the possible combinations of values in the screenbuffer since | |
// it can be subject to flipping of the first 4 bits | |
screenBuffer[screenBufferIndex] = | |
" '`-.|//,\\|\\_\\/#"[screenBuffer[screenBufferIndex]]; | |
// ---------------------- the mappings -------------- | |
// 0 maps into space | |
// 1 maps into ' 2 maps into ` 3 maps into - | |
// 4 maps into . 5 maps into | 6 maps into / | |
// 7 maps into / 8 maps into , 9 maps into \ | |
// 10 maps into | 11 maps into \ 12 maps into _ | |
// 13 maps into \ 14 maps into / 15 maps into # | |
} | |
COORD curPos = {0, 0}; | |
SetConsoleCursorPosition(hStdout, curPos); | |
// finally blit the screen buffer to screen | |
puts(screenBuffer); | |
Sleep(1000 / 60); | |
} | |
CloseHandle(hStdout); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment