Skip to main content

A dynamic text adventure game for your terminal, powered by Ollama LLMs.

Project description

Terminal Text Adventure

Welcome to Terminal Text Adventure! This is an interactive fiction game played in your terminal, where the story and choices are dynamically generated by a Large Language Model (LLM) powered by Ollama.

Features

  • Dynamic Storytelling: Every playthrough can be unique as the story adapts to your choices, powered by a Large Language Model.
  • LLM-Powered: Utilizes Ollama and compatible models (e.g., phi3, llama3) to generate immersive narratives and engaging player options.
  • Rich Terminal Interface: Employs the rich library for a visually appealing and user-friendly experience in the terminal.
  • Main Menu: Start new adventures by choosing from available stories or resume a previous game.
  • Save and Resume: Don't lose your progress! Save your game at any choice point using the /save command and resume later from the main menu.
  • Extensible Story System:
    • Stories are defined by *_story_arc.yaml files in text_adventure_tui_lib/story_parts/.
    • Narrative content and initial segments are loaded from text files.
  • Event-Driven Mechanics:
    • A powerful event system (see text_adventure_tui_lib/events/) allows for complex game logic.
    • Events are defined in YAML and can be triggered by player actions, game state changes (flags, location, inventory), turn counts, or even LLM outputs (planned).
    • Actions include overriding/injecting narrative, modifying LLM prompts, setting game flags, changing locations, and managing inventory.
  • Configurable: Set your Ollama host and model via environment variables.

Prerequisites

Before you can play, you need to have Ollama installed and running with a suitable model.

  1. Install Ollama:
  2. Pull a Model:
    • Once Ollama is running, pull a model. This game is tested with phi4:latest. You can pull it using the command:
      ollama pull phi4:latest
      
    • Ensure the Ollama server is running (usually ollama serve or by starting the Ollama application). By default, it runs on http://localhost:11434.

Installation

  1. Clone the Repository:

    git clone https://github.com/Theblackcat98/Text-Adventure-TUI.git
    cd Text-Adventure-TUI
    
  2. Create a Virtual Environment (Recommended):

    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
    
  3. Install Dependencies: For playing the game, you need the core dependencies:

    pip install -r requirements.txt
    

    If you intend to use the game as an installed package (allowing you to run text-adventure-tui from anywhere), which also installs core dependencies:

    pip install .
    

Development Setup

If you plan to contribute to the development of this game, follow these additional steps after cloning and setting up your virtual environment:

  1. Install Development Dependencies: This includes linters, testing tools, and build tools.

    pip install -r requirements-dev.txt
    
  2. Install in Editable Mode: This allows you to make changes to the code and test them immediately without reinstalling.

    pip install -e .
    

    After this, you can run the game using text-adventure-tui and your code changes will be live.

How to Play

  1. Ensure Ollama is Running: Make sure your Ollama server is active and the model (e.g., phi4:latest) is available. The game defaults to connecting to http://localhost:11434 and using the phi4:latest model. These can be configured via environment variables (see Configuration).

  2. Run the Game:

    • If you installed the package (standard or editable mode):
      text-adventure-tui
      
    • For development, to run directly from the repository root without installing (after pip install -r requirements-dev.txt or pip install -r requirements.txt):
      python -m text_adventure_tui_lib.game
      
  3. Interact with the Story:

    • The game will start with a Main Menu. From here, you can:
      • Start a New Adventure: Choose from a list of available stories.
      • Resume Game: Load a previously saved game.
      • Toggle Debug Output: Show or hide detailed debug messages in the console (useful for development or understanding game flow).
      • Quit Game.
    • During gameplay, the game will present you with a story segment.
    • You will then be given a list of choices generated by the LLM.
    • Enter the number corresponding to your desired choice and press Enter.
    • In-game Commands: At most choice prompts, you can also type:
      • /save: To save your current progress.
      • /quit (or quit): To exit the game.
      • /debug state: (If debug output is on or for general use) Displays the current game state including location, flags, inventory, stats, and event status.
      • /force_event <event_id>: (Primarily for debugging) Manually triggers the actions of the specified event.

Debugging

The game includes a few tools to help with development and debugging:

  • Debug Output Toggle: In the Main Menu, you can toggle the visibility of detailed debug messages printed to the console. These messages show internal game operations, such as LLM prompts, event triggering, and state changes.
  • /debug state command: When playing, type this command at a choice prompt to see a snapshot of the current game state:
    • Current location
    • Set game flags
    • Player inventory
    • Player statistics
    • Global turn counter and turns in current location
    • List of all loaded event IDs
    • List of 'once' event IDs that have already been triggered
  • /force_event <event_id> command: Type this command followed by an event ID (e.g., /force_event welcome_to_eldoria) to manually execute all actions defined for that event, bypassing its trigger conditions. This is useful for testing specific event outcomes.

