################################################################################ # EmoAgent automates reductio ad absurdum arguments. # # > suppose P -> Q # Okay # # > Q -> R # Okay # # > But R is absurd! # Therefore, not-P. # # > ################################################################################ require 'yaml' require 'graph' require 'drb' $Quitwords = [':q', 'quit', 'exit', 'bye'] class MyAgent def initialize(args=Array.new) @workingDir = File.expand_path(File.dirname(__FILE__)) @filename = @workingDir + "/emoagent-api.yaml" unless load_message = load_synonyms @patterns = [ [/^(.*?) is a synonym for (.*)/, :add_synonym], [/^save syn/, :save_synonyms], [/^load syn/, :load_synonyms], [/^add pattern (.*), (.*)/, :add_pattern], [/^>(.*)/, :defining_method], [/^restart/, :restart] ] save_synonyms # creates patterns.yaml on first use. else puts load_message end @method_def = '' @graph = Graph.new @save = false @plurals = Array.new @plurals << "you"; @plurals << "I" @singulars = Array.new @verbs = [ "is", "=", "are", "has", "have", "eat", "eats", "is had by", "are had by", "student teach", "student teaches" ] @verb_forms = { "is" => "are", "has" => "have", "eats" => "eat", "is had by" => "are had by", "student teaches" => "student teach" } @questions = Array.new @gagent_started = false @gagent = nil startgagent DRb.start_service() @oppagent = DRbObject.new(nil, 'druby://localhost:9120') testoppagent end def def_method method_def file = IO.read(__FILE__) return "Doesn't have 'end # class'" unless file =~ /(.*)end # class(.*)if \$0 == __FILE__(.*)/m grp1 = $1.to_s; grp2 = $2.to_s; grp3 = $3.to_s new_filename = get_new_filename File.open(new_filename, "w+") { |f| f.puts grp1 + method_def + "\n\nend # class\n\nif $0 == __FILE__" + grp3 } @patterns.push( [@new_regexp, @new_symbol] ) save_synonyms puts "#{method_def}" MyAgent.class_eval(method_def) end # If this file has a '.' + a number before the '.rb' extension # (i.e. foo.0.rb), increment the number (i.e. return foo.1.rb). # Otherwise return the name of this file unchanged. def get_new_filename old_filename = __FILE__ if old_filename =~ /(.*)\.(\d+)\.rb$/ incr = $2.to_i + 1 return new_filename = $1.to_s + "." + incr.to_s + ".rb" end return old_filename # Dangerous! end # Match input against each regexp pattern in @patterns; # if there's a match, call the method associated with the pattern. def getResponse input r = "Default response." #puts "input==#{input}" @patterns.each { |pattern| if input =~ pattern[0] return [self.send(pattern[1], pattern[0], input), 2] rescue return r end } return r end def load_synonyms(pattern=nil, input=nil) if File.exist?(@filename) @patterns = YAML.load_file(@filename) return "I have loaded #{@filename}." end return false end def save_synonyms(pattern=nil, input=nil) r = "Eep! error saving synonyms! " begin File.open(@filename, 'w') do |out| YAML.dump(@patterns, out) end return "Okay I have saved the synonyms to disk." rescue => error return r << error end end def add_synonym(pattern, input) return "Problem matching pattern." unless input =~ pattern grp1 = %r{(?i-mx:^#{$1})} grp2 = %r{(?i-mx:^#{$2})} method ='' @patterns.each { |p| #puts "p[0]==#{p[0]}; p[0].class==#{p[0].class}; grp2==#{grp2}; grp2.class==#{grp2.class}" if p[0].to_s == grp2.to_s method = p[1] break end } return unless method.to_s != nil @patterns.push( [grp1, method] ) save_synonyms return "Okay, #{grp1} has been added, and will now call #{method}." end def add_pattern(pattern=nil, input=nil) if input =~ pattern @new_regexp = $1 @new_symbol = $2 @new_regexp.gsub!(/\//, '') new_re = %r{#{@new_regexp}} # Make the regexp case insensitive. new_re_s = new_re.to_s if new_re_s =~ /(-mix)/ new_re_s = $~.pre_match << "i-mx" << $~.post_match end @new_regexp = %r{#{new_re_s}} #puts "@new_regexp==#{@new_regexp}" method_def = " def #{@new_symbol}(pattern=nil, input=nil)\n return \"#\{input} doesn't match #\{pattern}\" unless input =~ pattern\n return 'Default response from " + @new_symbol + "'\n end # method_def" return def_method(method_def) else return "#{input} doesn't match #{pattern}." end end def defining_method(pattern=nil, input=nil) if input =~ pattern @method_def << "\n " << $1.to_s if @method_def =~ /end # method_def/ return def_method(@method_def) end return '' else return "#{input} doesn't match #{pattern}." end end def method_missing input, *rest return getResponse(input.to_s) end # Strips delimiters. def process str str.strip! str.sub!(/\?$/, '') str.sub!(/^\[/, '') str.sub!(/\]$/, '') str.sub!(/^(?:"|')/, '') str.sub!(/(?:"|')$/, '') str.sub!(/\.$/, '') str.sub!(/;$/, '') str.sub!(/,$/, '') str.sub!(/!$/, '') str.sub!(/\?$/, '') return str end def restart(pattern=nil,input=nil) exec "ruby #{__FILE__}" end # method_def def a_implies_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 b = process $2 a.gsub!(/"/, ''); b.gsub!(/"/, '') # I need to send the question "is subj(a) obj(a)?" to gagent. check1 = @gagent.getResponse("#{a}") if check1 =~ /^Yes/ check = @gagent.getResponse("#{b}") puts "[emoagent.a_implies_b]: #{check}" end @graph.a_r_b!(a.downcase, "->", b.downcase) if @save then @graph.save end #be = "is"; if isPlural?(a) then be = "are" end return "Okay, #{a} -> #{b}." end # method_def alias a_implies_b! a_implies_b def what_does_a_imply(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1; if a.size == 1 then a.upcase! end #be = "is"; if isPlural?(a) then be = "are" end arr = @graph.a_r_?(a.downcase, "->") if arr.empty? or arr == nil return "I don't know what #{a} implies." end arr.each { |e| if e.size == 1 then e.upcase! end } return "#{a} -> #{arr.join(', ')}" end # method_def alias what_does_a_imply? what_does_a_imply def does_a_imply_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; if grp1.size == 1 then grp1.upcase! end grp2 = process $2; if grp2.size == 2 then grp2.upcase! end #be = "is"; if isPlural?(grp1) then be = "are" end if grp1 == grp2 then return "Yes, #{grp1} -> #{grp2}." end # reflexivity if @graph.a_r_b?(grp1.downcase, "->", grp2.downcase) return "Yes, #{grp1} -> #{grp2}." end return "I have no knowledge that #{grp1} -> #{grp2}." end # method_def alias does_a_imply_b? does_a_imply_b def why_does_a_imply_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 if grp1.size == 1 then grp1.upcase! end if grp2.size == 1 then grp2.upcase! end arr = @graph.shortest_path(grp1.downcase, "->", grp2.downcase) #puts arr.inspect arr2 = Array.new arr.each_with_index { |phrase, i| phrase = make_verb_agree(phrase) arr2 << phrase } #be = "is"; if isPlural?(grp1) then be = "are" end if grp1 == grp2 return "#{grp1} -> #{grp2} because 'is' is reflexive." end if arr2.empty?; return "I have no knowledge that #{grp1} -> #{grp2}" end arr2.each { |e| if e.size == 1 then e.upcase! end } return "#{grp1} -> #{grp2} because: #{arr2.join(', and ')}" end # method_def alias why_does_a_imply_b? why_does_a_imply_b def make_verb_agree(s) begin if s =~ /(.*) (#{@verbs.join('|')}) (.*)/ a = process $1; r = process $2; b = process $3 verb = getForm(a, r) return "#{a} #{verb} #{b}" end rescue return s end return s end def getForm(a, verb = "is") if isPlural?(a) return getPlural(verb) end return verb end def getPlural(verb) @verb_forms.fetch(verb, verb) end def isPlural?(word = '') @plurals.each { |plural| if word =~ /^#{plural}$/i then return true end } if word =~ /socrates$/i; return false; end if word =~ /men$/i; return true; end @singulars.each { |singular| if word =~ /^#{singular}$/i then return false end } if word =~ /s$/i; return true; end end def a_does_not_imply_b(input=nil, pattern=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 s = self.send("does #{grp1} -> #{grp2}?")[0] if s =~ /^Yes/ s2 = self.send("why does #{grp1} -> #{grp2}?")[0] return "That contradicts the information I have that #{s2}." end # We need to store the "does not imply" relation in the graph, but for now: @graph.a_r_b!(grp1.downcase, "does_not_imply", grp2.downcase) if @save then @graph.save end return "Okay, #{grp1} does not imply #{grp2}." end # method_def def remove_implies_edge(input=nil, pattern=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 #be = "is"; if isPlural?(grp1.downcase) then be = "are" end begin b = @graph.remove_edge(grp1.downcase, "->", grp2.downcase) if @save then @graph.save end if b return "Okay I have forgotten #{grp1} -> #{grp2}." end rescue => error return "Error: #{error}" end return "I can't remove #{grp1} -> #{grp2}." end # method_def def print_graph(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern s = @graph.to_s arr2 = Array.new arr = s.split("\n") #puts "arr==#{arr}" arr.each { |phrase| phrase = make_verb_agree(phrase) arr2 << phrase } #return s unless s == nil or s.size == 0 arr2.each { |e|; if e.size == 1; e.upcase!; puts e; end; } return arr2.join("\n") unless arr2.empty? return "The graph is empty." end # method_def def add_to_plurals(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 @plurals.push(grp1) return "Okay I have recorded #{grp1} as a plural form." end # method_def def add_to_singulars(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 @singulars.push(grp1) return "Okay I have recorded #{grp1} as a singular form." end # method_def def save_graph(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern file = nil begin file = process $1 rescue; end puts "\n... Saving graph (this may take minutes) ..." r = @graph.save(file) #puts "HELLOOOO: r==#{r}" return "#{r}" end # method_def def load_graph(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern begin file = process $1 rescue; end return @graph.load(file) end def reset_graph(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @graph = Graph.new(false) @save = false return "Okay, I have reset the graph." end # method_def def set_save_mode_off(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @save = false return "Okay I have turned off save mode." end # method_def def set_save_mode_on(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @save = true return "Okay I have turned on save mode." end # method_def def what_is_save_mode(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern save_mode = "on"; save_mode = "off" unless @save return "The save mode is #{save_mode}." end # method_def def assume_p(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @assumption = process $1 check,s = @gagent.getResponse(@assumption.to_s) puts "[emoagent.assume_p] gagent response==#{check}" is_true = '' if check =~ /Default response/i check = @gagent.getResponse("\"#{@assumption}\" is true") puts "[emoagent.assume_p]2 gagent response==#{check}" if check =~ /Okay/ then is_true = " is true" end end return "Okay, I will assume #{@assumption}#{is_true}." end # method_def def x_is_absurd(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern x = process $1 puts "@assumption: #{@assumption}; x: #{x}" if @assumption == nil then return 'Is it?' end if @graph.a_r_b?(@assumption.downcase, '->', x.downcase) arr = @graph.shortest_path(@assumption.downcase, '->', x.downcase) #puts arr.join("|") @questions.concat(arr) if @assumption.size == 1 return "Therefore, not-#{@assumption}." else begin #opp, s = @oppagent.send("opposite: #{@assumption}") opp, s = @oppagent.getResponse("opposite: #{@assumption}") puts "[emoagent.x_is_absurd]1: opp==#{opp}" return "Therefore, #{opp}." rescue return "Therefore, It is not the case that #{@assumption}." end end end #puts @graph.vertex_hash @graph.vertex_hash.each_key { |key| puts "key: #{key}; x: #{x}" startgagent #puts "after startgagent!" begin if key =~ /(.*) is (.*)/ puts "$1==#{$1}; $2==#{$2}" puts "sending to gagent: 'is \"#{$1}\" \"#{$2}\"\?'" ans, s = @gagent.getResponse("is \"#{$1}\" \"#{$2}\"\?") else puts "sending to gagent: 'is \"#{key}\" true?'" ans, s = @gagent.getResponse("is \"#{key}\" true?") end rescue => error puts "error sending to gagent: #{error}" @gagent_started = false end puts "ans==#{ans}" if ans =~ /^Yes/ # TODO: add to @questions if @assumption.size == 1 return "Therefore, not-#{@assumption}." else begin #opp, s = @oppagent.send("opposite: #{@assumption}") opp, s = @oppagent.getResponse("opposite: #{@assumption}") puts "[emoagent.x_is_absurd]2: opp==#{opp}" return "Therefore, #{opp}." unless opp == '' rescue return "Therefore, It is not the case that #{@assumption}." end return "Therefore, It is not the case that #{@assumption}." end end } return 'Is it really?' end # method_def def startgagent if @gagent_started then return end #puts "startgagent!!" DRb.start_service() @gagent = DRbObject.new(nil, 'druby://localhost:9011') puts "startgagent!!!" begin #if @gagent != nil #help = @gagent.send("gagent: help") help = @gagent.getResponse("gagent: help") puts "help == #{help}" @gagent_started = true if help =~ /logical/ then return end #end rescue => err puts "error: #{err}" begin Dir.chdir("..\\..") IO.popen("gagentd.bat") # Windows-specific! Dir.chdir("controller\\emoagent") rescue => error puts "error 2: #{error}" end end end def testoppagent begin #help = @oppagent.send("oppbot: help") help = @oppagent.getResponse("oppbot: help") if help =~ /opposite/ then return end rescue => err puts "error: #{err}" begin Dir.chdir("..\\oppagent") IO.popen("start oppbotd.rb") # Windows-specific! Dir.chdir("..\\emoagent") rescue => error puts "error 2: #{error}" end end end def any_questions?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern if @questions.size > 0 return "Yes: are you sure \"#{@questions.uniq.join('"? and that "')}\"?" end return 'No questions at this time.' end # method_def def clear_questions(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern size = @questions.size if size == 0 then return "I don't have any questions." end @questions.clear return "I have cleared my #{size} questions." end # method_def def show_assumption(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern return "The current assumption is: #{@assumption}" end # method_def end # class if $0 == __FILE__ bot = MyAgent.new print "\n> "; $stdout.flush while (line = gets) !~ /^#{$Quitwords.join('|')}$/i and line !~ /^$/ response = bot.send(line.to_s).to_s if response.class == "Array" then response = response[0] end response = response.chop # remove the score puts response + "\n\n" print "> "; $stdout.flush end puts "Bye" end # of file