Inspiration
How to Build a Fictional 1980s Text-Adventure Game Engine with Claude 3.5 Sonnet and Python
Dust off your digital parser. Learn how to combine Claude 3.5 Sonnet's structured JSON outputs with a lightweight Python state machine to build a classic, atmospheric text adventure game.
Updated 10/13/2026
The Charm of the Parser
Before photorealistic ray-tracing and complex physics engines, gaming was a battle of wits fought entirely in the command line. Games like Zork, The Hitchhiker’s Guide to the Galaxy, and Colossal Cave Adventure relied on evocative prose and a stubborn parser that demanded you type exactly what you meant. There is something intensely nostalgic about typing GO NORTH or TAKE BRASS LANTERN and waiting for a chunk of monochrome text to paint a picture in your mind.
Building a text adventure engine from scratch used to require writing complex string parsers and hard-coding massive, fragile state trees. But by combining the reasoning capabilities of Claude 3.5 Sonnet with a simple Python runtime, we can create a dynamic, atmospheric 1980s-style text adventure engine.
Instead of letting the LLM hallucinate the game world on the fly—which inevitably leads to broken quest logic and infinite loops—we will use Claude to generate a strict, highly detailed world map in JSON. Then, we will run that world inside a local Python state machine, using Claude only to parse complex player inputs into structured game commands. This hybrid approach keeps the game fast, deterministic, and delightfully vintage.
Step 1: Designing the JSON World Schema
To keep our game engine robust, we need a solid data structure. The game world is a graph of "rooms" connected by cardinal directions. Each room needs a description, an inventory of items, and potential hazards or locked doors.
Here is the schema we will use to define our retro-futuristic, abandoned space station world:
`json
{
"starting_room": "airlock",
"rooms": {
"airlock": {
"name": "Decompression Airlock",
"description": "A cold, metallic chamber. Red emergency lights pulse slowly. The outer hatch is sealed, but a heavy steel bulkhead door leads WEST into the corridor.",
"exits": {"west": "corridor"},
"items": ["magnetic_keycard"],
"hazards": {}
},
"corridor": {
"name": "Main Habitation Corridor",
"description": "Debris litters the floor. Flickering terminal screens cast long shadows. To the EAST is the airlock. To the NORTH lies the darkened bridge, but the door scanner is flashing red.",
"exits": {"east": "airlock", "north": "bridge"},
"items": [],
"hazards": {
"north": {
"required_item": "magnetic_keycard",
"locked_message": "The scanner chirps angrily. You need a magnetic keycard to open the bridge door."
}
}
},
"bridge": {
"name": "Command Bridge",
"description": "A panoramic viewport looks out onto a silent, swirling nebula. The main computer terminal hums softly. You have successfully restored power to the station. You win!",
"exits": {"south": "corridor"},
"items": [],
"hazards": {}
}
}
}
`
You can ask Claude 3.5 Sonnet to expand this JSON to create massive worlds. Simply feed it this schema and ask for a 20-room sci-fi horror or gothic fantasy map. It will generate perfectly linked rooms, keys, and puzzles without breaking the logical integrity of the world graph.
Step 2: Building the Python Engine
Now, we need a lightweight Python engine to load this JSON and manage the player's state (current room, inventory, and moves). This engine does not need an LLM to run basic commands; we will use standard Python logic for simple directions like go west or take keycard to keep the latency practically zero.
Create a file named engine.py:
`python
import json
import sys
import time
class GameEngine: def __init__(self, world_file): with open(world_file, 'r') as f: self.world = json.load(f) self.current_room = self.world['starting_room'] self.inventory = [] self.playing = True
def print_slow(self, text, delay=0.015): for char in text: sys.stdout.write(char) sys.stdout.flush() time.sleep(delay) print("")
def look_around(self): room = self.world['rooms'][self.current_room] self.print_slow(f"\n--- {room['name'].upper()} ---") self.print_slow(room['description']) if room['items']: self.print_slow(f"You spot: {', '.join(room['items'])}.")
def move(self, direction): room = self.world['rooms'][self.current_room] if direction not in room['exits']: self.print_slow("You can't go that way.") return
Check hazards/locks hazard = room['hazards'].get(direction) if hazard: if hazard['required_item'] not in self.inventory: self.print_slow(hazard['locked_message']) return else: self.print_slow(f"You use the {hazard['required_item']} and unlock the path!") # Remove hazard once resolved del room['hazards'][direction]
self.current_room = room['exits'][direction] self.look_around() if self.current_room == "bridge": self.playing = False
def take_item(self, item):
room = self.world['rooms'][self.current_room]
if item in room['items']:
room['items'].remove(item)
self.inventory.append(item)
self.print_slow(f"You picked up the {item}.")
else:
self.print_slow("That item isn't here.")
`
Step 3: Setting Up Claude as the Intelligent Parser
While simple commands are easy to hardcode, retro players love typing natural, weird phrases. If a player types "grab the plastic keycard off the filthy floor" or "head through the bulkhead door to the left", standard regex parsers will break. This is where Claude shines.
We will use the Anthropic API to parse free-text input into standard JSON commands. If Claude is unsure or if the player types something cheeky, Claude can reply with a witty, in-character refusal.
To handle API calls smoothly, configure a helper function that takes the user's input and returns a structured action. If you hit any API issues during setup, you can check our Claude troubleshooting hub for solutions.
Here is how we prompt Claude to behave as our parser:
`python
import anthropic
client = anthropic.Anthropic()
def parse_user_input(user_input, valid_directions, valid_items, inventory):
prompt = f"""
You are a 1980s text adventure parser. Your job is to translate user natural language into a single JSON command.
Valid directions: {valid_directions}
Valid items in room: {valid_items}
Current inventory: {inventory}
Respond ONLY with a JSON object in one of these three formats:
1. {{"action": "move", "target": "<direction>"}}
2. {{"action": "take", "target": "<item_name>"}}
3. {{"action": "invalid", "message": "<a witty 1980s-style parser error message explaining why you can't do that>"}}
User input: "{user_input}"
JSON response:
"""
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=100,
temperature=0,
messages=[{"role": "user", "content": prompt}]
)
return json.loads(response.content[0].text.strip())
`
Step 4: Putting It All Together
Now, build the main loop that connects the parser to our engine. This code will prompt the player, send their input to Claude for translation, execute the parsed move, and render the atmospheric results in real-time.
It takes only a single tick of the clock to realise how much more immersive this is than a standard chatbot experience. Because the game state is tracked locally, the game is completely stable, while Claude’s parser adds a layer of intelligence that early game developers could only dream of.
`python
def play_game():
engine = GameEngine('world.json')
engine.look_around()
while engine.playing: user_input = input("\n> ").strip() if not user_input: continue if user_input.lower() in ["quit", "exit"]: break
room = engine.world['rooms'][engine.current_room] valid_directions = list(room['exits'].keys()) valid_items = room['items']
try: parsed = parse_user_input(user_input, valid_directions, valid_items, engine.inventory) if parsed['action'] == 'move': engine.move(parsed['target']) elif parsed['action'] == 'take': engine.take_item(parsed['target']) else: engine.print_slow(parsed['message']) except Exception as e: engine.print_slow("The terminal static crackles. Your command was lost to the cosmos.")
engine.print_slow("\n* GAME OVER *")
if __name__ == "__main__":
play_game()
`
Adding the Vintage Polish
To make this game feel like it is running on an old CRT monitor, try these customisations:
- Terminal Fonts: Run your terminal in a bright green or amber retro font like Glass TTY VT220.
- Ambient Sound: Play a low hum or tape static loop in the background while you play.
- ASCII Art Title Card: Use an online generator to draw a massive retro sci-fi title card at the start of your Python script.
By leveraging Claude for parsing and keeping state strictly inside Python, you build a robust, responsive game that honors the golden age of interactive fiction while embracing modern AI.
Keep going
Build something with the prompt generator, decode the jargon in the glossary, or compare the tools on our platform deep-dives.
Which AI should I use for this?
Tickd is independent and not affiliated with any AI company.