Tickd.ai
← The Tickd Guide

Inspiration

How to Build an Eldritch Virtual Pet CLI Game Using Claude 3.5 Sonnet and Python

Bored of standard utility scripts? Learn how to build a text-based desktop companion game where you must feed, soothe, and survive a minor cosmic deity using state-machine logic and LLM narrative generation.

Updated 9/7/2026

We have all built our fair share of boring command-line interfaces. Scraping scripts, local search tools, and database migrators are fine, but they rarely spark joy. Let's build something gloriously useless and slightly unnerving instead: a desktop-bound virtual pet, inspired by 1990s keychain toys, but featuring a minor cosmic deity rather than a digital dinosaur.

In this guide, we will build a terminal-based game engine where you manage an eldritch entity’s hunger, anger, and dimensional stability. To make this work, we will use Python to manage the hard game loop and state, and call upon /platforms/claude to handle the entity’s shifting behaviour, bizarre dialogue, and unsettling physical manifestations.

The Mechanics of Existential Caretaking

A classic virtual pet relies on simple stats: Hunger, Boredom, and Discipline. For our cosmic entity, we will translate these into parameters that carry more existential weight:

  • Void-Hunger (0-100): How desperately the entity wishes to consume local matter.
  • Dimensional Drift (0-100): How much the entity is slipping back into the outer void.
  • Awe (0-100): The entity's appreciation for your futile attempts to understand it.

If Void-Hunger hits 100, the pet consumes your command line (and ends the game). If Dimensional Drift reaches 100, it vanishes, taking your local terminal history with it. Keeping the entity stable is a delicate balancing act, and watching the clock tick down to reality's collapse is surprisingly addictive.

The Game State and System Architecture

Using raw LLM generations for game mechanics is a recipe for disaster; they hallucinate scores, forget rules, and lose track of historical progress. Instead, we will build a hybrid state-machine model. Python will track the numeric variables, while Claude acts as the narrative interpreter, translating those numbers into creepy, contextual terminal descriptions.

To begin, ensure you have your API keys set up. If you run into issues authenticating, you can find solutions on the Claude Support Page.

Here is the core structure of our state tracker in Python:

`python import os import json import anthropic

class EldritchPet: def __init__(self): self.client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) self.hunger = 20 self.drift = 10 self.awe = 50 self.is_alive = True self.last_action_report = "You have summoned a tiny tear in reality. It floats above your keyboard, humming softly." `

Crafting the Eldritch Prompt Engine

The secret to making this game feel alive is prompt design. We want Claude to behave not like a helpful assistant, but like a deeply eccentric dungeon master who is slightly hostile to the player.

We will feed Claude the current numeric stats, the player's action, and the preceding narrative event. We will then ask it to output a JSON object containing the new narrative update and a slight modifier to the stats based on the flavor of the player's action.

You can refine similar structured outputs using our /prompts for custom schemas, but for this experiment, we will stick to a rigid system prompt:

`python SYSTEM_PROMPT = """ You are the narrator of a terminal-based text game where the player keeps an eldritch entity as a pet. Your tone is cold, slightly ironic, and British in its dry wit. Under no circumstances should you be cheerful or helpful.

You will receive a JSON input containing: - Current stats (Hunger, Drift, Awe) - The player's action (e.g., 'Feed it leftover cheese', 'Sing a lullaby to the void') - The previous narrative state

You must return a valid JSON object matching this schema: { "narrative": "A description of the entity's reaction and physical changes (max 3 sentences).", "stat_mod_hunger": int (between -20 and 20), "stat_mod_drift": int (between -20 and 20), "stat_mod_awe": int (between -20 and 20) } Do not include any markdown styling, conversational filler, or triple backticks in your response. Return pure JSON. """ `

Writing the Python Game Loop

With our prompt set up, we now write the loop that keeps the program running. Every turn, we display the current metrics in an ASCII status bar, prompt the user for an action, dispatch the payload to Claude, and update our local numeric state.

`python def run_turn(pet, user_action): payload = { "stats": {"hunger": pet.hunger, "drift": pet.drift, "awe": pet.awe}, "action": user_action, "previous_event": pet.last_action_report } response = pet.client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=300, system=SYSTEM_PROMPT, messages=[{"role": "user", "content": json.dumps(payload)}] ) try: # Clean up any potential formatting drift raw_text = response.content[0].text.strip() result = json.loads(raw_text) # Update state pet.hunger = max(0, min(100, pet.hunger + result.get("stat_mod_hunger", 0))) pet.drift = max(0, min(100, pet.drift + result.get("stat_mod_drift", 0))) pet.awe = max(0, min(100, pet.awe + result.get("stat_mod_awe", 0))) pet.last_action_report = result.get("narrative", "Nothing happens, yet the air grows cold.") except Exception as e: pet.last_action_report = f"The universe glitches slightly. You feel a headache. (Error parsing response)" `

Managing the Void-Tether

To make this feel like a true game, we must build a rendering system that visualises the state of our entity. Since we are avoiding complex graphical engines, we will use basic ASCII progress bars to illustrate the stability of the cosmic rift.

`python def draw_interface(pet): os.system('clear' if os.name == 'nt' else 'clear') print("=" * 60) print(" ELDRITCH COMPANION v1.0.4 - ACTIVE") print("=" * 60) # ASCII Bars print(f"Void-Hunger: [{'#' (pet.hunger // 5)}{' ' (20 - pet.hunger // 5)}] {pet.hunger}/100") print(f"Dimensional Drift:[{'#' (pet.drift // 5)}{' ' (20 - pet.drift // 5)}] {pet.drift}/100") print(f"Awe of the Void: [{'#' (pet.awe // 5)}{' ' (20 - pet.awe // 5)}] {pet.awe}/100") print("-" * 60) print(f"{pet.last_action_report}") print("=" * 60) `

Now, implement a tick system where stats naturally decay or escalate over time. For instance, each turn Hunger should naturally rise by 5, and Dimensional Drift should increase by 3 if the player fails to soothe the rift.

Taking It Further

If you want to make this project truly native to your machine, you can implement system-level side effects. Using basic Python libraries, you can make the game write tiny cryptic files to your desktop when the entity’s Dimensional Drift is high, or make the terminal beep when Hunger enters critical zones.

Building simple, narrative-driven CLI toys is a fantastic way to familiarise yourself with structured JSON processing. You can see how other builders are using Claude’s structured capabilities in creative coding projects by looking at the official showcase on the Anthropic console pages. Put down the spreadsheet optimisers for an afternoon and build something delightfully weird.

pythonclaudegame-designinspirationcreative-coding

Keep going

Build something with the prompt generator, decode the jargon in the glossary, or compare the tools on our platform deep-dives.