DEV Community

Mate Technologies
Mate Technologies

Posted on

๐Ÿ—บ๏ธ Building a Beginner-Friendly Text Adventure Game in Python (Tkinter)

In this tutorial, weโ€™ll build AdventureQuest, a GUI-based text adventure game using Python and Tkinter.

Youโ€™ll learn how to:

Create a windowed app with Tkinter

Manage game state (player, locations, inventory)

Handle user input and actions

Add combat, treasure, and random events

Apply light/dark themes

No advanced Python required โ€” just basic functions, dictionaries, and variables.

๐Ÿงฑ Step 1: Import Required Modules

We start by importing everything we need.

import sys
import os
import threading
import random
import tkinter as tk
from tkinter import ttk, messagebox
import sv_ttk
Enter fullscreen mode Exit fullscreen mode

Why these imports?

tkinter โ†’ GUI framework

ttk โ†’ modern themed widgets

messagebox โ†’ pop-up dialogs

random โ†’ enemies & treasure chance

threading โ†’ delayed UI updates

sv_ttk โ†’ modern light/dark themes

๐Ÿ›  Step 2: Helper Functions

These small helper functions keep our code clean.

Load bundled resources (for packaging later)

def resource_path(file_name):
    base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, file_name)
Enter fullscreen mode Exit fullscreen mode

This helps if you later package your app using PyInstaller.

Update the status bar safely

def set_status(msg):
    status_var.set(msg)
    root.update_idletasks()
Enter fullscreen mode Exit fullscreen mode

Weโ€™ll use this to display messages like combat results or errors.

Delay UI updates slightly

def delayed_update(event=None):
    threading.Timer(0.1, update_ui).start()
Enter fullscreen mode Exit fullscreen mode

This prevents the UI from freezing during quick updates.

๐Ÿ–ฅ Step 3: Create the Main Window

Now we create our main application window.

root = tk.Tk()
root.title("AdventureQuest")
root.geometry("900x600")
sv_ttk.set_theme("light")
Enter fullscreen mode Exit fullscreen mode

Tk() initializes the app

geometry() sets the window size

sv_ttk applies a modern theme

๐ŸŒ Step 4: Global Game State

These variables represent the entire game state.

UI State

dark_mode_var = tk.BooleanVar(value=False)
story_var = tk.StringVar(value="Welcome to AdventureQuest!\nYour journey begins...\n")
action_var = tk.StringVar()
Enter fullscreen mode Exit fullscreen mode

StringVar automatically updates the UI when changed

BooleanVar tracks dark mode

Player Data

player = {
    "health": 100,
    "gold": 0,
    "location": "forest",
    "attack": 10,
}
Enter fullscreen mode Exit fullscreen mode

This dictionary stores everything about the player.

Inventory

inventory = []
Enter fullscreen mode Exit fullscreen mode

Simple list = simple inventory system.

๐Ÿ—บ Step 5: Locations & World Data

Each location has:

A description

Valid actions

locations = {
    "forest": {
        "description": "You are in a dark forest. Paths lead north and east.",
        "actions": {"north": "cave", "east": "village"}
    },
    "cave": {
        "description": "A spooky cave. You see a shiny sword.",
        "actions": {"south": "forest", "take sword": None}
    },
    "village": {
        "description": "A small village with friendly villagers.",
        "actions": {"west": "forest", "shop": None}
    },
}
Enter fullscreen mode Exit fullscreen mode

If an action maps to None, itโ€™s a special action, not movement.

๐Ÿ‘พ Step 6: Enemies & Treasure Tables

Enemies depend on the playerโ€™s location.

enemies = {
    "forest": {
        "Goblin": {"health": 30, "attack": 5},
        "Wolf": {"health": 25, "attack": 6}
    },
    "cave": {
        "Bat": {"health": 20, "attack": 4},
        "Skeleton": {"health": 40, "attack": 8}
    },
}
Enter fullscreen mode Exit fullscreen mode

Treasures are also location-based.

