1

i am very new to python (we are using pycharm in my half-year programming class) and i am working on my final project which is a choose your own adventure kind of thing. as an example of what im asking, there is a part where you go into a crawlspace and retrieve a guide to fix something else. after you retrieve the guide, if you were to go back to the crawlspace it'd act as if you retrieved the guide again, which is what im unsure on how to solve. here's the main menu for the section (im sure there's a way to simplify all my code but we have to do it like this):

def left_path():
    left=input("""What will you do?
[1] Inspect computer
[2] Inspect window
[3] Inspect crawlspace
[4] Check notepad
Type here: """)
    if left=="1":
        computer()
    elif left=="2":
        window()
    elif left=="3":
        crawlspace()
    elif left=="4":
        print(inventory)
    else:
        print("That's not an option. Try again.")
        left_path()

and going to the crawlspace is this:

def crawlspace():
    print("You get on the floor and crawl into the crawlspace.")
    #sleep(3)
    print("You can't see, but you feel around and find a paper.")
    #sleep(3)
    print("You leave the crawlspace and look at the paper.")
    #sleep(3)
    print("It appears to be a guide to the wires...")
    #sleep(3)
    print("...but there's something written in the corner as well.")
    #sleep(3)
    print("You decide to write it down.")
    inventory.add_item(Item('5##, #△#, C##'))
    left_path()

same goes for other parts where you find pieces of passwords, but i only need to show the one to convey what i mean. hopefully that's clear enough, ive never asked anything on here before. basically, i just want it to be like, "you already explored the crawlspace" if you try to select it again. im sure it's a very easy fix, but again i'm incredibly new and know very little. as a side note im familiar with time.sleep, the hashtags are just so i can speed through it to make sure everything works.

  • 5
    Those are comments, not hashtags. Kids these days :) – Mad Physicist Dec 19 '19 at 18:15
  • 1
    Short answer is, you need to store a game state somewhere. It would list the places you visited, for example, or at least the some of the ones that matter. – Mad Physicist Dec 19 '19 at 18:16
  • 1
    a simple boolean variable crawlspace_already_visited would do that – B. Go Dec 19 '19 at 18:18
  • 1
    I agree with @MadPhysicist. In the same way you're adding things to your inventory, you should have a list (or a set!) of places you've visited, and then rather than just checking the input, you check the input and your list of visited areas. – pip install frisbee Dec 19 '19 at 18:18
  • I agree with @B.Go, a simple variable to check whether it's been visited, couple with an `if` statement would seem the easiest answer, and would work whether the code is written functionally or OOP – G. Anderson Dec 19 '19 at 18:23
  • @G.Anderson. While easiest in the very short term, it would be very hard to maintain as the size of the adventure increased. For a school project, it's probably OK. At the same time, I would not recommend learning that as a way to go in general. – Mad Physicist Dec 19 '19 at 18:28
  • @pipinstallMonica. I would recommend something more flexible than that, like a dictionary that each part of the game could keep its keys in. If a key is found and has a certain value, act a certain way. – Mad Physicist Dec 19 '19 at 18:29

2 Answers2

6

You need to save that state somewhere. You could use a global, but that would just pollute your code in my opinion. I would probably prefer binding the state to the function itself. E.g.

def crawlspace():
    explored = getattr(crawlspace, 'explored', False)
    if explored:
        return print('you already explored the crawlspace')
    crawlspace.explored = True
    print('exploring...')

crawlspace()
crawlspace()

Output:

exploring...
you already explored the crawlspace

EDIT: You could even use a simple decorator with an inner wrapper to avoid redundant copy-paste:

from functools import wraps

def to_explore_only_once(func):
    @wraps(func)
    def inner(*args, **kwargs):
        if getattr(inner, 'explored', False):
            return print(f'you already explored the {func.__name__}')
        inner.explored = True
        return func(*args, **kwargs)
    return inner

@to_explore_only_once
def crawlspace():
    print('exploring the crawlspace...')

@to_explore_only_once
def forest():
    print('exploring the forest...')

@to_explore_only_once
def city():
    print('exploring the city...')

crawlspace()
crawlspace()
crawlspace()
forest()
forest()
forest()
city()
city()
city.explored = False
city()

Output:

exploring the crawlspace...
you already explored the crawlspace
you already explored the crawlspace
exploring the forest...
you already explored the forest
you already explored the forest
exploring the city...
you already explored the city
exploring the city...
Jordan Brière
  • 1,045
  • 6
  • 8
  • Alternatively, since python follows the [EAPF principle](https://stackoverflow.com/a/11360880/9183344), you might consider a `try` block instead of `getattr(...)` – r.ook Dec 19 '19 at 18:36
  • I use a try block when this actually makes sense to do so. If the goal is to only retrieve the value or a default, it doesn't make much sense when `getattr` already does exactly that internally. It would make sense if there was more to get done in case of the attribute being [non]existent, but in that specific case, 1 line vs 4, and I also believe the try block to be slightly slower in that specific situation. Thanks for the input regardless. :-) – Jordan Brière Dec 19 '19 at 18:53
  • it fixed the crawlspace part, but there's another part where you fix a button. is there a way for it to only show "the button is fixed" once it's fixed? like, if the player needs to leave and find the guide to fixing it. with this code if you leave even without fixing it it'll say it's fixed. i can add the code i have if needed :) – billy abaddon Dec 19 '19 at 19:28
  • If the player leave before fixing the button, you could just reset the explored state. I've edited the code above, to bind the state to the inner and added an example how to reset the state. Basically, you just have to do: `something.explored = False`. – Jordan Brière Dec 19 '19 at 19:42
0

You should track system state somehow. For problem this simple, a variable would suffice.

Let's say, like this:
Add variable declaration visited = {} where you are initializing your game and later pass var visited to all your functions. Then crawlspace becomes:

def crawlspace(visited):
    # ... story here ...
    print("You decide to write it down.")
    inventory.add_item(Item('5##, #△#, C##'))
    visited['crawlspace'] = True
    left_path(visited)

And

def left_path(visited):
    left=input("""What will you do?
[1] Inspect computer
[2] Inspect window
[3] Inspect crawlspace
[4] Check notepad
Type here: """)
    if left=="1":
        computer(visited)
    elif left=="2":
        window(visited)
    elif left=="3":
        if not visited.get("crawlspace", None):
            crawlspace(visited)
        else:
            print("You don't find anything new.")
    elif left=="4":
        print(inventory)
    else:
        print("That's not an option. Try again.")
        left_path(visited)
julka
  • 1,172
  • 2
  • 13
  • 29