Welcome to The Dying Earth, an interactive text-based adventure game inspired by Jack Vance's legendary Dying Earth series. This project leverages OpenAI's ChatGPT and DALL·E 2 APIs to deliver a dynamic and immersive storytelling experience set in the twilight of civilization on a world immeasurably aged. Experience the opulent prose style of Jack Vance enriched with elements reminiscent of Monte Cook's Numenera, directly in your command-line interface (CLI). Created during Cascadia PHP 2024, with heartfelt thanks to the organizers and participants for inspiring and supporting this project!
This project is no longer in development as it was a good proof of concept for something larger to come.
The Dying Earth transports you to a grimdark world in the final epoch of existence, where:
- The sun wanes in the sky, casting spectral light over landscapes layered with the ruins of countless civilizations
- Magic and ancient technology blur together, their boundaries long forgotten
- Wonders from bygone eras lie half-buried, waiting to be discovered—or to ensnare the unwary
- Every location bears the weight of immeasurable history, with exotic locales and peculiar customs
- Cosmic horror lurks beneath the veneer of faded grandeur
- Knowledge and power await those willing to delve into the depths of time, though not without peril
The game captures the ornate, elaborate prose style of Jack Vance's original works, where every description drips with baroque detail and every encounter may lead to deeper enigmas. Players navigate this twilight realm where the boundaries between wonder and danger, knowledge and madness, are perilously thin.
- Prerequisites
- Installation
- Usage
- Gameplay Instructions
- Character Stats Explained
- Security Considerations
- Troubleshooting
- Customization
- License
- Credits
This project now features a concurrent animated loading spinner for both image and audio generation requests. The spinner provides immediate, responsive feedback to the user while waiting for API responses from Pollinations.ai.
- When generating images or audio, the spinner animation starts immediately before the API call and stops as soon as the response is received.
- The spinner runs as a background process (
app/SpinnerProcess.php), allowing it to animate independently of blocking operations. - The spinner message adapts to the context: "Generating image" or "Generating audio".
app/Utils.php— Utility functions for starting/stopping the spinner withshowLoadingAnimationandstopLoadingAnimation.app/SpinnerProcess.php— The background process that displays the spinner.app/ImageHandler.php&app/AudioHandler.php— Updated to start the spinner before and stop it after each API request.
$loading_pid = Utils::showLoadingAnimation('image'); // or 'audio'
// ... make API request ...
Utils::stopLoadingAnimation($loading_pid);- Improved UX: Users see instant feedback when waiting for image or audio generation.
- Accurate Timing: The spinner reflects the actual API wait time, not just a fixed delay.
- Non-blocking: The game remains responsive while the spinner animates.
Before you begin, ensure you have met the following requirements:
-
PHP 7.4 or Higher is installed on your system.
- Check your PHP version by running:
php -v
- If PHP is not installed, download it from the official website or use a package manager.
- Check your PHP version by running:
-
An API Key from one of the supported AI providers:
- OpenAI: Sign up at OpenAI's website and generate an API key from your API keys page.
- OpenRouter: Sign up at OpenRouter's website and generate an API key from your keys page.
-
Git installed (optional, for cloning the repository).
- Check if Git is installed by running:
git --version
- If not installed, download it from the official website or use a package manager.
- Check if Git is installed by running:
-
Clone the Repository
Clone the GitHub repository to your local machine using the following command:
git clone https://github.com/yourusername/the-dying-earth.git
Replace
yourusername/the-dying-earthwith the actual GitHub username and repository name. -
Navigate to the Project Directory
cd the-dying-earth -
Ensure Dependencies Are Met
- The project relies on PHP's
curlandgdextensions. - Verify they are installed by running:
php -m | grep -E 'curl|gd'
- If not installed, refer to your operating system's package manager or PHP installation guide to add these extensions.
- Ensure the
readlineextension is enabled for interactive input. Check withphp -m | grep readline.
- The project relies on PHP's
-
Create Necessary Directories
The game will automatically create an
imagesdirectory for temporary image storage. Ensure the PHP process has permissions to create directories in the project path.- The game also uses a
datadirectory to store the API key, game history, and debug logs. Ensure the PHP process has write permissions in the project's root directory to create this if it doesn't exist.
- The game also uses a
Run the game.php script using PHP:
php game.phpFirst-Time Setup:
-
Upon the first run, you'll be prompted to configure your AI provider:
- Select your AI provider (OpenAI or OpenRouter)
- Enter your API key for the selected provider
- Choose your preferred model from the available options
The configuration will be saved securely with permissions set to
0600(readable and writable only by you).
Subsequent Runs:
- The script will automatically use your saved configuration without prompting.
-
Configure AI Provider
To configure or change your AI provider and model:
php game.php --setup
- This will launch the interactive setup wizard to select your provider, enter your API key, and choose your model.
-
Display Current Configuration
To view your current AI provider configuration:
php game.php --config
-
Refresh OpenRouter Models
To clear the cached model list and fetch fresh models on next setup:
php game.php --refresh-models
- OpenRouter's model list is cached for 1 hour to improve performance
- Use this if you want to see the very latest models immediately
-
Start a New Game
To start a new game and clear any existing game history:
php game.php --new
- This will delete the
.game_historyfile if it exists and start a fresh adventure.
- This will delete the
-
Enable Debugging
For detailed debug logs:
php game.php --debug
- This will display internal processing messages in the console and write detailed logs to
data/debug_log.txt.
- This will display internal processing messages in the console and write detailed logs to
-
Enable Chunky Mode
To use the Chunky ASCII Art Converter:
php game.php --chunky
- This will use the Chunky font's rich character set to render game images with more detailed ASCII art.
-
Display Help
To see all available options:
php game.php --help
-
Combine Flags
You can combine multiple flags as needed:
php game.php --new --debug --chunky
-
Game Start
After launching the game, you'll receive a welcome message:
Welcome to 'The Dying Earth'! (Type 'exit' or 'quit' to end the game at any time.) -
Receiving Narration
The game will provide opulent and elaborate descriptions in the style of Jack Vance, painting the twilight world where ancient magics and forgotten technologies blur together, enriched with emojis to enhance visualization.
-
Making Choices
When prompted with
Your choice (1-4, or 'exit'):, enter the number corresponding to your desired action and press Enter.-
Some choices may involve hidden skill checks, saving throws, or sanity checks based on your character's stats. The outcome of these checks (Success/Failure) might alter the available options or narrative results.
-
Other Commands:
t: Type in a custom action instead of choosing a numbered option.s: Display your character sheet, showing stats and attributes.g: Toggle the generation of ASCII art images for scenes (On/Off).n: Start a new game (you will be asked for confirmation).qorquit: Exit the game.
-
Example:
[cyan]Your choice (1-4, or 'exit'): [/cyan] 2
-
-
Exiting the Game
At any prompt, type
exitorquitto gracefully end your adventure.
Your character possesses a set of attributes that influence their capabilities and resilience within the game world. These are divided into Primary Attributes and Derived Stats.
These 14 core attributes are randomly determined at the start of a new game (typically between 8 and 18) and represent your character's innate abilities:
- Agility: Physical dexterity, speed, and reflexes.
- Appearance: Physical attractiveness and grooming.
- Charisma: Social grace, likability, and leadership potential.
- Dexterity: Hand-eye coordination, fine motor skills, and nimbleness.
- Endurance: Physical stamina, resilience to fatigue and pain.
- Intellect: Reasoning ability, memory, and problem-solving skills.
- Knowledge: Learned information and expertise.
- Luck: Innate fortune and chance.
- Perception: Awareness of surroundings, ability to notice details.
- Spirit: Inner strength, morale, and connection to mystical forces.
- Strength: Physical power and brute force.
- Vitality: Overall health, constitution, and resistance to disease/poison.
- Willpower: Mental fortitude, self-control, and determination.
- Wisdom: Intuition, common sense, and insight.
Each Primary Attribute score provides a Modifier, calculated as floor((Score - 10) / 2). This modifier is added to relevant dice rolls (d20) during Skill Checks and Saving Throws.
These stats are calculated based on your Primary Attributes and represent your character's resources and current state:
- Health: Hit points; resistance to physical damage. Influenced by
VitalityandEndurance.- Formula: floor((Vitality * 2) + Endurance)
- Focus: Mental energy for special abilities or concentration. Influenced by
Willpower,Intellect, andWisdom.- Formula: floor(Willpower + ((Intellect + Wisdom) / 2))
- Stamina: Physical energy for strenuous actions. Influenced by
Endurance,Strength, andAgility.- Formula: floor((Endurance * 1.5) + ((Strength + Agility) / 2))
- Courage: Resistance to fear and intimidation. Influenced by
Willpower,Spirit, andCharisma.- Formula: floor(Willpower + ((Spirit + Charisma) / 2))
- Sanity: Mental stability and resistance to psychological stress. Influenced by
Willpower,Intellect, andPerception.- Formula: floor((Willpower * 1.5) + ((Intellect + Perception) / 2))
Many actions you attempt or situations you encounter will trigger a check or save:
- Skill Check: When attempting an action requiring a specific ability (e.g., climbing a wall using Strength), you roll a d20, add the relevant attribute's modifier (and potentially a proficiency bonus), and compare it to a Difficulty Class (DC). Format:
[SKILL_CHECK:Attribute:DC] - Saving Throw: When resisting an external effect (e.g., dodging a trap using Dexterity), you roll a d20, add the relevant attribute's modifier, and compare it to a DC. Specific saves use fixed attributes:
- Fortitude:
Vitality - Reflex:
Dexterity - Will:
Willpower - Social:
Charisma - Format:
[SAVE:SaveType:DC]
- Fortitude:
- Sanity Check: A special check against mental strain, rolling d20 plus your Sanity modifier against a DC. Failing can result in a loss of Sanity points. Format:
[SANITY_CHECK:DC]
Success or failure in these rolls determines the outcome of your actions and how the narrative progresses.
If you've been using the game with the old OpenAI-only configuration:
php migrate.phpThis will guide you through migrating your existing configuration to the new multi-provider system.
If you prefer to set up manually:
To continue using OpenAI:
- Run
php game.php- you'll be prompted to set up your provider - Select OpenAI as your provider
- Enter your existing OpenAI API key
- Select your preferred model (GPT-4o Mini is recommended for balance of cost and performance)
To switch to OpenRouter for access to more models:
- Get an API key from OpenRouter
- Run
php game.php --setup - Select OpenRouter as your provider
- Enter your OpenRouter API key
- Choose from 200+ available models
Your game history and saves will be preserved when switching providers.
Direct access to OpenAI's models including GPT-4o and GPT-3.5 Turbo.
Available Models:
- GPT-4o (Most capable)
- GPT-4o Mini (Fast and affordable)
- GPT-4 Turbo (Legacy)
- GPT-3.5 Turbo (Legacy, fast)
Access to 400+ models from multiple AI providers through a unified API, including many FREE models!
Key Providers Include:
- OpenAI: All GPT models
- Anthropic: Claude 3.5 Sonnet, Claude 3 Opus/Sonnet/Haiku
- Google: Gemini Pro 1.5, Gemini Flash, Gemma models
- Meta: Llama 3.3 70B, Llama 3.1 405B/70B/8B
- Mistral: Mistral Large, Mixtral, Codestral
- xAI: Grok Beta
- DeepSeek: DeepSeek Chat, DeepSeek Coder
- Cognitive Computations: Venice (Uncensored models)
- Qwen: Qwen 2.5 models
- Nous Research: Hermes models
- And 50+ more providers!
🆓 FREE Models Available: OpenRouter offers numerous free models perfect for testing and development:
- Venice Dolphin Mistral (Default) - Uncensored, creative writing focused
- Models from Google, Meta, Mistral, and more with
:freevariants - No API costs for these models (rate limits apply)
- Type
freeduring setup to see all free options
Dynamic Model Selection: When you select OpenRouter during setup, the game automatically fetches the latest list of available models from OpenRouter's API. This ensures you always have access to the newest models without needing to update the game.
Model Information Display: Each model shows helpful context:
- 🆓 FREE: Clearly marked free models
-
Pricing tier:
$, $ $, or $$$ for paid models - Context window: For models with large context (e.g., "128k ctx")
- Capabilities: Vision, Audio, Functions support
- Provider: Organized by provider for easy browsing
Setup Commands:
- Type
moreto see all 400+ models - Type
freeto see only free models - Enter partial model names for search
- Direct model ID entry supported
Benefits of OpenRouter:
- Start for FREE - Many models available at no cost
- Access to 400+ models through a single API key
- Real-time model availability - new models appear automatically
- Automatic fallback to alternative providers if one is unavailable
- Competitive pricing across different models
- No need for multiple API keys from different providers
-
API Key Storage
- Your API key is stored in a hidden file named
.data/.api_keyin the project directory. - Provider configuration is stored in
.data/.provider_config. - Both files have permissions set to
0600, making them readable and writable only by your user account. - Do Not Share: Never share your API key or commit it to any public repository.
- Your API key is stored in a hidden file named
-
Protecting Your API Key
- Revoking Access:
- For OpenAI: Revoke keys from your OpenAI account settings
- For OpenRouter: Revoke keys from your OpenRouter keys page
- Revoking Access:
-
File Permissions
- Ensure that the project directory is secure and not accessible by unauthorized users.
- Verify file permissions if you're running the script on a shared system.
After setting up your AI provider, you can test the connection:
php test_providers.phpThis will:
- Verify your API key is valid
- Test connectivity to your selected provider
- Check if your selected model is available
- Test function calling support
To see all available models from OpenRouter:
php fetch_models.phpThis utility:
- Fetches the complete list of 400+ models from OpenRouter
- Shows models organized by provider
- Displays pricing and capability information
- Optionally generates a configuration file with all models
- Works without requiring an API key
-
PHP Not Recognized
- Error Message:
'php' is not recognized as an internal or external command... - Solution: Ensure PHP is installed and added to your system's PATH variable.
- Error Message:
-
Invalid API Key
- Error Message:
API Error Message: Incorrect API key provided - Solution: Double-check your API key for typos and ensure it's valid.
- Error Message:
-
API Connection Errors
- Error Message:
Request Error: Could not resolve host: api.openai.com - Solution: Check your internet connection and DNS settings.
- Error Message:
-
SSL Certificate Issues
- Error Message:
SSL certificate problem: unable to get local issuer certificate - Solution: Update your system's CA certificates or disable SSL verification (not recommended).
Warning: Disabling SSL verification can expose you to security risks.
// Add this line before executing the API request curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- Error Message:
-
Permission Denied
- Error Message:
Permission deniedwhen accessing.openai_api_key - Solution: Ensure you have read/write permissions for the project directory and files.
- Error Message:
-
API Schema Validation Errors
- Error Message:
Invalid schema for response_format 'game_scene': In context=('properties', 'options'), 'minItems' is not permitted. - Solution: Ensure that the
minItemsandmaxItemsconstraints are removed from theresponse_formatschema ingame.php. The latest refactored code provided above addresses this issue.
- Error Message:
-
Changing the Game Theme
- Open
app/config.phpin a text editor. - Modify the
system_promptkey within the returned array to change the game's setting or rules.'system_prompt' => "You are an interactive text-based adventure game called 'The Dying Earth.' This is a grimdark world set in the twilight of civilization...",
- Open
-
Adjusting API Parameters
- In
app/config.php, modify parameters liketemperatureunder theapikey to change response creativity.'api' => [ // ... other settings 'temperature' => 0.9, // ... ],
- In
-
Using a Different Model
- Run the setup wizard to change your model:
php game.php --setup
- Or manually edit
.data/.provider_configto change the model. - You can also add new models to
app/config.phpin the providers section.
- Run the setup wizard to change your model:
-
Enhancing ASCII Art Quality
- Adjust the
scaleparameter under theimagekey inapp/config.phpto change the resolution of the ASCII art. - You can also experiment with different character sets in
app/AsciiArtConverter.php.
- Adjust the
-
Customizing Audio Settings
You can configure the voice and maximum text length for audio generation by editing the
audiosection inapp/config.php:'audio' => [ 'voice' => 'ash', // Change to your preferred voice 'model' => 'openai-audio', // Audio model used 'max_text_length' => 5120, // Maximum characters per audio request ],
- voice: The default voice for text-to-speech (e.g., 'ash').
- max_text_length: The maximum number of characters per audio generation request. If your narration is longer, it will be split into chunks.
Any changes here will automatically be used by the game's audio system.
This project is licensed under the MIT License. You are free to use, modify, and distribute this software as per the license terms.
- Developer: Spencer Thayer
- Contributor: nsbucky
- Inspiration: Cascadia PHP 2024
Thank you to all the participants and organizers of Cascadia PHP 2024 for fostering a community of innovation and collaboration. Your support made this project possible!
A PHP command-line tool that converts images into ASCII art using characters from the Chunky font.
- Converts images to terminal-friendly ASCII art
- Uses the Chunky font's extensive character set (366 characters)
- Supports color output with ANSI terminal colors
- Adaptive block sizing for different image types
- Multiple resolution options
- NEW: Density-based character mapping for accurate visual representation
- PHP 8.0 or higher
- PHP GD extension
- Terminal with ANSI color support
- Clone or download this repository
- Make sure you have PHP and the GD extension installed
- Run the tool from the command line
For a more convenient experience, you can add the bin directory to your PATH:
# Add this to your .bashrc, .zshrc, or equivalent shell configuration file
export PATH="/path/to/chunky/bin:$PATH"Then, you can run the tool simply by typing chunky from anywhere:
chunky image.png -aphp chunky.php <image_file> [options]Or using the bash wrapper (if installed):
chunky <image_file> [options]-b, --block-size <size>- Size of image blocks (default: 8)-s, --simple- Use simple character set (only basic block characters)-a, --adaptive- Use adaptive block sizing based on image content-c, --categorized- Use categorized characters instead of density-sorted-o, --output <file>- Save output to file instead of terminal-h, --help- Display help message
Convert an image using default settings (with density-based character selection):
chunky image.pngConvert an image with smaller block size (more detail):
chunky image.png -b 4Use adaptive block sizing for improved detail in complex areas:
chunky image.png -aUse traditional categorized character selection instead of density-based:
chunky image.png -cUse simple character set (less detailed but more compatible):
chunky image.png -sSave output to a file:
chunky image.png -o output.txtThe Chunky ASCII Art Converter can be used to enhance the visuals in "The Dying Earth" game. To enable Chunky mode:
php game.php --chunkyYou can combine this with other game flags:
php game.php --chunky --newphp game.php --chunky --debugThis will use the Chunky font's rich character set to render game images with more detailed and visually appealing ASCII art.
The Chunky ASCII Art Converter analyzes each region of the input image and maps it to an appropriate character from the Chunky font using two possible methods:
The converter uses characters sorted by their actual pixel density from the Chunky font:
- The tool analyzes the actual 8×8 pixel patterns of each character in the font
- Characters are sorted from least dense (fewest pixels) to most dense (most pixels)
- Image brightness is directly mapped to this density scale
- This provides the most accurate visual representation of the original image
Alternatively, the converter can use a more traditional approach:
- Characters are manually categorized by their visual properties
- Image regions are analyzed for brightness, edges, and texture
- Special characters are used for specific features (box drawing for edges, etc.)
- This can be enabled with the
-cor--categorizedflag
The converter also uses ANSI color codes to preserve color information from the original image.
The first time you run the tool with density-based selection, it will automatically analyze the Chunky font characters and sort them by pixel density. This analysis:
- Examines the 8×8 pixel grid for each character in the font
- Counts the number of "on" pixels in each character
- Sorts the entire 366-character set from least dense to most dense
- Saves this mapping to
app/chunky_density_sorted.phpfor future use
Chunky is an 8×8 pixel TTF textmode font created by Batfeula, featuring 366 characters including:
- ASCII characters
- Box drawing characters
- Block elements
- Special symbols
- Greek letters
- Math symbols
- And more!
This tool respects the Chunky font license and only uses the character mappings for ASCII art conversion.
This project is licensed under the MIT License - see the LICENSE file for details.
- Batfeula for creating the awesome Chunky font
- The PHP GD development team