xxxxxxxxxx
82
const FIRE = Dusa.compile(`
# Forest fire model
# https://en.wikipedia.org/wiki/Forest-fire_model
# Defaults, can be assert()ed to different values
pIgnition is 1. # P(Ignition) * 10000
pGrowth is 100. # P(Tree grows) * 10000
# Some math
#builtin INT_PLUS plus
dir 0 1. dir 0 -1. dir 1 0. dir -1 0.
# Default: no changes
next X Y is? State :- current X Y is State.
# A burning cell turns into an empty cell
next X Y is empty :-
current X Y is fire.
# A tree will burn if a neighbor is burining
next X Y is fire :-
dir DX DY,
current X Y is tree,
current (plus X DX) (plus Y DY) is fire.
# A tree ignites with probability pIgnition
next X Y is fire :-
current X Y is tree,
rand X Y is V, V < pIgnition.
# A tree grows in an empty cell with probability pGrowth
next X Y is tree :-
current X Y is empty,
rand X Y is V, V < pGrowth.
`);
const GRID_W = 100;
const GRID_H = 125;
const CELL_SIZE = 10;
const INIT_FIRE_X = Math.floor(Math.random() * GRID_W);
const INIT_FIRE_Y = Math.floor(Math.random() * GRID_H);
const INIT_TREE_P = 0.5;
let cells = [];
for (let x = 0; x < GRID_W; x++) {
for (let y = 0; y < GRID_H; y++) {
const state = { name: "empty" };
if (x === INIT_FIRE_X && y === INIT_FIRE_Y) {
state.name = 'fire';
} else if (Math.random() < INIT_TREE_P) {
state.name = 'tree';
}
cells.push({ name: 'current', args: [x,y], value: state })
}
}
function coordToFact([x, y, state]) {
return { name: "current", args: [x,y], value: state };
}
function setup() {
createCanvas(GRID_W * CELL_SIZE, GRID_H * CELL_SIZE);
}
function draw() {
background(0);
for (const { args: [x,y], value: {name: state} } of cells ) {
if (state === 'tree') {
fill("#0000FF");
rect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE);
} else if (state === 'fire') {
fill("#FF0000");
rect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE, CELL_SIZE);
}
}
const dusa = new Dusa(FIRE);
dusa.assert(cells);
dusa.assert(cells.map(({args}) => ({name: "rand", args, value: Math.floor(Math.random() * 10000)})));
cells = [dusa.solution.lookup("next")].map(coordToFact);
}