Eliza like chatterbot in Python

Basically, the brain takes in a list of two lists that defines it’s
personality : [patternResponsepairs,defefaultReplies].
The pattern response pairs contains a list of patterns and
their corresponding responses.
A PATTERN is a Python list containing any number of the following elements in any order:
WORD: This is a string containing alphabetic characters only. It must match an
element in a SENTENCE exactly as written.
0: This matches any number of WORDs in a SENTENCE.
1: This matches exactly one WORD in a SENTENCE
[0, identifier]: This matches zero or more WORDs in a SENTENCE, and binds them to the identifier.
[1, identifier]: This matches exactly one WORD in a SENTENCE, and binds it to the identifier.

A default response is responses that the bot will spit out when it can’t find a match.

#!/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
 
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():
    defReplys = [
             ['Is','that','right?'],
             ['Excellent'],
             ['Interesting'],
             ['You','dont','say'],
             ['I','wouldnt','have','guessed']
             ]
 
 
    patternResps = [
    #[[pattern],[response]]
    #Basics
    [[0,'my','name','is',[1,'Username'],0],['pleased','to','meet','you',[1,'Username']]],
    [['hello'],['Good','day']],
    [['how','are','you',[0,'a']],['Good','How','are','you',[0,'a']]],
    [['i','am','fine'],['Glad','to','hear','you','are','fine']],
    [[[1,'a'],'is','fine'],['Glad','to','hear',[1,'a'],'is','fine']],
    [['no'],['Understood']],
    [['yes'],['I','see']],
    [['you','said',[0,'we']],['I may have said ',[0,'we'],'What about it?']],
    [[[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','?']],
    [[0,'brother',0],['My','brother's','name','is','Henry.','Do','you','have','any','siblings?']],
    [[0,'sister',0],['I','dont','have','a','sister.','How','is','your','family','doing?']],
    #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'],'?']],
 
    ]
 
    ## Run the tests required in the Project specification
    talk([patternResps,defReplys])
 
if __name__=="__main__":
    start()
srijakEliza like chatterbot in Python

Comments 1

Leave a Reply

Your email address will not be published. Required fields are marked *