-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thanks for the fun quiz. For fun I over-egged the solution: tape.rb: # Turing machine tape. class Tape def initialize(string='') @string = string.length.zero? ? '_' : string.dup @offset = 0 end def read return @string[@offset].chr end def write(one_char_string) @string[@offset] = one_char_string[0] return end def move_head_left if @offset == 0 @string.insert(0, '_') else @offset -= 1 end return end def move_head_right @offset += 1 if @offset == @string.length @string.insert(-1, '_') end return end def content return @string.dup end attr_reader :offset end turing.rb: class Turing def initialize(lines) @action, @initial_state = load(lines) end def run(tape) state = @initial_state while action = @action[state][tape.read] state = do_action(tape, state, *action) end end def do_action(tape, current_state, next_state, write, move) tape.write(write) case move when "L" then tape.move_head_left when "R" then tape.move_head_right end return next_state end def load(lines) action = Hash.new { |h,k| h[k] = {} } initial_state = parse(lines) do |current_state, read, next_state, write, move| action[current_state][read] = [next_state, write, move] end return action, initial_state end # Make sure that the lines we are loading are all valid. Raise a runtime error # if we suspect that garbage is being passed in. As each line is parsed we yield # the validated information. The return value is the first current_state mentioned # in the lines passed in. # # The spec of the quiz maps neatly to a regex, so we'll go with the # terse "parsing" and generic error message (we could keep track of line # number in future if we wanted to be helpful) def parse(lines) initial_state = nil lines.each_line do |line| # deal with comments and blank lines instructions = line.sub(/\s*#.*/, '') next if instructions =~ /^\s*$/ unless instructions =~ /^ \s* (\w+) # current state => $1 \s+ (\w) # read char => $2 \s+ (\w+) # next state => $3 \s+ (\w) # char to write => $4 \s+ (L|R) # tape head move direction => $5 \s* $ /x raise RuntimeError, "bad line '#{line}'" end current_state, read, next_state, write, move = $1, $2, $3, $4, $5 initial_state ||= current_state; yield current_state, read, next_state, write, move end return initial_state end end Having Turing::run call Turing::do_action let me play with Console::ANSICode for fun: tm.rb: #!/usr/bin/env ruby # # Ruby quiz 162 # # usage: # # tm.rb [-d] program_file tape1 [tape2 [...]] require 'turing' require 'tape' class DisplayTuring < Turing require 'rubygems' require 'facets/ansicode' include Console::ANSICode def do_action(tape, current_state, next_state, write, move) content = tape.content offset = tape.offset puts blue{ current_state } + "\t" + blue{ content[0, offset] || '' } + black{ on_cyan { content[offset, 1] || '' } } + blue{ content[offset+1 .. -1] || '' } super end end machine_type = Turing if ARGV.length > 0 && ARGV[0] == '-d' machine_type = DisplayTuring ARGV.shift end if ARGV.length < 2 raise RuntimeError, "bad args on command line" end t = machine_type.new(File.open(ARGV.shift)) ARGV.each do |tape_content| tape = Tape.new(tape_content) t.run(tape) puts tape.content.tr('_', ' ').strip end - -- Mike Stok <mike / stok.ca> http://www.stok.ca/~mike/ The "`Stok' disclaimers" apply. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (Darwin) iEYEARECAAYFAkgneKsACgkQnsTBwAWZE9qfPgCeJ+jNl2Rk9baHPSfAqvOZJ3Ih R3EAniCupYipgp9OtejcQ/b+ddBfG3ND =pA5K -----END PGP SIGNATURE-----