The best way to make a prompt dependend on what's in a dictionary

I’m working on ex 43.

Some of my scenes can be visited multiple times, but every-time a scene is visited the choice the player chooses from a dictionary has to be deleted, so that the option is no longer available next time the scene is entered.

I’ve mocked up this little piece of code to play around with and I wonder if there is a way to alter the the input prompt so when a key in the dictionary is no longer the text changes accordingly?

class Restaurnante(object):


    restaturantChoice = {1:['burgerjoint', '1) Local five guys'],
    2:['cafe','2) The greasy spoon around the corner'],
    3:['vegan','3) The new indie vegan cafe']
    }

    def go(self):
        choice = int(input(f'These are the restaurants thats not full tonight.\nWhich do choose?\n{self.restaturantChoice.get(1)[1]}\n{self.restaturantChoice.get(2)[1]}\n{self.restaturantChoice.get(3)[1]}\n>>> '))

        keep = self.restaturantChoice.get(choice)[0]
        del self.restaturantChoice[choice]

        return keep

returnspot = Restaurnante()
keep = returnspot.go()
print(keep)

Have a read of pop().

Python pop() method removes an element from the dictionary. It removes the element which is associated to the specified key. If specified key is present in the dictionary, it remove and return its value. If the specified key is not present, it throws an error KeyError.

1 Like

Thanks @gpkesley but how does that help altering the?
Or do I have to make if-statements for every possible scenario according to the amount of options that is in my dictionary?

choice = int(input(f'These are the restaurants thats not full tonight.\nWhich do choose?\n{self.restaturantChoice.get(1)[1]}\n{self.restaturantChoice.get(2)[1]}\n{self.restaturantChoice.get(3)[1]}\n>>> '))

Instead of altering the string every time an item gets popped from the dictionary, you could build the string based on the current choices each time you need it. That sounds more complicated than it actually is.

1 Like

Your requirement was:

Personally I’d keep the question generic and the responses dict-driven. If the scene navigation is driven by the dict, you can remove values for dict items once visited. You could create a customer exception for the KeyError as a catch all behaviour in that case.

But given that you are learning, why not try implementing a few different things and see what is easiest, more logical, less maintenance, more testable? It’s all very useful learning.

Remember this is about a journey not an endpoint.

1 Like

Try putting the prompt formatting into a function on its own so that you just write:

int(input(prompt(state))

Then state is some dict with what’s going on right now, and prompt is a function that knows how to take state and return a string with the right prompt.

Also, you’re kind of overloading what input does. This is a good exercise for just figuring out how to do it, but I would print all the junk out then do a generic prompt rather than fill input() with overly complicated junk.

1 Like

Hi @florian
I can’t picture what you have in mind?

Thanks @gpkesley and @zedshaw

I settled on something like this.
Wondering if thats completely overkill and there is an easier way?

from random import randint

class Restaurnante(object):


    restaturantChoice = {1:['burgerjoint', 'Local five guys'],
    2:['cafe','The greasy spoon around the corner'],
    3:['vegan','The new indie vegan cafe']
    }

    def go(self):
        if len(self.restaturantChoice) > 0:

            #choice = int(input(f'These are the restaurants thats not full tonight.\nWhich do choose?\n{self.restaturantChoice.get(1)[1]}\n{self.restaturantChoice.get(2)[1]}\n{self.restaturantChoice.get(3)[1]}\n>>> '))
            prompt = self.promptcreation(self.restaturantChoice)

            print('These are the restaurants thats not full tonight.\nWhich do choose?')
            for choices in prompt:
                print(f'{choices}) {prompt[choices][1]}')

            choice = int(input('>>>'))
            optionCheck = prompt.get(choice)

            if optionCheck:
                date = prompt.pop(choice)[0]
                print(f'Very well, you chose: {date}')

            else:
                randomedate = randint(1,len(prompt)+1)
                date = prompt.pop(randomedate)[0]
                print(f"If you don't know how to key in the right number we'll just send you on a date\nYou are going {date}")


            self.restaturantChoice = prompt
            return date


        else:
            print('no more restaureantes to choose from')
            return 'No restaurante'


    def promptcreation(self, state):
        oldprompt = state
        options = range(1,len(oldprompt)+1)
        newprompt = {}

        for (key, options) in zip(oldprompt, options):
            newprompt[options] = [f'{oldprompt[key][0]}' ,f'{oldprompt[key][1]}']

        return newprompt




returnspot = Restaurnante()
date1 = returnspot.go()
print(date1)

date2 = returnspot.go()
print(date2)

date3 = returnspot.go()
print(date3)

date4 = returnspot.go()
print(date4)

I can’t picture what you have in mind?

If you pop each chosen location from the dict, you can generate the next prompt string from the remaining elements. Here’s a quick hack:

choices = { 1: "first", 2: "second", 3: "third" }

def make_prompt(choices):
    return "\n".join(f"{key}: {value}" for key, value in choices.items())

prompt = make_prompt(choices)
print(prompt)

# Output:
1: first
2: second
3: third

Does that help?

1 Like

I think this is more complicated than necessary.

I would take a look at the built-in enumerate function and use a list instead of a dictionary.

2 Likes

I’ll take a look at your suggestion tmr… Thank you @florian

Yes that looks a lot better. I can see you probably tested it while you were working on it.

Now, let me introduce you to more of the random API:

https://docs.python.org/3.1/library/random.html#random.choice

Specifically choice, shuffle, and others that work on lists and sets to produce randomized selections. I think some of this code could simplify your code in the else: clause you have there.

1 Like

Hi Florian

Playing around with your suggestion and trying to wrap my head around your return and list comprehension by taking it apart bit by bit.

I understand what .join() does.
I also understand what f"{key}: {value}" for key, value in choices.items() does.

I have been testing and playing around with the list comprehension and thought as an experiment to run

return f"{key}: {value}" for key, value in choices.items() does.

I was expecting to just get an output without line breaks.
However I get a syntax error.

return f’{key}: {value}’ for key, value in choices.items()
^
SyntaxError: invalid syntax

I’m wondering if I in the future I would make a comprehension where I’ll add some values into a string, how would I write that?

Do you mean something like this:

date = prompt.pop(random.choice(list(prompt.keys())))[0]

I can see how that can my code more simple.
I’ve tried to use enumerate in my for loop, however I just can’t get it to work.

for count, value in enumerate(list(oldprompt.values()))
            newprompt[count] = f'{count}) {value}'

I take all the values from my oldprompt dictionary and make them into a list so they can be fed the enumerate function.

However no matter what I do it gives me an syntax error.

for count, value in enumerate(list(oldprompt.values()))
^
SyntaxError: invalid syntax

What is it I don’t get?

Great. :slight_smile:

Maybe read up a bit on iterators and generators. (EDIT: I see now that reading this might be more confusing than helpful now… sorry.)

You get a syntax error because there are no parentheses or brackets enclosing the x for x in y expression. And think about this: What does that expression create, is it really a string? (Maybe it helps to take a look at join again, what kind of argument does it take?

Here the syntax error is caused by a missing colon at the end of the first line.


But my idea was still a bit different.

Look at it this way: Do you really need a dictionary at all just for enumerating the choices?
Maybe you can use only a list, remove (pop) used items, and generate the prompt string from the result of enumerate(list).

1 Like

I thought the use of dict was to maintain Zed’s map engine @florian

1 Like

Oh, in that case forget what I said. :innocent:

I was under the impression that this was going to be an independent “local” map just for the restaurant scene.

1 Like

That is a such a stupid mistake… But thank you for spotting it, I should have been capable of doing this myself. You are right I probably don’t need a dict.