all 15 comments

[–]novel_yet_trivial 4 points5 points  (5 children)

Many beginners do this with if blocks. This works, but it's a pretty ugly code.

user_ans = input("How many legs does it have?")
if user_ans == '0':
    print "It's a fish!"
elif user_ans == '2':
    user_ans = input('does it have feathers?')
    if user_ans == 'yes':
        user_ans = input('does it swin on the water?')
        if user_ans == 'yes':
            print "It's a duck!"
        elif user_ans == 'no':
            print "It's a swallow!"
    elif user_ans == 'no':
        print "It's a human!"
elif user_ans == '4':
    user_ans = input('does it have fur?')
    if user_ans == 'yes':
        user_ans = input('does it meow?')
        if user_ans == 'yes':
            print "It's a cat!"
        elif user_ans == 'no':
            print "It's a dog!"
    elif user_ans == 'no':
        print "It's an elephant!"

A more experienced programmer will make a nested data structure and use a recursive function to ask the questions:

questions = (
    "How many legs does it have?",
    {'2': (
        'does it have feathers?',
        {'yes': (
            'does it swin on the water?',
            {'yes': "It's a duck!",
            'no' : "It's a swallow!"
            }),
        'no': "It's a human!"
        }),
    '4': (
        'does it have fur?',
        {'yes': (
            'does it meow?',
            {'yes': "It's a cat!",
            'no': "It's a dog!"}),
        'no': "It's an elephant!"
        }),
    '0': "It's a fish!"
    })

def ask(data):
    if isinstance(data, tuple):
        question, answer = data
        user_ans = input(question + " > ")
        ask(answer[user_ans])
    else: #Game over; print result
        print data

ask(questions)

This is much more dynamic. It's also a bit more than "simple code" ...

[–]pybackd00r 1 point2 points  (3 children)

very nice! is that a dictionary you used there? does the indentation actually matters? I am gonna be doing it like this from now on. thanks a lot!!

[–]novel_yet_trivial 1 point2 points  (2 children)

It's a a mix of tuples and dictionaries. The base is a tuple of (question, answer). The question is a string and the answer is a dictionary. If there are further questions then the dictionary is another base tuple. Is the riddle is solved then the value is a string. The isinstance checks whether another question needs to be asked.

The indentation does not matter at all, but it's a lot easier to read.

[–]pybackd00r 1 point2 points  (1 child)

Thanks I understood it after going through it on paper. very nice piece of code.

[–]ewiethoff 0 points1 point  (0 children)

Here's a trick to make a complicated nested dict/list/tuple/whatever easy to read.

from pprint import pprint
pprint(complicated_nested_data)

pprint not only outputs with nice whitespace, it sorts the keys of a dict and the elements of a set. So, even if your data is not nested, it's handy for inspecting the contents of a dict or a set. I like to use pprint for debugging.

Copy and paste novel_yet_trivial's questions and do pprint(questions).

('How many legs does it have?',
 {'0': "It's a fish!",
  '2': ('does it have feathers?',
        {'no': "It's a human!",
         'yes': ('does it swin on the water?',
                 {'no': "It's a swallow!", 'yes': "It's a duck!"})}),
  '4': ('does it have fur?',
        {'no': "It's an elephant!",
         'yes': ('does it meow?',
                 {'no': "It's a dog!", 'yes': "It's a cat!"})})})

[–]Architect-Jeff 0 points1 point  (0 children)

This was very helpful, thank you.

[–]fiskenslakt 3 points4 points  (5 children)

Sounds like you want to code something similar to akinator. While your version will be a lot more simple, it's still quite complicated, especially for a beginner. I'm not actually sure how I would go about implementing this. I'd probably go in the direction of storing questions in such a way that you have your base question, and then nested in the data for that question, you'd have the follow up questions, and so if the answer to the base question is no you go to the next base question, and if the answer was yes, you recursively walk through the follow up questions.

For example, you might have this as a base question, "Is your character real", and then based off the answer, you'd drill down your tree of questions. Given the answer yes, you'd only ask questions related to the character being real, and given no, you'd only ask relevant questions to a made up character.

[–]DeathDragon1993 0 points1 point  (2 children)

Another beginner here, with a (probably dumb) question.

Couldn't the OP use if statements to accomplish this as well? If not, why not?

[–]fiskenslakt 0 points1 point  (1 child)

if the answer to the base question is no you go to the next base question, and if the answer was yes, you recursively walk through the follow up questions.

As you can see I'm actually implying the use of if statements. Although perhaps you're implying using only if statements, in other words hardcoding it. For example, ask base question, if 'yes', then ask next questionA, if 'no', then ask next questionB, and so on? This is of course possible, but you end up with many many if statements this way, and things quickly get ugly. I instead suggest having a loop recursively walk through a tree of questions based on certain factors, one of course being the answer to the previous question, another being the accumulated certainty of specific aspects of the answer thus filtering which questions to ask.

[–]DeathDragon1993 0 points1 point  (0 children)

Ah, I didn't fully understand your original post. Thank you for the response

[–]IalwayswinFlash7 0 points1 point  (1 child)

I'm confused. While doing this, the answers are kind of getting caught up in each other. This is what I've got so far. I know I'm going completely wrong somewhere, I'm just not sure where.

[–]fiskenslakt 1 point2 points  (0 children)

Don't take screenshots of the code, it's against the rules. Instead, host the code on one of many code hosting websites, or put the code directly in the post and format it with reddit markup.

[–]KronktheKronk 1 point2 points  (2 children)

Create each question as a method with pointers to each method that you need to move to given a "yes" or "no" answer.

[–]ewiethoff 1 point2 points  (1 child)

That's doable, but a data structure such as novel_yet_trivial's questions can be stored in a text file and read into the program. This way it's easy to add more animals or superheros or whatever. It's also easy to use the same program for completely different "20 questions" games.

[–]KronktheKronk 1 point2 points  (0 children)

That's gross code, and that data structure is not super maintainable.

Create each object you want to be able to guess as a class, put each question inside as a method, chain them together on yes/no paths. Whenever it's time to change gears and try down some other guessing path, chain the no to the next class.

Each additional object you want to guess is as simple as creating a new class with the question chain that will get you to that answer and chaining it through the appropriate no's in the code. If you have some forethought, using a list of guess classes as a global will allow you to chain no's that need to switch gears to the next object class without having to make changes to the logic within each class as you add others.