Nattacker
Nattacker

Reputation: 33

Chatbot problem - infinite recursion - Codecademy question

I'm learning how to code using Codecademy and I am really stuck on a question, and would love to be pointed in the right direction!

The code for the most part works, I just cannot get it to respond to the .match_reply function correctly. The idea is that the chatbot should identify the regex in self.alienbabble and respond with the appropriate answer. However, it is bugging out with an infinite recursion of all the responses of that function.

# importing regex and random libraries
import re
import random

class AlienBot:
  # potential negative responses
  negative_responses = ("no", "nope", "nah", "naw", "not a chance", "sorry")
  # keywords for exiting the conversation
  exit_commands = ("quit", "pause", "exit", "goodbye", "bye", "later")
  # random starter questions
  random_questions = (
        "Why are you here? ",
        "Are there many humans like you? ",
        "What do you consume for sustenance? ",
        "Is there intelligent life on this planet? ",
        "Does Earth have a leader? ",
        "What planets have you visited? ",
        "What technology do you have on this planet? "
    )

  def __init__(self):
    self.alienbabble = {'describe_planet_intent': '.*\s*your planet.*','answer_why_intent': 'why\sare.*', 'cubed_intent': '.*cube.*(\d+)'}

  # Define .greet() below:
  def greet(self):
    self.name = input("Hello. What is your name?")
    will_help = input(f"Hi {self.name}, I'm Etcetera. I'm not from this planet. Will you help me learn about your planet? ")
    if will_help in self.negative_responses:
      print ("Ok, have a nice Earth day!")
      return
    self.chat()

  # Define .make_exit() here:
  def make_exit(self, reply):
    for word in self.exit_commands:
      if word in reply:
        print ("Ok, have a nice Earth day!")
        return True

  # Define .chat() next:
  def chat(self):
    reply = input(random.choice(self.random_questions)).lower()
    while not self.make_exit(reply):
      reply = input(self.match_reply(reply))

  # Define .match_reply() below:
  def match_reply(self, reply):
    for key, value in self.alienbabble.items():
      intent = key
      regex = value
      #regex = 'describe_planet_intent'
      #reply = input(random.choice(self.random_questions)).lower()
      found_match = re.match(regex, reply)
      if found_match and intent == 'describe_planet_intent':
        return self.describe_planet_intent()
      elif found_match and intent == 'answer_why_intent':
        return self.answer_why_intent()
      elif found_match and intent == 'cubed_intent':
        return self.cubed_intent(found_match.groups()[0])
      else:
        return self.no_match_intent()

  # Define .describe_planet_intent():
  def describe_planet_intent(self):
    responses = ("My planet is a utopia of diverse organisms and species. ", "I am from Opidipus, the capital of the Wayward Galaxies. ")
    return random.choice(responses)

  # Define .answer_why_intent():
  def answer_why_intent(self):
    responses = ("I come in peace. ", "I am here to collect data on your planet and its inhabitants. ", "I heard the coffee is good. ")
    return random.choice(responses)

  # Define .cubed_intent():
  def cubed_intent(self, number):
    number = int(number)
    cubed_number = number * number * number
    return (f"The cube of {number} is {cubed_number}. Isn't that cool? ")

  # Define .no_match_intent():
  def no_match_intent(self):
    responses = ("Please tell me more. ", "Tell me more! ", "Why do you say that? ", "I see. Can you elaborate? ", "Interesting. Can you tell me more? ", "I see. How do you think? ", "Why? ", "How do you think I feel when you say that? ")
    return random.choice(responses)

# Create an instance of AlienBot below:
my_bot = AlienBot()
my_bot.greet()

I feel like there is a really simple solution to this, I've only been coding for 1 week so this is really new to me, and I appreciate your help :)

Upvotes: 1

Views: 209

Answers (1)

OfirD
OfirD

Reputation: 10460

The answer is indeed simple. Let's reduce your code to a minimally reproducible one:

def match_reply(self, reply):
    for key, value in self.alienbabble.items():
      # I omitted the 3 lines that were here - defining 'intent', 'regex_pattern' and 'regex' - 
      # since at this point they're yet not used
      reply = input(self.match_reply(reply)) # oops! self.match_reply is called again!

As you can see, you recursively call self.match_reply without anything to stop it.

EDIT:

There are another 2 things you need to fix:

  1. Let's change match_reply:

    a. Let's give it the more appropriate name match_alien_response.
    b. Let's make it do what it should: just match a reply. Thus, we don't need it to get another input from the user.
    c. Let's make sure it iterates all the keys in alienbabble and doesn't return immediately.
    d. We need to use re.findall to get all matches in a string

    All these changes give us the following code:

    def match_alien_response(self, userReply):
      found = False
      for intent, regPattern in self.alienbabble.items():
        found_match = re.findall(regPattern, userReply)
        if found_match and intent == 'describe_planet_intent':
          return self.describe_planet_intent()
        elif found_match and intent == 'answer_why_intent':
          return self.answer_why_intent()
        elif found_match and intent == 'cubed_intent':
          return self.cubed_intent(found_match[0])
      if not found:
        return self.no_match_intent()
    
  2. Inside no_match_intent, it should be responses = instead of responses:

Upvotes: 4

Related Questions