Ruled-Based Chatbots : Alien Bot Project

Hello,

I’ve had issues with the Project Alien Bot (https://www.codecademy.com/paths/build-chatbots-with-python/tracks/rule-based-chatbots/modules/rule-based-chatbots/projects/python-chatbot-alienbot).

It works perfectly after following the instructions, except that the cubed_intent and the answer_why_intent doesn’t work.
After trying a few changes, it looks like I can’t iterate through the self.alienbabble dictionnary. It only matches with the firs key:value pair (describe_planet_intent here).

Here is my code:

# 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': (r'.*\s*your planet.*'), 'answer_why_intent': (r'.*\s*why are.*'),
'cubed_intent': r'.*cube.*(\d+)'
}

  # Define .greet() below:
  def greet(self):
    self.name = input("What is your name? ")
    will_help = input(f"Hi {self.name}, I'm Jarvis. 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_pattern = value
      found_match = re.search(regex_pattern, reply)
      if found_match and intent == 'answer_why_intent':
        return self.answer_why_intent()
      
      elif found_match and intent == "describe_planet_intent":
        return self.describe_planet_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 an utopia of diverse organisms and species. ", "I am from Odipidus, 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):
    int(number)
    cubed_number = number ** 3
    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? ", "Why?")
    return random.choice(responses)
# Create an instance of AlienBot below:
Jarvis = AlienBot()
Jarvis.greet()

Any help please?

Thank you!

A quick glance suggests there’s an error in cubed intent, int(object) returns a value. What are you doing with it?
As for the second issue, I can’t see an issue with the answer_why_intent function. There is an error in the loop in match_reply though. Your description of the error means you should be able to locate it.

I’m facing the same challenges as you. I think one of the problems is that we’re expecting the chatbot to continue after we complete an intent, but we’re not calling for it to return to its default chat state.

I ended up following along with the Codecademy livestream from 2018 and was better able to wrap my head around the instructor’s design. One difference is that she reverses the order of the keys in the dictionary, so that the regex pattern is the key and can return a series of alien responses.

I wish I could see a complete example code, however, that follows the steps on the platform. Maybe I’m missing an option to view it? The way the instructor develops the chatbot is really differently conceived, so I felt like I learned valuable info about how to design a chatbot…just not THIS chatbot as presented on the platform. Maybe that’s why the curricular designer didn’t even link the code-along video here at all?

Isn’t that dictionary just… a list of pairs? No point using dict for that. Also, rather than putting strings in the dict, put the handler functions in the dict:

alienbabble = (
    (describe_planet_intent, r".*\s*your planet.*"),
    (answer_why_intent, r".*\s*why are.*"),
    (cubed_intent, r".*cube.*(\d+)"),
)
def match_reply(reply):
    for handler, pattern in alienbabble:
        if re.search(pattern, reply):
            return handler(reply)
    return no_match_intent(reply)

And if so, reversing the pairs would make no difference since no lookup by key is being done anyway.

The class is also not used, may as well use regular functions.

I’m not sure if you’re replying to me or the OP? If to me, I commented about the pair reversal to identify one of the first points of departure between the platform code design and the livestream design.

In the latter case, setting the regex pattern as key and responses as values takes us down a different path than the platform code, as there’s no need to create separate functions for different intents.

In any case, though the livestream didn’t help me resolve the issues with my own code (like OP), I felt it incredibly helpful overall. I ended up scrapping my code and just coding along with her, since I then was able to grasp and implement the optional reflections add-in task, which appears in the hint text for the final step.

I mean that the reversal is insignificant, no significance is given to the order. (and as such it shouldn’t be a dict either, this isn’t something dict helps with)

And if it’s insignificant, how is that enabling you to do something differently?

The intent functions have data in them, surely you can’t eliminate them without moving that data to some new place?

Maybe I’m missing something.

Looking briefly at that video, that data is put here in place of the functions:

alienbabble = (
    (describe_planet_intent, r".*\s*your planet.*"),
    (answer_why_intent, r".*\s*why are.*"),
    (cubed_intent, r".*cube.*(\d+)"),
)

And since the functions are doing the same thing (random.choice) for different data, then yeah, the only thing you need is that data, not the function. You don’t get that from swapping keys and values though.

You have a pair of two values, there’s no key!

I had the same problem - after quite a bit of digging, I found an error in the code which is what the instructions told us to do - the return self.no_match_intent() should be outside the for loop (instead of being inside an else statement inside the for loop). Otherwise every time you input an intent other than “your planet” it won’t even loop through the rest of the dictionary, instead it will go to no_match in the end.

This should fix your code! :slight_smile:

1 Like