Wireworld/Ruby: Difference between revisions

From Rosetta Code
Content added Content deleted
(Moving from main task page to shorten it)
 
No edit summary
 
(One intermediate revision by one other user not shown)
Line 1:
<lang ruby>class WireWorld
{{libheader|Ruby/Tk}}
EMPTY = ' '
 
HEAD = 'H'
The GUI is somewhat "halfway", in that it animates a text widget so it's not "real" graphics.
TAIL = 't'
<lang ruby>require 'tk'
 
class WireWorld
EMPTY = ' '
HEAD = 'H'
TAIL = 't'
CONDUCTOR = '.'
NEIGHBOURS = [-1,0,1].product([-1,0,1]) - [0,0]
 
def initialize(string)
max_row = 0
@grid = string.each_line.collect do |line|
line.chomp!.each_char.collect do |char|
max_row = [max_row, line.length].max
line.each_char.collect do |char|
case char
when EMPTY, HEAD, TAIL, CONDUCTOR then char
else EMPTY char
else
EMPTY
end
end
end
@width = @grid.collect{|row| row.length}.max + 1
@original_grid = Marshal.restore(Marshal.dump @grid) # this is a deep copy
@width = max_row
@height = @grid.length
pad_grid
@original_grid = Marshal.restore(Marshal.dump @grid) # this is a deep copy
end
 
# initialize from a file
def self.open(filename)
self.new(File.read(filename))
end
 
def reset
@grid = @original_grid
end
 
# ensure all rows are the same length by padding short rows with empty cells
def pad_grid
@grid << []
@grid.each do |row|
if row.concat(Array.new(@width >- row.length, EMPTY))
row.concat(Array.new(@width - row.length, EMPTY))
end
end
end
 
# the "to_string" method
def to_s
@grid.inject('')collect {|str, row| str << row.join}.join('') << "\n"})
end
 
# transition all cells simultaneously
def transition
Line 59 ⟶ 53:
end
end
 
# how to transition a single cell
def transition_cell(current, x, y)
Line 66 ⟶ 60:
when HEAD then TAIL
when TAIL then CONDUCTOR
else neighbours_with_state(HEAD, x, y).between?(1,2) ? HEAD : CONDUCTOR
end
end
 
# given a position in the grid, find the neighbour cells with a particular state
def neighbours_with_state(state, x, y)
NEIGHBOURS.count {|dx, dy| @grid[y+dy][x+dx] == 0HEAD}
([x-1, 0].max .. [x+1, @width-1].min).each do |xx|
([y-1, 0].max .. [y+1, @height-1].min).each do |yy|
next if x == xx and y == yy
count += 1 if state(xx, yy) == state
end
end
count
end
 
# return the state of a cell given a cartesian coordinate
def state(x, y)
@grid[y][x]
end
 
# run a simulation up to a limit of transitions, or until a recurring
# pattern is found
# This will print text to the console
def run(iterations = 25)
seen = []{}
for count =in 0..iterations
puts "Generation : #{count}"
loop do
puts selfto_s
puts
if seen[@grid]
 
if seen.include?(@grid)
puts "I've seen this grid before... after #{count} iterations"
breakreturn
end
if count == iterations
seen[@grid] = count
puts "ran through #{iterations} iterations"
break
end
 
seen << @grid
count += 1
transition
end
puts "ran through #{iterations} iterations"
end
end</lang>
 
Test (Text version)
<lang ruby># this is the "2 Clock generators and an XOR gate" example from the wikipedia page
text = <<WORLD
......tH
. ......
...Ht... .
....
. .....
....
tH...... .
. ......
...Ht...
WORLD
 
