Students write and read a file, save a dictionary with json, catch FileNotFoundError for a first run, then add save and load commands to their text adventure and test that a save survives a page reload.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 10 min · Milestone 1 10 min · Milestone 2 10 min · Make 10 min
Read the unit primer first. This lesson runs in the Python playground (real Python in the browser, Pyodide), not the turtle playground: it has real files. The first load takes a few seconds; before teaching it, open the site's Check your school page once on a student device at school. A student's files are kept in their browser for this course and listed in the Files tab, where they can be read and deleted; they survive closing the page, but not clearing the browser's data, and they do not follow a student to another device. open(name, "w") empties the file first (the Predict and the bug task); open(name) reads. with closes the file when its block ends. json.dump(data, f) writes a dictionary or list as JSON text, and json.load(f) gives the same dictionary back. Opening a missing file causes FileNotFoundError, caught exactly like the errors in Lesson 4. The game saves here, bag and things (everything that changes during play): saving only here and bag would let a player take the key twice.
Milestone 2 is the hard part: putting the loaded values back into here, bag and things. When students are stuck, ask them to say aloud what game holds after json.load, before giving any code.
| What you will see | A question to ask | The fix |
|---|---|---|
| FileNotFoundError when they load. | Did you save first, in this course's playground? | Save first, or catch FileNotFoundError and say there is no saved game. |
| The save file is empty, or json.load says the file is not valid JSON. | Which open() has "w" in it? | Read with open("game.json"), not "w": "w" empties the file. Delete the broken file in the Files tab and save again. |
| After load, the key is back in the kitchen as well as in the bag. | What else changes during play, apart from here and bag? | Save and load things too. |
| load says Loaded. but nothing changed. | Did you set here, bag and things from game, or only read the file? | here = game["here"], and the same for bag and things. |
One possible solution for each task. Other correct programs are fine.
import json
rooms = {
"hall": "A long hall with a creaky floor.",
"kitchen": "A kitchen that smells of toast.",
"garden": "A garden full of tall grass. A locked shed stands to the east.",
"shed": "A dusty shed. Something glints in the corner.",
}
exits = {
"hall": {"north": "kitchen", "east": "garden"},
"kitchen": {"south": "hall"},
"garden": {"west": "hall", "east": "shed"},
"shed": {"west": "garden"},
}
things = {
"hall": ["lamp"],
"kitchen": ["key"],
"garden": [],
"shed": ["crown"],
}
bag = []
here = "hall"
print(rooms[here])
while True:
command = input("> ").strip().lower()
words = command.split()
if command == "quit":
break
elif command == "look":
print(rooms[here])
print("Exits: " + ", ".join(exits[here]))
if len(things[here]) > 0:
print("You see: " + ", ".join(things[here]))
elif command == "bag":
print("You carry: " + ", ".join(bag))
elif len(words) == 2 and words[0] == "go":
if words[1] in exits[here]:
if exits[here][words[1]] == "shed" and "key" not in bag:
print("The shed is locked.")
else:
here = exits[here][words[1]]
print(rooms[here])
else:
print("You can't go that way.")
elif len(words) == 2 and words[0] == "take":
if words[1] in things[here]:
things[here].remove(words[1])
bag.append(words[1])
print("You take the " + words[1] + ".")
else:
print("There is no " + words[1] + " here.")
elif len(words) == 2 and words[0] == "drop":
if words[1] in bag:
bag.remove(words[1])
things[here].append(words[1])
print("You drop the " + words[1] + ".")
else:
print("You don't have a " + words[1] + ".")
elif command == "save":
game = {"here": here, "bag": bag, "things": things}
with open("game.json", "w") as f:
json.dump(game, f)
print("Saved.")
else:
print("Try: look, go north, take lamp, drop lamp, bag, save, load or quit")
if "crown" in bag:
print("You found the crown. You win!")
break
import json
rooms = {
"hall": "A long hall with a creaky floor.",
"kitchen": "A kitchen that smells of toast.",
"garden": "A garden full of tall grass. A locked shed stands to the east.",
"shed": "A dusty shed. Something glints in the corner.",
}
exits = {
"hall": {"north": "kitchen", "east": "garden"},
"kitchen": {"south": "hall"},
"garden": {"west": "hall", "east": "shed"},
"shed": {"west": "garden"},
}
things = {
"hall": ["lamp"],
"kitchen": ["key"],
"garden": [],
"shed": ["crown"],
}
bag = []
here = "hall"
print(rooms[here])
while True:
command = input("> ").strip().lower()
words = command.split()
if command == "quit":
break
elif command == "look":
print(rooms[here])
print("Exits: " + ", ".join(exits[here]))
if len(things[here]) > 0:
print("You see: " + ", ".join(things[here]))
elif command == "bag":
print("You carry: " + ", ".join(bag))
elif len(words) == 2 and words[0] == "go":
if words[1] in exits[here]:
if exits[here][words[1]] == "shed" and "key" not in bag:
print("The shed is locked.")
else:
here = exits[here][words[1]]
print(rooms[here])
else:
print("You can't go that way.")
elif len(words) == 2 and words[0] == "take":
if words[1] in things[here]:
things[here].remove(words[1])
bag.append(words[1])
print("You take the " + words[1] + ".")
else:
print("There is no " + words[1] + " here.")
elif len(words) == 2 and words[0] == "drop":
if words[1] in bag:
bag.remove(words[1])
things[here].append(words[1])
print("You drop the " + words[1] + ".")
else:
print("You don't have a " + words[1] + ".")
elif command == "save":
game = {"here": here, "bag": bag, "things": things}
with open("game.json", "w") as f:
json.dump(game, f)
print("Saved.")
elif command == "load":
try:
with open("game.json") as f:
game = json.load(f)
here = game["here"]
bag = game["bag"]
things = game["things"]
print("Loaded.")
print(rooms[here])
except FileNotFoundError:
print("There is no saved game yet.")
else:
print("Try: look, go north, take lamp, drop lamp, bag, save, load or quit")
if "crown" in bag:
print("You found the crown. You win!")
break
Open task: any program that does what the step asks.
Open task: any program that does what the step asks.
Stretch: Keep a best score in a second file (fewest moves to win), and only replace it when a new game beats it.
Support: Give students the save branch written out and ask them to write load by mirroring it, with json.load instead of json.dump.
Why does the game save things as well as here and bag?
things changes during play (taking moves an item out of a room), so without it a loaded game would put the item back in the room as well as in the bag.
Data detective reads data files (CSV and JSON) the same way. Web apps later store what users type in a file, then in a database.