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.




V allstates = ‘Ht. ’
V head      = allstates[0]
V tail      = allstates[1]
V conductor = allstates[2]
V empty     = allstates[3]
 
V w =
|‘tH.........
  .   .
     ...
  .   .
  Ht.. ......’
 
T WW
   [[Char]] world
   Int w
   Int h
   F (world, w, h)
      .world = world
      .w = w
      .h = h
 
F readfile(f)
   V world = f.map(row -> row.rtrim(Array[Char]("\r\n")))
   V height = world.len
   V width = max(world.map(row -> row.len))
   V nonrow = [‘ ’(‘ ’ * width)‘ ’]
   V world2 = nonrow [+] world.map(row -> ‘ ’String(row).ljust(@width)‘ ’) [+] nonrow
   V world3 = world2.map(row -> Array(row))
   R WW(world3, width, height)
 
F newcell(currentworld, x, y)
   V istate = currentworld[y][x]
   assert(istate C :allstates, ‘Wireworld cell set to unknown value "#."’.format(istate))
   V ostate = :empty
   I istate == :head
      ostate = :tail
   E I istate == :tail
      ostate = :conductor
   E I istate == :empty
      ostate = :empty
   E
      V n = sum([(-1, -1), (-1, +0), (-1, +1),
                 (+0, -1),           (+0, +1),
                 (+1, -1), (+1, +0), (+1, +1)].map((dx, dy) -> Int(@currentworld[@y + dy][@x + dx] == :head)))
      ostate = I n C 1..2 {:head} E :conductor
   R ostate
 
F nextgen(ww)
   V (world, width, height) = ww
   V newworld = copy(world)
   L(x) 1 .. width
      L(y) 1 .. height
         newworld[y][x] = newcell(world, x, y)
   R WW(newworld, width, height)
 
F world2string(ww)
   R ww.world[1 .< (len)-1].map(row -> (row[1 .< (len)-1]).join(‘’).rtrim((‘ ’, "\t", "\r", "\n"))).join("\n")
 
V ww = readfile(w.split("\n"))
 
L(gen) 10
   print(("\n#3 ".format(gen))‘’(‘=’ * (ww.w - 4))"\n")
   print(world2string(ww))
   ww = nextgen(ww)