diff --git a/doctor.txt b/doctor.txt new file mode 100644 index 0000000..2d7e3c1 --- /dev/null +++ b/doctor.txt @@ -0,0 +1,359 @@ +initial: Jak sie masz? Jaki masz problem? +final: Do widzenia. Dziekuje Ci za rozmowe. +quit: pa +quit: do widzenia +quit: zakoncz +pre: nie +pre: nie moge +pre: nie bede +pre: przypominam pamietam +pre: marzyl marze +pre: marzenia marzenie +pre: jak co +pre: kiedy co +pre: na pewno tak +pre: maszyna komputer +pre: komputery komputer +pre: byl +pre: jestes +pre: jestes +pre: taki sam podobny +post: jestem +post: twoj moh +post: ja ty +post: sobie siebie +post: siebie sobie +post: ja ty +post: ty ja +post: moj twoj +post: jestem jestes +synon: przekonanie czuc myslec wierzyc pranienie +synon: rodzina matka mama ojciec tata siostra brat zona dzieci dziecko +synon: pragienie chec potrzeba +synon: smutny nieszczesliwy przygnebiony chory +synon: szczesliwy podniecony zadowolony lepszy +synon: nie moge +synon: kazdy wszyscy nikt +synon: byc jestem jest sa byl +key: xnone + decomp: * + reasmb: Nie jestem pewien, czy w pelni cie rozumiem. + reasmb: Prosze, kontynuuj. + reasmb: Co ci to sugeruje ? + reasmb: Czy odczuwasz silne emocje przy omawianiu takich tematow ? +key: przepraszam + decomp: * + reasmb: Prosze, nie przepraszaj. + reasmb: Przeprosiny nie sa konieczne. + reasmb: Mowilam ci, ze przeprosiny nie sa wymagane. +key: przepraszac + decomp: * + reasmb: goto przepraszam +key: pamietac 5 + decomp: * pamietam * + reasmb: Czy czesto myslisz o (2) ? + reasmb: Czy myslenie o (2) przywodzi na mysl cokolwiek innego ? + reasmb: O czym jeszcze sobie przypominasz ? + reasmb: Dlaczego wlasnie teraz przypominasz sobie (2) ? + reasmb: Co w obecnej sytuacji przypomina (2) ? + reasmb: Jaki jest zwiazek miedzy mna a (2) ? + decomp: * czy pamietasz * + reasmb: Czy myslales, ze zapomne (2) ? + reasmb: Dlaczego myslisz, ze powinnam teraz przypomniec sobie (2)? + reasmb: A co z (2) ? + reasmb: goto co + reasmb: Wspomniales o (2) ? +key: jezeli 3 + decomp: * jezeli * + reasmb: Czy uwazasz, iz to prawdopodobne, ze (2) ? + reasmb: Czy chcesz, zeby (2) ? + reasmb: Co wiesz o (2) ? + reasmb: Naprawde, jesli (2) ? +key: marzylem 4 + decomp: * marzylem * + reasmb: Naprawde, (2) ? + reasmb: Czy kiedykolwiek fantazjowales o (2) kiedy nie spales ? + reasmb: Czy kiedykolwiek zdarzylo Ci sie snic o (2) ? + reasmb: goto dream +key: snic 3 + decomp: * + reasmb: Co sugeruje ci ten sen ? + reasmb: Czy czesto snisz ? + reasmb: Jakie osoby pojawiaja sie w twoich snach ? + reasmb: Czy wierzysz, ze sny maja cos wspolnego z Twoimi problemami ? +key: mozliwe + decomp: * + reasmb: Nie wygladasz na pewnego. + reasmb: Skad ten niepewny ton ? + reasmb: Nie mozesz byc bardziej pozytywny ? + reasmb: Nie jestes pewien ? + reasmb: Nie wiesz ? +key: name 15 + decomp: * + reasmb: Nie interesuje mnie Twoje imie. + reasmb: Jak wspomnialem wczesniej, nie interesuje mnie Twoje imie - prosze kontynuuj. +key: deutsch + decomp: * + reasmb: goto xforeign + reasmb: Jak wspomnialem wczesniej, nie znam niemieckiego. +key: francais + decomp: * + reasmb: goto xforeign + reasmb: Jak wspomnialem wczesniej, nie znam francuskiego +key: italiano + decomp: * + reasmb: goto xforeign + reasmb: Jak wspomnialem wczesniej, nie znam wloskiego. +key: espanol + decomp: * + reasmb: goto xforeign + reasmb: Jak wspomnialem wczesniej, nie znam hiszpanskiego. +key: xforeign + decomp: * + reasmb: Mowie wylacznie w jezyku polskim. +key: czesc + decomp: * + reasmb: Jak sie masz? Jaki masz problem? + reasmb: Czesc. Jaki masz problem? +key: komputer 50 + decomp: * + reasmb: Czy komputery Cie martwia? + reasmb: Dlaczego wspominasz komputery? + reasmb: Jak sadzisz, co komputery maja wspolnego z Twoim problemem? + reasmb: Nie sadzisz, ze komputery moga pomagac ludziom? + reasmb: Dlaczego maszyny budza Twoje zaniepokojenie? + reasmb: Co sadzisz o maszynach? +key: jestem + decomp: * jestem * + reasmb: Czy wierzysz, ze jestes (2) ? + reasmb: Chcialbys byc (2) ? + reasmb: Czy zyczysz sobie, zebym powiedzial Ci (2) ? + reasmb: Co by to oznaczalo gdybys byl (2) ? + reasmb: goto co + decomp: * + reasmb: Dlaczego mowisz 'jestem' ? + reasmb: Nie zrozumialem tego. +key: are + decomp: * jestem * + reasmb: Dlaczego interesuje Cie czy jestem (2) lub tez nie ? + reasmb: Czy wolalbys gdybym nie byl (2) ? + reasmb: Byc moze jestem (2) w Twoich fantazjach. + reasmb: Czy czasem myslisz, ze jestem (2) ? + reasmb: goto co + decomp: * jestes * + reasmb: Czy uwazasz, ze oni moga nie byc (2) ? + reasmb: Wolalbys gdyby nie byli (2) ? + reasmb: Co gdyby nie byli (2) ? + reasmb: Mozliwe, ze jestes (2) . +key: twoj + decomp: * twoj* + reasmb: Dlaczego martwi Cie moje (2) ? + reasmb: Co z twoim wlasnym (2) ? + reasmb: Czy martwi Cie czyjes (2) ? + reasmb: Naprawde, moje (2) ? +key: was 2 + decomp: * bylem * + reasmb: Co jesli byles (2) ? + reasmb: Czy uwazasz, ze byles (2) ? + reasmb: Byles (2) ? + reasmb: Co by to znaczylo, jezeli byles (2) ? + reasmb: Co (2) dla Ciebie znaczy ? + reasmb: goto co + decomp: * bylem * + reasmb: Naprawde byles ? + reasmb: Dlaczego mowisz mi, ze byles (2) teraz? + reasmb: Byc moze I juz wiem, ze byles (2) . + decomp: * byles * + reasmb: Czy wolisz wierzyc, ze ja bylem (2) ? + reasmb: Co sugeruje, ze ja bylem (2) ? + reasmb: Jak myslisz ? + reasmb: Byc moze bylem (2) . + reasmb: Co jezeli bylem (2) ? +key: i + decomp: * ja @chce * + reasmb: Co by to dla Ciebie znaczylo, jezeli dostalbys (3) ? + reasmb: Czego wlasciwie chcesz (3) ? + reasmb: Zalozmy, ze znajdziesz niedlugo (3) ? + reasmb: Co jezeli nigdy nie znajdziesz (3) ? + reasmb: Co znalezienie (3) znaczy dla Ciebie ? + reasmb: Co potrzeba (3) ma wspolnego z ta dyskusja ? + decomp: * ja jestem * @smutny * + reasmb: Przykro mi, ze jestes (3) . + reasmb: Myslisz, ze ta rozmowa ci pomoze i nie bedziesz juz (3) ? + reasmb: Jestem pewny, ze bycie (3)m nie jest przyjemne . + reasmb: Mozesz wyjasnic co sie stalo, ze jestes (3) ? + decomp: * ja jestem * @szczesliwy * + reasmb: Jak ci pomoglem byc (3)m ? + reasmb: Czy dzieki terapi jestes (3) ? + reasmb: Co sprawilo, ze jestes teraz (3) ? + reasmb: Mozesz powiedziec dlaczego nagle jestes (3) ? + decomp: * bylem * + reasmb: goto bylem + decomp: * ja @przekonanie * ja * + reasmb: Naprawde tak uwazasz ? + reasmb: Ale nie jestes pewnie, ze (3) . + reasmb: Naprawde watpisz, ze (3) ? + decomp: * ja * @przekonanie * ty * + reasmb: goto ty + decomp: * ja jestem * + reasmb: Rozmawiasz ze mna bo jestes (2) ? + reasmb: Jak dlugo jestes (2) ? + reasmb: Myslisz, ze to normalne, ze jestes (2) ? + reasmb: Cieszysz sie, ze jestes (2) ? + decomp: * @nie moge * + reasmb: Jak sadzisz, dlaczego (3) ? + reasmb: Sprobowales ? + reasmb: Moze moglbys teraz (3) . + reasmb: Na prawde chcialbys moc (3) ? + decomp: * nie wiem * + reasmb: Naprawde nie (2) ? + reasmb: Dlaczego nie (2) ? + reasmb: Chcesz moc (2) ? + reasmb: Martwki cie to ? + decomp: * czy czuje * + reasmb: Powiedz mi wiecej o tym uczuciu. + reasmb: Czesto czujesz sie (2) ? + reasmb: Lubisz czuc sie (2) ? + reasmb: Gdy czujesz sie (2) - o czym ci to przypomina? + decomp: * ja * ty * + reasmb: Moze w twoich marzeniach my (2) siebie nawzajem. + reasmb: Chcialbys mnie (2) ? + reasmb: Zdajesz sie mnie (2). + reasmb: Czy (2) kogos jeszcze ? + decomp: * + reasmb: Powiedziales (1) ? + reasmb: Mozesz rozwinac te mysl ? + reasmb: Powiedziles (1) z jakiejs konkretnej przyczyny ? + reasmb: To dosc interesujace. +key: ty + decomp: * ty przypominasz mi o * + reasmb: goto podobny + decomp: * ty jestes * + reasmb: Co sprawia, ze myslisz, ze jestem (2) ? + reasmb: Cieszy cie mysl, ze jestem (2) ? + reasmb: Chcialbys czasami byc (2) ? + reasmb: Moze chcialbys byc (2) . + decomp: * ty * mnie * + reasmb: Dlaczego uwazasz, ze ja (2) ciebie ? + reasmb: Chcialbys mysle, ze ja ciebie (2) -- nie ? + reasmb: Dlaczego uwazasz, ze ja ciebie (2) ? + reasmb: Naprawde, (2) ciebie ? + reasmb: Chcialbys wierzyc, ze ja ciebie (2) ? + reasmb: Zakladasz, ze ja (2) ciebie -- co to oznacza ? + reasmb: Czy jeszcze ktos wierzy, ze ja ciebie (2) ? + decomp: * ty * + reasmb: Mowilismy o tobie -- nie o mnie. + reasmb: Och, ja (2) ? + reasmb: Chyba nie mowisz o mnie, nie ? + reasmb: Jak sie teraz czujesz ? +key: tak + decomp: * + reasmb: Zdajesz sie byc pewny. + reasmb: Jestes pewny. + reasmb: Rozumiem. + reasmb: Rozumiem. +key: nie + decomp: * + reasmb: Mowisz nie na zlosc? + reasmb: Jestes troszeczke zlosliwy. + reasmb: Dlaczego nie ? + reasmb: Dlaczegoe 'nie' ? +key: moj 2 + decomp: $ * moj * + reasmb: Przedyskutujmy dlaczego twoj (2) . + reasmb: Wczesniej powiedziales, ze twoj (2) . + reasmb: Ale twoj (2) . + reasmb: Czy to ma jakis zwiazek z tym, ze twoj (2) ? + decomp: * moja * @rodzina * + reasmb: Opowiedz mi o twojej rodzinie. + reasmb: Kto jeszcze w twojej rodzinie (4) ? + reasmb: Twoj (3) ? + reasmb: Co jeszcze przychodzi ci do glowy, gdy myslisz o (3) ? + decomp: * moj * + reasmb: Twoj (2) ? + reasmb: Dlaczego mowisz, ze twoj (2) ? + reasmb: Czy to sugeruje cos innego, co nalezy do ciebie ? + reasmb: Czy wazne jest, aby twoj (2) ? +key: moge + decomp: * czy mozesz * + reasmb: Wierzysz, ze moge (2) prawda ? + reasmb: goto co + reasmb: Chesz, zebym byla w stanie (2) . + reasmb: Byc moze chcialbys miec mozliwosc (2) sobie. + decomp: * czy moge * + reasmb: To czy mozesz (2) zalezy bardziej od ciebie niz ode mnie. + reasmb: Chesz, zebym byla w stanie (2) ? + reasmb: Moze nie chcesz (2) . + reasmb: goto co +key: co + decomp: * + reasmb: Dlaczego pytasz ? + reasmb: Czy to pytanie cie interesuje ? + reasmb: Co tak naprawde chciales wiedziec ? + reasmb: Czy takie pytanie zaprzataja ci glowe ? + reasmb: Jaka odpowiedz najbardziej by cie ucieszyla ? + reasmb: Co myslisz ? + reasmb: Co przychodzi ci na mysl, gdy pytasz o to ? + reasmb: Czy zadawales wczesniej takie pytania ? + reasmb: Czy pytales kogos innego ? +key: poniewaz + decomp: * + reasmb: Czy to jest prawdziwy powod ? + reasmb: Nie przychodza ci na mysl zadne inne powody ? + reasmb: Czy ten powod wydaje sie wyjasniac cokolwiek innego ? + reasmb: Jakie inne powody moga istniec ? +key: dlaczego + decomp: * dlaczego nie * + reasmb: Uwierzysz, ze ja nie (2) ? + reasmb: Byc moze bede (2) w odpowiednim czasie. + reasmb: Czy powinienies (2) sobie ? + reasmb: Chesz, abym (2) ? + reasmb: goto co + decomp: * dlaczego ja nie moge * + reasmb: Czy uwazasz, ze powinienes miec mozliwosc (2) ? + reasmb: Czy chcesz miec mozliwosc (2) ? + reasmb: Czy uwazasz, ze to pomoze ci (2) ? + reasmb: Czy masz pojecie, dlaczego nie mozesz (2) ? + reasmb: goto co + decomp: * + reasmb: goto co +key: wszyscy 2 + decomp: * @wszyscy * + reasmb: Naprawde, (2) ? + reasmb: Zdecydowanie nie (2) . + reasmb: Czy przychodzi ci na mysl ktos szczegolny ? + reasmb: Kto, dla przyklady? + reasmb: Czy myslisz o bardzo szczegolnej osobie ? + reasmb: Kto, jezeli moge zapytac ? + reasmb: Moze ktos wyjatkowy ? + reasmb: Masz na mysli konkretna osobe, prawda ? + reasmb: Kogo masz konkretnie na mysli ? +key: wszyscy 2 + decomp: * + reasmb: goto wszyscy +key: nikt 2 + decomp: * + reasmb: goto wszyscy +key: nikt 2 + decomp: * + reasmb: goto wszyscy +key: zawsze 1 + decomp: * + reasmb: Czy mozesz podac jakis konkretny przyklad ? + reasmb: Kiedy ? + reasmb: O jakim wydarzeniu myslisz ? + reasmb: Naprawde, zawsze ? +key: podobnie 10 + decomp: * + reasmb: W jaki sposob ? + reasmb: Jakie widzisz podobienstwo ? + reasmb: Co to podobienstwo sugeruje ? + reasmb: Jakie inne zwiazki widzisz ? + reasmb: Co wedlug ciebie oznacza to podobienstwo ? + reasmb: Jaki to ma zwiazek, jak sadzisz ? + reasmb: Czy naprawde moze tu byc jakis zwiazek ? + reasmb: Jak ? +key: jak 10 + decomp: * @jestem * jak * + reasmb: goto podobnie + \ No newline at end of file diff --git a/eliza.py b/eliza.py new file mode 100644 index 0000000..681a5d1 --- /dev/null +++ b/eliza.py @@ -0,0 +1,234 @@ +import logging +import random +import re + +try: input = raw_input +except NameError: pass + +log = logging.getLogger(__name__) + + +class Key: + def __init__(self, word, weight, decomps): + self.word = word + self.weight = weight + self.decomps = decomps + + +class Decomp: + def __init__(self, parts, save, reasmbs): + self.parts = parts + self.save = save + self.reasmbs = reasmbs + self.next_reasmb_index = 0 + + +class Eliza: + def __init__(self): + self.initials = [] + self.finals = [] + self.quits = [] + self.pres = {} + self.posts = {} + self.synons = {} + self.keys = {} + self.memory = [] + + def load(self, path): + key = None + decomp = None + with open(path) as file: + for line in file: + if not line.strip(): + continue + tag, content = [part.strip() for part in line.split(':')] + if tag == 'initial': + self.initials.append(content) + elif tag == 'final': + self.finals.append(content) + elif tag == 'quit': + self.quits.append(content) + elif tag == 'pre': + parts = content.split(' ') + self.pres[parts[0]] = parts[1:] + elif tag == 'post': + parts = content.split(' ') + self.posts[parts[0]] = parts[1:] + elif tag == 'synon': + parts = content.split(' ') + self.synons[parts[0]] = parts + elif tag == 'key': + parts = content.split(' ') + word = parts[0] + weight = int(parts[1]) if len(parts) > 1 else 1 + key = Key(word, weight, []) + self.keys[word] = key + elif tag == 'decomp': + parts = content.split(' ') + save = False + if parts[0] == '$': + save = True + parts = parts[1:] + decomp = Decomp(parts, save, []) + key.decomps.append(decomp) + elif tag == 'reasmb': + parts = content.split(' ') + decomp.reasmbs.append(parts) + + def _match_decomp_r(self, parts, words, results): + if not parts and not words: + return True + if not parts or (not words and parts != ['*']): + return False + if parts[0] == '*': + for index in range(len(words), -1, -1): + results.append(words[:index]) + if self._match_decomp_r(parts[1:], words[index:], results): + return True + results.pop() + return False + elif parts[0].startswith('@'): + root = parts[0][1:] + if not root in self.synons: + raise ValueError("Unknown synonym root {}".format(root)) + if not words[0].lower() in self.synons[root]: + return False + results.append([words[0]]) + return self._match_decomp_r(parts[1:], words[1:], results) + elif parts[0].lower() != words[0].lower(): + return False + else: + return self._match_decomp_r(parts[1:], words[1:], results) + + def _match_decomp(self, parts, words): + results = [] + if self._match_decomp_r(parts, words, results): + return results + return None + + def _next_reasmb(self, decomp): + index = decomp.next_reasmb_index + result = decomp.reasmbs[index % len(decomp.reasmbs)] + decomp.next_reasmb_index = index + 1 + return result + + def _reassemble(self, reasmb, results): + output = [] + for reword in reasmb: + if not reword: + continue + if reword[0] == '(' and reword[-1] == ')': + index = int(reword[1:-1]) + if index < 1 or index > len(results): + raise ValueError("Invalid result index {}".format(index)) + insert = results[index - 1] + for punct in [',', '.', ';']: + if punct in insert: + insert = insert[:insert.index(punct)] + output.extend(insert) + else: + output.append(reword) + return output + + def _sub(self, words, sub): + output = [] + for word in words: + word_lower = word.lower() + if word_lower in sub: + output.extend(sub[word_lower]) + else: + output.append(word) + return output + + def _match_key(self, words, key): + for decomp in key.decomps: + results = self._match_decomp(decomp.parts, words) + if results is None: + log.debug('Decomp did not match: %s', decomp.parts) + continue + log.debug('Decomp matched: %s', decomp.parts) + log.debug('Decomp results: %s', results) + results = [self._sub(words, self.posts) for words in results] + log.debug('Decomp results after posts: %s', results) + reasmb = self._next_reasmb(decomp) + log.debug('Using reassembly: %s', reasmb) + if reasmb[0] == 'goto': + goto_key = reasmb[1] + if not goto_key in self.keys: + raise ValueError("Invalid goto key {}".format(goto_key)) + log.debug('Goto key: %s', goto_key) + return self._match_key(words, self.keys[goto_key]) + output = self._reassemble(reasmb, results) + if decomp.save: + self.memory.append(output) + log.debug('Saved to memory: %s', output) + continue + return output + return None + + def respond(self, text): + if text.lower() in self.quits: + return None + + text = re.sub(r'\s*\.+\s*', ' . ', text) + text = re.sub(r'\s*,+\s*', ' , ', text) + text = re.sub(r'\s*;+\s*', ' ; ', text) + log.debug('After punctuation cleanup: %s', text) + + words = [w for w in text.split(' ') if w] + log.debug('Input: %s', words) + + words = self._sub(words, self.pres) + log.debug('After pre-substitution: %s', words) + + keys = [self.keys[w.lower()] for w in words if w.lower() in self.keys] + keys = sorted(keys, key=lambda k: -k.weight) + log.debug('Sorted keys: %s', [(k.word, k.weight) for k in keys]) + + output = None + + for key in keys: + output = self._match_key(words, key) + if output: + log.debug('Output from key: %s', output) + break + if not output: + if self.memory: + index = random.randrange(len(self.memory)) + output = self.memory.pop(index) + log.debug('Output from memory: %s', output) + else: + output = self._next_reasmb(self.keys['xnone'].decomps[0]) + log.debug('Output from xnone: %s', output) + + return " ".join(output) + + def initial(self): + return random.choice(self.initials) + + def final(self): + return random.choice(self.finals) + + def run(self): + print(self.initial()) + + while True: + sent = input('> ') + + output = self.respond(sent) + if output is None: + break + + print(output) + + print(self.final()) + + +def main(): + eliza = Eliza() + eliza.load('doctor.txt') + eliza.run() + +if __name__ == '__main__': + logging.basicConfig() + main()