Wireworld
Conway's Game of Life
It is capable of doing sophisticated computations with appropriate programs
(it is actually Turing complete),
and is much simpler to program for.

A Wireworld arena consists of a Cartesian grid of cells,
each of which can be in one of four states.
All cell transitions happen simultaneously.

The cell transition rules are this:



 Input State

 Output State

 Condition


 empty

 empty




 electron head 

 electron tail 




 electron tail 

 conductor




 conductor

 electron head 

 if 1 or 2 cells in the neighborhood of the cell are in the state “electron head”


 conductor

 conductor

 otherwise



Task

Create a program that reads a Wireworld program from a file and displays an animation of the processing. Here is a sample description file (using "H" for an electron head, "t" for a tail, "." for a conductor and a space for empty) you may wish to test with, which demonstrates two cycle-3 generators and an inhibit gate:

tH.........
.   .
   ...
.   .
Ht.. ......

While text-only implementations of this task are possible, mapping cells to pixels is advisable if you wish to be able to display large designs. The logic is not significantly more complex.




(* Maximilian Wuttke 12.04.2016 *)
 
type world = char vector vector
 
fun getstate (w:world, (x, y)) = (Vector.sub (Vector.sub (w, y), x)) handle Subscript => #" "
 
fun conductor (w:world, (x, y)) =
	let
	  val s = [getstate (w, (x-1, y-1)) = #"H", getstate (w, (x-1, y)) = #"H", getstate (w, (x-1, y+1)) = #"H",
	           getstate (w, (x,   y-1)) = #"H",                                getstate (w, (x,   y+1)) = #"H",
	           getstate (w, (x+1, y-1)) = #"H", getstate (w, (x+1, y)) = #"H", getstate (w, (x+1, y+1)) = #"H"]
	  (* Count `true` in s *)
	  val count = List.length (List.filter (fn x => x=true) s)
	in
	  if count = 1 orelse count = 2 then #"H" else #"."
	end
 
fun translate (w:world, (x, y)) =
	case getstate (w, (x, y)) of
	   #" " => #" "
	 | #"H" => #"t"
	 | #"t" => #"."
	 | #"." => conductor (w, (x, y))
	 | s    => s
 
fun next_world (w : world) = Vector.mapi (fn (y, row) => Vector.mapi (fn (x, _) => translate (w, (x, y))) row) w
 
 
(* Test *)
 
(* makes a list of strings into a world *)
fun make_world (rows : string list) : world =
	Vector.fromList (map (fn (row : string) => Vector.fromList (explode row)) rows)
 
 
(* word_str reverses make_world *)
fun vec_str (r:char vector) = implode (List.tabulate (Vector.length r, fn x => Vector.sub (r, x)))
fun world_str (w:world)     = List.tabulate (Vector.length w, fn y => vec_str (Vector.sub (w, y)))
fun print_world (w:world)   = (map (fn row_str => print (row_str ^ "\n")) (world_str w); ())
 
val test = make_world [
	"tH.........",
	".   .      ",
	"   ...     ",
	".   .      ",
	"Ht.. ......"]