X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=eliza.py;fp=eliza.py;h=2a3e04cf810185ae9e5c6cd34ae107a37cd98b0a;hb=3e0b9e4fe16e573e9343495665d68ef836810d67;hp=0000000000000000000000000000000000000000;hpb=6acc8955119fca21887585530303300d67a77ce7;p=eliza.git diff --git a/eliza.py b/eliza.py new file mode 100644 index 0000000..2a3e04c --- /dev/null +++ b/eliza.py @@ -0,0 +1,125 @@ + +# coding: utf-8 +import yaml +import collections +import random + +Match = collections.namedtuple('Match', 'text, rule, bindings') + +pronoun_swaps = { + 'i': 'you', + 'me': 'you', + 'my': 'your', + 'mine': 'yours', + 'am': 'are' +} + +def read_rules(rules_file): + with open(rules_file) as f: + rules = [{'pattern': r['pattern'].split(), + 'responses': [t.split() for t in r['responses']]} + for r in yaml.load(f)] + return rules + +def is_var(word): + return word[0] == '?' + + +def splits(item): + return [(item[:i], item[i:]) for i in range(len(item)+1)] + +def match(text, rule): + return all_matches([Match(text, rule, {})]) + +def all_matches(matches): + successes = [] + while matches: + # print(matches, successes) + current = matches[0] + new_matches = [] + if successful_match(current): + successes += [current.bindings] + elif current.rule: + new_matches = match_item(current.text, current.rule, current.bindings) + matches = matches[1:] + new_matches + return successes + +def successful_match(match): + return match.text == [] and match.rule == [] + +def match_item(text, rule, bindings): + r0 = rule[0] + if is_var(r0): + if r0 in bindings: + # already seen this variable + if text[:len(bindings[r0])] == bindings[r0]: + return [Match(text[(len(bindings[r0])):], rule[1:], bindings)] + else: + return [] + else: + # not seen this variable yet + matches = [] + for pre, suf in splits(text): + new_bindings = bindings.copy() + new_bindings[r0] = pre + matches += [Match(suf, rule[1:], new_bindings)] + return matches + elif text and text[0] == r0: + return [Match(text[1:], rule[1:], bindings)] + else: + return [] + +def candidate_rules(rules, comment): + return [(rule, bindings) + for rule in rules + for bindings in match(comment, rule['pattern'])] + + +def fill(response, bindings): + filled_response = [] + for w in response: + if is_var(w): + if w in bindings: + filled_response += bindings[w] + else: + filled_response += ['MISSING'] + else: + filled_response += [w] + return filled_response + + +def pronoun_person_swap(bindings): + def swapped(words): + sw = [] + for w in words: + if w in pronoun_swaps: + sw += [pronoun_swaps[w]] + else: + sw += [w] + return sw + + return {var: swapped(bindings[var]) for var in bindings} + +def respond(rule, bindings): + return fill(random.choice(rule['responses']), bindings) + + +def eliza_loop(rules): + print("Hello. I'm Eliza. What seems to be the problem?") + while True: + c = input("> ") + if c.strip() in 'quit halt exit stop'.split(): break + comment = c.split() + rule, bindings = candidate_rules(rules, comment)[0] + swapped_bindings = pronoun_person_swap(bindings) + print(' '.join(respond(rule, swapped_bindings))) + + +all_rules = read_rules('rules.yaml') +eliza_loop(all_rules) + + + + + +