################################################################################ # Balance Sheet agent. # # c:\MyAgent>ruby bsagent.rb # # > hello # Default response # # > add pattern /^hello/, hello # # > hello # Default response from hello # # > > def hello(pattern=nil,input=nil) # > > if @count==nil; @count=1; return 'I am ready for my first lesson.'; end # > > return 'Hello again.' # > > end # method_def # # > hello # I am ready for my first lesson. # # > hi is a synonym for hello # Okay, (?i-mx:^hi) has been added, and will now call hello. # # > hi # Hello again. # # > ################################################################################ require 'yaml' require './balancesheet' $Quitwords = [':q', 'quit', 'exit', 'bye'] class MyAgent attr_accessor :DEBUG def initialize(args=Array.new) @workingDir = File.expand_path(File.dirname(__FILE__)) @filename = @workingDir + "/bsagent-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], [/^show api/, :show_api], [/^show (.*)'s api/, :show_one_method_api] ] save_synonyms # creates patterns.yaml on first use. else puts load_message end @method_def = '' @DEBUG = 0 @balancesheets = [] 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." @score = 2 puts "getResponse: input==#{input}" unless @DEBUG == 0 @patterns.each { |pattern| puts "getResponse: pattern[0]=#{pattern[0]}" unless @DEBUG == 0 # <= 2 puts "getResponse: input = #{input}." unless @DEBUG == 0 #<= 2 if input =~ pattern[0] puts "getResponse: input matches pattern." 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}" unless @DEBUG == 0 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!(/\?$/, '') str.sub!(/^\//, '') str.sub!(/\/$/, '') 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 # Contains methods and patterns from MyAgent-api.yaml. @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] } # If more than one pattern maps to a method, only the first is stored. # 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 def show_one_method_api(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 # Supplied method name. 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 == 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 def add_assets_and_liabilities(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs = process $1.to_s; assets = process $2.to_s; liabilities = process $3.to_s #fix adding just a liability: check $2 for "liability" if assets == 'liabilities' then assets = ' ' end assetsarr = assets.split(', '); liabilitiesarr = liabilities.split(', ') puts bs, "assets: #{assetsarr}", "liabilities: #{liabilitiesarr}" unless @DEBUG == 0 bs2 = bs.gsub(/ /, '_') if !@balancesheets.include?(eval("@#{bs2}")) instance_variable_set("@#{bs2}", BalanceSheet.new(bs)) end # draw separator line between balance sheet modifications if (eval "@#{bs2}")['assets'].size > 0 (eval "@#{bs2}")['assets'].push('----------') end if (eval "@#{bs2}")['liabilities'].size > 0 (eval "@#{bs2}")['liabilities'].push('----------') end assetsarr.each { |a| (eval "@#{bs2}")['assets'].push(a)} liabilitiesarr.each { |l| (eval "@#{bs2}")['liabilities'].push(l)} @balancesheets.push(eval("@#{bs2}")) return (eval "@#{bs2}").draw end # method_def def assets(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs = $1.to_s bs2 = bs.gsub(/ /, '_') return (eval "@#{bs2}")['assets'].join(', ') end # method_def def liabilities(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs = $1.to_s bs2 = bs.gsub(/ /, '_') return (eval "@#{bs2}")['liabilities'].join(', ') end # method_def def show_all_balance_sheets(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @balancesheets.each { |bs| bs.draw } return '' end # method_def def set_reserve_requirement(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern begin reserve_requirement = $1.to_f rescue reserve_requirement = 0.2 end @balancesheets.each { |bs| bs.RESERVE_REQUIREMENT = reserve_requirement } return "Okay, I have set the reserve requirement to #{reserve_requirement}." end # method_def def lend_long(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern target = process $1.to_s puts "target: #{target}" unless @DEBUG == 0 @balancesheets.each { |bs| puts "bs.name: #{bs.name}" unless @DEBUG == 0 if bs.name =~ /#{target}/ bs.lend_long bs.draw end } return end # method_def def add_liability(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern target = process $1.to_s; liability = $2.to_s #target = target.gsub(/ /, '_') #target = eval("@#{target}") #@balancesheets.each { |bs| # if bs.name =~ /#{target.name}/ # bs['liabilities'].push(liability) # #bs['assets'] # bs.draw unless @DEBUG == 0 # end #} response = getResponse("#{target}: your assets are and your liabilities are #{liability}") return end # method_def def add_asset(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern target = process $1.to_s; asset = $2.to_s #target = target.gsub(/ /, '_') #target = eval("@#{target}") #puts "add_asset: target==#{target.name}. asset==#{asset}." #@balancesheets.each { |bs| # #puts " add_asset: bs==#{bs.name}." # if bs.name =~ /^#{target.name}$/ # bs['assets'].push(asset) # #bs['assets'] # bs.draw unless @DEBUG == 0 # end #} response = getResponse("#{target}: your assets are #{asset} and your liabilities are ") return end # method_def def transfer_liability(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern liability1 = process $2.to_s; liability2 = process $3.to_s bs2 = bs.gsub(/ /, '_') bs = eval("@#{bs2}") puts "bs=#{bs.name}; liability1=#{liability1}; liability2=#{liability2}." unless @DEBUG == 0 bs['liabilities'].each { |l| if liability1 == l #new_liability1 = liability1.sub(/(?:\d+(?:.)?\d*)+/, '0') new_liability1 = liability1.sub(/\d+(?:.)?\d*/, '0 ') bs['liabilities'].delete(liability1) bs['liabilities'].push(new_liability1) end } begin # to delete the defunct liability bs['liabilities'].delete(liability1) rescue; end bs['liabilities'].push(liability2) bs.draw unless @DEBUG == 0 end # method_def def delete_asset(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern asset1 = process $2.to_s bs = $1.gsub(/ /, '_') bs = eval("@#{bs}") puts "bs=#{bs.name}; asset1=#{asset1}." unless @DEBUG == 0 begin # to delete the asset r = bs['assets'].delete(asset1) if r == nil then r = bs['assets'].delete(asset1.lower) end if r == nil then r = bs['assets'].delete(asset1.capitalize) end if r != nil then return "#{asset1} deleted from #{bs.name}." end rescue; end bs.draw unless @DEBUG == 0 return "Problem deleteing #{asset} from #{bs.name}." end # method_def def delete_liability(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern liability1 = process $2.to_s bs = $1.gsub(/ /, '_') #puts "delete_liability: bs==#{bs}." bs = eval("@#{bs}") puts "bs=#{bs.name}; liability1=#{liability1}." unless @DEBUG == 0 begin # to delete the liability bs['liabilities'].delete(liability1) bs['liabilities'].delete(liability1.lower) bs['liabilities'].delete(liability1.capitalize) rescue #puts "couldn't delete liability: #{liability1}." #puts bs.inspect end #puts bs.inspect bs.draw unless @DEBUG == 0 end # method_def def buy_asset_from_seller_for_item(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern buyer = process $1.to_s asset = process $2.to_s seller = process $3.to_s item = process $4.to_s buyer.gsub!(/ / , '_') seller.gsub!(/ /, '_') puts "buyer: #{buyer}; asset: #{asset}; seller: #{seller}; item: #{item}." unless @DEBUG == 0 getResponse("#{buyer}: your assets are +#{asset}, -#{item}") getResponse("#{seller}: your assets are +#{item}, -#{asset}") #response = getResponse("#{seller}: delete asset #{asset}") #response = getResponse("#{seller}: add asset #{item}") #response = getResponse("#{buyer}: delete asset #{item}") #response = getResponse("#{buyer}: add asset #{asset}") #puts "response=#{response}" response = getResponse("show all balance sheets") #puts "response=#{response}" return response[0] #return 'Default response from pay_asset_to_seller_for_asset' end # method_def def read_file(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern file = process $1.to_s IO.foreach(file) { |statement| puts statement if statement =~ /^#/ then next end if statement.strip == '' then next end self.send(statement) #getResponse(statement) } #return 'Default response from read_file' end # method_def def transfer_asset_to_person(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern donor = process $1 asset = process $2 recipient = process $3 puts "transfer_asset_to_person: donor: #{donor}; asset: #{asset}; recipient: #{recipient}." unless @DEBUG == 0 #self.send("#{donor}: delete asset #{asset}") self.getResponse("#{donor}: delete asset #{asset}") #self.send("#{recipient}: add asset #{asset}") self.getResponse("#{recipient}: add asset #{asset}") return 'Default response from transfer_asset_to_person' end # method_def def clear_all_balance_sheets(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @balancesheets.each { |bs| bs['assets'].clear bs['liabilities'].clear } return end # method_def def echo(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern string = $1.to_s puts string unless @DEBUG == 2 return string end # method_def def show_one_balance_sheet(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs = $1.to_s bs = bs.gsub(/ /, '_') bs = eval("@#{bs}") #bs.draw return bs.draw_as_string end # method_def def give_asset_to_recipient(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern giver = process $1.to_s asset = process $2.to_s recipient = process $3.to_s giver = giver.gsub(/ /, '_') recipient = recipient.gsub(/ /, '_') puts "give_asset_to_recipient: giver=#{giver}; asset=#{asset}; recipient=#{recipient}." unless @DEBUG == 0 response = getResponse("#{giver}: delete asset #{asset}") if response !~ /^Problem/i response = getResponse("#{giver}: delete liability #{asset}") end response = getResponse("#{recipient}: add asset #{asset}") end # method_def def draw_side_by_side(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs1 = $1.to_s; bs2 = $2.to_s bs1.gsub!(/ /, '_'); bs2.gsub!(/ /, '_') bs1_string = eval("@#{bs1}").draw_as_string bs2_string = eval("@#{bs2}").draw_as_string bs1_array = bs1_string.split("\n") bs2_array = bs2_string.split("\n") #puts bs1_array.to_s #puts bs2_array.to_s output = '' max_bs1_array_line = 0 bs1_array.each_with_index { |line, index| if line.size > max_bs1_array_line then max_bs1_array_line = line.size end if bs2_array.size > index output += line + " " + bs2_array[index] + "\n" else output += line + " " + " \n" end } bs1_dummy_line = '' max_bs1_array_line.times do bs1_dummy_line += ' ' end if bs2_array.size > bs1_array.size bs2_array.each_with_index{ |line, index| if index >= bs1_array.size output += bs1_dummy_line + " " + bs2_array[index] + "\n" end } end return output end # method_def def decrement_entry(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs1 = process $1.to_s entry = process $2.to_s eval("@#{bs1}").decrement_entry(entry) #bs1_object = eval("@#{bs1}") #bs1_object.decrement_entry(entry) return "#{entry} decremented." end # method_def def increment_entry(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs1 = process $1.to_s entry = process $2.to_s eval("@#{bs1}").increment_entry(entry) return "#{entry} decremented." end # method_def def x_make_loan_to_y(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs1 = process $1.to_s bs2 = process $2.to_s getResponse("#{bs1}: your assets are Loan and your liabilities are Deposit.") getResponse("#{bs2}: your assets are Deposit and your liabilities are Loan.") return getResponse("Draw #{bs1} and #{bs2} balance sheets")[0] end # method_def # TODO: Person: deposit money in bank. def deposit_asset_in_bank(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern bs1 = process $1.to_s asset = process $2.to_s bs2 = process $3.to_s puts "bs1: #{bs1}; asset: #{asset}; bs2: #{bs2}." #eval("@#{bs1}"): your assets are Deposit #eval("@#{bs2""): your assets are #{asset} and your liabilities are Deposit return 'Default response from deposit_asset_in_bank' 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