Abelian sandpile model
Terminal based
Defaults to a stack of 1000 and showing progress. Pass in a custom stack size if desired and -hide-progress to run without displaying progress (much faster.)
sub cleanup { print "\e[0m\e[?25h\n"; exit(0) }
signal(SIGINT).tap: { cleanup(); exit(0) }
unit sub MAIN ($stack = 1000, :$hide-progress = False );
my @color = "\e[38;2;0;0;0m█",
"\e[38;2;255;0;0m█",
"\e[38;2;255;255;0m█",
"\e[38;2;0;0;255m█",
"\e[38;2;255;255;255m█"
;
my ($h, $w) = qx/stty size/.words».Int;
my $buf = $w * $h;
my @buffer = 0 xx $buf;
my $done;
@buffer[$w * ($h div 2) + ($w div 2) - 1] = $stack;
print "\e[?25l\e[48;5;232m";
repeat {
$done = True;
loop (my int $row; $row < $h; $row = $row + 1) {
my int $rs = $row * $w; # row start
my int $re = $rs + $w; # row end
loop (my int $idx = $rs; $idx < $re; $idx = $idx + 1) {
if @buffer[$idx] >= 4 {
my $grains = @buffer[$idx] div 4;
@buffer[ $idx - $w ] += $grains if $row > 0;
@buffer[ $idx - 1 ] += $grains if $idx - 1 >= $rs;
@buffer[ $idx + $w ] += $grains if $row < $h - 1;
@buffer[ $idx + 1 ] += $grains if $idx + 1 < $re;
@buffer[ $idx ] %= 4;
$done = False;
}
}
}
unless $hide-progress {
print "\e[1;1H", @buffer.map( { @color[$_ min 4] }).join;
}
} until $done;
print "\e[1;1H", @buffer.map( { @color[$_ min 4] }).join;
cleanup;
Passing in 2048 as a stack size results in: Abelian-sandpile-model-perl6.png (offsite .png image)
SDL2 Animation
use NativeCall;
use SDL2::Raw;
my ($width, $height) = 900, 900;
unit sub MAIN ($stack = 10000);
my int ($w, $h) = 160, 160;
my $buf = $w * $h;
my @buffer = 0 xx $buf;
@buffer[$w * ($h div 2) + ($w div 2) - 1] = $stack;
SDL_Init(VIDEO);
my SDL_Window $window = SDL_CreateWindow(
"Abelian sandpile - Raku",
SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK,
$width, $height,
RESIZABLE
);
my SDL_Renderer $renderer = SDL_CreateRenderer( $window, -1, ACCELERATED +| TARGETTEXTURE );
my $asp_texture = SDL_CreateTexture($renderer, %PIXELFORMAT<RGB332>, STREAMING, $w, $h);
my $pixdatabuf = CArray[int64].new(0, $w, $h, $w);
my @color = 0x00, 0xDE, 0x14, 0xAA, 0xFF;
sub render {
my int $pitch;
my int $cursor;
# work-around to pass the pointer-pointer.
my $pixdata = nativecast(Pointer[int64], $pixdatabuf);
SDL_LockTexture($asp_texture, SDL_Rect, $pixdata, $pitch);
$pixdata = nativecast(CArray[int8], Pointer.new($pixdatabuf[0]));
loop (my int $row; $row < $h; $row = $row + 1) {
my int $rs = $row * $w; # row start
my int $re = $rs + $w; # row end
loop (my int $idx = $rs; $idx < $re; $idx = $idx + 1) {
$pixdata[$idx] = @buffer[$idx] < 4 ?? @color[@buffer[$idx]] !! @color[4];
if @buffer[$idx] >= 4 {
my $grains = floor @buffer[$idx] / 4;
@buffer[ $idx - $w ] += $grains if $row > 0;
@buffer[ $idx - 1 ] += $grains if $idx - 1 >= $rs;
@buffer[ $idx + $w ] += $grains if $row < $h - 1;
@buffer[ $idx + 1 ] += $grains if $idx + 1 < $re;
@buffer[ $idx ] %= 4;
}
}
}
SDL_UnlockTexture($asp_texture);
SDL_RenderCopy($renderer, $asp_texture, SDL_Rect, SDL_Rect.new(:x(0), :y(0), :w($width), :h($height)));
SDL_RenderPresent($renderer);
}
my $event = SDL_Event.new;
main: loop {
while SDL_PollEvent($event) {
my $casted_event = SDL_CastEvent($event);
given $casted_event {
when *.type == QUIT {
last main;
}
}
}
render();
print fps;
}
say '';
sub fps {
state $fps-frames = 0;
state $fps-now = now;
state $fps = '';
$fps-frames++;
if now - $fps-now >= 1 {
$fps = [~] "\b" x 40, ' ' x 20, "\b" x 20 ,
sprintf "FPS: %5.2f ", ($fps-frames / (now - $fps-now)).round(.01);
$fps-frames = 0;
$fps-now = now;
}
$fps
}
Passing in a stack size of 20000 results in: Abelian-sandpile-sdl2.png (offsite .png image)
Last updated