The Forest Fire Model: Let it burn!

Search for a command to run...

No comments yet. Be the first to comment.
Sometimes an example says more than 1000 pages of documentation. This series will take specific algorithmns and discuss their implementation in PicoLisp step by step. https://rosettacode.org/
This task is actually not included in the Rosetta code, but still it is a quite frequent algorithmic task: How to find the optimal path between two nodes of a graph. In this post, we will show a brute-force solution using Pilog to get from A to B. A...
In our previous post, we introduced how to utilize Discrete Event Simulation (DES) in PicoLisp. Now, let's explore another application: a railroad model simulation that includes tracks and trains. The visualization is ASCII-based, so feel free to unl...

The Dining Philosophers

In this post, we will introduce the concept of co-routines and how they are handled in PicoLisp. This will be the foundation for the subsequent posts on Discrete Event Simulation and finally a couple of examples, including an ASCII model railway simu...

One of the many nice things of PicoLisp is that it comes with a built-in database. This makes it easy to collect and store our own data, for example via a mobile app. But not all data is best represented in tables and lists: sometimes a colorful litt...

In the last two posts, we have built a little app that shows the current location and displays the nearest ice cream shops: https://picolisp-explored.com/an-openstreetmap-app-written-in-picolisp-pt-1 https://picolisp-explored.com/an-openstreetmap-ap...

Implement the Drossel and Schwabl definition of the forest-fire model.
It is basically a 2D cellular automaton where each cell can be in three distinct states: empty, tree and burning, and evolves according to the following rules:
- A burning cell turns into an empty cell,
- A tree will burn if at least one heighbor is burning,
- A tree ignites with probability
feven if no neighbor is burning,- An empty space fills with a tree with probability
p.
Neighborhood is the Moore neighborhood; boundary conditions are so that on the boundary the cells are always empty ("fixed" boundary condition).
At the beginning, populate the lattice with empty and tree cells according to a specific probability (e.g. a cell has the probability 0.5 to be a tree). Then, let the system evolve.
We solve this task graphically with help of the canvas.l library. In the end, it will look like this:

Parameters:
f: 0.01 %p: 1 %simul.l library fileIn cellular automata, the Moore neighborhood is defined on a two-dimensional square lattice and is composed of a central cell and the eight cells that surround it.

We can use the simul.l library for this purpose, which defines the functions grid, west, east, south and north in the namespace simul.
Let's load the libraries and functions:
(load "@lib/http.l" "@lib/xhtml.l" "@lib/form.l" "@lib/canvas.l" "@lib/simul.l")
(import simul~grid simul~west simul~east simul~south simul~north)
First of all, we define the grid size *CanvasDX and *CanvasDY, go(), drawCanvas and a function called forest:
(setq *CanvasDX 480 *CanvasDY 360)
(de drawCanvas (Id Dly)
]
(de forest ()
(and (app) *Port% (redirect (baseHRef) *SesId *Url))
(action
(html 0 "Forest Fire" "@lib.css" NIL
(form NIL
]
(de go()
(server 8080 "!forest"))
Now we can define the grid *FireGrid by using the grid function from simul.l. We can return a random T/NIL value by calling (rand T).
(for Col (setq *FireGrid (grid *CanvasDX *CanvasDY))
(for This Col
(=: tree (rand T)) ) )
*FireGrid objectThis approach uses the convenient fact that the canvas "ID" can actually be an object. Let's pass the object *FireGrid to <canvas> and re-draw it every 40 milliseconds:
(<canvas> "$*FireGrid" *CanvasDX *CanvasDY)
(javascript NIL "onload=drawCanvas('$*FireGrid', 40)")
We define that the *FireGrid object has three attributes:
tree, which is T if the tree is intact,burn, which is T if the tree is burning,next, which shows the status in the next iteration.Now we define the drawCanvas function. In the first step, we iterate over every cell in *FireGrid and set the next attribute according to the following rules:
next will be NIL.next will be burn, if a neighbor tree is burning.next will be burn with a probability of 0.1 % (ignition)next is tree.NIL: next is tree with a probability of 1 %, otherwise NIL.(for Col *FireGrid
(for This Col
(=: next
(cond
((: burn) NIL)
((: tree)
(if
(or
(find # Neighbor burning?
'((Dir) (get (Dir This) 'burn))
(quote
west east south north
((X) (south (west X)))
((X) (north (west X)))
((X) (south (east X)))
((X) (north (east X))) ) )
(=0 (rand 0 9999)) )
'burn
'tree ) )
(T (and (=0 (rand 0 99)) 'tree)) ) ) ) )
Then we iterate over *FireGrid again, this time in order to set each cell to the value stored in next:
(for Col *FireGrid
(for This Col
(if (: next)
(put This @ T)
(=: burn)
(=: tree) ) ) )
Now that the new state of *FireGrid is defined, let's draw it:
tree cell, we draw a green dot.burn cell, we draw a red dot.We do this with the csDrawDots function, which does not directly map to a JavaScript function. Instead, we need to define a list of pixel positions which are rendered to the canvas with help of the JavaScript ctx.fillRect function. This is more efficient than defining each pixel in a loop.
We call csDrawDots two times: First for green pixels of size 1 (in x and y direction) to represent the trees, and then red pixels of size 3 to represent the fire.
(make
(csClearRect 0 0 *CanvasDX *CanvasDY)
(csFillStyle "green")
(csDrawDots 1 1
(make
(for (X . Col) *FireGrid
(for (Y . This) Col
(and (: tree) (link X Y)) ) ) ) )
(csFillStyle "red")
(csDrawDots 3 3
(make
(for (X . Col) *FireGrid
(for (Y . This) Col
(and (: burn) (link X Y)) ) ) ) ) )
You can find the source code of the finished example here.
http://rosettacode.org/wiki/Forest_fire
https://en.wikipedia.org/wiki/Cellular_automaton
https://en.wikipedia.org/wiki/Moore_neighborhood