RosettaCodeData/Task/Forest-fire/Rust/forest-fire.rs

159 lines
3.9 KiB
Rust

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();
}