#!/usr/bin/ruby

define RED = "\e[1;31m";
define YELLOW = "\e[1;33m";
define GREEN = "\e[1;32m";

define DIRS = [
    [-1, -1], [0, -1], [1, -1],
    [-1,  0],          [1,  0],
    [-1,  1], [0,  1], [1,  1],
]

enum (Empty, Tree, Heating, Burning);
define pix = [' ', GREEN + "*", YELLOW + "*", RED + "*"];

class Forest(p=0.01f, f=0.001f, height, width) {

    has rw = ^width
    has rh = ^height

    has spot = []
    has neighbors = []

    method init {
        spot = height.of { width.of { [true, false].pick ? Tree : Empty } }
        self.init_neighbors
    }

    method init_neighbors {
        for i=rh, j=rw {
            neighbors[i][j] = gather {
                for dir in DIRS {
                    take(\(spot[i + dir[0]][j + dir[1]] \\ next));
                 }
            }
        }
    }

    method step {
        var heat = []

        for i=rh, j=rw {
            given (spot[i][j]) {
                when Empty   { spot[i][j] = Tree    if (1.rand < p) }
                when Tree    { spot[i][j] = Heating if (1.rand < f) }
                when Heating { spot[i][j] = Burning; heat << [i, j] }
                when Burning { spot[i][j] = Empty }
            }
        }

        for i,j in heat {
            neighbors[i][j].each { |ref|
                *ref = Heating if (*ref == Tree)
            }
        }
    }

    method show {
        print spot.map {|row|
            join('', pix[row...])
        }.join("\n")
    }
}

STDOUT.autoflush(true)

var width  = Num(`tput cols`  || 80)-1
var height = Num(`tput lines` || 24)-1

var forest = Forest(height: height, width: width)
print "\e[2J"

loop {
    print "\e[H"
    forest.show
    forest.step
}