treasures = {
    "forest": ["gold coin", "healing herb"],
    "cave": ["gold coin", "magic potion"],
    "village": ["gold coin"]
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŒ— Step 7: Light / Dark Theme Toggle

This function switches between light and dark mode.

def toggle_theme():
    style.theme_use("clam")
    bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF"
    fg = "white" if dark_mode_var.get() else "black"

    root.configure(bg=bg)

    for w in ["TFrame", "TLabel", "TLabelframe", "TLabelframe.Label", "TCheckbutton"]:
        style.configure(w, background=bg, foreground=fg)
Enter fullscreen mode Exit fullscreen mode

We also update the text area colors:

    text_area.configure(
        bg="#1e1e1e" if dark_mode_var.get() else "white",
        fg="white" if dark_mode_var.get() else "black",
        insertbackground="white" if dark_mode_var.get() else "black"
    )
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”„ Step 8: Updating the Story UI

Whenever the player moves or acts, we refresh the story text.

def update_ui():
    location = player["location"]
    loc_info = locations[location]

    story_text = f"{loc_info['description']}\n"
    story_text += (
        f"\nHealth: {player['health']} | "
        f"Gold: {player['gold']} | "
        f"Inventory: {', '.join(inventory) if inventory else 'Empty'}"
    )

    story_var.set(story_text)
Enter fullscreen mode Exit fullscreen mode

โš”๏ธ Step 9: Random Encounters & Treasure
Enemy encounters (60% chance)

def encounter_enemy():
    location = player["location"]

    if location in enemies and random.random() < 0.6:
        enemy_name = random.choice(list(enemies[location].keys()))
        enemy = enemies[location][enemy_name]
        set_status(f"โš”๏ธ You encounter a {enemy_name}!")
        combat(enemy_name, enemy)
Enter fullscreen mode Exit fullscreen mode

Finding treasure (50% chance)

def find_treasure():
    location = player["location"]

    if location in treasures and random.random() < 0.5:
        item = random.choice(treasures[location])
        inventory.append(item)
        set_status(f"๐Ÿ’Ž You found a {item}!")

Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Step 10: Combat System

Combat loops until someoneโ€™s health reaches zero.

def combat(enemy_name, enemy):
    while enemy["health"] > 0 and player["health"] > 0:
        damage = player["attack"] + (5 if "sword" in inventory else 0)
        enemy["health"] -= damage
        set_status(f"You hit {enemy_name} for {damage} damage!")
Enter fullscreen mode Exit fullscreen mode

Enemy strikes back:

        player["health"] -= enemy["attack"]
        set_status(f"{enemy_name} hits you for {enemy['attack']} damage!")
Enter fullscreen mode Exit fullscreen mode

If the player dies:

        if player["health"] <= 0:
            messagebox.showinfo("Game Over", "You have died!")
            reset_game()
            return
Enter fullscreen mode Exit fullscreen mode

๐ŸŽฎ Step 11: Processing Player Actions

This function reads player input and reacts.

def process_action():
    action = action_var.get().strip().lower()
    location = player["location"]
    valid_actions = locations[location]["actions"]
Enter fullscreen mode Exit fullscreen mode

Movement:

    if action in valid_actions and valid_actions[action]:
        player["location"] = valid_actions[action]
        encounter_enemy()
        find_treasure()
Enter fullscreen mode Exit fullscreen mode

Special actions:

    elif action == "take sword":
        inventory.append("sword")
Enter fullscreen mode Exit fullscreen mode

โ“ Step 12: Help Window

A pop-up window explaining the rules.

def show_help():
    win = tk.Toplevel(root)
    win.title("AdventureQuest - Help")
Enter fullscreen mode Exit fullscreen mode

Uses a read-only Text widget for formatted instructions.

๐Ÿงฉ Step 13: UI Layout
Title & Story Area

ttk.Label(main, text="AdventureQuest", font=("Segoe UI", 22, "bold")).pack()
Enter fullscreen mode Exit fullscreen mode

Story display:

text_area = tk.Text(
    text_frame,
    wrap="word",
    state="disabled"
)
Enter fullscreen mode Exit fullscreen mode

Action Input

action_entry = ttk.Entry(action_frame, textvariable=action_var)
action_entry.bind("<Return>", lambda e: process_action())
Enter fullscreen mode Exit fullscreen mode

Buttons:

ttk.Button(action_frame, text="Act", command=process_action)
ttk.Button(action_frame, text="โ“ Help", command=show_help)
Enter fullscreen mode Exit fullscreen mode

๐Ÿš€ Step 14: Start the Game Loop

update_ui()
root.mainloop()
Enter fullscreen mode Exit fullscreen mode

This launches the application and keeps it running.

๐ŸŽ‰ Final Thoughts

You now have:

A working GUI game

A clean separation of logic and UI

A foundation for saving, leveling, sound effects, or maps

Ideas to Extend:

Add XP & leveling

Add save/load system

Add images or sound effects

Turn it into a roguelike

Top comments (0)