Got eliza working
[eliza.git] / eliza.py
1
2 # coding: utf-8
3 import yaml
4 import collections
5 import random
6
7 Match = collections.namedtuple('Match', 'text, rule, bindings')
8
9 pronoun_swaps = {
10 'i': 'you',
11 'me': 'you',
12 'my': 'your',
13 'mine': 'yours',
14 'am': 'are'
15 }
16
17 def read_rules(rules_file):
18 with open(rules_file) as f:
19 rules = [{'pattern': r['pattern'].split(),
20 'responses': [t.split() for t in r['responses']]}
21 for r in yaml.load(f)]
22 return rules
23
24 def is_var(word):
25 return word[0] == '?'
26
27
28 def splits(item):
29 return [(item[:i], item[i:]) for i in range(len(item)+1)]
30
31 def match(text, rule):
32 return all_matches([Match(text, rule, {})])
33
34 def all_matches(matches):
35 successes = []
36 while matches:
37 # print(matches, successes)
38 current = matches[0]
39 new_matches = []
40 if successful_match(current):
41 successes += [current.bindings]
42 elif current.rule:
43 new_matches = match_item(current.text, current.rule, current.bindings)
44 matches = matches[1:] + new_matches
45 return successes
46
47 def successful_match(match):
48 return match.text == [] and match.rule == []
49
50 def match_item(text, rule, bindings):
51 r0 = rule[0]
52 if is_var(r0):
53 if r0 in bindings:
54 # already seen this variable
55 if text[:len(bindings[r0])] == bindings[r0]:
56 return [Match(text[(len(bindings[r0])):], rule[1:], bindings)]
57 else:
58 return []
59 else:
60 # not seen this variable yet
61 matches = []
62 for pre, suf in splits(text):
63 new_bindings = bindings.copy()
64 new_bindings[r0] = pre
65 matches += [Match(suf, rule[1:], new_bindings)]
66 return matches
67 elif text and text[0] == r0:
68 return [Match(text[1:], rule[1:], bindings)]
69 else:
70 return []
71
72 def candidate_rules(rules, comment):
73 return [(rule, bindings)
74 for rule in rules
75 for bindings in match(comment, rule['pattern'])]
76
77
78 def fill(response, bindings):
79 filled_response = []
80 for w in response:
81 if is_var(w):
82 if w in bindings:
83 filled_response += bindings[w]
84 else:
85 filled_response += ['MISSING']
86 else:
87 filled_response += [w]
88 return filled_response
89
90
91 def pronoun_person_swap(bindings):
92 def swapped(words):
93 sw = []
94 for w in words:
95 if w in pronoun_swaps:
96 sw += [pronoun_swaps[w]]
97 else:
98 sw += [w]
99 return sw
100
101 return {var: swapped(bindings[var]) for var in bindings}
102
103 def respond(rule, bindings):
104 return fill(random.choice(rule['responses']), bindings)
105
106
107 def eliza_loop(rules):
108 print("Hello. I'm Eliza. What seems to be the problem?")
109 while True:
110 c = input("> ")
111 if c.strip() in 'quit halt exit stop'.split(): break
112 comment = c.split()
113 rule, bindings = candidate_rules(rules, comment)[0]
114 swapped_bindings = pronoun_person_swap(bindings)
115 print(' '.join(respond(rule, swapped_bindings)))
116
117
118 all_rules = read_rules('rules.yaml')
119 eliza_loop(all_rules)
120
121
122
123
124
125