2

Fairly new to python programming.
I'm writing a program for learning about US Presidents, the code works, but it is very rigid, I don't know how to add exceptions if the answer is wrong, but not technically wrong. i.e. If the question is Who is the 32nd President? The user should be able to answer Franklin D. Roosevelt, or, Franklin Roosevelt, or FDR or just Roosevelt etc... The way my code is setup only allows for one answer, is there a way to add exceptions to where the user can get the answer right without having to answer exactly?

import random
def main():
    presidents = {"first":"George Washington", "second":"John Adams","third":"Thomas Jefferson",
                  "fourth":"James Madison", "fifth":"James Monroe", "sixth":"John Quincy Adams",
                  "seventh":"Andrew Jackson", "eighth":"Martin Van Buren", "ninth":"William Henry Harrison",
                  "tenth":"John Tyler","eleventh":"James K. Polk", "twelth":"Zachary Taylor",}
    wrong = []
    print("President Test \n")
    incorrect_answers = False

    while len(presidents)>0:
        pick=random.choice(list(presidents.keys()))
        correct_answer=presidents.get(pick)
        print("Who was the",pick,"President of the United States?"
        answer=input("Your answer?: ")

        if answer.lower()==correct_answer.lower():
            print("That's Correct!\n")
            del presidents[pick]
        else:
            print("That's Incorrect.")
            print("The correct answer is", correct_answer)
            wrong.append(pick)
            incorrect_answers = True

    Print("You missed", len(wrong), "Presidents.\n")
    
    if incorrect_answers:
        print("Here are the ones that you may want to brush up on:/n")
        for each in wrong:
            print(each)
    else:
        print("Perfect!")

main()
   

I was hoping that if I simply added an "or" statement in the list code that it would magically work, but I see that it doesn't.

"eleventh":"James K. Polk" or "James Polk"

Also hoping that it would be able to use more than 2 options, "thirty fifth":"John F. Kennedy" or "John Kennedy" or "JFK"

Is there a way to do this with a list?

Thanks

A. K. M.
  • 21
  • 1
  • 5
    If you had it formatted like `"thirty fifth": ["John F. Kennedy", "John Kennedy", "JFK"]` you could then just check if the object attached to that keys is a string or a list, and if it's a list, compare with the `in` operator, and if it's a string, compare with the `==` operator. – Random Davis Apr 12 '23 at 20:33
  • 1
    Also, you can make the user's answer case insensitive by using the `lower()` method on their answer, just be sure that your answers are also lowercase. – Shmack Apr 12 '23 at 20:38
  • Keep in mind there were two presidents named Roosevelt - Teddy and Franklin. – MattDMo Apr 12 '23 at 20:39
  • You should use a list of correct answers, and compare the input with the similarity of the correct answer. There are a couple of ways here https://stackoverflow.com/questions/17388213/find-the-similarity-metric-between-two-strings – Pablo Estevez Apr 12 '23 at 20:54
  • You should consider mapping every name thereafter map positions to those 3 letter ids – Kayvan Shah Apr 16 '23 at 09:27

2 Answers2

1

Make each entry in your presidents dict a list (or a set, or a tuple) of lowercased acceptable answers:

    "eleventh": ["james k. polk", "james polk"]

and then when you check the answer, see if the lowercase version of it is in the list:

        correct_answer=presidents.get(pick)
        if answer.lower() in correct_answer:
            print("That's Correct!\n")

If correct_answer is mixed case:

    "eleventh": ["James K. Polk", "James Polk"]

you can do the case-insensitive check by constructing the lower-case version on the fly:

        if answer.lower() in [a.lower() for a in correct_answer]:
            print("That's Correct!\n")
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • This will only work if the answer perfectly matches an entire entry in the list. Partial matches ("roosevelt" matching a list item "Franklin D. Roosevelt") won't work, even using `lower()`. Instead, use `any([answer.lower() in item.lower() for item in correct_answer])`, assuming `correct_answer` is a list. – MattDMo Apr 12 '23 at 20:45
  • That's not what OP asked for -- it's trivially easy to game that by giving single-letter answers. :) Doing fuzzy matching in a useful way is certainly doable but it's not what OP was asking about. – Samwise Apr 12 '23 at 20:47
  • Premature optimization, I suppose, even though as you point out it introduces a bug or hole in the logic. I hadn't thought that far ahead yet... – MattDMo Apr 12 '23 at 20:54
  • A pretty standard way of doing fuzzy matching would be to compute the Levenshtein distance between the user's answer and the correct answers, and have some threshold where it's "close enough". `difflib` makes this pretty easy, and there are probably other modules that make it even easier, but it's still significantly more complicated than where the rest of this question is at. – Samwise Apr 12 '23 at 20:57
  • Very true. A quick search pointed me to [`fuzzywuzzy`](https://pypi.org/project/fuzzywuzzy/), although it appears to be unmaintained since 2020, and its replacement since 2021. You can always use `python-Levenshtein` directly, too. Many ways to skin that cat. – MattDMo Apr 12 '23 at 21:07
  • Thank you for your help, this worked, I have to do a lot of retyping, but this definitely works! – A. K. M. Apr 13 '23 at 00:56
  • @Samwise Levenshtein distance is probably a bit advanced for where the OP is right now. But would be a great goal for them to get to if they want. – Code-Apprentice Apr 13 '23 at 16:53
0

You can't add "or" to a list because a list is data and "or" is logic. You can solve your problem by changing the data structure, though, and with a small change to the logic of the code.

One way to do this is to store a list of correct answers for each key in the dictionary rather than just a single string. Then you can use the in operator to check if the entered answer is correct rather than == for strict equality.

If you want to allow case-insensitive comparison, then you will need to write a manual loop rather than using the in operator.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268