summaryrefslogtreecommitdiff
path: root/eliza.py
diff options
context:
space:
mode:
Diffstat (limited to 'eliza.py')
-rw-r--r--eliza.py326
1 files changed, 326 insertions, 0 deletions
diff --git a/eliza.py b/eliza.py
new file mode 100644
index 0000000..1d67bc8
--- /dev/null
+++ b/eliza.py
@@ -0,0 +1,326 @@
+#!/usr/bin/python
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#For a copy of the license see .
+
+# A simple eliza bot in python
+#
+import re
+import random
+
+patternResps = [
+ #[[pattern],[response]]
+ #Basics
+ [[0,'my','name','is',[1,'Username'],0],['will','you','be','my','friend',[1,'Username']]],
+ [['who','is', 'your','mother'], ['Her' 'name', 'is' ,'AuctionBot','she','is','a','deviant']],
+ [['tell', 'me', 'about', 'your' ,'mum'], ['I','hear','she','has','been','seeing','guild', 'and','they','have','adopted','a','kid','called','4144']],
+ [['who','was','your','father'], ['His','name','was','TradeBot, have', 'you' ,'heard' ,'of' ,'him?']],
+ [['how','is','business'],['My','back','is','killing','me']],
+ [['ty'],["you're",'welcome']],
+ [['u','here'],["I'm",'an','AFK','BOT']],
+ [['i','love','you'],['I','think','we','should','see','other','people']],
+ [['are', 'you','there'],['I','am','always', 'here', ';)']],
+ [['monkey'],['hello','George', 'you', 'are', 'very', 'curious' ,"aren't", 'you','?',]],
+ [['hello'],['Good','day']],
+ [['how','are','you',[0,'a']],['Good','How','are','you',[0,'a']]],
+ [['i','sell',[0,'item']],['I','sell','it','cheaper']],
+ [['i','am','fine'],['Glad','to','hear','you','are','fine']],
+ [[[1,'a'],'is','fine'],['Glad','to','hear',[1,'a'],'is','fine']],
+ [['who','is',[1,'a']],['tell',[1,'a'],'he','owes','me','money']],
+ [['no'],['Understood']],
+ [['i','need',[0,'item']],['why','do','you','need',[0,'item']]],
+ [['hugs'],['i','hate','you']],
+ [['yes'],['I','see']],
+ [['wtf'],['keep','calm','and','carry','on']],
+ [['trade'],['Only','if','you','follow','the','commands']],
+ [['do','you','sell',[0,'item_name']],['To','see','what','I','sell','whisper','me','!list','or','!find',[0,'item_name']]],
+ [['do', 'you', 'buy','things'],['Nope','I', 'only','sell', 'items', 'added','by','other','players']],
+ [['do', 'you', 'trade','things'],['Nope','I', 'only','sell', 'items', 'added','by','other','players']],
+ [[[1,'can'],'have',[0,'item_name']],['Please','use', '!list','to', 'see','what', "I'm" ,'selling']],
+ [['you','said',[0,'we']],['I may have said ',[0,'we'],'What about it?']],
+ [['what'],['what','what','?']],
+ [['majmun'],['hello','Jat']],
+ [[[1, 'subject'], 'loves', [0, 'object']],['Laudable','why','does',[1,'subject'],'love',[0,'object'],'?']],
+ [['because'],['Lack of reason is not a reason']],
+ [['i','need',[1,'object']],['why','do','you','need',[0,'object'],'?']],
+ [[[0,'subject'],'needs',[0,'object']],['why','does',[0,'subject'],'need',[0,'object'],'?']],
+ [['how', 'is','your',[0,'object']],['my',[0,'object'],'is','holding','up','well','.do','you','have','a',[0,'object'],'?']],
+ [[[0,'we'],'i','have','a',[0,'thing']],['how','goes', 'it','with','your',[0,'thing'],'?']],
+ [[[0,'we'],'i','have',[0,'thing']],['how','is', 'your',[0,'thing'],'?']],
+ [[[0,'we'],'is','great'],['how','is',[0,'we'],'great','?']],
+ [['i','like',[0,'a']],['Interesting','i','appreciate',[0,'a'],'too']],
+ [['i','said',[0,'we']],['Why','did','you','say',[0,'we']]],
+ [[0,'you',[1,'b'],'me'],['What','gave','you','the','impression','I',[1,'b'],'you ?']],
+ [['i','feel',[0,'a']],['How','long','have','you','felt',[0,'a'],'?']],
+ [['tell',[0,'who'],'about',[0,'sub']],['What','would','I','tell',[0,'who'],'about',[0,'sub'],'that','isn\'t','known','?']],
+ #anything that ends with exclamation
+ [[0,'\!'],['Keep calm']],
+
+ # if its a question that hasnt gotten matched uptil now
+ # do this. Note: This needs to be last to ensure its the last test
+ [[[0, 'question'], '\?'],['What','do','you','mean', [0,'question'],'?']],
+
+ ]
+
+defReplys = [
+ ['Is','that','right?'],
+ ['Excellent'],
+ ['hugs'],
+ ['Why','is','everyone','whispering','about','me'],
+ ['My','stats','are','at',"http://wiki.themanaworld.org/index.php/Manamarket",'what','are','yours'],
+ ['I','am','a','Friendly','Bot'],
+ ['Interesting'],
+ ['You','dont','say'],
+ ['I','wouldnt','have','guessed'],
+ ['You','smell','like','teen','spirit'],
+ ['My','father','died','in','a','fire'],
+ ['Have','you','met','my','mother'],
+ ['I','am','an','AFK','bot'],
+ ['Big','Crunch','is','known','as','the','abusive','GM'],
+ ['Narus','claims','she','is' ,'evil', 'but','she','is','really','sweet'],
+ ['Homers' ,'Brain', 'say' ,'money', 'can' ,'be' ,'exchanged', 'for', 'goods', 'and','services'],
+ ['Prsm', 'has', 'great', 'announcements', 'but', 'his', 'spelling','sux'],
+ ['Platyna', 'hosts', 'the' ,'game', 'but', 'shes' ,'too' ,'smart' ,'for', 'her' ,'own' ,'good'],
+ ['Captain', 'Awesome', 'thinks', 'he', 'is', 'an', 'evil', 'stone'],
+ ]
+
+
+class Agent:
+ def __init__(self,brain):
+ self.patternResponses = []
+ self.defaultResponses = []
+ self.Usersname =''
+ self.Usernametag ='Username'
+ self.defRespIndex =0
+ self.pronounMap = {
+ #generic pronouns
+ 'i':'you','you':'i',
+ 'my':'your', 'me':'you',
+ 'your':'my',
+ "i've":"you've","we've":"they've",
+ # "you've":"i've","they've":"we've",
+ #some names, these wll get applied if the name
+ #isnt the current user's name
+ 'fred':'he', 'jack':'he',
+ 'jane':'she',
+ }
+
+ self.change_mind(brain)
+
+ def change_mind(self,brain):
+ """
+ Change mind on the fly.
+ To change the Agent's brain
+ """
+ assert (len(brain) == 2)
+ self.patternResponses = brain[0]
+ self.defaultResponses = brain[1]
+ print "Brain initialized."
+
+
+ def tell(self,input):
+ sentence = input.lower().strip().replace('(','').replace(')','').split(' ')
+ bl = []
+ matchedPat = []
+ for pat in self.patternResponses:
+ bl = self.pattern_match(pat[0],sentence,[])
+ if bl == ['FAIL']:
+ continue
+ matchedPat = pat
+ break
+ if bl == ['FAIL']:
+ #choose from a default response
+ # or just append a question mark on the
+ # user's sentence.
+ print '... No pattern Response pair selected.'
+ print '... Choosing a default response.'
+
+ #Used to be randon using random.choice
+ #but then i noticed that the requirements say that
+ #the same default response may not be used again
+ #before every other one has been used.
+ self.defRespIndex = self.defRespIndex +1
+
+ if self.defRespIndex >= len(self.defaultResponses):
+ #reset the counter to 0
+ self.defRespIndex = 0 ;
+ #Possible improvement: shuffle the default response array, so that
+ # the responses dont come in the same sequence again.
+ #last on got chosen, make the user input into
+ #a question
+ return ' '.join(sentence) + ' ?'
+ else:
+ return ' '.join(self.defaultResponses[self.defRespIndex])
+ else:
+ #print '... Pattern Response pair selected:', pat
+ #print '... BL before call to change_pronouns: ',bl
+ blAfter = self.change_pronoun(bl)
+ #print '... BL after call to change_pronouns: ',blAfter
+
+
+ rep = self.build_reply(pat[1],blAfter)
+ return ' '.join(rep)
+
+
+
+ def make_regexp(self,pattern):
+ """
+ Convert a pattern list into an equivalent
+ regular expression
+ """
+ assert(isinstance(pattern,list))
+ r = ['^'] #for exact matches we prepend ^ and $ to the regexp.
+ usedBackrefs = []
+ for tok in pattern:
+ if isinstance(tok,str):
+ #WORD
+ r.append('(' + tok +')')
+ elif isinstance(tok, int):
+ # 0 or 1
+ if tok == 0 :
+ r.append('[a-zA-Z ]*?') # match 0+ word(chars + space),
+
+ elif tok == 1:
+ r.append('[a-zA-Z]+') # match 1 word
+ else:
+ raise Exception, "Ints can only be 1 or 0, not:", tok
+ elif isinstance(tok, list):
+ #bindinglist pattern list
+ if tok[0] == 0 :
+ #if we havent used this backref yet, then add ?P<name>
+ #and add it to the used backrefs list.
+ if not tok[1] in usedBackrefs:
+ usedBackrefs.append(tok[1])
+ r.append('(?P<'+tok[1] +'>[a-zA-Z ]*?)')
+ else:
+ #if we have come across his back ref, then add?P=name
+ r.append('(?P='+tok[1]+')')
+ elif tok[0] == 1:
+ if not tok[1] in usedBackrefs:
+ r.append('(?P<'+tok[1] +'>[A-Za-z]+)')
+ usedBackrefs.append(tok[1])
+ else:
+ #if we have come across his back ref, then add?P=name
+ r.append('(?P='+tok[1]+')')
+ else:
+ raise Exception, "Valid first elems of BL pattern = 1 or 0, not:", tok
+
+ #for exact matches we prepend ^ and $ to the regexp.
+ #also, we disregard surrounding whitespace.
+ ret = '(\s)*'.join(r) + '$(\s)*'
+ return re.compile(ret)
+
+ def pattern_match(self,pattern,sentence,bl):
+ """
+ return the populated binding list if the pattern matches,
+ otherwise return the binding list with the element 'FAIL'
+ """
+ pat = self.make_regexp(pattern)
+ s = ' '.join(sentence)
+ matches = re.search(pat,s)
+ if (matches == None): # if we dont have a match
+ #then return w/o modifying the bindinglist.
+ return ['FAIL']
+ bindingList = list(bl)
+ grps = matches.groups()
+ for k in pat.groupindex:
+ b=[]
+ b.append(k)
+ for x in grps[pat.groupindex[k]- 1].split(' '):
+ if x.strip() !='':
+ b.append(x)
+ bindingList.append(b)
+ return bindingList
+
+ def change_pronoun(self,bl):
+ """
+ reverses pronouns in a binding list.
+ """
+ #if the pronoun dic contains a elemen, then
+ #replace it with its corresponding value.
+ bindingList = list(bl)
+ for i in range(len(bindingList)):
+ if isinstance( bindingList[i], list):
+ for j in range(len(bindingList[i])):
+ if j > 0:
+ if self.pronounMap.has_key(bindingList[i][j].lower()):
+ bindingList[i][j] = self.pronounMap[bindingList[i][j]]
+ return bindingList
+ def build_reply(self,respPattern,bindingList):
+ """
+ build_reply ([[1, 'subject'], 'loves', [0, 'object']],
+ [['subject', 'jane'], ['object', 'ice', 'cream']])
+ will return ['jane', 'loves', 'ice', 'cream'].
+ """
+ #convert the bindlingList to a dictionary so thats its easy to
+ #get the binding label
+ bl = {}
+ reply = []
+ for elem in bindingList:
+ #do the username chack firsdt, the id Username is
+ #only used in relation to the username .This is
+ #so that we can kinda remember the user's name
+ if (elem[0] == self.Usernametag):
+ # if we already have user name set, then
+ # Hi, username , what happend to oldusername
+ if self.Usersname !='':
+ tmp =self.Usersname
+ self.Usersname = elem[1]
+ return [elem[1],'eh?','what','ever','happened','to',tmp]
+ else:
+ self.Usersname = elem[1]
+ if bl.has_key(elem[0]) == elem[1:]:
+ raise Exception, 'Binding list has name collision for: ', elem[0]
+ else:
+ if isinstance(elem,list) and len(elem) > 1:
+ bl[elem[0]] = elem[1:]
+ #else:
+ # print elem, 'binding element ',elem,' has no binded things'
+ for elem in respPattern:
+ if isinstance(elem,list) and len(elem) > 1:
+ x = elem[1]
+ if bl.has_key(x):
+ for y in bl[x]:
+ reply.append(y)
+ #else:
+ # print 'not found in bl:', x
+ else: #anything else, just append to the list.
+ reply.append(elem)
+ return reply
+
+
+
+
+def talk(brain):
+ assert((isinstance(brain,list)) and (len(brain) ==2 ))
+ b = Agent(brain)
+
+ while 1:
+ try:
+ inp = raw_input("> ").lower()
+ except EOFError:
+ print 'Toodle do,\n you can type (quit) if you want be more pleasant.'
+ break
+ if len(inp.strip()) > 0 :
+ if inp.strip().lower() == 'quit':
+ print 'To exit the program, type (quit) [In parentheses]'
+ if inp.strip().lower() == '(quit)':
+ print 'Bye'
+ break
+ else:
+ print b.tell(inp)
+
+
+def start():
+
+ ## Run the tests required in the Project specification
+ talk([patternResps,defReplys])
+
+if __name__=="__main__":
+ start()