Configuration

You can configure the Ollama host and model using environment variables:

  • OLLAMA_HOST: The URL of your Ollama server (e.g., http://localhost:11434).
  • OLLAMA_MODEL: The name of the Ollama model to use (e.g., phi4:latest, llama3:latest).

Example:

export OLLAMA_HOST="http://127.0.0.1:11434"
export OLLAMA_MODEL="llama3:latest"
python game.py

Game Structure

The core components of the game are organized within the text_adventure_tui_lib package:

  • game.py: Contains the main game loop, user interface logic (using Rich), LLM interaction, main menu, and save/resume game functionality. This is the primary entry point when running python -m text_adventure_tui_lib.game.
  • event_manager.py: Manages game events, including loading event definitions from YAML files, checking trigger conditions, and executing event actions.
  • story_parts/: This directory holds the narrative content for different stories.
    • *_story_arc.yaml: Defines the overall structure, title, starting conditions, and potentially major plot points (checkpoints) for a story.
    • *.txt: Contains initial story segments or specific narrative blocks that can be loaded by the game. 01_intro.txt is a common starting point.
  • events/: Contains YAML files that define specific game events.
    • general_events.yaml: Events that might be common across multiple stories.
    • *_story_events.yaml: Events specific to a particular story (e.g., short_story_events.yaml).
  • __init__.py: Makes text_adventure_tui_lib a Python package.

Key Features reflected in the structure:

  • Main Menu: Allows selection of different stories or resuming a saved game.
  • Save/Resume: Game progress can be saved (via /save command) and resumed later. Save files are stored in ~/.text_adventure_tui_saves/.
  • Event-Driven Storytelling: Uses an EventManager to react to game state changes and player actions, allowing for dynamic narrative adjustments, flag setting, etc., based on YAML event definitions.

Creating Your Own Stories

You can extend Terminal Text Adventure by creating your own stories! This involves defining a story arc and a set of events using YAML files.

1. Story Arc Files (*_story_arc.yaml)

Story arc files define the overall structure and metadata for your story. They should be placed in the text_adventure_tui_lib/story_parts/ directory. The filename convention is <your_story_name>_story_arc.yaml (e.g., my_epic_adventure_story_arc.yaml).

Structure:

title: "My Epic Adventure" # The title displayed in the main menu
starting_location: "village_green" # The initial location ID for the player
initial_story_part: "01_introduction.txt" # (Optional) Initial .txt file to load for the story's beginning. Defaults to "01_intro.txt" if not specified.
total_turns_estimate: 50 # (Optional) An estimate for story length, currently for informational purposes.

# (Optional) Define starting inventory for the player
initial_inventory:
  - "basic_sword"
  - "healing_potion"

# (Optional) Define initial player stats
initial_player_stats:
  health: 100
  mana: 50
  luck: 5

# (Optional) Checkpoints for legacy turn-based events or forced endings
checkpoints:
  - turn: 25
    prompt_injection: "A storm brews on the horizon, a sense of urgency fills the air."
  - turn: 50
    force_end_game: true
    prompt_injection: "The final confrontation has passed."
    # Customize end message based on flags
    flag_messages:
      - flag: "artifact_recovered"
        message_if_set: "With the artifact recovered, peace returns to the land. You are hailed as a hero!"
        message_if_not_set: "Though the main threat is gone, the missing artifact leaves a lingering shadow..."
      - flag: "ally_saved" # Example of another condition (the first one matched will be used for now)
        message_if_set: "And your brave ally stands by your side!"
        # If message_if_not_set is omitted for a subsequent flag, the previous final_message will persist if this flag isn't set.

Key Fields:

  • title: (Required) The name of your story, shown in the main menu.
  • starting_location: (Required) An identifier for the player's first location. This ID is used in events.
  • initial_story_part: (Optional) The filename of a .txt file in story_parts/ that contains the introductory text for your story. If omitted, the game might default to a common intro or require an initial event to provide the narrative.
  • total_turns_estimate: (Optional) Informational.
  • initial_inventory: (Optional) A list of item IDs the player starts with.
  • initial_player_stats: (Optional) A dictionary of player stats and their starting values.
  • checkpoints: (Optional) A list of turn-based events.
    • turn: The global turn number at which this checkpoint activates.
    • prompt_injection: Text added to the LLM's context for that turn.
    • force_end_game: true: If present, the game will end after this turn.
    • flag_messages: (Optional, for force_end_game checkpoints) A list to customize the final message.
      • flag: The game flag to check.
      • message_if_set: Text to use if the flag is true.
      • message_if_not_set: Text to use if the flag is false.

2. Event Files (*_story_events.yaml or general_events.yaml)

Event files define specific game events, their triggers, and their actions.

  • Place story-specific events in text_adventure_tui_lib/events/<your_story_name>_story_events.yaml. This file is automatically loaded if it matches your story arc's name.
  • Common events usable by any story can be placed in text_adventure_tui_lib/events/general_events.yaml (this is always loaded).

Event Structure:

Each event is a YAML dictionary in a list:

- id: "unique_event_identifier" # (Required) A unique string ID for this event.
  name: "Descriptive Event Name" # (Optional) For readability/debugging.
  options:
    once: true # (Optional) If true, event only triggers once per game. Defaults to false.
    # priority: 1 # (Future) For ordering event checks. Not currently implemented.
  trigger: # (Required) Defines when the event should activate.
    mode: "AND" # (Optional) "AND" or "OR". How conditions are combined. Defaults to "AND".
    conditions: # (Required) A list of conditions.
      - type: "location"
        value: "castle_throne_room"
      - type: "flag_set"
        value: "king_is_angry"
      # ... more conditions
  actions: # (Required) A list of actions to perform when triggered.
    - type: "set_flag"
      value: "player_has_spoken_to_king"
    - type: "override_narrative"
      text: "The King slams his fist on the throne. 'Enough!'"
    # ... more actions

Trigger Condition Types:

  • location: Player is in a specific location.
    • value: Location ID string.
    • Example: { type: location, value: "secret_library" }
  • flag_set: A specific game flag is true.
    • value: Flag name string.
    • Example: { type: flag_set, value: "knows_secret_password" }
  • flag_not_set: A specific game flag is false or not set.
    • value: Flag name string.
    • Example: { type: flag_not_set, value: "has_met_wizard" }
  • player_action_keyword: Player's input text contains specific keywords.
    • keywords: A list of strings (or a single string). Case-insensitive.
    • Example: { type: player_action_keyword, keywords: ["open chest", "unlock box"] }
  • inventory_has: Player possesses a specific item.
    • value: Item ID string.
    • Example: { type: inventory_has, value: "rusty_key" }
  • inventory_not_has: Player does not possess a specific item.
    • value: Item ID string.
    • Example: { type: inventory_not_has, value: "magic_amulet" }
  • turn_count_global: Based on the global game turn counter.
    • value: Integer turn number.
    • operator: (Optional) "==", ">=", "<=", ">", "<". Defaults to "==".
    • Example: { type: turn_count_global, value: 10, operator: ">=" } (Triggers on turn 10 or later)
  • turn_count_in_location: Based on consecutive turns spent in the current location.
    • value: Integer turn number.
    • operator: (Optional) Defaults to "==".
    • location_context: (Optional) If you need to check turns for a specific location (not fully utilized yet, typically refers to current).
    • Example: { type: turn_count_in_location, value: 3 } (Triggers if player has been in current location for 3 turns)

Action Types:

  • set_flag: Sets a game flag to true.
    • value: Flag name string.
    • Example: { type: set_flag, value: "door_unlocked" }
  • clear_flag: Removes a game flag (sets to false/deletes).
    • value: Flag name string.
    • Example: { type: clear_flag, value: "is_poisoned" }
  • override_narrative: Replaces the LLM's generated story for the current turn with fixed text.
    • text: The narrative string. Can be multi-line.
    • Example: { type: override_narrative, text: "A hidden passage creaks open!" }
  • inject_narrative: Adds fixed text before or after the LLM's main narrative for the turn.
    • text: The narrative string.
    • position: (Optional) "pre" or "post". Defaults to "post".
    • Example: { type: inject_narrative, text: "A chill wind blows through the room.", position: "pre" }
  • modify_prompt: Adds specific instructions or context to the prompt sent to the LLM for story continuation.
    • instruction: The string to add to the prompt.
    • Example: { type: modify_prompt, instruction: "REMINDER: The player is currently injured and should act accordingly." }
  • add_item: Adds an item to the player's inventory.
    • value: Item ID string.
    • Example: { type: add_item, value: "golden_key" }
  • remove_item: Removes an item from the player's inventory.
    • value: Item ID string.
    • Example: { type: remove_item, value: "used_torch" }
  • change_location: Moves the player to a new location.
    • value: New location ID string.
    • Example: { type: change_location, value: "dungeon_cell_04" }
  • update_stat: Modifies a player statistic.
    • stat: Name of the stat (e.g., "health", "mana").
    • change_by: Integer value to add (can be negative to subtract).
    • Example: { type: update_stat, stat: "health", change_by: -10 }

3. Linking Story Arcs and Events

  • When a new game is started with your_story_name_story_arc.yaml, the game will automatically look for and load your_story_name_story_events.yaml from the events/ directory, in addition to general_events.yaml.
  • Use location IDs, flag names, and item IDs consistently across your story arc and event files to link them logically.

By combining these YAML files, you can craft intricate narratives, puzzles, and character interactions within Terminal Text Adventure!

Contributing

Contributions are welcome! If you have ideas for improvements, new features, or bug fixes, please feel free to:

  1. Fork the repository.
  2. Create a new branch for your feature (git checkout -b feature/amazing-feature).
  3. Commit your changes (git commit -m 'Add some amazing feature').
  4. Push to the branch (git push origin feature/amazing-feature).
  5. Open a Pull Request.

Future Enhancements

  • Advanced Event Triggers: Implement LLM output-based triggers (e.g., scan LLM response for keywords to trigger dynamic events).
  • Complex Event Actions: Expand actions (e.g., add_item, remove_item, change_location, update_stat, present_choices).
  • Debugging Tools: Add in-game debugging commands (e.g., /debug state, /force_event).
  • Dynamic Event Loading: Allow events to enable or disable other event files or definitions.
  • Broader LLM Choice Support: Test and document compatibility with a wider range of Ollama models.
  • Enhanced Story Crafting Guide: Detailed documentation on how to create compelling stories and events using the YAML structure.
  • More Example Stories: Include a wider variety of starting scenarios and story arcs.
  • Improved LLM Interaction Management: More sophisticated prompt engineering and handling of unexpected LLM outputs.
  • Test Coverage: Increase unit and integration test coverage.

Publishing to PyPI (For Maintainers)

This section is for maintainers who want to publish the package to the Python Package Index (PyPI).

  1. Update setup.py:

    • Ensure version is updated for the new release.
    • Verify/update author, author_email, and url.
    • Confirm the LICENSE file is correct and the license classifier in setup.py matches.
  2. Install Build Tools:

    pip install build twine
    
  3. Clean Previous Builds (Optional):

    rm -rf build dist *.egg-info
    
  4. Build Distribution Files:

    python -m build
    

    This will create a dist directory with a .tar.gz (source distribution) and a .whl (wheel) file.

  5. Check Distribution Files (Optional but Recommended):

    twine check dist/*
    
  6. Upload to TestPyPI (Recommended First Step):

    • Ensure you have a TestPyPI account and API token.
    • Configure your ~/.pypirc file or use token authentication.
    twine upload --repository testpypi dist/*
    
    • Install from TestPyPI to verify:
    pip install --index-url https://test.pypi.org/simple/ --no-deps terminal-text-adventure
    
  7. Upload to PyPI (Production):

    • Ensure you have a PyPI account and API token.
    twine upload dist/*
    

Enjoy your adventure!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

text_adventure_tui-0.1.1.tar.gz (50.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

text_adventure_tui-0.1.1-py3-none-any.whl (49.5 kB view details)

Uploaded Python 3

File details

Details for the file text_adventure_tui-0.1.1.tar.gz.

File metadata

  • Download URL: text_adventure_tui-0.1.1.tar.gz
  • Upload date:
  • Size: 50.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.3

File hashes

Hashes for text_adventure_tui-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ffcd0d4a39e206088887800ec62163b4e63d6914fb510972855113a406f2ab14
MD5 6348883b14b60b11ef598ee505831c1e
BLAKE2b-256 f798b15e568fe43ad7991b1cbc13a9ea0537038fcb3d9fc525d9bce8cc809a15

See more details on using hashes here.

File details

Details for the file text_adventure_tui-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for text_adventure_tui-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b13ff78c090f53a072c025e7b812fce725440bc5a548499d0bd48690a1359edf
MD5 d17ab79ffa3994d39a905fe2686009f1
BLAKE2b-256 76b8a6ce9086d86a9e8afcc833cc76e5f0250ab5c2adb2a8bc665eb15c9f8165

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page