๐ Adventure Terminal Documentation
Complete guide: playing, uploading, writing games, and using Python callbacks.
โก Quickstart
New here? These two cards get you running in under a minute.
โถ Play an existing game
- Open the Terminal page.
- Pick a game from the dropdown.
- Click โถ Play, then type commands and press Enter.
โฌ Upload your own game
- Create
mygame.txt(game definition) andmygame.py(Python runner). - Click โฌ Upload and drop both files.
- Select your game from the dropdown and click โถ Play.
โ๏ธ Write in the browser
- Go to ๐ Home and create your project.
- Open the โ๏ธ Editor and create
my_game.txtandmy_game.py. - Click โถ Run โ the Terminal opens and plays automatically.
๐ Explore the Gallery
- Go to ๐ Gallery.
- Click โถ Play on any card to play it instantly.
- Click Copy to My Project on an example to use it as a starting point.
.py file can be as short as two lines:
from advengine import Game Game('mygame.txt').run()
1 Playing a Game
The toolbar at the top of the Terminal page controls everything:
- Pick a game from the dropdown menu.
- Click โถ Play โ the game starts in the terminal below.
- Read what the game tells you, then type a command and press Enter.
- Click โ Stop to quit at any time.
In-game commands
Commands are typed at the > prompt. They are not case-sensitive.
| Command | What it does |
|---|---|
| look | Describe the current room again. |
| go north / south / east / west | Move in that direction (also: n s e w). |
| take <thing> | Pick up a thing in the room. |
| drop <thing> | Leave a thing in the current room. |
| use <thing> | Use a thing from your inventory. |
| attack <person> | Fight a person (if they have hp > 0). |
| talk <person> | Talk to a person. |
| check self | Show health, inventory, and score. |
| save / load | Save or restore progress. |
| menu / m | Show all available commands. |
| quit / q | Exit the game. |
2 Uploading Your Own Game
A game consists of two files with matching names:
mygame.txtโ the game definition (rooms, items, story text)mygame.pyโ the Python runner (loads the engine and callsrun())
.txt definition.
- Click โฌ Upload in the toolbar.
- Drag and drop both files onto the upload area โ or click to browse.
- A green checkmark confirms each file was received.
- Close the dialog. Your game now appears in the dropdown.
.py and .txt files accepted. Maximum 2 MB per file.
Names may only contain letters, numbers, hyphens, underscores, and dots.
5 Writing a Game
The .txt game file uses an indented hierarchy.
Top-level entries use no indentation; their properties are indented with one tab;
sub-items (exits, things inside a location) are also indented one tab, and
their properties take two tabs.
Game header
title: My Adventure description: A short game to learn the engine. start: Forest Path # name of the first location end: Treasure Chamber # reaching this location wins the game player: health: 100 strength: 10 max_weight: 20
Places (locations)
place: Forest Path # Properties indented one tab description: A winding dirt path through tall oaks. Sunlight filters through. image: forest.txt # optional ASCII art file play: t120 ~triangle v6 c4q e4q g4q r # optional music loop exit: north to Old Mill exit: east to River Crossing thing: lantern description: A brass oil lantern. Its flame is steady. weight: 2 person: wolf description: A gray wolf with yellow eyes. hp: 20 conflict_damage: 4
Entity keywords
| Keyword | Synonyms | Use for | Key properties |
|---|---|---|---|
| place: | location: | A room or area the player can be in | description, image, play |
| exit: | direction: | A way to move from one place to another | to, hidden, locked, key, description |
| thing: | item:, object: | Objects the player can carry or interact with | weight, heals, attack_power, play |
| person: | npc:, character: | Characters โ peaceful or hostile depending on attributes | hp (set 0 for peaceful), conflict_damage |
The play: field (synthesised music)
Any place or thing can have a play: field.
For places the music plays on entry; for things it plays when the player uses them.
play: t120 ~sine v7 c4q e4q g4q c5dh r # loops forever play: t160 ~square v8 c4s e4s g4s c5q r3 # repeats 3 times then stops
| Token | Meaning | Example |
|---|---|---|
| t<bpm> | Set tempo (beats per minute) | t120 |
| ~wave | Waveform: sine triangle square sawtooth | ~triangle |
| v<0-9> | Volume (0 = silent, 9 = loudest) | v6 |
| <note><oct><dur> | Note: aโg, optional #/b, octave 0โ8, duration w h q e s t | c4q f#5e |
| d suffix | Dotted note (ร1.5 duration) | c4dq |
| s<dur> | Silence (rest) | sq |
| r<n> | Repeat preceding section n times | r3 |
| r | Loop forever (until player presses Enter) | r |
ASCII art in descriptions
Wrap art in --- fences inside a description:. No indentation needed inside the fence.
place: Dragon's Lair
description: You enter the cave. ...The heat is intense.
---
/\_____/\
/ o o \
( == ^ == )
---
A dragon eyes you suspiciously.
The .py runner
from advengine import Game # Minimal โ just load and run Game('mygame.txt').run() # Or save the game object to attach callbacks first game = Game('mygame.txt') game.add_callback('use', 'magic wand', wand_callback) game.run()
6 Example Gallery
Seventeen ready-to-run examples ship with the engine. Load any of them from the dropdown
on the Terminal page. Each file pair is in the examples/ folder.
| # | Title | What it demonstrates |
|---|---|---|
| 01 | Minimal | The fewest lines possible: two rooms, one exit, one thing. Start here. |
| 02 | Items | Picking up, dropping, and using items. Inventory weight limits. takedropuse |
| 03 | Hidden Secrets | Revealing hidden objects and concealed exits when conditions are met. hiddenreveal() |
| 04 | Combat |
Fighting persons with hp and conflict_damage. Defeat scoring.
personattackhp
|
| 05 | Dramatic Pauses |
Using ... in descriptions to create suspense and pacing.
storytelling
|
| 06 | Callbacks | Adding custom Python code that fires when the player does something. Core feature of the engine. add_callbackuse |
| 07 | Multiple Enemies | Rooms with several hostile persons. Organizing combat encounters. persondefeat_score |
| 08 | Complex Puzzles | Chained multi-step puzzles: unlock door A with key B found behind secret C. hiddencallbacks |
| 09 | Fatigue System | Health decreases each move, creating urgency. Time-pressure mechanics. fatiguehealth |
| 10 | Complete Adventure | Full game combining combat, puzzles, ANSI color, high scores, and multiple paths. showcase |
| 11 | Space Exploration | Sci-fi themed adventure. A captain exploring alien worlds. themecallbacks |
| 12 | Treasure Hunt | Score tracking, high score file, callbacks that award points on treasure pickup. scorehighscorescallbacks |
| 13 | Color Quest |
ANSI 256-color and 24-bit color in game text. Using ansi_colors.py.
ansicolor
|
| 14 | Color Markup |
Inline color markup: [red]warning![/red] in description text.
color markup
|
| 15 | ASCII Art (mono) | Embedding large monochrome ASCII art in room descriptions. ascii artimage |
| 16 | ASCII Art (color) | Same art as #15 but with ANSI 24-bit color added via the Utilities converter. ascii artansi |
| 17 | Sound Demo |
Synthesised music with the play: field on locations and items.
The Enchanted Conservatory puzzle.
play:music
|
| 18 | Haunted House | A full 18-room classic adventure. Navigate a condemned Victorian house, survive the killer, rescue the crying boy, and escape before your health runs out. Features room ASCII art, health-draining fatigue, and Python callbacks. full gamefatiguecallbacksASCII artpictures |
7 Callback System
Callbacks let you attach custom Python functions to in-game events.
The function is called automatically whenever the player performs the matching action.
This is how you add puzzles, scoring, conversations, and any custom logic beyond
what the .txt file alone can express.
Registering a callback
# Specific: fires only when the player uses the 'golden key' game.add_callback('use', 'golden key', use_golden_key) # Generic: fires when the player uses ANY item game.add_callback('use', use_any_item) # Move: fires when the player moves (any direction) game.add_callback('move', on_move)
Callback function signature
def my_callback(game, command, target): # game โ the Game object (access player, locations, etc.) # command โ string like 'use', 'take', 'drop', 'move' # target โ the thing name, direction, or place name print("Custom logic here!") return True # True = suppress default engine action # False = let engine also handle the command
Hookable events
| Event | Fires whenโฆ | target value |
|---|---|---|
| use | Player types use <thing> | thing name |
| take | Player types take <thing> | thing name |
| drop | Player types drop <thing> | thing name |
| move | Player moves to a new room | direction string ('north' etc.) |
Useful game object attributes
| Expression | What you get |
|---|---|
| game.player.health | Current player health (int) |
| game.player.max_health | Maximum player health (int) |
| game.player.score | Current score (int) |
| game.player.inventory | List of Thing objects in inventory |
| game.player.has_thing("sword") | True if "sword" is in inventory |
| game.player.current_location | The current Location object |
| game.player.current_location.get_thing("key") | Find a Thing in the current room |
| game.player.current_location.get_exit("north") | Find an Exit in the current room |
| thing.reveal() | Make a hidden thing visible |
| exit.reveal() | Make a hidden exit visible |
8 Python Callback Cookbook
Each entry below shows a self-contained callback snippet illustrating one
Python topic in a game context. Click any topic to expand it.
Drop the code into your .py file โ it's ready to adapt!
1 Printing โ display extra flavor text when the player examines a torch
print() is how callbacks communicate with the player.
You can print multiple lines, use escape sequences for spacing,
and combine it with f-strings for dynamic messages.
Return True from the callback to prevent the engine from
also printing its default description.
from advengine import Game def examine_torch(game, command, thing_name): print("\nThe torch flickers in a sudden draught.") print("Its orange glow illuminates carved runes on the wall:") print(" 'NORTH LEADS TO PERIL โ SOUTH TO REFUGE'") print() # blank line for spacing return True # suppress the default "You use the torch." message game = Game('mygame.txt') game.add_callback('use', 'torch', examine_torch) game.run()
2 Variables & Types โ count gold coins collected with an integer counter
Variables outside your functions are module-level variables
that persist across the whole game session.
Python has several built-in types: int for counting,
str for text, bool for True/False flags,
and float for decimal numbers.
from advengine import Game gold_count = 0 # int โ counts coins player_title = "" # str โ earned title has_crown = False # bool โ tracks a one-time event def take_coin(game, command, thing_name): global gold_count, player_title gold_count += 1 if gold_count == 1: player_title = "Apprentice" elif gold_count == 5: player_title = "Treasure Seeker" elif gold_count >= 10: player_title = "Master of Gold" print(f"\nCoins collected: {gold_count} | Title: {player_title}") return False # also let the engine pick up the coin normally game = Game('mygame.txt') game.add_callback('take', 'gold coin', take_coin) game.run()
3 User Input โ a riddle door that asks a question before opening
input() pauses the game and waits for the player to type something,
then returns it as a str. Use .strip().lower() to normalize
the answer so capitalisation and extra spaces don't matter.
from advengine import Game def use_riddle_door(game, command, thing_name): print("\nThe stone door rumbles. A voice echoes:") print(" 'What has teeth but cannot bite?'") answer = input("Your answer: ").strip().lower() if answer == "comb": print("The door grinds open! 'Correct,' the voice whispers.") door = game.player.current_location.get_thing("riddle door") north = game.player.current_location.get_exit("north") if north: north.reveal() else: print(f"'{answer}?' the door cackles. 'Wrong! Try again.'") return True game = Game('mygame.txt') game.add_callback('use', 'riddle door', use_riddle_door) game.run()
4 Math Operators โ a healing potion that restores exactly 25% of max health
Python's arithmetic operators โ + - *
/ // % ** โ work naturally
on integers and floats. Integer division (//) is handy when you need
a whole number result (e.g., a quarter of max health).
min() / max() prevent values from going out of range.
from advengine import Game def drink_potion(game, command, thing_name): max_hp = game.player.max_health current_hp = game.player.health heal_amount = max_hp // 4 # integer division: 25% of max new_hp = current_hp + heal_amount # addition new_hp = min(new_hp, max_hp) # cap at max โ can't overheal actual_healed = new_hp - current_hp game.player.health = new_hp print(f"\nYou drink the potion. Healed {actual_healed} HP.") print(f"Health: {new_hp} / {max_hp}") return True game = Game('mygame.txt') game.add_callback('use', 'potion', drink_potion) game.run()
5 String Operators โ greet a king with a personalized banner built from strings
Strings support + for concatenation, * for repetition,
and f-strings (f"...") for embedding expressions.
Common methods: .upper(), .lower(), .strip(),
.replace(), .split().
from advengine import Game hero_name = "Adventurer" # could be set from input() earlier def talk_to_king(game, command, thing_name): border = "*" * 40 # string repetition greeting = "Hail, " + hero_name + "!" # concatenation title = hero_name.upper() # string method print(f"\n{border}") print(f" {greeting}") print(f" 'We have been expecting you, {title}.'") print(f" 'The kingdom is yours to save.'") print(border) return True game = Game('mygame.txt') game.add_callback('talk', 'king', talk_to_king) game.run()
6 Comments โ document a multi-phase mill puzzle so teammates can understand it
Comments (lines starting with #) are notes for human readers โ
Python ignores them completely. Good comments explain why code does something,
not just what it does. Docstrings ("""...""" right after a def)
describe what a function does and appear in help tools.
from advengine import Game # Phase tracking: False = puzzle not yet solved lever_pulled = False def use_lever(game, command, thing_name): """Pull the mill lever to grind the grain and reveal the hidden passage.""" global lever_pulled # Once pulled, the lever is stuck โ don't allow a second pull if lever_pulled: print("The lever is already in position.") return True # Mark the puzzle phase as complete lever_pulled = True print("\nThe millstone grinds to life โ revealing a hidden passage below!") # Reveal the concealed exit so the player can go down passage = game.player.current_location.get_exit("down") if passage: passage.reveal() return True game = Game('mygame.txt') game.add_callback('use', 'lever', use_lever) game.run()
7 Comparison & Logical Operators โ a drawbridge that only opens if you have the key AND enough health
Comparison operators (== != < >
<= >=) return True or False.
Logical operators (and or not) combine those
booleans. in tests membership in a sequence.
from advengine import Game def use_drawbridge(game, command, thing_name): has_key = game.player.has_thing("iron key") # bool low_hp = game.player.health < 20 # comparison full_hp = game.player.health == game.player.max_health if has_key and not low_hp: print("You unlock the drawbridge and stride across confidently.") game.player.current_location.get_exit("north").reveal() elif has_key and low_hp: print("You limp across the drawbridge. Better find a healer soon!") game.player.current_location.get_exit("north").reveal() elif not has_key: print("The drawbridge is locked. You need an iron key.") return True game = Game('mygame.txt') game.add_callback('use', 'drawbridge', use_drawbridge) game.run()
8 Floating Point & Rounding โ show health as a percentage with one decimal place
Division (/) always returns a float in Python 3.
Use round(value, digits) to control decimal places.
Be aware of floating-point imprecision: 0.1 + 0.2 is
0.30000000000000004 โ round() is your friend.
from advengine import Game def check_vitals(game, command, thing_name): hp = game.player.health max_hp = game.player.max_health ratio = hp / max_hp # float division, e.g. 0.6666... pct = round(ratio * 100, 1) # e.g. 66.7 # Determine status string based on percentage if pct >= 75.0: status = "Excellent condition" elif pct >= 50.0: status = "Lightly wounded" elif pct >= 25.0: status = "Badly hurt" else: status = "Near death!" print(f"\n[Vitals] HP: {hp}/{max_hp} ({pct}%) โ {status}") return True game = Game('mygame.txt') game.add_callback('use', 'mirror', check_vitals) game.run()
9 While Loops โ an oracle that keeps asking riddles until you answer correctly
A while loop repeats as long as its condition is True.
It's useful when you don't know in advance how many times to repeat โ
for example, retrying until the player gives the right answer.
from advengine import Game RIDDLES = [ ("What runs but has no legs?", "water"), ("What has a head but no face?", "coin"), ("The more you take, the more you leave behind.", "footsteps"), ] def consult_oracle(game, command, thing_name): print("\nThe oracle speaks: 'Answer my riddles to earn a reward.'") score = 0 i = 0 while i < len(RIDDLES): question, correct = RIDDLES[i] answer = input(f"Riddle {i+1}: {question}\n> ").strip().lower() if answer == correct: print("'Correct!' the oracle chimes.") score += 1 else: print(f"'Wrong โ the answer was {correct!r}.'") i += 1 print(f"\nYou scored {score} / {len(RIDDLES)}.") if score == len(RIDDLES): print("'Worthy!' A gem appears at your feet.") return True game = Game('mygame.txt') game.add_callback('talk', 'oracle', consult_oracle) game.run()
10 For Loops โ a crystal ball that lists every item in your inventory
A for loop iterates over any sequence โ a list, a string, a range.
game.player.inventory is a list of Thing objects,
which makes it a natural target for iteration.
Use enumerate() to get both the index and value in one loop.
from advengine import Game def use_crystal_ball(game, command, thing_name): inventory = game.player.inventory if not inventory: print("\nThe crystal ball shows... nothing. Your pack is empty.") return True print("\nThe crystal ball reveals your possessions:") total_weight = 0 for i, item in enumerate(inventory, 1): print(f" {i}. {item.name} โ {item.description}") total_weight += item.weight print(f"Total weight carried: {total_weight}") return True game = Game('mygame.txt') game.add_callback('use', 'crystal ball', use_crystal_ball) game.run()
11 Break & Continue โ an armory scanner that finds your first weapon and skips non-weapons
break exits a loop immediately.
continue skips the rest of the current iteration and moves to the next.
Together they let you write efficient searches and filters inside loops.
from advengine import Game WEAPONS = ["sword", "dagger", "bow", "axe", "spear"] def use_armory_mirror(game, command, thing_name): print("\nThe armory mirror scans your kit...") best_weapon = None for item in game.player.inventory: if item.name not in WEAPONS: continue # skip anything that isn't a weapon best_weapon = item.name break # stop after the first weapon found if best_weapon: print(f"Your primary weapon: {best_weapon}") else: print("You carry no weapons! The dungeon ahead is dangerous.") return True game = Game('mygame.txt') game.add_callback('use', 'armory mirror', use_armory_mirror) game.run()
12 Functions & Parameters โ a helper function that returns a health status string, reused by multiple callbacks
Define reusable logic in helper functions with descriptive parameters. A function is a named block of code; parameters are the inputs it accepts, and arguments are the actual values passed when calling it. Helper functions keep callbacks short and readable.
from advengine import Game def health_status(hp, max_hp): """Return a descriptive string for the given health values.""" pct = hp / max_hp * 100 if pct > 75: return "You feel strong and ready for anything." if pct > 50: return "You have a few cuts but press on." if pct > 25: return "You are badly hurt. Find a healer." return "You are on the brink of collapse!" def use_mirror(game, command, thing_name): msg = health_status(game.player.health, game.player.max_health) print(f"\nThe mirror reflects your weary face. {msg}") return True def enter_inn(game, command, direction): # Reuse the same helper function in a different callback msg = health_status(game.player.health, game.player.max_health) print(f"\nThe innkeeper looks you over. {msg}") return False game = Game('mygame.txt') game.add_callback('use', 'mirror', use_mirror) game.add_callback('move', 'Inn', enter_inn) game.run()
13
Namespaces in Functions
โ a magic fountain that behaves differently on each visit using global
Variables defined inside a function are local โ they disappear
after the function returns. Variables defined at the top of the file are
global. To modify a global variable from inside a function,
declare it with the global keyword first; otherwise Python creates a
new local variable with the same name and the global stays unchanged.
from advengine import Game drink_count = 0 # global โ tracks how many times the fountain was used def use_fountain(game, command, thing_name): global drink_count # required to modify the global variable drink_count += 1 if drink_count == 1: print("\nYou drink. The water tastes sweet and cool.") game.player.health = min(game.player.health + 10, game.player.max_health) elif drink_count == 2: print("\nYou drink again. A warm tingling fills your hands.") game.player.health = min(game.player.health + 5, game.player.max_health) else: print("\nThe fountain has run dry. Not even a drip remains.") print(f"Health: {game.player.health} / {game.player.max_health}") return True game = Game('mygame.txt') game.add_callback('use', 'fountain', use_fountain) game.run()
14 Return Values โ a helper function checks all vault conditions and returns True/False
A return statement sends a value back to the caller and exits the function.
Functions that return True or False (predicates) are
especially useful for checking game-state conditions in a readable way.
Callbacks themselves return True (handled) or False (pass through).
from advengine import Game def can_open_vault(game): """Return True only when all vault conditions are met.""" has_gem = game.player.has_thing("blue gem") has_scroll = game.player.has_thing("ancient scroll") alive = game.player.health > 0 return has_gem and has_scroll and alive # returns a bool def use_vault_door(game, command, thing_name): if can_open_vault(game): # call the helper and check its return value print("\nThe vault door clicks and swings open!") east = game.player.current_location.get_exit("east") if east: east.reveal() else: print("\nThe vault door won't budge. Something is still missing...") return True game = Game('mygame.txt') game.add_callback('use', 'vault door', use_vault_door) game.run()
15 Exceptions โ read a quest journal from file and handle missing or corrupt data gracefully
An exception is an error that occurs at runtime.
A try / except block lets you catch specific exceptions instead of
crashing. Always catch the most specific exception type you expect
(e.g., FileNotFoundError, ValueError) rather than
using a bare except:.
import json from advengine import Game NOTES_FILE = "quest_notes.json" def use_journal(game, command, thing_name): try: with open(NOTES_FILE) as f: data = json.load(f) print("\n=== Quest Journal ===") for entry in data["entries"]: print(f" โข {entry}") except FileNotFoundError: print("\nThe journal is blank โ no entries recorded yet.") except json.JSONDecodeError: print("\nThe journal pages are smudged and unreadable.") except KeyError: print("\nThe journal format is unfamiliar. Nothing to read.") return True game = Game('mygame.txt') game.add_callback('use', 'journal', use_journal) game.run()
16 Tuples โ track the player's (x, y) grid position as an immutable coordinate pair
A tuple is an ordered, immutable sequence: once created it
cannot be changed. This makes tuples ideal for fixed-structure data like coordinates,
RGB colors, or paired values you want to unpack in one line:
x, y = position.
Tuples use parentheses: (x, y).
from advengine import Game # Map each direction to a (dx, dy) offset โ tuples are immutable COMPASS = { "north": (0, 1), "south": (0, -1), "east": (1, 0), "west": (-1, 0), } player_pos = (0, 0) # starting position as a tuple def track_position(game, command, direction): global player_pos dx, dy = COMPASS.get(direction, (0, 0)) # unpack the tuple x, y = player_pos # unpack current position player_pos = (x + dx, y + dy) # create a new tuple print(f" [Map position: {player_pos}]") return False game = Game('mygame.txt') game.add_callback('move', track_position) game.run()
17 Lists โ record every room the player visits in an exploration log
A list is an ordered, mutable sequence of values.
Unlike tuples, you can add, remove, and change items after creation.
Lists are created with square brackets: [1, 2, 3].
Use in to test membership and len() for the count.
from advengine import Game visited_rooms = [] # mutable list โ grows as the player explores def log_visit(game, command, direction): location_name = game.player.current_location.name if location_name not in visited_rooms: # membership test visited_rooms.append(location_name) print(f" [New discovery! Rooms explored: {len(visited_rooms)}]") return False def use_explorer_badge(game, command, thing_name): print("\nRooms you have explored:") for room in visited_rooms: print(f" โ {room}") print(f"Total: {len(visited_rooms)}") return True game = Game('mygame.txt') game.add_callback('move', log_visit) game.add_callback('use', 'explorer badge', use_explorer_badge) game.run()
18 For Loops & Lists โ a cursed altar that checks your inventory for dangerous items
Combining for loops with lists is one of Python's most used patterns.
You can loop over a list to search, filter, transform, or accumulate values.
Building a new list inside the loop (instead of modifying the original
while iterating) avoids subtle bugs.
from advengine import Game CURSED_KEYWORDS = ["skull", "shadow", "cursed", "dark"] def use_altar(game, command, thing_name): print("\nThe altar glows blue, scanning your belongings...") cursed_items = [] # build a new list of matches for item in game.player.inventory: for keyword in CURSED_KEYWORDS: if keyword in item.name.lower(): cursed_items.append(item.name) break # only add the item once even if multiple keywords match if cursed_items: print("Cursed objects detected:") for name in cursed_items: print(f" โ {name}") print("The altar trembles with disapproval.") else: print("Your pack is clean. The altar hums approvingly.") return True game = Game('mygame.txt') game.add_callback('use', 'altar', use_altar) game.run()
19 List Methods โ a quest log that uses append, sort, and remove to manage entries
Lists have many built-in methods: .append(x) adds to the end,
.remove(x) deletes the first occurrence, .sort() sorts in place,
.pop() removes and returns the last item, .index(x) finds
a position, and .count(x) counts occurrences.
These all modify the list in place โ no assignment needed.
from advengine import Game quest_log = [] # shared list โ no global needed if we only mutate, not reassign def take_map(game, command, thing_name): quest_log.append("Found the treasure map") # append quest_log.sort() # sort alphabetically print(f"Quest log updated ({len(quest_log)} entries).") return False def take_key(game, command, thing_name): quest_log.append("Obtained the bronze key") quest_log.sort() print(f"Quest log updated ({len(quest_log)} entries).") return False def use_campfire(game, command, thing_name): print("\n=== Quest Log ===") if not quest_log: print(" (empty)") for i, entry in enumerate(quest_log, 1): print(f" {i}. {entry}") return True game = Game('mygame.txt') game.add_callback('take', 'map', take_map) game.add_callback('take', 'bronze key', take_key) game.add_callback('use', 'campfire', use_campfire) game.run()
20 File I/O โ a trophy room that saves and loads a high-score table from a JSON file
Use the built-in open() function to read and write files.
The with statement ensures the file is closed automatically.
json is the standard library module for reading and writing JSON โ
a simple, human-readable format for structured data.
Always use try / except around file operations.
import json from advengine import Game SCORE_FILE = "trophy_scores.json" def load_scores(): """Read scores from file; return [] if file missing or invalid.""" try: with open(SCORE_FILE) as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): return [] def save_score(name, score): """Append a new score, sort descending, keep only top 10, write to file.""" scores = load_scores() scores.append({"name": name, "score": score}) scores.sort(key=lambda s: s["score"], reverse=True) with open(SCORE_FILE, "w") as f: json.dump(scores[:10], f, indent=2) # write top 10 only def use_trophy(game, command, thing_name): name = input("Enter your name for the scoreboard: ").strip() if name: save_score(name, game.player.score) print(f"Score of {game.player.score} saved!") print("\nโโ High Scores โโ") for i, entry in enumerate(load_scores(), 1): print(f" {i:2}. {entry['name']:<15} {entry['score']}") return True game = Game('mygame.txt') game.add_callback('use', 'trophy', use_trophy) game.run()
9 Troubleshooting
| Problem | Fix |
|---|---|
| Game not in the dropdown | Upload the .py file (it needs a few seconds to appear), then re-open the Upload dialog and close it to refresh. |
| "Did you forget to upload the .txt file?" error | Upload the matching .txt definition โ it must have the same base name as the .py file. |
play: field is silent / empty |
Check that the line uses a real tab character for indentation, not spaces. Spaces are silently ignored by the parser. |
| Can't type in the terminal | Click inside the black terminal area first to focus it. |
| Screen looks garbled / misaligned | Resize the browser window slightly โ this forces the terminal to recalculate its layout. |
| Callback fires but the target doesn't match | The target string must exactly match the thing's name in the .txt file (case-insensitive, but spelling matters). |
| Global variable not updating between calls | Add global my_variable as the first line inside the function whenever you assign to a module-level variable. |
| Game hangs / won't stop | Click โ Stop to force-quit the Python process. |