HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Save and load the state of a role-playing game using Pickle

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
playingthepicklerolesavegamestateusingloadand

Problem

I am working on a text-based adventure game and I'm implementing new features, one of these features being saves and loads. This code works, and I know that there is a more efficient way of coding it, but this is sufficient. I will first post the code (feel free to play with it, but bear in mind that this is only the beginning of the game, and I have not yet started on the fighting and weapons etc.), and I will then post the saving and loading functions.

Keep in mind that it creates the save file in the same directory as the .py, I will explain later how to save them and load them from a separate directory.

```
#imports
import random
import sys
import time
import pickle

#Player Class
class Player:
def __init__(self, name):
self.name = name
self.maxhealth = 100
self.health = self.maxhealth
self.attack = 15
self.money = 0
def display(self, name):
print("Name:",self.name,"\nHealth:",self.health,"/",self.maxhealth,"\nAttack Damage:",self.attack,"\nMoney:",self.money)
#Enemy Class
class Dragon:
def __init__(self, name):
self.name = name
self.maxhealth = 150
self.health = self.maxhealth
self.attack = 5

#Start
def main():
print("Welcome to The Ultimate Dragon fighter RPG!")
print("1] Start")
print("2] Load")
print("3] Profile")
print("4] Exit")
option = input("--> ").upper()
if option == "1" or option == "S" or option == "START":
nameInputAsk()
elif option == "2" or option == "L" or option == "LOAD":
load()
nameInputAsk()
elif option == "3" or option == "P" or option == "PROFILE":
stats()
elif option == "4" or option == "E" or option == "EXIT":
print("Goodbye!")
time.sleep(2)
sys.exit()
else:
main()

#User Inputs IG Name
def nameInputAsk():
print("Hello! What do you want your name to be?")
option = input("--> ")
global PlayerIG
PlayerIG = Player(opt

Solution

Comments

You have #Player Class and #Enemy Class as comments. Comments should be used to clarify something that may otherwise be unclear. The line class Player: means that you are creating the Player class, so that comment does nothing.

Spacing

You need a little whitespace. Between the methods in the Player class, for example, I would add a blank line. Between the Player class and the Dragon class, I would add two blank lines. PEP 8, the official style guide for Python, agrees: https://www.python.org/dev/peps/pep-0008/#blank-lines.

Chained or

In a few places, you use if option == ... or option == ... or .... That's longer than it needs to be. You should take advantage of in (See also https://stackoverflow.com/q/15112125/5827958). For example:

if option in {"1", "S", "START"}:
    nameInputAsk()
elif option in {"2", "L", "LOAD"}:
    load()
    nameInputAsk()
...


Global variables

Global variables are evil. Note that global constants are okay, but if you're using the global keyword, it's probably a bad idea. A much better idea is to pass PlayerIG as an argument to whatever function needs it. In addition to avoiding the scourge of global variables, there is also a more clear advantage. You can now use these functions with multiple players.

Naming

According to PEP 8 (https://www.python.org/dev/peps/pep-0008/#function-names):


Function names should be lowercase, with words separated by underscores as necessary to improve readability.


mixedCase is allowed only in contexts where that's already the prevailing style (e.g. threading.py), to retain backwards compatibility.

This is also true of "instance" variables (https://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables). That means PlayerIG, for example, would also follow this rule and become player_ig.

String formatting

In many places, you use the "old-style" formatting; i.e. %s, %d, etc. There has been a new style of formatting introduced that has actually been around since Python 2.6; i.e. .format(). Since Python 3.0, that is the preferred method (see https://docs.python.org/2/library/stdtypes.html#str.format), though the old style isn't actually deprecated (as pointed out by jpmc26). That means changing something like:

input("Do you want to see your stats %s?" % player_ig.name)


to:

input("Do you want to see your stats {}?".format(player_ig.name))


Even the %d ones don't need any special handling. If you really want, you can use {:d} to make sure it treats it as an integer, but when you are dealing with real integers (not a subclass), it looks the same either way.

Incorrect while loop

In adventuringMarshMount(), you have a while loop that is guaranteed to execute only once, which makes the loop useless. This is because you wrote break at the end of the loop. Whether spinmarshMount is/isn't "marsh"; whether or not it's blank, that loop will break. Come to think of it, it's okay if it executes only once. This isn't user input; it's a random choice, so it's guaranteed to come out with something we're okay with. You can just skip the loop.

The same thing is true of randomDragonAppear(). In that function, however, I have another problem:

true and false as strings

True and False are constants in Python. You might as well use them like that instead of coming up with your own constants. They even work better in the if check:

drag_appear = random.choice([True, False])
if drag_appear:
    print("THERE IS A DRAGON!")
    dragonRunFight()
else:
    print("the noise was only a Pigeon. Pesky things.")


That looks so clean now.

Saving

You have askSave() do the saving for you in addition to asking. What if you decide sometime that you'll save automatically every 5 minutes, say? You have no way of saving except either duplicating code or asking the user anyway. I would put the saving code in its own function, and then call that function from askSave(). Better, just have askSave() return True or False, and let the calling function decide what to do.

Recursion

The default maximum recursion limit is 1000. That means that someone who is trying to crash your program can say something other than one of the correct options 1000 times and you're program will have a nasty error. Maybe you don't care about people who are trying to mess it up, but you might as well keep that from happening. I don't like recursion in most cases. Sometimes it's unfeasable to use a loop, but generally I prefer a loop. For example, askSave() can look like this:

while True:
    ask = input("Do you want to save?\n--> ").upper()
    if ask in {"Y", "YES"}:
        save_game()
    elif ask in {"N", "NO"}:
        print("Okay, maybe next time!")
    else:
        print("Sorry, that does not compute with me! Please try again!")
        continue
    break

Code Snippets

if option in {"1", "S", "START"}:
    nameInputAsk()
elif option in {"2", "L", "LOAD"}:
    load()
    nameInputAsk()
...
input("Do you want to see your stats %s?" % player_ig.name)
input("Do you want to see your stats {}?".format(player_ig.name))
drag_appear = random.choice([True, False])
if drag_appear:
    print("THERE IS A DRAGON!")
    dragonRunFight()
else:
    print("the noise was only a Pigeon. Pesky things.")
while True:
    ask = input("Do you want to save?\n--> ").upper()
    if ask in {"Y", "YES"}:
        save_game()
    elif ask in {"N", "NO"}:
        print("Okay, maybe next time!")
    else:
        print("Sorry, that does not compute with me! Please try again!")
        continue
    break

Context

StackExchange Code Review Q#159818, answer score: 11

Revisions (0)

No revisions yet.