thecodex.expert · The Codex Family of Knowledge
Tier 2 · Intermediate · Python Project

Text Adventure Game

A branching story game where choices lead to different rooms and endings. Learn to model a world as connected data and navigate it.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a text adventure: the player reads a scene, picks from choices, and moves to new scenes — some leading to victory, some to doom. It teaches modelling a world as a graph of connected rooms and writing an engine that walks that graph based on player choices.

Where this shows up: game design, chatbots and conversational flows, decision trees, state machines, interactive tutorials, menu systems. Modelling “you are here, these are your options, this leads there” is a broadly useful structure.

2 How to Think About It

Think about how to represent the world, before any code:

The plan — in plain English
1. Each room has a description and a set of choices; each choice points to another room. → 2. Store all rooms in a dictionary keyed by name. → 3. The engine starts in one room, shows it, takes a choice, and moves to the linked room. → 4. Some rooms are endings, where the game stops.

No

Yes

Start room

Show description + choices

Player picks a choice

Move to linked room

Is it an ending?

Show ending, stop

3 The Build — explained part by part

Here is the complete adventure engine. Each part is explained below.

Pythonadventure.py
# A branching text adventure.

rooms = {
    "start": {
        "text": "You stand at a fork. Left is dark, right is bright.",
        "choices": {"left": "cave", "right": "meadow"},
    },
    "cave": {
        "text": "A dragon sleeps here. You grab the gold and flee!",
        "choices": {},          # no choices = an ending
    },
    "meadow": {
        "text": "A peaceful meadow. A path leads back.",
        "choices": {"back": "start"},
    },
}

def play(rooms, start="start"):
    current = start
    while True:
        room = rooms[current]
        print("\n" + room["text"])
        if not room["choices"]:
            print("THE END")
            break
        print("Options:", ", ".join(room["choices"]))
        choice = input("> ").lower()
        if choice in room["choices"]:
            current = room["choices"][choice]
        else:
            print("You cannot go that way.")

if __name__ == "__main__":
    play(rooms)
What each part does — in plain words
rooms = { "start": {...}, ... } — the world as data: a dictionary where each room has a description (text) and choices mapping a word to the next room’s name. This is the game map.

"choices": {} — an empty choices dictionary marks an ending: there is nowhere to go, so the game stops.

room = rooms[current] — look up the room we are in by name.

if not room["choices"]: — if there are no choices, this is an ending: print THE END and break.

current = room["choices"][choice] — the heart of navigation: follow the chosen exit to the next room’s name, and loop. The engine never changes — only the data does.
Common mistakes — and how to avoid them
✗ A choice pointing to a room name that does not exist — the game crashes on KeyError.
✓ Validate every choice points to a real room (the test catches this).
✗ Forgetting to mark endings — the loop never stops.
✓ Give ending rooms an empty choices dictionary.
✗ Not lowercasing the choice — “Left” fails to match “left”.
✓ Lowercase the input before checking.

4 Test & Prove Each Part

We test the navigation logic — that choices lead to the right rooms and endings are detected — without playing interactively.

A valid choice leads to the linked room
A room with no choices is an ending
Every choice points to a room that exists
Pythontest_adventure.py
ROOMS = {
    "start": {"text": "fork", "choices": {"left": "cave", "right": "meadow"}},
    "cave": {"text": "dragon", "choices": {}},
    "meadow": {"text": "meadow", "choices": {"back": "start"}},
}


def next_room(rooms, current, choice):
    return rooms[current]["choices"].get(choice, current)


def is_ending(rooms, current):
    return not rooms[current]["choices"]


def test_valid_choice():
    """'left' from start leads to the cave."""
    assert next_room(ROOMS, "start", "left") == "cave"


def test_ending():
    """The cave is an ending."""
    assert is_ending(ROOMS, "cave") is True


def test_all_links_valid():
    """Every choice points to a real room."""
    for room in ROOMS.values():
        for destination in room["choices"].values():
            assert destination in ROOMS

Run with pytest -v. The all-links-valid test is special: it checks the data is consistent — no choice leads to a room that does not exist. Validating your data this way catches a whole class of bugs.

5 The Interface

INPUTa choice wordone of the room's options
What it expects
You stand at a fork...
Options: left, right
> left
OUTPUTnext scenethe linked room or an ending
What it returns
A dragon sleeps here. You grab the gold and flee!
THE END

6 Run It & Automate It

Run it locally
python3 adventure.py
Read each scene and type a choice to explore. Add your own rooms to the dictionary.

Jenkins runs the tests automatically — each line explained below.

What you should see when it works
Terminala real run
You stand at a fork. Left is dark, right is bright.
Options: left, right
> left

A dragon sleeps here. You grab the gold and flee!
THE END
If it breaks — how to fix it
🚨 KeyError when moving.
A choice points to a non-existent room. Check your room names match exactly.
🚨 The game never ends.
Your ending rooms still have choices. Give them "choices": {}.
GroovyJenkinsfile
// Jenkinsfile — automated tests on every change.
pipeline {
    agent any
    stages {
        stage('Get the code') { steps { checkout scm } }       // download the code
        stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } }  // test tool
        stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } }   // run every test
    }
    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed — look above.' }
    }
}
🎯 Try this next — make it yours
  1. Inventory. Let the player collect items that unlock paths. (Teaches: tracking state.)
  2. Load from a file. Store the map in JSON so writers can edit it. (Teaches: data-driven content.)
  3. Win and lose endings. Mark endings as good or bad and tally outcomes. (Teaches: richer room data.)
What you learned
You learned to model a world as connected data (a graph of rooms) and write an engine that navigates it — plus validating that the data itself is consistent. This graph-and-navigation pattern powers games, chatbots, and state machines. Related: Variables & Types, Control Flow.