ww = WireWorld.new text
# the gui version
def run_tk
ww.run
@tk_root = TkRoot.new("title" => "WireWorld")
puts 'bye'</lang>
{{out}}
<pre style="height:64ex;overflow:scroll">
Generation : 0
......tH
. ......
...Ht... .
....
. .....
....
tH...... .
. ......
...Ht...
Generation : 1
.......t
. H.....
..Ht.... .
....
. .....
....
.tH..... .
. ......
..Ht....
Generation : 2
........
. tH....
.Ht....H .
....
. .....
....
..tH.... .
. ......
.Ht.....
Generation : 3
........
. .tH...
Ht....Ht .
....
. .....
....
...tH... .
. ......
Ht......
Generation : 4
........
H ..tH..
t....Ht. .
....
. .....
....
....tH.. .
H ......
t.......
Generation : 5
H.......
t ...tH.
....Ht.. .
....
. .....
....
H....tH. .
t ......
........
Generation : 6
tH......
. ....tH
...Ht... .
....
. .....
....
tH....tH .
. ......
........
Generation : 7
.tH.....
. .....t
..Ht.... H
....
. .....
....
.tH....t .
. H.....
........
Generation : 8
..tH....
. ......
.Ht..... t
HHH.
. .....
....
..tH.... .
. tH....
.......H
Generation : 9
...tH...
. ......
Ht...... .
tttH
H H....
....
...tH... .
. .tH...
......Ht
Generation : 10
....tH..
H ......
t....... .
...t
t tH...
HHHH
....tH.. .
. ..tH..
.....Ht.
Generation : 11
H....tH.
t ......
........ .
....
. .tH..
tttt
.....tH. .
. ...tH.
....Ht..
Generation : 12
tH....tH
. ......
........ .
....
. ..tH.
....
......tH .
. ....tH
...Ht...
Generation : 13
.tH....t
. H.....
........ .
....
. ...tH
....
.......t H
. H....t
..Ht....
Generation : 14
..tH....
. tH....
.......H .
....
. ....t
HHH.
........ t
. tH....
.Ht....H
Generation : 15
...tH...
. .tH...
......Ht .
....
H H....
tttH
........ .
. .tH...
Ht....Ht
Generation : 16
....tH..
. ..tH..
.....Ht. .
HHHH
t tH...
...t
........ .
H ..tH..
t....Ht.
Generation : 17
.....tH.
. ...tH.
....Ht.. .
tttt
. .tH..
....
H....... .
t ...tH.
....Ht..
Generation : 18
......tH
. ....tH
...Ht... .
....
. ..tH.
....
tH...... .
. ....tH
...Ht...
Generation : 19
.......t
. H....t
..Ht.... H
....
. ...tH
....
.tH..... H
. .....t
..Ht....
Generation : 20
........
. tH....
.Ht....H t
HHH.
. ....t
HHH.
..tH.... t
. ......
.Ht.....
Generation : 21
........
. .tH...
Ht....Ht .
tttH
. H....
tttH
...tH... .
. ......
Ht......
Generation : 22
........
H ..tH..
t....Ht. .
...t
. t....
...t
....tH.. .
H ......
t.......
Generation : 23
H.......
t ...tH.
....Ht.. .
....
. .....
....
H....tH. .
t ......
........
I've seen this grid before... after 23 iterations
bye
</pre>
 
The GUI version
@tk_text = TkText.new(@tk_root,
{{libheader|Ruby/Tk}}
:width => @width,
:height => @height,
:font => 'courier')
@tk_text.insert('end', self.to_s).state('disabled')
 
The GUI is somewhat "halfway", in that it animates a text widget so it's not "real" graphics.
<lang ruby>require 'tk'
class WireWorld
def run_tk
@tk_root = TkRoot.new(title: "WireWorld")
@tk_text = TkText.new(width: @width, height: @height, font: 'courier')
@tk_text.insert('end', self.to_s).state('disabled')
@tk_after_interval = 150
faster_cmd = proc {@tk_after_interval = [25, @tk_after_interval-25].max}
Line 130 ⟶ 399:
@tk_root.destroy
end
 
controls = TkFrame.new(@tk_root)
[ TkButton.new(controls, text:text => 'Slower', command:command => slower_cmd),
TkButton.new(controls, text:text => 'Faster', command:command => faster_cmd),
TkButton.new(controls, text:text => 'Reset', command:command => reset_cmd),
TkButton.new(controls, text:text => 'Close', command:command => close_cmd),
].each {|btn| btn.pack(expand:expand => 1, fill:fill => 'x', side:side => 'left')}
 
@tk_text.pack(expand:expand => 1, fill:fill => 'both')
controls.pack(:fill =>: 'x')
 
@tk_after_id = @tk_root.after(500) {animate}
Tk.mainloop
end
 
def animate
transition
Line 155 ⟶ 424:
end
 
ww = WireWorld.new text
# this is the "2 Clock generators and an XOR gate" example from the wikipedia page
ww.run_tk</lang>
ww = WireWorld.new <<WORLD
 
{{libheader|Shoes}}
......tH
<lang ruby>ww = WireWorld.new text
. ......
Shoes.app(title: "Wireworld") do
...Ht... .
world = para('', family: 'monospace')
....
animate(4) do
. .....
world.text = ...ww.to_s
ww.transition
tH...... .
end
. ......
end</lang>
...Ht...
 
WORLD
 
ww.run
ww.reset
ww.run_tk
puts 'bye'</lang>

Latest revision as of 07:14, 23 June 2014

