#!/usr/bin/ruby

define w = Number(`tput cols`  || 80)
define h = Number(`tput lines` || 24)
define r = "\033[H"

define red = "\033[31m"
define green = "\033[32m"
define yellow = "\033[33m"

define chars = [' ', green+'*', yellow+'&', red+'&']

define tree_prob = 0.05f
define burn_prob = 0.0002f

enum |Empty, Tree, Heating, Burning|

define dirs = [
    %n(-1 -1), %n(-1 0), %n(-1 1), %n(0 -1),
    %n(0   1), %n(1 -1), %n(1  0), %n(1  1),
]

var forest = h.of { w.of { 1.rand < tree_prob ? Tree : Empty } }

var range_h = h.range
var range_w = w.range

func iterate {
    var new_forest = h.of{ w.of(0) }
    for i = range_h, j = range_w {
        given (new_forest[i][j] = forest[i][j]) {
          when (Tree) {
            1.rand < burn_prob && (new_forest[i][j] = Heating; next)
            for y,x in (dirs) {
                y += i; x += j
                range_h.contains(y) || next
                range_w.contains(x) || next
                new_forest[i][j] = Heating if (forest[y][x] == Heating)
            }
          }
          when (Heating)            { new_forest[i][j] = Burning }
          when (Burning)            { new_forest[i][j] = Empty   }
          case (1.rand < tree_prob) { new_forest[i][j] = Tree    }
        }
    }
    forest = new_forest
}

STDOUT.autoflush(true)

func init_forest {
    print r
    forest.each { |row|
        print chars[row...]
        print "\033[E\033[1G"
    }
    iterate()
}

loop { init_forest() }