--- /dev/null
+open Printf
+
+
+(* ------------------------------------------------------------------------- *
+ * Constants
+ * ------------------------------------------------------------------------- *)
+let f = 0.01 (* Probability of spontaneous ignition *)
+let p = 1.0 (* Probability of spontaneous growth *)
+
+let default_x = 80
+let default_y = 25
+
+let char_empty = ' '
+let char_tree = 'T'
+let char_burning = '#'
+
+let ansi_color_tree = "\027[0;32m" (* Green *)
+let ansi_color_burning = "\027[1;31m" (* Red *)
+let ansi_color_off = "\027[0m"
+
+let ansi_code_clear = "\027[2J" (* Clear screen *)
+let ansi_code_reset = "\027[1;1H" (* Reset cursor position *)
+
+
+(* ------------------------------------------------------------------------- *
+ * Types
+ * ------------------------------------------------------------------------- *)
+type cell_state =
+ | Empty | Tree | Burning
+
+
+type direction =
+ | N | NE | E | SE | S | SW | W | NW
+
+
+type options =
+ { size : int * int
+ }
+
+
+(* ------------------------------------------------------------------------- *
+ * Utils
+ * ------------------------------------------------------------------------- *)
+
+(* Hack to sleep less than 1 sec *)
+let minisleep subsec =
+ ignore (Unix.select [] [] [] subsec)
+
+
+let term_clear () =
+ print_string ansi_code_clear
+
+
+let term_reset () =
+ print_string ansi_code_reset
+
+
+let get_opts argv =
+ let usage = ""
+
+ and x = ref default_x
+ and y = ref default_y in
+
+ let speclist = Arg.align [
+ ("-x", Arg.Set_int x, " X.");
+ ("-y", Arg.Set_int y, " Y.");
+ ] in
+
+ Arg.parse speclist (fun _ -> ()) usage;
+
+ { size = !x, !y
+ }
+
+
+(* ------------------------------------------------------------------------- *
+ * Core
+ * ------------------------------------------------------------------------- *)
+let directions =
+ [N; NE; E; SE; S; SW; W; NW]
+
+
+let offset_of_direction = function
+ (* Direction -> x, y *)
+ | N -> 0, -1
+ | NE -> 1, -1
+ | E -> 1, 0
+ | SE -> 1, 1
+ | S -> 0, 1
+ | SW -> -1, 1
+ | W -> -1, 0
+ | NW -> -1, -1
+
+
+let offsets =
+ List.map (offset_of_direction) directions
+
+
+let is_probable = function
+ | probability when (Random.float 1.0) <= probability -> true
+ | _ -> false
+
+
+let init_cell_state = function
+ | () when is_probable p -> Tree
+ | () -> Empty
+
+
+let init_forest (x, y) =
+ Array.map (Array.map (init_cell_state)) (Array.make_matrix y x ())
+
+
+let string_of_state = function
+ | Empty -> sprintf "%c" char_empty
+ | Tree -> sprintf "%s%c%s" ansi_color_tree char_tree ansi_color_off
+ | Burning -> sprintf "%s%c%s" ansi_color_burning char_burning ansi_color_off
+
+
+let new_state = function
+ | Burning, _ -> Empty
+ | Tree, 0 when is_probable f -> Burning
+ | Tree, neighbors_burning when neighbors_burning > 0 -> Burning
+ | Empty, _ when is_probable p -> Tree
+ | state, _ -> state
+
+
+let print_forest forest =
+ Array.iter
+ (
+ fun row ->
+ Array.iter
+ (
+ fun state ->
+ print_string (string_of_state state)
+ )
+ row;
+ print_newline ()
+ )
+ forest
+
+
+let is_onside width height (x, y) =
+ x >= 0 && y >= 0 && x < width && y < height
+
+
+let next_generation forest (width, height) =
+ Array.mapi
+ (
+ fun iy row ->
+ Array.mapi
+ (
+ fun ix state ->
+ let neighbors = List.map (fun (ox, oy) -> ox + ix, oy + iy) offsets in
+ let neighbors = List.filter (is_onside width height) neighbors in
+ let neighbor_states = List.map (fun (x, y) -> forest.(y).(x)) neighbors in
+ let burning_states = List.filter (fun s -> s == Burning) neighbor_states in
+ new_state (state, (List.length burning_states))
+ )
+ row
+ )
+ forest
+
+
+let rec burn forest size =
+ term_reset ();
+ print_forest forest;
+ minisleep 0.1;
+ burn (next_generation forest size) size
+
+
+let main argv =
+ Random.self_init ();
+
+ let opts = get_opts argv in
+ let forest = init_forest opts.size in
+
+ term_clear ();
+ burn forest opts.size
+
+
+let () = main Sys.argv