⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

This is a graphical implementation of [[implementation of task::Wireworld]]. {{works with|Tcl|8.6}} or {{libheader|TclOO}} {{libheader|Tk}}

package require TclOO
package require Tk
 
# The color scheme.
# The order is: empty, conductor, electronTail, electronHead
set colors "#000000 #000080 #8080ff #ffffff"
 
# Encapsulate the per-cell logic in a class to simplify it
oo::class create wireCell {
    variable X Y S0 S1 Neighbours
 
    constructor {state x y} {
	upvar 1 at at
 
	set X $x
	set Y $y
 
	switch -- $state {
	    conductor {
		set S0 1
	    }
	    electronTail {
		set S0 2
	    }
	    electronHead {
		set S0 3
	    }
	    default {
		return -code error "invalid state name \"$state\""
	    }
	}
	set at($x,$y) [self]
    }
 
    # Method used to allow each (non-background) cell to know about its
    # surrouding non-background cells. This makes the connectivity
    # calculations much simpler and faster!
    method initConnectivity {} {
	upvar 1 at at
	foreach dx {-1 -1 -1 0 0 1 1 1} dy {-1 0 1 -1 1 -1 0 1} {
	    set pos [expr {$X+$dx}],[expr {$Y+$dy}]
	    if {[info exists at($pos)]} {
		lappend Neighbours $at($pos)
	    }
	}
    }
 
    method state {} {return $S0}
    method x {} {return $X}
    method y {} {return $Y}
 
    # Perform the transition in two stages, so that we can do the transition
    # "simultaneously" across all cells. The transition0 method calculates
    # what state we're going to change to, and the transition1 method actually
    # moves to the state.
    method transition0 {} {
	if {$S0 == 3} {
	    set S1 2
	} elseif {$S0 == 2} {
	    set S1 1
	} else {
	    set count 0
	    foreach n $Neighbours {
		incr count [expr {[$n state] == 3}]
	    }
	    set S1 [expr {($count == 1 || $count == 2) ? 3 : 1}]
	}
    }
    method transition1 {} {
	set S0 $S1
    }
}

# A subclass that knows how to display state changes on the GUI
oo::class create GUIwireCell {
    superclass wireCell
    variable S0 S1 X Y
    constructor {state x y} {
	global colors
	next $state $x $y
	pixels put [lindex $colors $S0] -to $X $Y
    }
    method transition1 {} {
	# Only do the plot of the state changed; more efficient
	if {$S0 != $S1} {
	    global colors
	    pixels put [lindex $colors [next]] -to $X $Y
	}
    }
}

# How to load a layout/program from a file
proc loadWires filename {
    global cells colors
 
    # Read the file in
    set f [open $filename]
    set data [read $f]
    close $f
 
    # Initialize the list of interacting cells and the connectivity map
    set cells {}
    array set at {}
 
    # Calculate the width of the program
    set lines [split $data \n]
    set len 0
    foreach line $lines {
	if {[string length $line] > $len} {
	    set len [string length $line]
	}
    }
 
    # Create the arena image
    image create photo pixels
 
    # Initialize the image to "empty cell"s; interacting parts will be overlaid
    pixels put [lindex $colors 0] -to 0 0 $len [llength $lines]
 
    # Parse the input data and create the interacting cells
    set y 0
    foreach line $lines {
	set x 0
	foreach char [split $line {}] {
	    switch $char {
		H {
		    lappend cells [GUIwireCell new electronHead $x $y]
		}
		t {
		    lappend cells [GUIwireCell new electronTail $x $y]
		}
		. {
		    lappend cells [GUIwireCell new conductor $x $y]
		}
	    }
	    incr x
	}
	incr y
    }
 
    # Now inform each cell about its connectivity
    foreach cell $cells {
	$cell initConnectivity
    }
    unset at
}

# How to save the current state as a layout that [loadWires] can load
proc saveWires {filename} {
    global cells

    # Make a grid of empty characters of the right size
    set chs [lrepeat [image height pixels] [lrepeat [image width pixels] " "]]

    # Transcribe the non-empty cells into the grid
    foreach cell $cells {
	lset chs [$cell y] [$cell x] [string index " .tH" [$cell state]]
    }

    # Write the characters to stdout or a file
    if {$filename eq "-"} {set f stdout} else {set f [open $filename w]}
    foreach row $chs {
	puts $f [join $row ""]
    }
    if {$f ne "stdout"} {close $f}
}

# How to perform the animation timestep
proc timeStep {t} {
    global cells
 
    # Arm the transition for all interacting cells
    foreach cell $cells {
	$cell transition0
    }
    # Perform the transition for all interacting cells
    foreach cell $cells {
	$cell transition1
    }
 
    # Reschedule
    after $t [list timeStep $t]
}
 
# Initialize the GUI (such as it is) and load and start the animation
wm title . "Wireworld: [lindex $argv 0]"
loadWires [lindex $argv 0]
pack [label .l -image pixels]
bind . <KeyPress> {saveWires -}
after 1000 timeStep 250