

 This page uses content from Wikipedia. The original article was at Forest-fire model. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)



Task

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 (as given by Wikipedia)

 A burning cell turns into an empty cell
 A tree will burn if at least one neighbor is burning
 A tree ignites with probability   f    even 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.

Task's requirements do not include graphical display or the ability to change parameters (probabilities    p    and    f )   through a graphical or command line interface.



Related tasks

   See   Conway's Game of Life 
   See   Wireworld.


extern crate rand;
extern crate ansi_term;
 
#[derive(Copy, Clone, PartialEq)]
enum Tile {
    Empty,
    Tree,
    Burning,
    Heating,
}
 
impl fmt::Display for Tile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let output = match *self {
            Empty => Black.paint(" "),
            Tree => Green.bold().paint("T"),
            Burning => Red.bold().paint("B"),
            Heating => Yellow.bold().paint("T"),
        };
        write!(f, "{}", output)
    }
}
 
// This has been added to the nightly rust build as of March 24, 2016
// Remove when in stable branch!
trait Contains<T> {
    fn contains(&self, T) -> bool;
}
 
impl<T: PartialOrd> Contains<T> for std::ops::Range<T> {
    fn contains(&self, elt: T) -> bool {
        self.start <= elt && elt < self.end
    }
}
 
const NEW_TREE_PROB: f32 = 0.01;
const INITIAL_TREE_PROB: f32 = 0.5;
const FIRE_PROB: f32 = 0.001;
 
const FOREST_WIDTH: usize = 60;
const FOREST_HEIGHT: usize = 30;
 
const SLEEP_MILLIS: u64 = 25;
 
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::io::BufWriter;
use std::io::Stdout;
use std::process::Command;
use std::time::Duration;
use rand::Rng;
use ansi_term::Colour::*;
 
use Tile::{Empty, Tree, Burning, Heating};
 
fn main() {
    let sleep_duration = Duration::from_millis(SLEEP_MILLIS);
    let mut forest = [[Tile::Empty; FOREST_WIDTH]; FOREST_HEIGHT];
 
    prepopulate_forest(&mut forest);
    print_forest(forest, 0);
 
    std::thread::sleep(sleep_duration);
 
    for generation in 1.. {
 
        for row in forest.iter_mut() {
            for tile in row.iter_mut() {
                update_tile(tile);
            }
        }
 
        for y in 0..FOREST_HEIGHT {
            for x in 0..FOREST_WIDTH {
                if forest[y][x] == Burning {
                    heat_neighbors(&mut forest, y, x);
                }
            }
        }
 
        print_forest(forest, generation);
 
        std::thread::sleep(sleep_duration);
    }
}
 
fn prepopulate_forest(forest: &mut [[Tile; FOREST_WIDTH]; FOREST_HEIGHT]) {
    for row in forest.iter_mut() {
        for tile in row.iter_mut() {
            *tile = if prob_check(INITIAL_TREE_PROB) {
                Tree
            } else {
                Empty
            };
        }
    }
}
 
fn update_tile(tile: &mut Tile) {
    *tile = match *tile {
        Empty => {
            if prob_check(NEW_TREE_PROB) == true {
                Tree
            } else {
                Empty
            }
        }
        Tree => {
            if prob_check(FIRE_PROB) == true {
                Burning
            } else {
                Tree
            }
        }
        Burning => Empty,
        Heating => Burning,
    }
}
 
fn heat_neighbors(forest: &mut [[Tile; FOREST_WIDTH]; FOREST_HEIGHT], y: usize, x: usize) {
    let neighbors = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)];
 
    for &(xoff, yoff) in neighbors.iter() {
        let nx: i32 = (x as i32) + xoff;
        let ny: i32 = (y as i32) + yoff;
        if (0..FOREST_WIDTH as i32).contains(nx) && (0..FOREST_HEIGHT as i32).contains(ny) &&
           forest[ny as usize][nx as usize] == Tree {
            forest[ny as usize][nx as usize] = Heating
        }
    }
}
 
fn prob_check(chance: f32) -> bool {
    let roll = rand::thread_rng().gen::<f32>();
    if chance - roll > 0.0 {
        true
    } else {
        false
    }
}
 
fn print_forest(forest: [[Tile; FOREST_WIDTH]; FOREST_HEIGHT], generation: u32) {
    let mut writer = BufWriter::new(io::stdout());
    clear_screen(&mut writer);
    writeln!(writer, "Generation: {}", generation + 1).unwrap();
    for row in forest.iter() {
        for tree in row.iter() {
            write!(writer, "{}", tree).unwrap();
        }
        writer.write(b"\n").unwrap();
    }
}
 
fn clear_screen(writer: &mut BufWriter<Stdout>) {
    let output = Command::new("clear").output().unwrap();
    write!(writer, "{}", String::from_utf8_lossy(&output.stdout)).unwrap();
}
 