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
richlibrary 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
/savecommand and resume later from the main menu. - Extensible Story System:
- Stories are defined by
*_story_arc.yamlfiles intext_adventure_tui_lib/story_parts/. - Narrative content and initial segments are loaded from text files.
- Stories are defined by
- 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.
- A powerful event system (see
- 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.
- Install Ollama:
- Follow the instructions on the official Ollama website.
- 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 serveor by starting the Ollama application). By default, it runs onhttp://localhost:11434.
- Once Ollama is running, pull a model. This game is tested with
Installation
-
Clone the Repository:
git clone https://github.com/Theblackcat98/Text-Adventure-TUI.git cd Text-Adventure-TUI
-
Create a Virtual Environment (Recommended):
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
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-tuifrom 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:
-
Install Development Dependencies: This includes linters, testing tools, and build tools.
pip install -r requirements-dev.txt
-
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-tuiand your code changes will be live.
How to Play
-
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 tohttp://localhost:11434and using thephi4:latestmodel. These can be configured via environment variables (see Configuration). -
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.txtorpip install -r requirements.txt):python -m text_adventure_tui_lib.game
- If you installed the package (standard or editable mode):
-
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(orquit): 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.
- The game will start with a Main Menu. From here, you can:
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 statecommand: 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 runningpython -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.txtis 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: Makestext_adventure_tui_liba 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
/savecommand) and resumed later. Save files are stored in~/.text_adventure_tui_saves/. - Event-Driven Storytelling: Uses an
EventManagerto 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.txtfile instory_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, forforce_end_gamecheckpoints) 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 loadyour_story_name_story_events.yamlfrom theevents/directory, in addition togeneral_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:
- Fork the repository.
- Create a new branch for your feature (
git checkout -b feature/amazing-feature). - Commit your changes (
git commit -m 'Add some amazing feature'). - Push to the branch (
git push origin feature/amazing-feature). - 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).
-
Update
setup.py:- Ensure
versionis updated for the new release. - Verify/update
author,author_email, andurl. - Confirm the
LICENSEfile is correct and the license classifier insetup.pymatches.
- Ensure
-
Install Build Tools:
pip install build twine
-
Clean Previous Builds (Optional):
rm -rf build dist *.egg-info
-
Build Distribution Files:
python -m build
This will create a
distdirectory with a.tar.gz(source distribution) and a.whl(wheel) file. -
Check Distribution Files (Optional but Recommended):
twine check dist/*
-
Upload to TestPyPI (Recommended First Step):
- Ensure you have a TestPyPI account and API token.
- Configure your
~/.pypircfile 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
-
Upload to PyPI (Production):
- Ensure you have a PyPI account and API token.
twine upload dist/*
Enjoy your adventure!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffcd0d4a39e206088887800ec62163b4e63d6914fb510972855113a406f2ab14
|
|
| MD5 |
6348883b14b60b11ef598ee505831c1e
|
|
| BLAKE2b-256 |
f798b15e568fe43ad7991b1cbc13a9ea0537038fcb3d9fc525d9bce8cc809a15
|
File details
Details for the file text_adventure_tui-0.1.1-py3-none-any.whl.
File metadata
- Download URL: text_adventure_tui-0.1.1-py3-none-any.whl
- Upload date:
- Size: 49.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b13ff78c090f53a072c025e7b812fce725440bc5a548499d0bd48690a1359edf
|
|
| MD5 |
d17ab79ffa3994d39a905fe2686009f1
|
|
| BLAKE2b-256 |
76b8a6ce9086d86a9e8afcc833cc76e5f0250ab5c2adb2a8bc665eb15c9f8165
|