Recently while reading an article I was introduced to the Abelian sandpile model. The article and wikipedia page do a great job introducing the model, The Abelian sandpile; a mathematical introduction serves as a good tool for understanding the deeper mathematics behind the model. For fun I coded out an Abelian sandpile model simulator that lets you play around some of the sandpiles properties.
Black indicates there are 0 grains, red indicates 1 grain, blue indicates 2 grains, green indicates 3 grains.
The code for the above simulation is as follows:
var width = 350;
var height = 350;
var stop = false;
var drawingTime = null;
var sand;
var oldSand;
var redraw_rate = 500;
const colors = {
0: "black",
1: "red",
2: "blue",
3: "green",
};
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
document.getElementById("height").value = height + "";
document.getElementById("width").value = width + "";
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
function height_change() {
console.log("new height:", document.getElementById("height").value);
height = document.getElementById("height").value;
restart();
}
function redraw_change() {
redraw_rate = document.getElementById("redraw").value;
}
function width_change() {
console.log("new width:", document.getElementById("width").value);
width = document.getElementById("width").value;
restart();
}
function stop_sim() {
stop = true;
clearTimeout(drawingTime);
}
function restart() {
clearTimeout(drawingTime);
ctx.fillStyle = "black";
start_round = new Function(document.getElementById("add").value)();
sand = new Array(height);
oldSand = new Array(height);
for (var i = 0; i < height; i++) {
sand[i] = new Array(width);
oldSand[i] = new Array(width);
for (var j = 0; j < width; j++) {
sand[i][j] = 0;
oldSand[i][j] = 0;
}
}
for (var i = 0; i < height; i++) {
for (var j = 0; j < width; j++) {
ctx.fillRect(i * 2, j * 2, 2, 2);
}
}
var i = 0;
function run() {
if (stop) return;
start_round(height, width, sand);
i++;
if (i % redraw_rate == 0) {
console.log("done step", i);
drawingTime = setTimeout(function () {
draw();
run();
}, 2000);
} else {
run();
}
}
run();
}
restart();
function draw() {
for (var i = 0; i < height; i++) {
for (var j = 0; j < width; j++) {
if (sand[i][j] != oldSand[i][j]) {
const toColor = colors[sand[i][j]] || "yellow";
ctx.fillStyle = toColor;
ctx.fillRect(i * 2, j * 2, 2, 2);
}
}
}
oldSand = sand.map(function (arr) {
return arr.slice();
});
}
function add_sand(x, y, inc) {
if (x < 0 || y < 0 || x >= height || y >= width) return;
sand[x][y] += inc;
while (sand[x][y] >= 4) {
//console.log(x, y, sand[x][y])
sand[x][y] -= 4;
add_sand(x + 1, y, 1);
add_sand(x - 1, y, 1);
add_sand(x, y + 1, 1);
add_sand(x, y - 1, 1);
}
}
Comments
test date