################################################################################ # logicagent.rb implements a natural language interface to graph.rb. # (see subbot.org/logicagent/graph.rb). # # To run: put subbot.org/logicagent/graph.rb and subbot.org/logicagent/logicagent-api.yaml # in the same directory as this file. Then: # # C:\trane\logicagent>ruby logicagent.rb # I have loaded C:/trane/logicagent/logicagent-api.yaml. # # > All metals are solid. # Okay, All metals are solid. # # > Mercury is a metal. # Okay, Mercury is a metal. # # > a metal is all metals # Okay, a metal is all metals. # # > is Mercury solid? # Yes, Mercury is solid. # # > Mercury is not solid. # Okay, Mercury is not solid. # # > forget "all metals are solid." # Okay I have forgotten all metals are solid. # # > is Mercury solid? # No, Mercury is not solid. # # > ################################################################################ require 'yaml' begin require './graph' rescue LoadError require_relative 'graph' end require 'drb' $Quitwords = [':q', 'quit', 'exit', 'bye'] class MyAgent attr_accessor :plurals, :singulars, :verbs, :verb_forms, :names, :DEBUG def initialize(args=Array.new) @workingDir = File.expand_path(File.dirname(__FILE__)) @filename = @workingDir + "/logicagent-api.yaml" @logicagent_state = @workingDir + "/memory/logicagent_state.txt" 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], [/^show api/, :show_api], [/^show (.*)'s api/, :show_one_method_api], [/^(#{$Quitwords.join('|')})$/, :quit] ] save_synonyms # creates patterns.yaml on first use. else puts load_message end @method_def = '' @graph = Graph.new @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", "contains" ] @verb_forms = { "is" => "are", "has" => "have", "eats" => "eat", "is had by" => "are had by", "student teaches" => "student teach", "contains" => "contain" } @opposites = Hash.new @opposites["is taller than"] = "is shorter than" @opposites["is shorter than"] = "is taller than" @names = Array.new @save = false @already_asked = false @quit_question_state = '' @logging = false # not being used; $logger is called from client DRb.start_service() @monty = DRbObject.new(nil, 'druby://localhost:9070') @those = nil @DEBUG = 0 # @last_file_read must be the last line in initialize @last_file_read = "highest.txt" end # push new pattern before another pattern associated with an existing symbol def smart_push found = false @patterns.each_with_index { |e,i| puts "[smart_push]: e.inspec==#{e.inspect}" unless @DEBUG == 0 pattern = e[0].to_s symbol = e[1].to_s puts "[smart_push]: pattern==#{pattern}" unless @DEBUG == 0 #if pattern =~ Regexp.new(Regexp.escape("(?i-mx:(.*)")) if symbol == @new_symbol.to_s puts "[smart_push]: symbol matched!" unless @DEBUG == 0 @patterns.insert(i, [@new_regexp, @new_symbol]) found = true end if found then return end } @patterns.push([@new_regexp, @new_symbol]) 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] ) # replace above line with 'smart push' smart_push save_synonyms puts "#{method_def} added." 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." @score = 2 # Strip names from input (don't strip if input is "NAME is not a name") @names.each { |name| escaped_name = Regexp.escape(name) if input =~ /^#{escaped_name}[:, ]*(.*)/ new_input = $1.strip break if new_input =~ /^is not a name/ input = new_input break end } #puts "input==#{input}" @patterns.each { |pattern| if input =~ pattern[0] puts "[getResponse] pattern=#{pattern.inspect}" unless @DEBUG == 0 return [self.send(pattern[1], pattern[0], input), @score] 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 # a is a synonym for b, or /a/ is a synonym for /b/ def add_synonym(pattern, input) return "Problem matching pattern." unless input =~ pattern g1 = $1; g2 = $2; #puts "[add_synonym] g1==#{g1}; g2=#{g2}" # Remove enclosing "/", if present. if g1 =~ /^\// then g1.sub!(/^\//, '') end if g1 =~ /\/$/ then g1.sub!(/\/$/, '') end if g2 =~ /^\// then g2.sub!(/^\//, '') end if g2 =~ /\/$/ then g2.sub!(/\/$/, '') end #puts "[add_synonym] g1==#{g1}; g2=#{g2}" grp1 = %r{(?i-mx:^#{g1})} grp2 = %r{(?i-mx:^#{g2})} #puts "[add_synonym] grp1==#{grp1}; grp2=#{grp2}" method ='' index = -1 # To add the new pattern before the old one. @patterns.each_with_index { |p,i| #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] index = i break end } # If still no match, try again with slightly different regexps. if method == '' then grp1 = %r{(?i-mx:#{g1})}; grp2 = %r{(?i-mx:#{g2})}; end @patterns.each_with_index {|p,i| if p[0].to_s == grp2.to_s; method = p[1]; index = i; break; end} if method == '' then grp1 = %r{#{g1}}; grp2 = %r{#{g2}}; end @patterns.each_with_index {|p,i| if p[0].to_s == grp2.to_s; method = p[1]; index = i; break; end} if method == '' then grp1 = %r{^#{g1}}; grp2 = %r{^#{g2}}; end @patterns.each_with_index {|p,i| if p[0].to_s == grp2.to_s; method = p[1]; index = i; break; end} return unless method.to_s != nil puts "add_synonym: index==#{index}" unless @DEBUG == 0 @patterns.insert(index, [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 quit(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern if @save then return 'quit' end if @already_asked then return 'quit' end #if @already_asked then return end @already_asked = true @quit_question_state = "waiting for yes/no" return "Do you wish to save the graph to graph.yaml?" end def no(pattern=nil, input=nil) #puts "@quit_question_state==#{@quit_question_state}" return "#{input} doesn't match #{pattern}" unless input =~ pattern if @quit_question_state == "waiting for yes/no" @quit_question_state = 'answered' return 'quit' end return '' end def yes(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern if @quit_question_state == "waiting for yes/no" @quit_question_state = 'answered' self.send("save graph") return 'quit' end return '' 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!(/\?$/, '') str.sub!(/\"/, '') # strips double quotes from inside the string return str end def restart(pattern=nil,input=nil) exec "ruby #{__FILE__}" end # method_def def show_api(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern apiarr= Array.new @patterns.each { |p| regexp = p[0].to_s; method = p[1].to_s; found = false puts "regexp=#{regexp}; method=#{method}" unless @DEBUG == 0 apiarr.each { |arr| if arr.include? method then found = true end } next unless not found apiarr.push [regexp, method] #puts "apiarr.inspect=#{apiarr.inspect}" unless @DEBUG == 0 } # Try to pretty-print. toReturn = '' puts "apiarr.inspect=#{apiarr.inspect}" unless @DEBUG == 0 apiarr.each { |arr| puts "1. arr=#{arr}" unless @DEBUG == 0 regex = arr[0]; method = arr[1] if regex =~ /\(\?(?:[i\-mx]*)(?:\:)?(?:\^)?(.*)\)/ regex = $1.to_s end if method == nil or method == '' then method = "Default response" end toReturn << method.to_s << " | " << regex.to_s << "\n" } return toReturn end # method_def def show_one_method_api(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 # Supplied method name. puts "[show_one_method_api] grp1 == #{grp1}." unless @DEBUG == 0 apiarr= Array.new # To hold methods and patterns from MyAgent-api.yaml. @patterns.each { |p| # Collect all the regexps for the supplied method. regexp = p[0].to_s; method = p[1].to_s puts "regexp=#{regexp}; method=#{method}." unless @DEBUG == 0 if method =~ /(.+)(\!|\?)+/ then method = $1 end if method == grp1 then apiarr.push [regexp, method] end } # Try to pretty-print. toReturn = '' puts "apiarr.inspect=#{apiarr.inspect}" unless @DEBUG == 0 apiarr.each { |arr| puts "1. arr=#{arr}" unless @DEBUG == 0 regex = arr[0]; method = arr[1] if regex =~ /\(\?(?:[i\-mx]*)(?:\:)?(?:\^)?(.*)\)/ # try to clean regex regex = $1.to_s end if method == nil or method == '' then method = "Default response" end toReturn << method.to_s << " | " << regex.to_s << "\n" } return toReturn end # method_def # Begin custom logicagent methods. def a_r_b!(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 b = process $2 if $2 =~ /\?$/ then return end # to prevent misinterpretation of questions newa = a; newa = a.downcase unless a =~ /http\:\/\// # special cases if a =~ /sudo / then newa = a end newb = b; newb = b.downcase unless b =~ /http\:\/\// if b =~ /sudo / then newb = b end puts "a==#{a}, newa==#{newa}; b==#{b}, newb==#{newb}" unless @DEBUG == 0 @graph.a_r_b!(newa, "is", newb) if @save then @graph.save end be = "is"; if isPlural?(a) then be = "are" end return "Okay, #{a} #{be} #{b}." end # method_def def what_is(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 be = "is"; if isPlural?(a) then be = "are" end arr = @graph.a_r_?(a.downcase, ["is", "="]) puts "[what_is] arr==#{arr.inspect}" unless @DEBUG == 0 # Delete redundant responses if arr.include?(a.downcase) arr.delete(a.downcase) unless arr.size == 1 end if arr.empty? or arr == nil arr = @graph.what_r_b?(["is", "="], a.downcase) return "I don't know what #{a} #{be}." unless arr.size > 0 return "#{a} #{be} #{arr.join(', ')}" end @score = 4 return "#{a} #{be} #{arr.join(', ')}" end # method_def def is_a_b?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 grp2 = process $2 if grp1 == "" or grp1 == nil then return "No" end puts "[logicagent] is_a_b?: grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 be = "is"; if isPlural?(grp1) then be = "are" end if grp1 == grp2 then return "Yes, #{grp1} #{be} #{grp2}." end # reflexivity if @graph.a_r_b?(grp1.downcase, "is_not", grp2.downcase) return "No, #{grp1} #{be} not #{grp2}." end #if @graph.a_r_b?(grp1.downcase, ["is", "="], grp2.downcase) if @graph.a_r_b?(grp1.downcase, ["is", "=", "is a member of"], grp2.downcase) #if @graph.a_r_b?(grp1.downcase, "is", grp2.downcase) return "Yes, #{grp1} #{be} #{grp2}." end # To deal with "who" clauses: # ask: "What are #{grp1}'s relations?" relations_array = raw_a_what?(/What are (.*)'s relations/, "What are #{grp1}'s relations?")#.split(" ") puts "1 relations_array==#{relations_array.inspect}" unless @DEBUG == 0 # for each two-element combination in grp1's relations, # ask: "Is #{two-element combination} #{grp2}?" # if yes, return yes. # no "combination" method in Ruby 1.8.6! Try to use backports code. begin require './permutation' rescue LoadError require_relative 'permutation' end relations_array.permutation(2) { |c| puts "2 relations_array.permutation==#{c}" unless @DEBUG == 0 whoclause = c.join(' who ') whoclause.sub!(/^is /, '') whoclause.gsub!(/ /, ' ') whoclause.strip! puts "3 whoclause==#{whoclause}" unless @DEBUG == 0 question = "is \"#{whoclause}\" \"#{grp2}\"?" puts "4 question==#{question}" unless @DEBUG == 0 response = getResponse(question)[0] puts "5 response==#{response}" unless @DEBUG == 0 return response unless response !~ /^Yes/i } return "I have no knowledge that #{grp1} #{be} #{grp2}." end # method_def # why is ___ ____ def why(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 puts "why: grp1 == #{grp1}; grp2 == #{grp2}." if grp1 == grp2 then return "Because of reflexivity." end # reflexivity #arr = @graph.shortest_path(grp1.downcase, "is", grp2.downcase) #arr = @graph.shortest_path(grp1.downcase, ["is", "="], grp2.downcase) arr = @graph.shortest_path(grp1.downcase, ["is", "=", "is a member of"], grp2.downcase) puts "[logicagent] why: #{arr.inspect}" unless @DEBUG == 0 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} #{be} #{grp2} because 'is' is reflexive." end if arr2.empty?; return "I have no knowledge that #{grp1} #{be} #{grp2}" end return "#{grp1} #{be} #{grp2} because: #{arr2.join(', and ')}" end # method_def def make_verb_agree_1(input=nil, pattern=nil) # wrapper for make_verb_agree s return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 puts "[make verb agree]: grp1==#{grp1}" unless @DEBUG == 0 make_verb_agree(grp1) end # method_def 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 # "___ is not ___" def a_is_not_b(input=nil, pattern=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 s = self.send("is #{grp1} #{grp2}?")[0] if s =~ /^Yes/ s2 = self.send("why is #{grp1} #{grp2}?")[0] #return "That contradicts the information I have that #{s2}." toReturn "That contradicts the information I have that #{s2}." end # Here we need to store the "is not" relation in the graph, but for now: @graph.a_r_b!(grp1.downcase, "is_not", grp2.downcase) if @save then @graph.save end return "Okay, #{grp1} is not #{grp2}." end # method_def def a_does_not_equal_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 @graph.a_r_b!(grp1.downcase, "!=", grp2.downcase) if @save then @graph.save end return "Okay, #{grp1} != #{grp2}." end # method_def # "___ does not have ___" def a_does_not_have_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} have #{grp2}?")[0] if s =~ /^Yes/ s2 = self.send("why does #{grp1} have #{grp2}?")[0] return "That contradicts the information I have that #{s2}." end # We need to store the "does not have" relation in the graph, but for now: return "Okay" end # method_def def generic_remove_edge(input=nil, pattern=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 puts "[generic_remove_edge] grp2==#{grp2}" unless @DEBUG == 0 begin b = @graph.remove_edge(grp1.downcase, grp2.downcase, grp3.downcase) if @save then @graph.save end if b return "Okay I have forgotten #{grp1} #{grp2} #{grp3}." end rescue => error return "Error: #{error}" end return "I can't remove #{grp1} #{grp2} #{grp3}." end # "remove edge ____ is ____" def remove_is_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, "is", grp2.downcase) if @save then @graph.save end if b return "Okay I have forgotten #{grp1} #{be} #{grp2}." end rescue => error return "Error: #{error}" end return "I can't remove #{grp1} #{be} #{grp2}." end # method_def # "remove edge ___ = ___" def remove_equals_edge(input = nil, pattern = nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 begin #b = @graph.remove_edge(grp1.downcase, "=", grp2.downcase) b = @graph.remove_edge(grp1.downcase, "=", grp2.downcase) c = @graph.remove_edge(grp2.downcase, "=", grp1.downcase) @graph.remove_edge(grp2.downcase, "=", grp2.downcase) @graph.remove_edge(grp1.downcase, "=", grp1.downcase) if @save then @graph.save end if b and c return "Okay I have deleted #{grp1} = #{grp2}." end rescue => error return "I can't remove #{grp1} = #{grp2}. Error: #{error}" end end # method_def def remove_has_edge(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1; r = "has"; b = process $2 has = "has"; if isPlural?(a) then has = "have" end if @graph.remove_edge!(a, r, b) if @save then @graph.save end return "Okay I have forgotten #{a} #{has} #{b}." end return "I can't find #{a} #{has} #{b}..." 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 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_opposites(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 @opposites[grp1] = grp2 @opposites[grp2] = grp1 return "Okay, I have recorded that #{grp1} and #{grp2} are opposites." end # method_def def what_is_the_opposite_of(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 opposite = @opposites.fetch(grp1) if opposite == nil then return "I don't know what the opposite of #{grp1} is." end @score += 2 return opposite 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 file = "#{@workingDir}/#{file}" unless file =~ /#{@workingDir}\/#{file}/ puts "\n... Saving graph to #{file}..." r = @graph.save(file) @score += 2 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 a_equals_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 puts "[logicagent.a_equals_b] grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 @graph.a_r_b!(grp1.downcase, "=", grp2.downcase) @graph.a_r_b!(grp2.downcase, "=", grp1.downcase) # symmetric @graph.a_r_b!(grp1.downcase, "=", grp1.downcase) # reflexive @graph.a_r_b!(grp2.downcase, "=", grp2.downcase) #@graph.a_r_b!(grp1, "=", grp2) #@graph.a_r_b!(grp2, "=", grp1) # symmetric #@graph.a_r_b!(grp1, "=", grp1) # reflexive #@graph.a_r_b!(grp2, "=", grp2) if @save then @graph.save end return "Okay, #{grp1} = #{grp2}." end # method_def def a_includes_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 puts "[logicagent.a_includes_b] grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 @graph.a_r_b!(grp1.downcase, "includes", grp2.downcase) @graph.a_r_b!(grp2.downcase, "is a member of", grp1.downcase) #@graph.a_r_b!(grp1.downcase, ">=", grp2.downcase) #@graph.a_r_b!(grp2.downcase, "<=", grp1.downcase) if @save then @graph.save end return "Okay, #{grp1} includes #{grp2}." end # method_def def does_a_equal_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 puts "[logicagent.does_a_equal_b] grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 #if @graph.a_r_b?(grp1.downcase, "=", grp2.downcase) if @graph.a_r_b?(grp1.downcase, "!=", grp2.downcase) return "No, #{grp1} != #{grp2}." end if @graph.a_r_b?(grp1, "=", grp2) return "Yes, #{grp1} = #{grp2}." end return "I am not certain whether #{grp1} = #{grp2}." end # method_def def why_does_a_equal_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 #arr = @graph.shortest_path(grp1.downcase, "=", grp2.downcase) arr = @graph.shortest_path(grp1, "=", grp2) if arr.empty?; return "I have no knowledge that #{grp1} = #{grp2}." end return "#{grp1} = #{grp2} because: #{arr.join(', and ')}" end # method_def def what_does_a_equal(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 puts "[logicagent.what_does_a_equal] grp1==#{grp1}" unless @DEBUG == 0 #arr = @graph.a_r_?(grp1.downcase, "=") arr = @graph.a_r_?(grp1, "=") puts "arr==#{arr}" unless @DEBUG == 0 return "#{grp1} = #{arr.join(', and ')}" end # method_def # "all birds have wings" def a_has_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1; r = process $2; b = process $3 r2 = r if r == "have" then r2 = "has" end @graph.a_r_b!(a.downcase, r2.downcase, b.downcase) if @save then @graph.save end return "Okay, #{a} #{r} #{b}." end # method_def def what_does_a_have(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; a = process $2 is_array = @graph.a_r_?(a.downcase, "is") has_array = Array.new is_array.each { |e| has_array.concat(@graph.a_r_?(e, "has")) } ar = @graph.a_r_?(a.downcase, "has") ar.concat(has_array) has = "has"; if isPlural?(a); has = "have"; end if a =~ /^i$/i then a = "you"; has = "have" end return "#{a} #{has}: #{ar.join(', and ')}" end # method_def def does_a_have_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; a = process $2; b = process $3 r = ["has","is"] @has_includes_equals = true # dangerous; set this option manually at runtime. if @has_includes_equals == true r = ["has", "is", "="] end bool = @graph.a_r_b?(a.downcase, r, b.downcase) has = "has"; if isPlural?(a) then has = "have" end if a =~ /^i$/i then a = "you"; has = "have" end if bool then return "Yes, #{a} #{has} #{b}" end return "I have nothing to indicate that #{a} #{has} #{b}." end # method_def def why_does_a_have_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 arr = @graph.shortest_path(grp1.downcase, ["has", "is"], grp2.downcase) arr2 = Array.new arr.each_with_index { |phrase, i| phrase = make_verb_agree(phrase) arr2 << phrase } has = "has"; if isPlural?(grp1) then has = "have" end if grp1 =~ /^i$/i then grp1 = "You"; has = "have"; end if arr2.empty? return "I have no knowledge that #{grp1} #{has} #{grp2}." end return "#{grp1} #{has} #{grp2} because: #{arr2.join(', and ')}" end # method_def def a_may_make_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1; b = process $2 r = "may make" @graph.a_r_b!(a.downcase, r, b.downcase) if @save then @graph.save end return "Okay, #{a} #{r} #{b}." end # method_def def can_a_make_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1; b = process $2 bool = @graph.a_r_b?(a.downcase, ["may make", "is"], b.downcase) if bool then return "Yes, #{a} may make #{b}." end return "I have no knowledge that #{a} may make #{b}." end # method_def def a_is_part_of_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern b = process $1; a = process $2 has = "has"; if isPlural?(a) then has = "have" end @graph.a_r_b!(a.downcase, "has", b.downcase) if @save then @graph.save end return "Okay, #{a} #{has} #{b.downcase}." end # method_def def a_flies(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 fly = "flies"; if isPlural?(a) then fly = "fly" end @graph.a_r_b!(a.downcase, "flies", ' ') if @save then @graph.save end return "Okay, #{a} #{fly}." end # method_def def can_a_fly(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 if @graph.a_r_b?(a.downcase, ["flies", "is"] , ' ') then return "Yes, #{a} can fly." end return "No, #{a} can't fly." end # method_def def why_does_a_fly(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.shortest_path(grp1.downcase, ["flies", "is"], ' ') arr2 = Array.new arr.each_with_index { |phrase, i| phrase = make_verb_agree(phrase) arr2 << phrase } fly = "flies"; if isPlural?(grp1) then fly = "fly" end if grp1 =~ /^i$/i then grp1 = "You"; fly = "fly"; end if arr2.empty? return "I have no knowledge that #{grp1} #{fly}." end return "#{grp1} #{fly} because: #{arr2.join(', and ')}" end # method_def def begin_log(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern file = process $1 file = @workingDir + "/log.txt" unless file != nil and file != '' @logging = true $logger = File.open(file, "w") t = Time.now; y = t.year; m = t.month; d = t.day; h= t.hour; min = t.min; s = t.sec mins = sprintf("%02d", min) secs = sprintf("%02d", s) time = "#{y}/#{m}/#{d} #{h}:#{mins}:#{secs}" return "Okay I have started logging to #{file} at #{time}" end # method_def def end_log(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern t = Time.now; y = t.year; m = t.month; d = t.day; h= t.hour; min = t.min; s = t.sec mins = sprintf("%02d", min) secs = sprintf("%02d", s) time = "#{y}/#{m}/#{d} #{h}:#{mins}:#{secs}" r = "Okay I have stopped logging at #{time}." $logger.puts(r); $logger.close; $logger == nil @logging = false return r end # method_def def how_many_facts(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern begin; a = @graph.vertex_hash.keys; rescue => boom; puts boom; end puts 'hello' unless @DEBUG == 0 n = a.size vertices = "vertices"; if n == 1 then vertices = "vertex" end return "I have #{n} #{vertices} stored in my database." end # method_def def add_name(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern #grp1 = process $1 grp1 = $1.strip grp1.sub!(/^(?:"|')/, '') grp1.sub!(/(?:"|')$/, '') grp1.sub!(/(?:,)$/, '') @names.push(grp1) return "Okay, #{grp1} is a name." end # method_def def what_are_names(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern return @names.join(", ") 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 x_is_not_a_name(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern name_to_del = $1 result = @names.delete(name_to_del) if result == nil return "I can't find #{name_to_del} among the names I have stored." end return "Okay, I have deleted #{result}." end # method_def def is_x_a_name(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern name = $1 if @names.include?(name) return "Yes, #{name} is a name." end return "No, #{name} is not a name." end # method_def alias is_x_a_name? is_x_a_name 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 read_file(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern file = process $1 if file !~ /^highest.txt/ if file !~ /^openmind(\\|\/)/ then file = "openmind/" + file end end turn_save_back_on = false if @save; @save = false; turn_save_back_on = true; end count = 0 IO.foreach(file) { |assertion| if assertion =~ /^#/ then next end if assertion.strip == '' then next end count += 1 puts "#{count}: " + assertion unless @DEBUG == 0 self.send(assertion) } if turn_save_back_on; self.send("save graph"); @save = true; end @last_file_read = file write_last_file(file) #unless @save == false plural = 's'; if count == 1 then plural = ''; end return "Okay, #{file} read; #{count} assertion#{plural}." end # method_def def write_last_file(file) orig = IO.read(__FILE__) orig =~ /(.*?)\@last_file_read \= (.*?)( end.*)/m grp1 = $1; grp2 = $2; grp3 = $3 new_filename = get_new_filename File.open(new_filename, "w+") { |f| f.puts grp1 + "@last_file_read = \"" + file + "\"\n" + grp3 } end def what_was_last_file_read(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern return "The last file I read was: #{@last_file_read}." end # method_def def you_are_likely_to_find(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern two_phrases = process $1 # now use link or montylingua to separate the two phrases return "Not yet implemented." end # method_def def how_many_factoids(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern hash = @graph.vertex_hash count = 0 keys = hash.keys keys.each { |key| values = @graph.vertex_hash.fetch(key) values.each { |value| count += 1 #puts "#{count}: #{value}" } } return "I know #{count} factoids." end # method_def def is_blank_true(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 subject = @monty.send("subject: #{grp1}") verb = @monty.send("verb: #{grp1}") object = @monty.send("object: #{grp1}") puts "subject==#{subject}; verb==#{verb}; object==#{object}" unless @DEBUG == 0 if @graph.a_r_b?(subject.downcase, "recommends", object.downcase) return "Yes, #{grp1}." else return "I have no knowledge that #{grp1}." end end # method_def def does_a_recommend_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 s = 's'; if isPlural?(grp1) then s = ''; end puts "a==#{grp1}; b==#{grp2}" unless @DEBUG == 0 if @graph.a_r_b?(grp1.downcase, "recommends", grp2.downcase) return "Yes, #{grp1} recommend#{s} #{grp2}." else return "I have no knowledge that #{grp1} recommend#{s} #{grp2}." end end def a_recommends_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 @graph.a_r_b!(grp1.downcase, "recommends", grp2.downcase) if @save then @graph.save end return "Okay, #{grp1} recommends #{grp2}" end # method_def def x_eat_y(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 @graph.a_r_b!(grp1.downcase, "eat", grp2.downcase) if @save then @graph.save end return "Okay, #{grp1} eat #{grp2}" end # method_def def does_x_eat_y(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 s = 's'; if isPlural?(grp1) then s = ''; end puts "x==#{grp1}; y==#{grp2}" unless @DEBUG == 0 if @graph.a_r_b?(grp1.downcase, "eat", grp2.downcase) return "Yes, #{grp1} eat#{s} #{grp2}." else return "I have no knowledge that #{grp1} eat#{s} #{grp2}." end end # method_def def why_does_a_eat_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 arr = @graph.shortest_path(grp1.downcase, ["eat"], grp2.downcase) arr2 = Array.new arr.each_with_index { |phrase, i| phrase = make_verb_agree(phrase) arr2 << phrase } eats = "eats"; if isPlural?(grp1) then eats = "eat" end if grp1 =~ /^i$/i then grp1 = "You"; eats = "eat"; end if arr2.empty? return "I have no knowledge that #{grp1} #{eats} #{grp2}." end return "#{grp1} #{eats} #{grp2} because: #{arr2.join(', and ')}" end # method_def def what_do_x_eat(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 eat = "eats"; if isPlural?(grp1) then eat = "eat" end #arr = @graph.a_r_?(grp1.downcase, ["eat","is"]) arr = @graph.a_r_?(grp1.downcase, ["eat"]) if arr.empty? or arr == nil return "I don't know what #{grp1} #{eat}." end return "#{grp1} #{eat} #{arr.join(', ')}" end # method_def def what_eat_x(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.what_r_b?("eat", grp1.downcase) #puts "arr==#{arr.inspect}" if arr.empty? or arr == nil return "I don't know what eats #{grp1}" end return "#{arr.join(', ')} eat #{grp1}." end # method_def def x_student_teach(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 es = "es"; if isPlural?(grp1) then es = "" end @graph.a_r_b!(grp1.downcase, "student teach", "") return "Okay, #{grp1} student teach#{es}." end # method_def def do_x_student_teach(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 es = "es"; if isPlural?(grp1) then es = "" end if @graph.a_r_b?(grp1.downcase, ["student teach", "is had by", "is"], "") return "Yes, #{grp1} student teach#{es}." end return "I have no knowledge that #{grp1} student teach#{es}." end # method_def def why_do_x_student_teach(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 es = "es"; if isPlural?(grp1) then es = "" end arr = @graph.shortest_path(grp1.downcase, ["student teach", "is had by", "is"], "") arr2 = Array.new arr.each_with_index { |phrase, i| phrase = make_verb_agree(phrase) arr2 << phrase } if arr2.size > 0 return "#{grp1} student teach#{es} because: #{arr2.join(', and ')}." end return "I have no knowledge that #{grp1} student teach#{es}." end # method_def def a_is_had_by_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 is = "is"; if isPlural?(grp1) then is = "are" end @graph.a_r_b!(grp1.downcase, "is had by", grp2.downcase) return "Okay, #{grp1} #{is} had by #{grp2}." end # method_def def a_contains_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 puts "[logicagent.a_contains_b] grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 @graph.a_r_b!(grp1.downcase, "contains", grp2.downcase) contains = "contains"; if isPlural?(grp1) then contains = "contain"; end return "Okay, #{grp1} #{contains} #{grp2}." end # method_def def does_a_contain_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 contains = "contains"; if isPlural?(grp1) then contains = "contain"; end puts "[logicagent.does_a_contain_b] grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 if @graph.a_r_b?(grp1.downcase, "contains", grp2.downcase) return "Yes, #{grp1} #{contains} #{grp2}." end return "I have no knowledge that #{grp1} #{contains} #{grp2}." end # method_def def why_does_a_contain_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 s = 's'; if isPlural?(grp1) then s = ''; end arr = @graph.shortest_path(grp1.downcase, ["contains"], grp2.downcase) arr2 = Array.new arr.each_with_index { |phrase, i| phrase = make_verb_agree(phrase) arr2 << phrase } if arr2.size > 0 return "#{grp1} contain#{s} #{grp2} because: #{arr2.join(', and ')}." end return "I have no knowledge that #{grp1} contain#{s} #{grp2}." end # method_def def what_contains_x(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.what_r_b?("contains", grp1.downcase) if arr.size > 0 return "#{arr.join(', ')} contain #{grp1}." end return "I have no knowledge that anything contains #{grp1}." end # method_def def how_many_contain_x(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.what_r_b?("contains", grp1.downcase) @those = arr return "#{arr.size}" end # method_def def what_are_those(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern if @those != nil arr = @those @those = nil return "#{arr.join(', ')}" end if @those != nil and @those.size == 0 @those = nil return "Nothing" end return "I don't know what 'those' refers to." end # method_def def what_does_a_contain(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.a_r_?(grp1.downcase, "contains") s = "s"; if isPlural?(grp1) then s = ''; end if arr.size == 0 return "I have no knowledge that #{grp1} contain#{s} anything." end return "#{grp1} contain#{s}: #{arr.join(', ')}" end # method_def def what_color_is_x(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 if @graph.a_r_b?(grp1.downcase, ["is", "="], "a color") == false is = "is"; if isPlural?(grp1) then is = "are" end return "I don't know what color #{grp1} #{is}." end arr = @graph.shortest_path(grp1.downcase, ["is", "="], "a color") prop = arr[-2] if prop =~ / are (.*)/ or prop =~ / is (.*)/ or prop =~ / = (.*)/ color = process $1 else color = prop end @score = 4 is = "is"; if isPlural?(grp1) then is = "are" end return "I think you said #{grp1} #{is} #{color}." end # method_def # generalized version of the above def what_x_is_y(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 if @graph.a_r_b?(grp2.downcase, ["is", "="], "a #{grp1.downcase}") == false is = "is"; if isPlural?(grp1) then is = "are" end return "I don't know what #{grp1} #{grp2} #{is}." end arr = @graph.shortest_path(grp2.downcase, ["is", "="], "a #{grp1.downcase}") puts "[what_x_is_y]: arr==#{arr.inspect}" unless @DEBUG == 0 prop = arr[-2] if prop =~ / are (.*)/ or prop =~ / is (.*)/ or prop =~ / = (.*)/ ans = process $1 else ans = prop end @score = 4 is = "is"; if isPlural?(grp1) then is = "are" end return "I think you said #{grp2} #{is} #{ans}." end # method_def def a_is_related_to_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 b = process $2 @graph.a_r_b!(a.downcase, "is related to", b.downcase) if @save then @graph.save end be = "is"; if isPlural?(a) then be = "are" end return "Okay, #{a} #{be} related to #{b}." end # method_def def what_is_a_related_to?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 be = "is"; if isPlural?(a) then be = "are" end arr = @graph.a_r_?(a.downcase, "is related to") if arr.empty? or arr == nil return "I don't know what #{a} #{be} related to." end return "#{a} #{be} related to #{arr.join(', ')}" end # method_def alias what_is_a_related_to what_is_a_related_to? def is_a_related_to_b?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 grp2 = process $2 if grp1 == "" or grp1 == nil then return "No" end puts "[logicagent] is_a_related_to_b?: grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 be = "is"; if isPlural?(grp1) then be = "are" end if grp1 == grp2 then return "Yes, #{grp1} #{be} related to #{grp2}." end # reflexivity if @graph.a_r_b?(grp1.downcase, ["is related to", "is", "="], grp2.downcase) return "Yes, #{grp1} #{be} related to #{grp2}." end return "I have no knowledge that #{grp1} #{be} related to #{grp2}." end # method_def def why_is_a_related_to_b?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 arr = @graph.shortest_path(grp1.downcase, ["is related to", "is", "="], 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} #{be} related to #{grp2} because 'is' is reflexive." end if arr2.empty?; return "I have no knowledge that #{grp1} #{be} related to #{grp2}" end return "#{grp1} #{be} related to #{grp2} because: #{arr2.join(', and ')}" end # method_def def a_sat_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 b = process $2 @graph.a_r_b!(a.downcase, "sat", b.downcase) if @save then @graph.save end return "Okay, #{a} sat #{b}." end # method_def def what_did_a_sit(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 arr = @graph.a_r_?(a.downcase, "sat") if arr.empty? or arr == nil return "I don't know what #{a} sat." end return "#{a} sat #{arr.join(', ')}" end # method_def def did_a_sit_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 grp2 = process $2 if grp1 == "" or grp1 == nil then return "No" end puts "[logicagent] did_a_sit_b: grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 if grp1 == grp2 then return "Yes, #{grp1} sat #{grp2}." end # reflexivity if @graph.a_r_b?(grp1.downcase, ["sat", "is", "="], grp2.downcase) return "Yes, #{grp1} sat #{grp2}." end return "I have no knowledge that #{grp1} sat #{grp2}." end # method_def def why_did_a_sit_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 arr = @graph.shortest_path(grp1.downcase, ["sat", "is", "="], grp2.downcase) if arr2.empty?; return "I have no knowledge that #{grp1} sat #{grp2}" end return "#{grp1} sat #{grp2} because: #{arr.join(', and ')}" end # method_def def filter_arr_for_str(arr, str) if str.class == String then str == [str] end arr2 = Array.new arr.each { |elem| if elem =~ /(?:#{str.join("|")})/ arr2.push(elem) end } #puts "[filter_arr_for_str] arr2==#{arr2.inspect}" return arr2 end # method_def def when_is_a(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.a_r_?(grp1.downcase, ["is", "="]) if arr.empty? or arr == nil # do a fuzzier search for a return "I don't know when #{grp1} was." end arr2 = filter_arr_for_str(arr, ["from", "years", "in"]) if arr2.empty? or arr2 == nil return "I don't know when #{grp1} is." end return "#{grp1} is #{arr2.join(',')}" end # method_def def what_is_a_used_for(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 arr = @graph.a_r_?(grp1.downcase, ["is", "="]) puts "[what_is_a_used_for] arr==#{arr.inspect}" unless @DEBUG == 0 toReturn = Array.new arr.each { |elem| if elem =~ /\bus(?:es|ed|ing)\b/ toReturn.push(elem) end } be = "is"; if isPlural?(grp1) then be = "are" end return "#{grp1} #{be} #{toReturn.join(', ')}" unless toReturn.empty? return "I don't know what #{grp1} #{be} used for." end # method_def def update_graph(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern begin require 'net/http' Net::HTTP.start("subbot.org") { |http| resp = http.get("/logicagent/graph.yaml") open("graph.yaml", "wb") { |file| file.write(resp.body) } } rescue => err; return "Eep! #{err}"; end @score += 4 return "graph.yaml updated." end # method_def def a_is_heavier_than_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1; b = process $2 @graph.a_r_b!(a.downcase, "is heavier than", b.downcase) if @save then @graph.save end be = "is"; if isPlural?(a) then be = "are" end return "Okay, #{a} #{be} heavier than #{b}." end # method_def def what_is_a_heavier_than?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern a = process $1 be = "is"; if isPlural?(a) then be = "are" end arr = @graph.a_r_?(a.downcase, "is heavier than") if arr.empty? or arr == nil return "I don't know what #{a} #{be} heavier than." end return "#{a} #{be} heavier than #{arr.join(', ')}" end # method_def alias what_is_a_heavier_than what_is_a_heavier_than? def is_a_heavier_than_b?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 grp2 = process $2 if grp1 == "" or grp1 == nil then return "No" end puts "[logicagent] is_a_heavier_than_b?: grp1==#{grp1}; grp2==#{grp2}" unless @DEBUG == 0 be = "is"; if isPlural?(grp1) then be = "are" end if grp1 == grp2 then return "Yes, #{grp1} #{be} heavier than #{grp2}." end # reflexivity if @graph.a_r_b?(grp1.downcase, ["is heavier than", "is", "="], grp2.downcase) return "Yes, #{grp1} #{be} heavier than #{grp2}." end return "I have no knowledge that #{grp1} #{be} heavier than #{grp2}." end # method_def def why_is_a_heavier_than_b?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 arr = @graph.shortest_path(grp1.downcase, ["is heavier than", "is", "="], 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} #{be} heavier than #{grp2} because 'is' is reflexive." end if arr2.empty?; return "I have no knowledge that #{grp1} #{be} heavier than #{grp2}" end return "#{grp1} #{be} heavier than #{grp2} because: #{arr2.join(', and ')}" end # method_def # Generic method to add patterns of the type: A is greater than B. # Expects a pattern with three groups: /(.*) (is greater than) (.*)/ def generic_a_r_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 puts "[generic_a_r_b] grp1==#{grp1}; grp2==#{grp2}; grp3==#{grp3}." unless @DEBUG == 0 @graph.a_r_b!(grp1.downcase, grp2.downcase, grp3.downcase) grp2_opp = find_opposite_of(grp2) if grp2_opp != nil @graph.a_r_b!(grp3.downcase, grp2_opp.downcase, grp1.downcase) end return "Okay, #{grp1} #{grp2} #{grp3}." end # method_def def find_opposite_of(word = '') opposite = @opposites[word] return opposite end # For "A hits B" relations def generic_does_a_r_b(pattern=nil, input=nil) generic_a_r_b?(pattern,input,false) end # For "A is taller than B" relations def generic_is_a_r_b(pattern=nil, input=nil) generic_a_r_b?(pattern,input) end def generic_a_r_b?(pattern=nil, input=nil, is=true) puts "generic_a_r_b?" unless @DEBUG == 0 return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 # If grp2 == something, include "something" in the search. response, score = self.send("what is \"is #{grp2}\"?") arr = response.split(/ is |, /) if response =~ /don't know/ then arr = Array.new end puts "0. [generic_a_r_b?] arr==#{arr.inspect}" unless @DEBUG == 0 if arr == nil or arr.empty? then response, score = self.send("what is #{grp2}?") end arr = response.split(/ is |, /) if response =~ /don't know/ then arr = Array.new end puts "1. [generic_a_r_b?] arr==#{arr.inspect}" unless @DEBUG == 0 # If arr contains a math sign, then call a_sign_b if arr.include?(">") then return self.send("#{grp1.to_f} > #{grp3.to_f}")[0] end arr.push(grp2.downcase); arr.push("is"); arr.push("=") arr.push("is a member of") arr.push("includes") #arr.push("includes") puts "2. [generic_a_r_b?] arr==#{arr.inspect}" unless @DEBUG == 0 # deal with is_not s = "is \"#{grp1.downcase}\" \"#{grp2.downcase} #{grp3.downcase}\"?" puts "[generic_a_r_b?] s==#{s}" unless @DEBUG == 0 is_not_test, score = self.send(s) puts "[generic_a_r_b?] is_not_test = #{is_not_test}" unless @DEBUG == 0 if is_not_test =~ /\bnot\b/ then return "No, #{grp1} is not #{grp2} #{grp3}" end # puts "[generic_a_r_b?]: grp1==#{grp1.downcase}, arr==#{arr.inspect}, grp3==#{grp3.downcase}" unless @DEBUG == 0 if @graph.a_r_b?(grp1.downcase, arr, grp3.downcase) path = @graph.shortest_path(grp1.downcase, arr, grp3.downcase) puts "[generic_a_r_b?]: 1. path==#{path.inspect}" unless @DEBUG == 0 path.each() { |p| if p =~ /\b is a member of\b/ then path.delete(p) end if p =~ /\bis\b#{grp3}/ then path.delete(p) end if p =~ /\b=\b/ then path.delete(p) end } unless path.size < 2 puts "[generic_a_r_b?]: 2. path==#{path.inspect}" unless @DEBUG == 0 # no "be" for generic relations - 10/9/2012 if is be = "is"; if isPlural?(grp1) then be = "are" end return "Yes, #{grp1} #{be} #{grp2} #{grp3}." unless path.size == 0 end return "Yes, #{grp1} #{grp2} #{grp3}." unless path.size != 1 puts "[generic_a_r_b?]: 3. path==#{path.inspect}" unless @DEBUG == 0 if path.size > 1 # special case where aRb and b=c, so aRc if path.select { |elem| elem =~ /#{grp2}/}.size == 1 if path.select { |elem| elem =~ /\bis\b#{grp3}/}.size == 0 return "Yes, #{grp1} #{grp2} #{grp3}." end end end end if is then return "I have no knowledge that #{grp1} is #{grp2} #{grp3}." end return "I have no knowledge that #{grp1} #{grp2} #{grp3}." end # method_def def generic_what_did_a_r(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2 # Try to get verb and object, in case they aren't correctly delimited #clause = grp1 << grp2 #verb = getVerb(clause); obj = getObj(clause); arr = @graph.a_r_?(grp2.downcase, ["is", "="]) if arr == nil then arr = Array.new end if not arr.include?(grp2.downcase) then arr.push(grp2.downcase) end puts "[generic_what_did_a_r] arr==#{arr.inspect}" unless @DEBUG == 0 arr2 = @graph.a_r_?(grp1.downcase, arr) if arr2 == nil or arr2.empty? @score -= 1 return "I don't know what #{grp1} #{grp2}." end @score += 2 # TODO: change the verb (grp2) to the correct tense grp2_past = self.send("what is the past tense of #{grp2}?")[0] if grp2_past =~ /I don't know/ then grp2_past = grp2 end if grp2_past =~ /the past tense of (.*) is (.*)/i then grp2_past = process $2 end return "#{grp1} #{grp2_past} #{arr2.join(', ')}." end # method_def def generic_why_is_a_r_b(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 arr = @graph.shortest_path(grp1.downcase, [grp2.downcase, "is", "="], grp3.downcase) #arr = @graph.shortest_path(grp1.downcase, [grp2.downcase, "="], grp3.downcase) puts arr.inspect unless @DEBUG == 0 #if arr.empty?; return "I have no knowledge that #{grp1} is #{grp2} #{grp3}" end if arr.empty?; return "I have no knowledge that #{grp1} #{grp2} #{grp3}" end #return "#{grp1} is #{grp2} #{grp3} because: #{arr.join(', and ')}" return "#{grp1} #{grp2} #{grp3} because: #{arr.join(', and ')}" end # method_def def a_sign_b(pattern=nil, input=nil) puts "[a_sign_b] pattern==#{pattern.to_s}; input==#{input}" unless @DEBUG == 0 return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = $1.to_f; grp2 = $2.to_s; grp3 = $3.to_f if grp1 == 0.0 and grp3 == 0.0 then return "I don't know" end puts "[a_sign_b] grp1==#{grp1}; grp2==#{grp2}; grp3==#{grp3}" unless @DEBUG == 0 response = '' begin response = eval(input) rescue Exception => boom puts boom unless @DEBUG == 0 end if response == '' or response == "Default response." begin if grp2 == ">" then response = eval("#{grp1} > #{grp3}") end rescue Exception => exc return exc end end puts "[a_sign_b] response==#{response}." unless @DEBUG == 0 if response.to_s =~ /true/ then return "Yes" end if response.to_s =~ /false/ then return "No" end return "I don't know." end # method_def # Handles "which is heavier, 5g or 10g?" # NOTE: First define: "is heavier than" == ">" def which_is_blanker(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 response = self.send("is #{grp2} #{grp1} than #{grp3}?")[0] puts "[which_is_blanker] response==#{response}." unless @DEBUG == 0 if response =~ /yes/i then return "#{grp2} is #{grp1}." end #if response =~ /no/i and response !~ /knowledge/ resp2 = self.send("is #{grp3} #{grp1} than #{grp2}?")[0] #end if resp2 =~ /yes/i then return "#{grp3} is #{grp1}." end #if resp2 =~ /no/i then return "#{grp2} appears to equal #{grp3}." end return "I don't know which is #{grp1}." end # method_def def handle_three_inputs_on_one_line(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 response = self.send(grp1.to_s)[0] puts "[handle_3_inputs] #{grp1}; response=#{response}" unless @DEBUG == 0 response = self.send(grp2.to_s)[0] puts "[handle_3_inputs] #{grp2}; response=#{response}" unless @DEBUG == 0 response = self.send(grp3.to_s)[0] puts "[handle_3_inputs] #{grp3}; response=#{response}" unless @DEBUG == 0 return response end # method_def def a_whistles(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process($1.to_s) @graph.a_r_b!(grp1.downcase, "whistles", '') return "Okay, #{grp1} whistles." end # method_def def does_a_whistle(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1.to_s bool = @graph.a_r_b?(grp1.downcase, "whistles", '') if bool == true then return "Yes, #{grp1} whistles." end return "I have no knowledge that #{grp1} whistles." end # method_def # Returns an array of all a's relations. def raw_a_what?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1.to_s relations = @graph.vertex_hash.fetch(grp1.downcase, Array.new) puts "raw_a_what: grp1==#{grp1}" unless @DEBUG == 0 # The following should be moved to graph.rb but for now, I do it here. toReturn = Array.new relations.each { |edge| source = edge.fetch("source", nil) if source == nil then next end if source =~ /^#{grp1}$/i label = edge.fetch("label", nil) if label == nil then next end target = edge.fetch("target", nil) if target == nil then target = '' end toReturn.push(label + ' ' + target + ' ') end } return toReturn end def a_what?(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 toReturn = raw_a_what?(pattern, input) toReturn2 = Array.new; toDelete = '' # Start the response with a __ is ___ relation toReturn.each { |element| if element =~ /^(is|are) / then toReturn2.push element end toDelete = element } toReturn.delete(toDelete) toReturn2.concat(toReturn) #return "#{grp1} #{toReturn.join(' who ')}" unless toReturn.size == 0 return "#{grp1} #{toReturn2.join(' who ')}" unless toReturn2.size == 0 return "I can't find any relations for #{grp1}." end # method_def def is_np_np_who_vp(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1; grp2 = process $2; grp3 = process $3 new_input = "What are #{grp1}'s relations?" relations = raw_a_what?(/What are (.*)'s relations/, new_input) puts "is_np_np_who_vp: #{relations.inspect}." unless @DEBUG == 0 puts "is_np_np_who_vp: grp2==#{grp2}. grp3==#{grp3}." unless @DEBUG == 0 yes1 = false; yes2 = false relations.each { |relation| puts "is_np_np_who_vp: relation==#{relation}." unless @DEBUG == 0 if relation =~ /#{grp2}/i then yes1 = true end if relation =~ /#{grp3}/i then yes2 = true end } if yes1 and yes2 then return "Yes, #{grp1} is #{grp2} who #{grp3}." end return "I have no knowledge that #{grp1} is #{grp2} who #{grp3}." end # method_def end # class if $0 == __FILE__ bot = MyAgent.new print "\n> "; $stdout.flush while (line = gets)# !~ /^#{$Quitwords.join('|')}$/i and line !~ /^$/ begin; $logger.puts("> #{line}"); rescue; end response = bot.send(line.to_s).to_s if response.class == "Array" then response = response[0] end response = response.chop # remove the score if response.strip == 'quit' then break end puts response + "\n\n" begin; $logger.puts(response); rescue; end print "> "; $stdout.flush end puts "Bye" end # of file