Set generation interval in CLI options.
[cellular-automata.git] / forest-fire / 001 / src / forest_fire.ml
CommitLineData
debaffdd
SK
1open Printf
2
3
4(* ------------------------------------------------------------------------- *
5 * Constants
6 * ------------------------------------------------------------------------- *)
8143820a
SK
7let default_f = 0.01 (* Probability of spontaneous ignition *)
8let default_p = 1.0 (* Probability of spontaneous growth *)
debaffdd 9
0d55050e
SK
10let default_interval = 0.1 (* Induced interval between generations *)
11
debaffdd
SK
12let default_x = 80
13let default_y = 25
14
15let char_empty = ' '
16let char_tree = 'T'
17let char_burning = '#'
18
19let ansi_color_tree = "\027[0;32m" (* Green *)
20let ansi_color_burning = "\027[1;31m" (* Red *)
21let ansi_color_off = "\027[0m"
22
23let ansi_code_clear = "\027[2J" (* Clear screen *)
24let ansi_code_reset = "\027[1;1H" (* Reset cursor position *)
25
26
27(* ------------------------------------------------------------------------- *
28 * Types
29 * ------------------------------------------------------------------------- *)
30type cell_state =
31 | Empty | Tree | Burning
32
33
34type direction =
35 | N | NE | E | SE | S | SW | W | NW
36
37
38type options =
0d55050e
SK
39 { size : int * int
40 ; prob : float * float
41 ; interval : float
debaffdd
SK
42 }
43
44
45(* ------------------------------------------------------------------------- *
46 * Utils
47 * ------------------------------------------------------------------------- *)
48
49(* Hack to sleep less than 1 sec *)
50let minisleep subsec =
51 ignore (Unix.select [] [] [] subsec)
52
53
54let term_clear () =
55 print_string ansi_code_clear
56
57
58let term_reset () =
59 print_string ansi_code_reset
60
61
62let get_opts argv =
63 let usage = ""
64
0d55050e 65 and interval = ref default_interval
8143820a
SK
66 and f = ref default_f
67 and p = ref default_p
debaffdd
SK
68 and x = ref default_x
69 and y = ref default_y in
70
8143820a
SK
71 let speclist =
72 Arg.align
73 [ ("-f", Arg.Set_float f, " Probability of spontaneous ignition.")
74 ; ("-p", Arg.Set_float p, " Probability of spontaneous growth.")
75 ; ("-x", Arg.Set_int x, " Forest width.")
76 ; ("-y", Arg.Set_int y, " Forest height.")
0d55050e 77 ; ("-i", Arg.Set_float interval, " Induced interval between generations.")
8143820a
SK
78 ]
79 in
debaffdd
SK
80
81 Arg.parse speclist (fun _ -> ()) usage;
82
0d55050e
SK
83 { size = !x, !y
84 ; prob = !f, !p
85 ; interval = !interval
debaffdd
SK
86 }
87
88
89(* ------------------------------------------------------------------------- *
90 * Core
91 * ------------------------------------------------------------------------- *)
92let directions =
93 [N; NE; E; SE; S; SW; W; NW]
94
95
96let offset_of_direction = function
97 (* Direction -> x, y *)
98 | N -> 0, -1
99 | NE -> 1, -1
100 | E -> 1, 0
101 | SE -> 1, 1
102 | S -> 0, 1
103 | SW -> -1, 1
104 | W -> -1, 0
105 | NW -> -1, -1
106
107
108let offsets =
109 List.map (offset_of_direction) directions
110
111
112let is_probable = function
113 | probability when (Random.float 1.0) <= probability -> true
114 | _ -> false
115
116
8143820a 117let init_cell_state (_, p) = function
debaffdd
SK
118 | () when is_probable p -> Tree
119 | () -> Empty
120
121
8143820a
SK
122let init_forest (x, y) prob =
123 Array.map (Array.map (init_cell_state prob)) (Array.make_matrix y x ())
debaffdd
SK
124
125
126let string_of_state = function
127 | Empty -> sprintf "%c" char_empty
128 | Tree -> sprintf "%s%c%s" ansi_color_tree char_tree ansi_color_off
129 | Burning -> sprintf "%s%c%s" ansi_color_burning char_burning ansi_color_off
130
131
132let new_state = function
8143820a
SK
133 | Burning, _, _ -> Empty
134 | Tree, 0, (f, _) when is_probable f -> Burning
135 | Tree, n_burning, _ when n_burning > 0 -> Burning
136 | Empty, _, (_, p) when is_probable p -> Tree
137 | state, _, _ -> state
debaffdd
SK
138
139
140let print_forest forest =
141 Array.iter
142 (
143 fun row ->
144 Array.iter
145 (
146 fun state ->
147 print_string (string_of_state state)
148 )
149 row;
150 print_newline ()
151 )
152 forest
153
154
155let is_onside width height (x, y) =
156 x >= 0 && y >= 0 && x < width && y < height
157
158
8143820a 159let next_generation forest (width, height) prob =
debaffdd
SK
160 Array.mapi
161 (
162 fun iy row ->
163 Array.mapi
164 (
165 fun ix state ->
166 let neighbors = List.map (fun (ox, oy) -> ox + ix, oy + iy) offsets in
167 let neighbors = List.filter (is_onside width height) neighbors in
168 let neighbor_states = List.map (fun (x, y) -> forest.(y).(x)) neighbors in
169 let burning_states = List.filter (fun s -> s == Burning) neighbor_states in
8143820a 170 new_state (state, (List.length burning_states), prob)
debaffdd
SK
171 )
172 row
173 )
174 forest
175
176
0d55050e 177let rec burn forest size prob interval =
debaffdd
SK
178 term_reset ();
179 print_forest forest;
0d55050e
SK
180 minisleep interval;
181 burn (next_generation forest size prob) size prob interval
debaffdd
SK
182
183
184let main argv =
185 Random.self_init ();
186
187 let opts = get_opts argv in
8143820a 188 let forest = init_forest opts.size opts.prob in
debaffdd
SK
189
190 term_clear ();
0d55050e 191 burn forest opts.size opts.prob opts.interval
debaffdd
SK
192
193
194let () = main Sys.argv
This page took 0.032801 seconds and 4 git commands to generate.