patternpythonModerate
Save and load the state of a role-playing game using Pickle
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
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
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
In a few places, you use
Global variables
Global variables are evil. Note that global constants are okay, but if you're using the
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
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.
to:
Even the %d ones don't need any special handling. If you really want, you can use
Incorrect
In
The same thing is true of
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
That looks so clean now.
Saving
You have
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,
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
orIn 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 loopIn
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
breakCode 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
breakContext
StackExchange Code Review Q#159818, answer score: 11
Revisions (0)
No revisions yet.