open Core.Std
-module Terminal : sig
+let (|-) g f x = f (g x)
+
+
+module Terminal :
+sig
type color = [ `green
| `red
+ | `white
]
val string_with_color : string -> color -> string
val clear : unit -> unit
val reset : unit -> unit
-end = struct
+end =
+struct
type color = [ `green
| `red
+ | `white
]
let ansi_code_clear = "\027[2J" (* Clear screen *)
let string_of_color = function
| `green -> "\027[0;32m"
| `red -> "\027[1;31m"
+ | `white -> "\027[1;37m"
let string_with_color s c =
sprintf "%s%s\027[0m" (string_of_color c) s
end
-module type MATRIX = sig
- module Point : sig
+module Matrix :
+sig
+ module Point :
+ sig
type t = {r : int; k : int}
end
val iter : 'a t -> f:(Point.t -> 'a -> unit) -> unit
val print : 'a t -> to_string:('a -> string) -> unit
-end
-
-module Matrix : MATRIX = struct
- module Point = struct
+end =
+struct
+ module Point =
+ struct
type t = {r : int; k : int}
let (+) p p' =
}
end
- module Direction = struct
+ module Direction =
+ struct
type t = NW | N | NE
| W | E
| SW | S | SE
end
-module Msg = struct
- type t = string
-end
-
-
-module State = struct
- type t = string
-end
-
-
-module PhenoType : sig
+module PhenoType :
+sig
type t
val create : char -> Terminal.color option -> t
val to_string : t -> string
-end = struct
+end =
+struct
type t = { color : Terminal.color option
; character : char
}
end
-module Cell = struct
- type t = { msg : Msg.t
+module Cell =
+struct
+ module State =
+ struct
+ type intention = Friendly
+ | Neutral
+ | Hostile
+
+ type t = Alive of intention
+ | Dead
+ end
+
+ type t = { state : State.t
; pheno : PhenoType.t
- ; state : State.t
}
end
-module type RULE = sig
+module type RULE =
+sig
val create : unit -> Cell.t
- val transition : state:State.t -> inputs:Msg.t list -> Cell.t
+ val transition : self:Cell.State.t
+ -> neighbors:Cell.State.t list
+ -> Cell.t
end
-module Conway : RULE = struct
- type state = D | A
+module Life : RULE =
+struct
+ module State :
+ sig
+ type t = D | A
- let state_of_string : (string -> state) = function
- | "D" -> D
- | "A" -> A
- | _ -> assert false
+ val of_int : int -> t
- let state_of_int : (int -> state) = function
- | 0 -> D
- | 1 -> A
- | _ -> assert false
+ val is_alive : t -> bool
- let int_of_state : (state -> int) = function
- | D -> 0
- | A -> 1
+ val to_cell : t -> Cell.t
- let string_of_state : (state -> string) = function
- | D -> "D"
- | A -> "A"
+ val of_cell_state : Cell.State.t -> t
- let msg_of_state : (state -> Msg.t) =
- string_of_state
+ val next : t -> live_neighbors:int -> t
+ end =
+ struct
+ type t = D | A
- let pheno_of_state : (state -> PhenoType.t) = function
- | D -> PhenoType.create ' ' None
- | A -> PhenoType.create 'o' None
+ let of_int = function
+ | 0 -> D
+ | 1 -> A
+ | _ -> assert false
- let int_of_msg msg =
- msg |> state_of_string |> int_of_state
+ let is_alive = function
+ | D -> false
+ | A -> true
- let next state ~live_neighbors =
- match state with
- | A when live_neighbors < 2 -> D
- | A when live_neighbors < 4 -> A
- | A when live_neighbors > 3 -> D
- | D when live_neighbors = 3 -> A
- | A -> A
- | D -> D
+ let to_pheno = function
+ | D -> PhenoType.create ' ' None
+ | A -> PhenoType.create 'o' (Some `white)
- let cell_of_state s =
- { Cell.msg = s |> msg_of_state
- ; Cell.pheno = s |> pheno_of_state
- ; Cell.state = s |> string_of_state
- }
+ let of_cell_state = function
+ | Cell.State.Dead -> D
+ | Cell.State.Alive Cell.State.Friendly -> A
+ | Cell.State.Alive Cell.State.Neutral -> A
+ | Cell.State.Alive Cell.State.Hostile -> D
+
+ let to_cell_state = function
+ | D -> Cell.State.Dead
+ | A -> Cell.State.Alive Cell.State.Neutral
+
+ let to_cell t =
+ { Cell.state = t |> to_cell_state
+ ; Cell.pheno = t |> to_pheno
+ }
+
+ let next t ~live_neighbors =
+ match t with
+ | A when live_neighbors < 2 -> D
+ | A when live_neighbors < 4 -> A
+ | A when live_neighbors > 3 -> D
+ | D when live_neighbors = 3 -> A
+ | A -> A
+ | D -> D
+ end
let create () =
- Random.int 2 |> state_of_int |> cell_of_state
-
- let live_neighbors inputs =
- inputs
- |> List.filter ~f:(function "D" | "A" -> true | _ -> false)
- |> 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
+ Random.int 2 |> State.of_int |> State.to_cell
+
+ let count_of_live =
+ List.map ~f:State.of_cell_state
+ |- List.filter ~f:State.is_alive
+ |- List.length
+
+ let transition ~self ~neighbors =
+ self |> State.of_cell_state
+ |> State.next ~live_neighbors:(count_of_live neighbors)
+ |> State.to_cell
end
-module ForestFire : RULE = struct
- type state = E | T | B
+module ForestFire : RULE =
+struct
+ module State :
+ sig
+ type t = E | T | B
- let string_of_state : (state -> string) = function
- | E -> "E"
- | T -> "T"
- | B -> "B"
+ val is_burning : t -> bool
- let msg_of_state : (state -> Msg.t) =
- string_of_state
+ val of_int : int -> t
- let pheno_of_state : (state -> PhenoType.t) = function
- | E -> PhenoType.create ' ' None
- | T -> PhenoType.create 'T' (Some `green)
- | B -> PhenoType.create '#' (Some `red)
+ val to_cell : t -> Cell.t
- let cell_of_state s =
- { Cell.msg = s |> msg_of_state
- ; Cell.pheno = s |> pheno_of_state
- ; Cell.state = s |> string_of_state
- }
+ val of_cell_state : Cell.State.t -> t
+
+ val next : t -> burning_neighbors:int -> t
+ end =
+ struct
+ type t = E | T | B
+
+ let is_burning = function
+ | E -> false
+ | T -> false
+ | B -> true
+
+ let of_int = function
+ | 0 -> E
+ | 1 -> T
+ | 2 -> B
+ | _ -> assert false
+
+ let to_pheno = function
+ | E -> PhenoType.create ' ' None
+ | T -> PhenoType.create 'T' (Some `green)
+ | B -> PhenoType.create '#' (Some `red)
+
+ let of_cell_state = function
+ | Cell.State.Dead -> E
+ | Cell.State.Alive Cell.State.Friendly -> T
+ | Cell.State.Alive Cell.State.Neutral -> E
+ | Cell.State.Alive Cell.State.Hostile -> B
- let state_of_string : (string -> state) = function
- | "E" -> E
- | "T" -> T
- | "B" -> B
- | _ -> assert false
+ let to_cell_state = function
+ | E -> Cell.State.Dead
+ | T -> Cell.State.Alive Cell.State.Friendly
+ | B -> Cell.State.Alive Cell.State.Hostile
- let state_of_int : (int -> state) = function
- | 0 -> E
- | 1 -> T
- | 2 -> B
- | _ -> assert false
+ let to_cell t =
+ { Cell.state = t |> to_cell_state
+ ; Cell.pheno = t |> to_pheno
+ }
+
+ let f = 0.000001 (* Probability of spontaneous ignition *)
+ let p = 0.1 (* Probability of spontaneous growth *)
+
+ let is_probable p =
+ (Random.float 1.0) <= p
+
+ let next t ~burning_neighbors =
+ match t, burning_neighbors with
+ | E, _ when is_probable p -> T
+ | E, _ -> E
+ | T, 0 when is_probable f -> B
+ | T, _ when burning_neighbors > 0 -> B
+ | T, _ -> T
+ | B, _ -> E
+ end
let create () =
- Random.int 3 |> state_of_int |> cell_of_state
-
- let f = 0.000001 (* Probability of spontaneous ignition *)
- let p = 0.1 (* Probability of spontaneous growth *)
-
- let is_probable = function
- | probability when (Random.float 1.0) <= probability -> true
- | _ -> false
-
- let next state ~burning_neighbors =
- match state, burning_neighbors with
- | E, _ when is_probable p -> T
- | E, _ -> E
- | T, 0 when is_probable f -> B
- | T, _ when burning_neighbors > 0 -> B
- | T, _ -> T
- | B, _ -> E
-
- let burning_neighbors inputs =
- inputs
- |> List.filter_map ~f:(function "B" -> Some 1 | _ -> None)
- |> List.fold_left ~init:0 ~f:(+)
-
- let transition ~state ~inputs =
- state
- |> state_of_string
- |> next ~burning_neighbors:(burning_neighbors inputs)
- |> cell_of_state
+ Random.int 3 |> State.of_int |> State.to_cell
+
+ let count_of_burning =
+ List.map ~f:State.of_cell_state
+ |- List.filter ~f:State.is_burning
+ |- List.length
+
+ let transition ~self ~neighbors =
+ self |> State.of_cell_state
+ |> State.next ~burning_neighbors:(count_of_burning neighbors)
+ |> State.to_cell
end
-module Automaton : sig
+module Automaton :
+sig
type t
val create : rows:int
-> t
val loop : t -> unit
-end = struct
+end =
+struct
type cell = { data : Cell.t
; rule : (module RULE)
}
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))
+ ~self:data.Cell.state
+ ~neighbors:(List.map neighbors ~f:(fun c -> c.data.Cell.state))
in
{rule; data}
)
end
-let main () =
+let main interval () =
Random.self_init ();
let rows, columns = Or_error.ok_exn Linux_ext.get_terminal_size () in
- let interval = 0.1 in
let rules =
- [ (module Conway : RULE)
+ [ (module Life : RULE)
; (module ForestFire : RULE)
]
in
let spec =
let summary = "Polymorphic Cellular Automata" in
- let spec = Command.Spec.empty in
+ let spec = Command.Spec.(empty
+ +> flag "-i" (optional_with_default 0.1 float)
+ ~doc:" Induced interval between generations."
+ )
+ in
Command.basic ~summary spec main