# Implementation of the Loebner Prize Protocol # http://www.loebner.net/Prizef/2009_Contest/LP_2009.html require 'drb' DRb.start_service() $pingwatcher = DRbObject.new(nil, 'druby://localhost:8997') puts "$pingwatcher connected to port 8997" puts "Loebner Prize Contest 2010" puts "When Directory Chooser appears, choose the dir to write program output and read judge's input." puts "(If the file other_dir.txt exists in this directory, that directory will be automatically selected. Delete that file and run the program again to select a directory other than the one in the other_dir.txt file.)" # Don't display Directory Chooser dialog if there's an other_dir.txt if not File.exists?("other_dir.txt") # Have the user choose a directory to write responses to and read input from. system("python DirChooser.py") end # Store the chosen directories. File.open("other_dir.txt", "r") { |f| $other_dir = f.gets } # Judge's directory is the same directory that the bot will write responses to. $judge_dir = $other_dir # Uncomment the following line if $judge_dir is not the same as $other_dir #File.open("judge_dir.txt", "r") { |f| $judge_dir = f.gets } puts "judge's input #{$judge_dir}" puts "bot's output #{$other_dir}" # Start the bot. Dir.chdir("Z:\\controlbot\\controller") require 'controlbot' $DELAY = 0.7 # added between typing strokes class Lpp def initialize @the_count = 0 # Variables to deal with assuming a Judge's Return if they don't enter it. @last_size = 0 @WAIT = 4 # seconds to wait before assuming a Return end def read #$pingwatcher.ping("reading line..."); $pingwatcher.set_pid(Process.pid) begin input_hash = Hash.new dir = Dir.new($judge_dir) if dir.entries.size == 2 then return false end # contains only "." and ".." # is there a Return? return_found = false dir.each { |d| if d =~ /(.*)\.Return\.judge$/ then return_found = true end } dir.each { |d| if d =~ /(.*)\.Return\.judge$/ and dir.entries.size == 3 then delete_judge_directories; @last_size = 0; @last_time = nil; return false end } # If no Return, if there's been a delay longer than @WAIT, assume a return. size = dir.entries.size if @last_time == nil then @last_time = Time.now end puts "*****\n" puts "dir.entries.size==#{dir.entries.size}." puts "@last_size==#{@last_size}." puts "@last_time==#{@last_time}." puts "Time.now==#{Time.now}." puts "return_found==#{return_found}." if (size > 2) and (size == @last_size) and (return_found == false) if (Time.now - @last_time) > @WAIT return_found = true @last_size = 0 end end if size > @last_size then @last_time = Time.now end @last_size = size if not return_found then return false end dir.sort.each { |d| if d =~ /(.*)\.(.*)\.judge$/ time = $1 char = $2 if char =~ /backspace/i to_delete = input_hash.sort.delete_at(input_hash.length - 1) #puts "[Lpp.read] to_delete==#{to_delete.inspect}" begin input_hash.delete(to_delete[0]); rescue; end next end if char =~ /return/i then return_found = true end char = rev_substitute char input_hash["#{time}"] = "#{char}" end } #puts "input_hash.sort==#{input_hash.sort.inspect}" input = '' input_hash.sort.each do |time,char| input << char end #puts "[Lpp.read] input==#{input}" delete_judge_directories @last_time = nil; @last_size = 0 return input rescue Exception => e; puts e; retry; end end def delete_judge_directories save_dir = Dir.pwd Dir.chdir($judge_dir) require 'fileutils' file_path = File.dirname(".") file_name = File.basename(file_path) dir = File.dirname(file_path) Dir.foreach(dir) do |f| if f == file_name or f == '.' or f == '..' or f =~ /\.other$/ then next end if File.directory?(f) and f =~ /\.judge$/ FileUtils.rm_rf(f) end end Dir.chdir(save_dir) end def write response response = insert_backspaces response size = response.size size.downto(1) { |n| char = response.slice!(0,1) char = substitute(char) time = "%.4f" % Time.new.to_f time = time.sub(/\./, '') # Play with the time. #time = time.to_i + 10 #too slow time = time.to_i + 1 time = time.to_s time = time.rjust(18, "0") dirname = "#{$other_dir}/#{time}.#{char}.other" #puts "[Lpp.write] dirname==#{dirname}" begin; Dir.mkdir(dirname); rescue Exception => e; puts e; retry; end if $DELAY > 1 then r = rand + $DELAY end if $DELAY <= 1 then r = rand - $DELAY end if r < 0 then r = 0.2 end if $DELAY <= 0.0 then r = 0.0 end $pingwatcher.ping("writing line..."); $pingwatcher.set_pid(Process.pid) sleep(r) } # Print a line return at the end of the message time = "%.4f" % Time.new.to_f time = time.sub(/\./, '') time = time.to_i + 100 time = time.to_s time = time.rjust(18, "0") dirname = "#{$other_dir}/#{time}.Return.other" begin; Dir.mkdir(dirname); rescue Exception => e; puts "e==#{e}"; retry; end end def insert_backspaces response if response =~ / the / if @the_count % 3 == 0 response.sub!(/ the /, " teh\b\bhe ") end @the_count += 1 end if response =~ /you/ if rand(3) == 1 response.sub!(/you/, "yuo\b\bou") sleep(1) end end if response =~ /what/ if rand(3) == 1 response.sub!(/what/, "waht\b\b\bhat") sleep(0.5) end end if response =~ /\.\.\./ if rand(3) == 1 response.sub!(/\.\.\./, ",,,\b\b\b...") end end if response =~ /y/ if rand(3) == 1 response.sub!(/y/, "u\by") sleep(0.5) end end if rand(9) == 1 response = "I don't kno\b\b\b\b\b\b\b\b\b\b\b" + response end if rand(10) == 1 response = response + "\\" end if rand(10) == 5 if response =~ /T/ then response.sub!(/T/, "/t\b\bT") end end if rand(10) == 7 if response =~ /r/ then response.sub!(/r/, "r4\b") end end if response =~ /p/ if rand(5) == 2 then response.sub!(/p/, "[\bp") end end return response end def rev_substitute char #puts "[Lpp.substitute] char==#{char}" if char == "braceleft" then char = "{" end if char == "braceright" then char = "}" end if char == "bracketleft" then char = "[" end if char == "bracketright" then char = "]" end if char == "parenleft" then char = "(" end if char == "parenright" then char = ")" end if char == "space" then char = " " end if char == "comma" then char = "," end if char == "period" then char = "." end if char == "greater" then char = ">" end if char == "less" then char = "<" end if char == "slash" then char = "/" end if char == "backslash" then char = "\\" end if char == "bar" then char = "|" end if char == 'quotedbl' then char = '"' end if char == "quoteright" then char = "'" end if char == "Tab" then char = "\t" end if char == "equal" then char = "=" end if char == "underscore" then char = "_" end if char == "plus" then char = "+" end if char == "minus" then char = "-" end if char == "exclam" then char = "!" end if char == "at" then char = "@" end if char == "numbersign" then char = "#" end if char == "dollar" then char = "$" end if char == "percent" then char = "%" end if char == "asterisk" then char = "*" end if char == "asciicircum" then char = "^" end if char == "asciitilde" then char = "~" end if char == "quoteleft" then char = "`" end if char == "ampersand" then char = "&" end if char == "Return" then char = "\n" end if char == "colon" then char = ":" end if char == "semicolon" then char = ";" end if char == "question" then char = "?" end if char == "BackSpace" then char = "BackSpace" end return char end def substitute char if char == "{" then char = "braceleft" end if char == "}" then char = "braceright" end if char == "[" then char = "bracketleft" end if char == "]" then char = "bracketright" end if char == "(" then char = "parenleft" end if char == ")" then char = "parenright" end if char == " " then char = "space" end if char == "," then char = "comma" end if char == "." then char = "period" end if char == ">" then char = "greater" end if char == "<" then char = "less" end if char == "/" then char = "slash" end if char == "\\" then char = "backslash" end if char == "|" then char = "bar" end if char == '"' then char = "quotedbl" end if char == "'" then char = "quoteright" end if char == "\t" then char = "Tab" end if char == "=" then char = "equal" end if char == "_" then char = "underscore" end if char == "+" then char = "plus" end if char == "-" then char = "minus" end if char == "!" then char = "exclam" end if char == "@" then char = "at" end if char == "#" then char = "numbersign" end if char == "$" then char = "dollar" end if char == "%" then char = "percent" end if char == "*" then char = "asterisk" end if char == "^" then char = "asciicircum" end if char == "~" then char = "asciitilde" end if char == "`" then char = "quoteleft" end if char == "&" then char = "ampersand" end if char == "\n" then char = "Return" end if char == ":" then char = "colon" end if char == ";" then char = "semicolon" end if char == "?" then char = "question" end #if char == "BackSpace" then char = "BackSpace" end if char == "\b" then char = "BackSpace" end return char end end # get/set $DELAY def process_lpp_commands_in line case line when /type faster/ $DELAY -= 0.5; return "The $DELAY is #{$DELAY}" when /type slower/, /slow down/ $DELAY += 0.5; return "The $DELAY is #{$DELAY}" when /what is.*typing speed/, /show.*delay/i, /^what is.*delay/i return "The $DELAY is #{$DELAY}" when /^set.*delay.*?(\d+\.*\d*)/i $DELAY = $1.to_f; return "$DELAY set to #{$DELAY}" end return false return false end # Entry point quitwords = ["buh-byenow", "trane: quit"] b = ControlBot.new lpp = Lpp.new puts "Hello" $pingwatcher.ping("First Ping!"); $pingwatcher.set_pid(Process.pid) # read each entry with a .judge extension under $judge_dir to get input while (line = lpp.read) !~ /^(#{quitwords.join('|')})$/ $pingwatcher.ping("Ping from while loop!"); $pingwatcher.set_pid(Process.pid) if not line then next end puts "line==#{line}" response = "" response = b.getResponse(line) unless response = process_lpp_commands_in(line) print "\nresp==" + response + "\n" # write each character of response to a directory name under $other_dir lpp.write(response) #sleep($DELAY) unless $DELAY < 0 end # kill the bot system("Z:\\controlbot\\controller\\exit\\rubykill.exe")