<lang ruby>class WireWorld

 EMPTY     = ' '
 HEAD      = 'H'
 TAIL      = 't'
 CONDUCTOR = '.'
 NEIGHBOURS = [-1,0,1].product([-1,0,1]) - [0,0]
 
 def initialize(string)
   @grid = string.each_line.collect do |line|
             line.chomp.each_char.collect do |char| 
               case char
               when EMPTY, HEAD, TAIL, CONDUCTOR
                 char
               else
                 EMPTY
               end 
             end
           end
   @width = @grid.collect{|row| row.length}.max + 1
   @height = @grid.length
   pad_grid
   @original_grid = Marshal.restore(Marshal.dump @grid)   # this is a deep copy
 end
 
 # initialize from a file
 def self.open(filename)
   self.new(File.read(filename))
 end
 
 def reset
   @grid = @original_grid
 end
 
 # ensure all rows are the same length by padding short rows with empty cells
 def pad_grid
   @grid << []
   @grid.each do |row|
     row.concat(Array.new(@width - row.length, EMPTY))
   end
 end
 
 # the "to_string" method
 def to_s
   @grid.collect {|row| row.join}.join("\n")
 end
 
 # transition all cells simultaneously
 def transition
   @grid = @grid.each_with_index.collect do |row, y| 
             row.each_with_index.collect do |state, x| 
               transition_cell(state, x, y)
             end
           end
 end
 
 # how to transition a single cell
 def transition_cell(current, x, y)
   case current
   when EMPTY then EMPTY
   when HEAD  then TAIL
   when TAIL  then CONDUCTOR
   else neighbours_with_state(x, y).between?(1,2) ? HEAD : CONDUCTOR
   end
 end
 
 # given a position in the grid, find the neighbour cells with a particular state
 def neighbours_with_state(x, y)
   NEIGHBOURS.count {|dx, dy| @grid[y+dy][x+dx] == HEAD}
 end
 
 # run a simulation up to a limit of transitions, or until a recurring
 # pattern is found
 # This will print text to the console
 def run(iterations = 25)
   seen = {}
   for count in 0..iterations
     puts "Generation : #{count}"
     puts to_s
     
     if seen[@grid]
       puts "I've seen this grid before... after #{count} iterations"
       return
     end
     
     seen[@grid] = count
     transition
   end
   puts "ran through #{iterations} iterations"
 end

end</lang>

Test (Text version) <lang ruby># this is the "2 Clock generators and an XOR gate" example from the wikipedia page text = <<WORLD

......tH

. ......

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

. ......

...Ht...

WORLD

ww = WireWorld.new text

ww.run puts 'bye'</lang>

Output:
Generation : 0
 ......tH              
.        ......        
 ...Ht...      .       
              ....     
              .  ..... 
              ....     
 tH......      .       
.        ......        
 ...Ht...              
                       
Generation : 1
 .......t              
.        H.....        
 ..Ht....      .       
              ....     
              .  ..... 
              ....     
 .tH.....      .       
.        ......        
 ..Ht....              
                       
Generation : 2
 ........              
.        tH....        
 .Ht....H      .       
              ....     
              .  ..... 
              ....     
 ..tH....      .       
.        ......        
 .Ht.....              
                       
Generation : 3
 ........              
.        .tH...        
 Ht....Ht      .       
              ....     
              .  ..... 
              ....     
 ...tH...      .       
.        ......        
 Ht......              
                       
Generation : 4
 ........              
H        ..tH..        
 t....Ht.      .       
              ....     
              .  ..... 
              ....     
 ....tH..      .       
H        ......        
 t.......              
                       
Generation : 5
 H.......              
t        ...tH.        
 ....Ht..      .       
              ....     
              .  ..... 
              ....     
 H....tH.      .       
t        ......        
 ........              
                       
Generation : 6
 tH......              
.        ....tH        
 ...Ht...      .       
              ....     
              .  ..... 
              ....     
 tH....tH      .       
.        ......        
 ........              
                       
Generation : 7
 .tH.....              
.        .....t        
 ..Ht....      H       
              ....     
              .  ..... 
              ....     
 .tH....t      .       
.        H.....        
 ........              
                       
Generation : 8
 ..tH....              
.        ......        
 .Ht.....      t       
              HHH.     
              .  ..... 
              ....     
 ..tH....      .       
.        tH....        
 .......H              
                       
Generation : 9
 ...tH...              
.        ......        
 Ht......      .       
              tttH     
              H  H.... 
              ....     
 ...tH...      .       
.        .tH...        
 ......Ht              
                       
Generation : 10
 ....tH..              
H        ......        
 t.......      .       
              ...t     
              t  tH... 
              HHHH     
 ....tH..      .       
