Wireworld: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Ruby}}: incorrect output)
Line 200: Line 200:
=={{header|Ruby}}==
=={{header|Ruby}}==


{{incorrect|The output is not correct: t's are transitioning to H's}}
{{incorrect|Ruby}}


Text only.
Text only.

Revision as of 20:09, 9 September 2009

Task
Wireworld
You are encouraged to solve this task according to the task description, using any language you may know.

Wireworld is a cellular automaton with some similarities to Conway's Game of Life. It is capable of doing sophisticated computations (e.g., calculating primeness!) with appropriate programs, 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

To implement this 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.

Python

<lang python> Wireworld implementation.

from io import StringIO from collections import namedtuple from pprint import pprint as pp import copy

WW = namedtuple('WW', 'world, w, h') head, tail, conductor, empty = allstates = 'Ht. '


infile = StringIO(\ tH......... . .

  ...

. . Ht.. ......\ )

def readfile(f):

   'file > initial world configuration'
   world  = f.readlines()
   world  = [row.rstrip('\r\n') for row in world]
   height = len(world)
   width  = max(len(row) for row in world)
   # fill right and frame in empty cells
   nonrow = [ " %*s " % (-width, "") ]
   world  = ( nonrow
              + [ " %*s " % (-width, row) for row in world ]
              + nonrow[:] )    
   world = [list(row) for row in world]
   return WW(world, width, height)

def newcell(currentworld, x, y):

   istate = currentworld[y][x]
   assert istate in allstates, 'Wireworld cell set to unknown value "%s"' % istate
   if istate == head:
       ostate = tail
   elif istate == tail:
       ostate = conductor
   elif istate == empty:
       ostate = empty
   else: # istate == conductor
       n = sum( currentworld[y+dy][x+dx] == head
                for dx,dy in ( (-1,-1), (-1,+0), (-1,+1),
                               (+0,-1),          (+0,+1),
                               (+1,-1), (+1,+0), (+1,+1) ) )
       ostate = head if 1 <= n <= 2 else conductor
   return ostate

def nextgen(ww):

   'compute next generation of wireworld'
   world, width, height = ww
   newworld = copy.deepcopy(world)
   for x in range(1, width+1):
       for y in range(1, height+1):
           newworld[y][x] = newcell(world, x, y)
   return WW(newworld, width, height)

def world2string(ww):

   return '\n'.join( .join(row[1:-1]).rstrip() for row in ww.world[1:-1] )

ww = readfile(infile) infile.close()

for gen in range(10):

   print ( ("\n%3i " % gen) + '=' * (ww.w-4) + '\n' )
   print ( world2string(ww) )
   ww = nextgen(ww)

</lang>

Sample Output

  0 =======

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

  1 =======

.tH........
H   .
   ...
H   .
t... ......

  2 =======

H.tH.......
t   .
   ...
t   .
.H.. ......

  3 =======

tH.tH......
.   H
   ...
.   .
HtH. ......

  4 =======

.tH.tH.....
H   t
   HHH
H   .
t.tH ......

  5 =======

H.tH.tH....
t   .
   ttt
t   .
.H.t ......

  6 =======

tH.tH.tH...
.   H
   ...
.   .
HtH. ......

  7 =======

.tH.tH.tH..
H   t
   HHH
H   .
t.tH ......

  8 =======

H.tH.tH.tH.
t   .
   ttt
t   .
.H.t ......

  9 =======

tH.tH.tH.tH
.   H
   ...
.   .
HtH. ......

Ruby

This example is incorrect. It does not accomplish the given task. Please fix the code and remove this message.

Text only.

