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: 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 = '.'
CONDUCTOR = '.'
NEIGHBOURS = [-1,0,1].product([-1,0,1]) - [0,0]

def initialize(string)
def initialize(string)
max_row = 0
@grid = string.each_line.collect do |line|
@grid = string.each_line.collect do |line|
line.chomp!
line.chomp.each_char.collect do |char|
max_row = [max_row, line.length].max
line.each_char.collect do |char|
case char
case char
when EMPTY, HEAD, TAIL, CONDUCTOR then char
when EMPTY, HEAD, TAIL, CONDUCTOR
else EMPTY
char
else
EMPTY
end
end
end
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
@height = @grid.length
pad_grid
pad_grid
@original_grid = Marshal.restore(Marshal.dump @grid) # this is a deep copy
end
end

# initialize from a file
# initialize from a file
def self.open(filename)
def self.open(filename)
self.new(File.read(filename))
self.new(File.read(filename))
end
end

def reset
def reset
@grid = @original_grid
@grid = @original_grid
end
end

# ensure all rows are the same length by padding short rows with empty cells
# ensure all rows are the same length by padding short rows with empty cells
def pad_grid
def pad_grid
@grid << []
@grid.each do |row|
@grid.each do |row|
if @width > row.length
row.concat(Array.new(@width - row.length, EMPTY))
row.concat(Array.new(@width - row.length, EMPTY))
end
end
end
end
end

# the "to_string" method
# the "to_string" method
def to_s
def to_s
@grid.inject('') {|str, row| str << row.join('') << "\n"}
@grid.collect {|row| row.join}.join("\n")
end
end

# transition all cells simultaneously
# transition all cells simultaneously
def transition
def transition
Line 59: Line 53:
end
end
end
end

# how to transition a single cell
# how to transition a single cell
def transition_cell(current, x, y)
def transition_cell(current, x, y)
Line 66: Line 60:
when HEAD then TAIL
when HEAD then TAIL
when TAIL then CONDUCTOR
when TAIL then CONDUCTOR
else neighbours_with_state(HEAD, x, y).between?(1,2) ? HEAD : CONDUCTOR
else neighbours_with_state(x, y).between?(1,2) ? HEAD : CONDUCTOR
end
end
end
end

# given a position in the grid, find the neighbour cells with a particular state
# given a position in the grid, find the neighbour cells with a particular state
def neighbours_with_state(state, x, y)
def neighbours_with_state(x, y)
count = 0
NEIGHBOURS.count {|dx, dy| @grid[y+dy][x+dx] == HEAD}
([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
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
# run a simulation up to a limit of transitions, or until a recurring
# pattern is found
# pattern is found
# This will print text to the console
# This will print text to the console
def run(iterations = 25)
def run(iterations = 25)
seen = []
seen = {}
count = 0
for count in 0..iterations
puts "Generation : #{count}"
loop do
puts self
puts to_s
puts
if seen[@grid]

if seen.include?(@grid)
puts "I've seen this grid before... after #{count} iterations"
puts "I've seen this grid before... after #{count} iterations"
break
return
end
end
if count == iterations
seen[@grid] = count
puts "ran through #{iterations} iterations"
break
end

seen << @grid
count += 1
transition
transition
end
end
puts "ran through #{iterations} iterations"
end
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
@tk_after_interval = 150
faster_cmd = proc {@tk_after_interval = [25, @tk_after_interval-25].max}
faster_cmd = proc {@tk_after_interval = [25, @tk_after_interval-25].max}
Line 130: Line 399:
@tk_root.destroy
@tk_root.destroy
end
end

controls = TkFrame.new(@tk_root)
controls = TkFrame.new
[ TkButton.new(controls, :text => 'Slower', :command => slower_cmd),
[ TkButton.new(controls, text: 'Slower', command: slower_cmd),
TkButton.new(controls, :text => 'Faster', :command => faster_cmd),
TkButton.new(controls, text: 'Faster', command: faster_cmd),
TkButton.new(controls, :text => 'Reset', :command => reset_cmd),
TkButton.new(controls, text: 'Reset', command: reset_cmd),
TkButton.new(controls, :text => 'Close', :command => close_cmd),
TkButton.new(controls, text: 'Close', command: close_cmd),
].each {|btn| btn.pack(:expand => 1, :fill => 'x', :side => 'left')}
].each {|btn| btn.pack(expand: 1, fill: 'x', side: 'left')}

@tk_text.pack(:expand => 1, :fill => 'both')
@tk_text.pack(expand: 1, fill: 'both')
controls.pack(:fill => 'x')
controls.pack(fill: 'x')

@tk_after_id = @tk_root.after(500) {animate}
@tk_after_id = @tk_root.after(500) {animate}
Tk.mainloop
Tk.mainloop
end
end

def animate
def animate
transition
transition
Line 155: Line 424:
end
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>