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.




#SingleInstance, Force
#NoEnv
SetBatchLines, -1
File := "Wireworld.txt"
CellSize := 20
CellSize2 := CellSize - 2
C1 := 0xff000000
C2 := 0xff0066ff
C3 := 0xffd40055
C4 := 0xffffcc00
 
if (!FileExist(File)) {
	MsgBox, % "File(" File ") is not present."
	ExitApp
}
 
; Uncomment if Gdip.ahk is not in your standard library
; #Include, Gdip.ahk
If !pToken := Gdip_Startup(){
	MsgBox, 48, Gdiplus error!, Gdiplus failed to start. Please ensure you have Gdiplus on your system.
	ExitApp
}
OnExit, Exit
 
A := [], Width := 0
Loop, Read, % File
{
	Row := A_Index
	Loop, Parse, A_LoopReadLine
	{
		if (A_Index > Width)
			Width := A_Index
		if (A_LoopField = A_Space)
			continue
		A[Row, A_Index] := A_LoopField
	}
}
 
Width := Width * CellSize + 2 * CellSize
, Height := Row * CellSize + 2 * CellSize
, Row := ""
, TopLeftX := (A_ScreenWidth - Width) // 2
, TopLeftY := (A_ScreenHeight - Height) // 2
 
Gui, 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
Gui, 1: Show, NA
 
hwnd1 := WinExist()
, hbm := CreateDIBSection(Width, Height)
, hdc := CreateCompatibleDC()
, obm := SelectObject(hdc, hbm)
, G := Gdip_GraphicsFromHDC(hdc)
, Gdip_SetSmoothingMode(G, 4)
 
Loop {
	pBrush := Gdip_BrushCreateSolid(C1)
	, Gdip_FillRectangle(G, pBrush, 0, 0, Width, Height)
	, Gdip_DeleteBrush(pBrush)
 
	for RowNum, Row in A
		for CellNum, Cell in Row
			C := Cell = "H" ? C2 : Cell = "t" ? C3 : C4
			, pBrush := Gdip_BrushCreateSolid(C)
			, Gdip_FillRectangle(G, pBrush, CellNum * CellSize + 1, RowNum * CellSize - 2, CellSize2, CellSize2)
			, Gdip_DeleteBrush(pBrush)
 
 
	UpdateLayeredWindow(hwnd1, hdc, TopLeftX, TopLeftY, Width, Height)
	, Gdip_GraphicsClear(G)
	, A := NextState(A)
	Sleep, 600
}
 
NextState(A) {
	B := {}
	for RowNum, Row in A {
		for CellNum, Cell in Row {
			if (Cell = "H")
				B[RowNum, CellNum] := "t"
			else if (Cell = "t")
				B[RowNum, CellNum] := "."
			else if (Cell = ".") {
				H_Count := 0
				Loop 3 {
					Y := RowNum - 2 + A_Index
					Loop, 3 {
						X := CellNum - 2 + A_Index
						if (A[Y, X] = "H")
							H_Count++
					}
				}
				if (H_Count = 1 || H_Count = 2)
					B[RowNum, CellNum] := "H"
				else
					B[RowNum, CellNum] := "."
			}
		}
	}
	return B
}
 
p::Pause
 
Esc::
Exit:
Gdip_Shutdown(pToken)
ExitApp