<lang ruby>class WireWorld

 def initialize(filename)
   max_row = 0
   @grid = File.foreach(filename).collect do |line|
             line.chomp!
             max_row = [max_row, line.length].max
             line.each_char.collect {|char| Cell.new(char)}
           end
   @width = max_row
   @height = grid.length
   pad_grid
 end
 attr_reader :grid, :width, :height
 # ensure all rows are the same length by padding short rows with empty cells
 def pad_grid
   @grid.each do |row|
     if @width > row.length
       row.concat(Array.new(@width - row.length, Cell.new(' ')))
     end
   end
 end
 # the "to_string" method
 def to_s
   @grid.inject() do |str, row|
     str << row.join() << "\n"
   end
 end
 # find the neighbour cells of a position in the grid
 def neighbours(x, y)
   neighbours = []
   ([x-1, 0].max .. [x+1, @width-1].min).each do |xx|
     ([y-1, 0].max .. [y+1, @height-1].min).each do |yy|
       neighbours << @grid[yy][xx] unless x == xx and y == yy
     end
   end
   neighbours
 end
 # transition all cells
 def transition
   changed = 0
   new_grid = []
   @grid.each_with_index do |row, y|
     new_row = []
     row.each_with_index do |cell, x|
       new_cell = cell.transition( neighbours(x,y) )  
       changed += 1 if cell != new_cell
       puts "#{cell} -> #{new_cell}" if $DEBUG
       new_row << new_cell
     end
     new_grid << new_row
   end
   @grid = new_grid
   changed
 end

end

class Cell

 EMPTY = ' '
 HEAD = 'H'
 TAIL = 't'
 CONDUCTOR = '.'
 def initialize(char)
   unless [EMPTY, HEAD, TAIL, CONDUCTOR].include?(char)
     if $DEBUG
       warn "found invalid character '#{char}' -- ignoring it and using empty"
     end
     char = EMPTY
   end
   @state = char 
 end
 attr_reader :state
 def empty?; @state == EMPTY; end
 def head?; @state == HEAD; end
 def tail?; @state == TAIL; end
 def conductor?; @state == CONDUCTOR; end
 def transition(neighbours)
   p [neighbours] if $DEBUG
   next_state = if    empty? then EMPTY
                elsif head?  then TAIL
                elsif tail?  then HEAD
                elsif neighbours.count {|cell| cell.head?}.between?(1,2) then HEAD 
                else CONDUCTOR
                end
   self.class.new(next_state)
 end 
 def to_s
   @state
 end
 def inspect
   @state.inspect
 end
 def ==(other)
   @state == other.state
 end

end

def evolve(filename)

 ww = WireWorld.new(filename)
 puts "size = #{ww.width} x #{ww.height}" if $DEBUG
 puts ww
 prev_changed = -1
 loop do
   puts 
   num_changed = ww.transition
   puts ww
   break if num_changed == prev_changed
   prev_changed = num_changed 
 end

end

evolve 'wireworld.data'</lang>

Outputs

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

HtH........
H   .
   ...
H   .
tH.. ......

tHtH.......
t   .
   ...
t   .
HtH. ......

HtHtH......
H   H
   ...
H   .
tHtH ......

tHtHtH.....
t   t
   HHH
t   H
HtHt ......

HtHtHtH....
H   H
   ttt
H   t
tHtH H.....

tHtHtHtH...
t   t
   HHH
t   H
HtHt tH....

HtHtHtHtH..
H   H
   ttt
H   t
tHtH HtH...

tHtHtHtHtH.
t   t
   HHH
t   H
HtHt tHtH..

HtHtHtHtHtH
H   H
   ttt
H   t
tHtH HtHtH.

tHtHtHtHtHt
t   t
   HHH
t   H
HtHt tHtHtH

HtHtHtHtHtH
H   H
   ttt
H   t
tHtH HtHtHt

Tcl

Works with: Tcl version 8.6

<lang tcl>package require Tcl 8.6 package require Tk

  1. The color scheme.
  2. The order is: empty, conductor, electronTail, electronHead

set colors "#000000 #000080 #8080ff #ffffff"

  1. Encapsulate the per-cell logic in a class to simplify it

oo::class create wireCell {

   variable X Y S0 S1 Neighbours
   constructor {x y state} {

global cells colors 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\"" } lappend cells [self] set at($x,$y) [self] pixels put [lindex $colors $S0] -to $x $y

   }
   # 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}
   # 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 {} {

if {$S0 != $S1} { global colors set S0 $S1 pixels put [lindex $colors $S0] -to $X $Y }

   }

}

  1. 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 { wireCell new $x $y electronHead } t { wireCell new $x $y electronTail } . { wireCell new $x $y conductor } } incr x } incr y

   }
   # Now inform each cell about its connectivity
   foreach cell $cells {

$cell initConnectivity

   }
   unset at

}

  1. 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]

}

  1. 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] after 1000 timeStep 250</lang>