Search code examples
pythonfunctionsave

How do I save my place in a text adventure (a function) to a file?


I'm starting a rudimentary text adventure game. I got a prototype working, but because each choice is a function, I can't figure out how to safely save my place in a file.

I considered saving the function's name to a file, but I can't think of a good way to get the function from its name as a str after reading the file. eval on an arbitrary str is notoriously unsafe. I considered a dict mapping every function to its name as a str, but it seems that as more choices pile up, this dict is gonna bloat my script.

def choice1():
    while True:
        text = input("A or B?: ")
        if text == "A":
            return False, choice2
        elif text == "B":
            saygameover()
            return True, None
        elif askedforsave(text):
            return True, choice1
        else:
            saytryagain()

def choice2():
    while True:
        text = input("C or D?: ")
        if text == "C":
            print("you win!")
            return True, None
        elif text == "D":
            saygameover()
            return True, None
        elif askedforsave(text):
            return True, choice2
        else:
            saytryagain()

def askedforsave(text):
    if text == "save":
        return True
    else:
        return False

def saytryagain():
    print("try again...")

def saygameover():
    print("game over.")

def play(choice = choice1):
    done = False
    while not done:
        done, choice = choice()
    if choice != None:
        save(choice)

def save(choice):
    pass

def load(file):
    pass
    return choice

Solution

  • This is what I got so far. isidentifier is probably unnecessary when I'm already checking for a name in globals() but it's a good trick for making sure you're working with a valid Python name instead of an expression.

    import types
    
    def load(string):
            # if string is valid Python name
        if (string.isidentifier() and
    
            # if string in this global scope's symbol table
            string in (thisglobal := globals()) and
    
            # if object is a non-builtin function
            isinstance(something := thisglobal[string], types.FunctionType)):
    
            return something
        else:
            return nogame
        
    def nogame():
        return True, None