.        ..tH..        
 .....Ht.              
                       
Generation : 11
 H....tH.              
t        ......        
 ........      .       
              ....     
              .  .tH.. 
              tttt     
 .....tH.      .       
.        ...tH.        
 ....Ht..              
                       
Generation : 12
 tH....tH              
.        ......        
 ........      .       
              ....     
              .  ..tH. 
              ....     
 ......tH      .       
.        ....tH        
 ...Ht...              
                       
Generation : 13
 .tH....t              
.        H.....        
 ........      .       
              ....     
              .  ...tH 
              ....     
 .......t      H       
.        H....t        
 ..Ht....              
                       
Generation : 14
 ..tH....              
.        tH....        
 .......H      .       
              ....     
              .  ....t 
              HHH.     
 ........      t       
.        tH....        
 .Ht....H              
                       
Generation : 15
 ...tH...              
.        .tH...        
 ......Ht      .       
              ....     
              H  H.... 
              tttH     
 ........      .       
.        .tH...        
 Ht....Ht              
                       
Generation : 16
 ....tH..              
.        ..tH..        
 .....Ht.      .       
              HHHH     
              t  tH... 
              ...t     
 ........      .       
H        ..tH..        
 t....Ht.              
                       
Generation : 17
 .....tH.              
.        ...tH.        
 ....Ht..      .       
              tttt     
              .  .tH.. 
              ....     
 H.......      .       
t        ...tH.        
 ....Ht..              
                       
Generation : 18
 ......tH              
.        ....tH        
 ...Ht...      .       
              ....     
              .  ..tH. 
              ....     
 tH......      .       
.        ....tH        
 ...Ht...              
                       
Generation : 19
 .......t              
.        H....t        
 ..Ht....      H       
              ....     
              .  ...tH 
              ....     
 .tH.....      H       
.        .....t        
 ..Ht....              
                       
Generation : 20
 ........              
.        tH....        
 .Ht....H      t       
              HHH.     
              .  ....t 
              HHH.     
 ..tH....      t       
.        ......        
 .Ht.....              
                       
Generation : 21
 ........              
.        .tH...        
 Ht....Ht      .       
              tttH     
              .  H.... 
              tttH     
 ...tH...      .       
.        ......        
 Ht......              
                       
Generation : 22
 ........              
H        ..tH..        
 t....Ht.      .       
              ...t     
              .  t.... 
              ...t     
 ....tH..      .       
H        ......        
 t.......              
                       
Generation : 23
 H.......              
t        ...tH.        
 ....Ht..      .       
              ....     
              .  ..... 
              ....     
 H....tH.      .       
t        ......        
 ........              
                       
I've seen this grid before... after 23 iterations
bye

The GUI version

Library: Ruby/Tk

The GUI is somewhat "halfway", in that it animates a text widget so it's not "real" graphics. <lang ruby>require 'tk'

class WireWorld

 def run_tk
   @tk_root = TkRoot.new(title: "WireWorld")
   
   @tk_text = TkText.new(width: @width, height: @height, font: 'courier')
   @tk_text.insert('end', self.to_s).state('disabled')
   
   @tk_after_interval = 150
   faster_cmd = proc {@tk_after_interval = [25, @tk_after_interval-25].max}
   slower_cmd = proc {@tk_after_interval += 25}
   reset_cmd = proc {self.reset}
   close_cmd = proc do
     @tk_root.after_cancel(@tk_after_id)
     @tk_root.destroy
   end
   
   controls = TkFrame.new
   [ TkButton.new(controls, text: 'Slower', command: slower_cmd),
     TkButton.new(controls, text: 'Faster', command: faster_cmd),
     TkButton.new(controls, text: 'Reset',  command: reset_cmd),
     TkButton.new(controls, text: 'Close',  command: close_cmd),
   ].each {|btn| btn.pack(expand: 1, fill: 'x', side: 'left')}
   
   @tk_text.pack(expand: 1, fill: 'both')
   controls.pack(fill: 'x')
   
   @tk_after_id = @tk_root.after(500) {animate}
   Tk.mainloop
 end
 
 def animate
   transition 
   @tk_text.state('normal') \
           .delete('1.0','end') \
           .insert('end', self.to_s) \
           .state('disabled')
   @tk_after_id = @tk_root.after(@tk_after_interval) {animate}
 end

end

ww = WireWorld.new text ww.run_tk</lang>

Library: Shoes

<lang ruby>ww = WireWorld.new text Shoes.app(title: "Wireworld") do

 world = para(, family: 'monospace')
 animate(4) do
   world.text = ww.to_s
   ww.transition
 end

end</lang>