+
+ let cell_of_state s =
+ { Cell.msg = s |> msg_of_state
+ ; Cell.pheno = s |> pheno_of_state
+ ; Cell.state = s |> string_of_state
+ }
+
+ let create () =
+ Random.int 2 |> state_of_int |> cell_of_state
+
+ let live_neighbors inputs =
+ inputs |> List.map ~f:int_of_msg |> List.fold_left ~init:0 ~f:(+)
+
+ let transition ~state ~inputs =
+ state
+ |> state_of_string
+ |> next ~live_neighbors:(live_neighbors inputs)
+ |> cell_of_state
+end
+
+
+module Automaton : sig
+ type t
+
+ val create : rows:int
+ -> columns:int
+ -> interval:float
+ -> rules: (module RULE) list
+ -> t
+
+ val loop : t -> unit
+end = struct
+ type cell = { data : Cell.t
+ ; rule : (module RULE)
+ }
+
+ type t = { grid : cell Matrix.t
+ ; interval : Time.Span.t
+ ; bar : string
+ }
+
+ let create ~rows:rs ~columns:ks ~interval ~rules =
+ let n = List.length rules in
+ let i = Random.int n in
+ let init () =
+ let rule = List.nth_exn rules i in
+ let module Rule = (val rule : RULE) in
+ { rule
+ ; data = Rule.create ()
+ }
+ in
+ { grid = Matrix.map ~f:init (Matrix.create ~rs ~ks ())
+ ; interval = Time.Span.of_float interval
+ ; bar = String.make ks '-'
+ }
+
+ let cell_to_string cell =
+ cell.data.Cell.pheno
+
+ let print t =
+ print_endline t.bar;
+ Matrix.print t.grid ~to_string:cell_to_string;
+ print_endline t.bar
+
+ let next t =
+ let grid =
+ Matrix.mapi t.grid ~f:(
+ fun point {rule; data} ->
+ let module Rule = (val rule : RULE) in
+ let neighbors = Matrix.get_neighbors t.grid point in
+ let data =
+ Rule.transition
+ ~state:data.Cell.state
+ ~inputs:(List.map neighbors ~f:(fun cell -> cell.data.Cell.msg))
+ in
+ {rule; data}
+ )
+ in
+ {t with grid}
+
+ let rec loop t =
+ print t;
+ Time.pause t.interval;
+ loop (next t)