Last active
October 10, 2025 11:32
-
-
Save DonBattery/511e0a41756c04515e6fac8d21c594b8 to your computer and use it in GitHub Desktop.
Golang implementation of Yusuke Endoh ASCII Fluid
This file contains hidden or 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 is the Golang version of | |
| // Davide Della Casa's de-obfuscated version https://github.com/davidedc/Ascii-fluid-simulation-deobfuscated | |
| // original obfuscated version by Yusuke Endoh | |
| package main | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "io" | |
| "math" | |
| "os" | |
| "time" | |
| ) | |
| const ( | |
| console_width = 80 | |
| console_height = 24 | |
| ) | |
| type Particle struct { | |
| xPos float64 | |
| yPos float64 | |
| density float64 | |
| wallflag float64 | |
| xForce float64 | |
| yForce float64 | |
| xVelocity float64 | |
| yVelocity float64 | |
| } | |
| var ( | |
| xSandboxAreaScan = 0 | |
| ySandboxAreaScan = 0 | |
| particles = make([]Particle, console_width*console_height*2) | |
| xParticleDistance float64 = 0 | |
| yParticleDistance float64 = 0 | |
| particlesInteraction float64 = 0 | |
| particlesDistance float64 = 0 | |
| totalParticles int = 0 | |
| gravity float64 = 1 | |
| pressure float64 = 4 | |
| viscosity float64 = 7 | |
| lookup = " '`-.|//,\\|\\_\\/#" // 16 chars, index 0..15 | |
| ) | |
| func main() { | |
| // Clear the screen once | |
| fmt.Print("\x1b[2J") | |
| // read the input file to create the walls and particles. | |
| bytesIn, err := io.ReadAll(os.Stdin) | |
| if err != nil { | |
| panic(err) | |
| } | |
| particlesCounter := 0 | |
| for i := 0; i < len(bytesIn); i++ { | |
| ch := bytesIn[i] | |
| switch ch { | |
| case '\n': | |
| ySandboxAreaScan += 2 | |
| xSandboxAreaScan = -1 | |
| case ' ': | |
| // nothing | |
| case '#': | |
| // mark as wall on two particles (guard check) | |
| if particlesCounter+1 < len(particles) { | |
| particles[particlesCounter].wallflag = 1 | |
| particles[particlesCounter+1].wallflag = 1 | |
| // set their positions too (so they exist in space) | |
| particles[particlesCounter].xPos = float64(xSandboxAreaScan) | |
| particles[particlesCounter].yPos = float64(ySandboxAreaScan) | |
| particles[particlesCounter+1].xPos = float64(xSandboxAreaScan) | |
| particles[particlesCounter+1].yPos = float64(ySandboxAreaScan) + 1 | |
| particlesCounter += 2 | |
| totalParticles += 2 | |
| } | |
| default: | |
| // any other non-space char creates two particles one above the other | |
| if particlesCounter+1 < len(particles) { | |
| particles[particlesCounter].xPos = float64(xSandboxAreaScan) | |
| particles[particlesCounter].yPos = float64(ySandboxAreaScan) | |
| particles[particlesCounter+1].xPos = float64(xSandboxAreaScan) | |
| particles[particlesCounter+1].yPos = float64(ySandboxAreaScan) + 1 | |
| particlesCounter += 2 | |
| totalParticles += 2 | |
| } | |
| } | |
| xSandboxAreaScan++ | |
| } | |
| // flags buffer: holds 0..15 masks for each character cell | |
| flags := make([]byte, console_width*console_height) | |
| for { | |
| // densities | |
| for p := 0; p < totalParticles; p++ { | |
| particles[p].density = particles[p].wallflag * 9 | |
| for q := 0; q < totalParticles; q++ { | |
| xParticleDistance = particles[p].xPos - particles[q].xPos | |
| yParticleDistance = particles[p].yPos - particles[q].yPos | |
| particlesDistance = math.Sqrt(xParticleDistance*xParticleDistance + yParticleDistance*yParticleDistance) | |
| particlesInteraction = particlesDistance/2 - 1 | |
| if math.Floor(1-particlesInteraction) > 0 { | |
| particles[p].density += particlesInteraction * particlesInteraction | |
| } | |
| } | |
| } | |
| // forces | |
| for p := 0; p < totalParticles; p++ { | |
| particles[p].yForce = gravity | |
| particles[p].xForce = 0 | |
| for q := 0; q < totalParticles; q++ { | |
| xParticleDistance = particles[p].xPos - particles[q].xPos | |
| yParticleDistance = particles[p].yPos - particles[q].yPos | |
| particlesDistance = math.Sqrt(xParticleDistance*xParticleDistance + yParticleDistance*yParticleDistance) | |
| particlesInteraction = particlesDistance/2 - 1 | |
| if math.Floor(1-particlesInteraction) > 0 { | |
| particles[p].xForce += particlesInteraction * (xParticleDistance*(3-particles[p].density-particles[q].density)*pressure + particles[p].xVelocity*viscosity - particles[q].xVelocity*viscosity) / particles[p].density | |
| particles[p].yForce += particlesInteraction * (yParticleDistance*(3-particles[p].density-particles[q].density)*pressure + particles[p].yVelocity*viscosity - particles[q].yVelocity*viscosity) / particles[p].density | |
| } | |
| } | |
| } | |
| // clear flags (0..15 per cell) | |
| for i := range flags { | |
| flags[i] = 0 | |
| } | |
| // update particles | |
| for p := 0; p < totalParticles; p++ { | |
| if particles[p].wallflag == 0 { | |
| forceMag := math.Sqrt(particles[p].xForce*particles[p].xForce + particles[p].yForce*particles[p].yForce) | |
| if forceMag < 4.2 { | |
| particles[p].xVelocity += particles[p].xForce / 10 | |
| particles[p].yVelocity += particles[p].yForce / 10 | |
| } else { | |
| particles[p].xVelocity += particles[p].xForce / 11 | |
| particles[p].yVelocity += particles[p].yForce / 11 | |
| } | |
| particles[p].xPos += particles[p].xVelocity | |
| particles[p].yPos += particles[p].yVelocity | |
| } | |
| px := int(particles[p].xPos) | |
| py := int(particles[p].yPos) / 2 // scale correction | |
| if py >= 0 && py < console_height && px >= 0 && px < console_width { | |
| idx := py*console_width + px | |
| // set 4 bits mapping | |
| // top-left -> 8, top-right -> 4, bottom-left -> 2, bottom-right -> 1 | |
| flags[idx] |= 8 | |
| if px+1 < console_width { | |
| flags[idx+1] |= 4 | |
| } | |
| if py+1 < console_height { | |
| flags[idx+console_width] |= 2 | |
| if px+1 < console_width { | |
| flags[idx+console_width+1] |= 1 | |
| } | |
| } | |
| } | |
| } | |
| // build output string row by row | |
| var b bytes.Buffer | |
| for row := 0; row < console_height; row++ { | |
| for col := 0; col < console_width; col++ { | |
| b.WriteByte(lookup[flags[row*console_width+col]]) | |
| } | |
| b.WriteByte('\n') | |
| } | |
| // move cursor home and print | |
| fmt.Print("\x1b[1;1H") | |
| fmt.Print(b.String()) | |
| // sleep for 16 milliseconds, which is about 60 fps | |
| time.Sleep(16 * time.Millisecond) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment