Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VS Simulator

VS Simulator is a character battle simulation system grounded in the VS Battles Wiki.

The simulator resolves fights turn by turn using only the documented abilities of each character—no invented powers, no numeric stats, and no arbitrary rule sets. The outcome of any action is determined by whether the acting character's ability set can logically produce that effect against the defending character's documented resistances and nature. The wiki acts as the absolute ground truth.


How It Works

         ┌──────────────┐
         │  Character DB│  ← SQLite + FAISS semantic index
         └──────┬───────┘
                │
    ┌───────────▼───────────┐
    │    Session Setup      │  Resolve names → Load cores → Build combat personalities
    │    + Context Caching  │  Create Gemini CachedContent per fighter (full ability sheet)
    └───────────┬───────────┘
                │
    ┌───────────▼───────────┐    
    │      Turn Loop        │   
    │                       │   
    │  ┌────────┬────────┐  │   Fighter A and B decide in parallel
    │  │Fighter │Fighter │  │   (neither sees the other's choice)
    │  │Agent A │Agent B │  │   
    │  └────┬───┴───┬────┘  │   
    │       └───┬───┘       │   
    │    ┌──────▼──────┐    │   Resolver determines what actually happened
    │    │  Resolver   │    │   (ability interactions, state changes)
    │    └──────┬──────┘    │   
    │    ┌──────▼──────┐    │   Prose agent narrates (overlaps with next turn)
    │    │  Prose Agent│    │   
    │    └─────────────┘    │   
    └───────────────────────┘

Each agent is powered by Gemini 3.5 Flash with structured output schemas (Pydantic) and function calling for ability retrieval.


Important Data & Parse Disclaimer

Warning

Data Quality & Recovery Note: The raw scraped wiki data (vswiki.json) suffered from various formatting issues, including unescaped quotation marks and parsing errors (e.g., the {{Nihongo}} template scrapers putting literal placeholder names instead of the actual Japanese translation names).

Due to these raw JSON issues, a custom boundary-based item-by-item recovery script using json_repair was run during data ingestion to reconstruct valid JSON objects. While this pipeline successfully recovered 100% of the character entities, some resolved text content, ability names, or descriptions may still contain slight parsing anomalies or inaccuracies.


Features

  • Pit Mode: A fully autonomous, simulated fight between two characters.
  • Context Caching: Pre-loads complete character sheets (all abilities, no truncation) into Gemini explicit context caches at fight start. Subsequent turns only send dynamic state, reducing per-turn cost by ~75% on cached tokens.
  • Parallel Fighters: Both fighter agents decide simultaneously via ThreadPoolExecutor. Neither sees the other's choice for the current turn—correct simultaneous action selection.
  • Overlapped Prose: Prose generation runs in a background thread that overlaps with the next turn's fighter decisions, reducing total fight time.
  • Stateful Prose Generation: Attempts to use the Gemini Interactions API for narrative continuity across turns. Falls back to standard generateContent on Vertex AI endpoints where the Interactions API is not yet supported.
  • Structured Outputs: Fighter declarations and resolver resolutions use Pydantic schemas enforced via response_schema, eliminating JSON parsing failures.
  • Hybrid Search: Combines SQLite FTS5 full-text search with local sentence-embedding semantic search (via FAISS) to match fuzzy/inconsistent ability concepts (e.g., "immunity to mind control").
  • Automatic Retry: The Gemini client automatically retries on 503/429 errors with exponential backoff and jitter.
  • Token Logging: Every API call logs prompt, candidate, total, and cached token counts for cost analysis.

Architecture

src/
├── main.py                      # CLI entry point (Rich terminal UI)
├── logging_config.py            # File-based logging setup
├── agents/
│   ├── gemini_client.py         # Gemini API client with retry logic
│   ├── context_cache.py         # Explicit CachedContent creation per fighter
│   ├── fighter.py               # Fighter agent (cached + uncached modes)
│   ├── resolver.py              # Resolver agent (turn adjudication)
│   ├── prose.py                 # Prose agent (Interactions API + fallback)
│   ├── schemas.py               # Pydantic models for structured output
│   └── prompts/                 # System prompt templates
│       ├── fighter.txt           # Full fighter prompt (uncached mode)
│       ├── fighter_static.txt    # Static fighter prompt (cached mode)
│       ├── resolver.txt          # Resolver prompt
│       └── prose.txt             # Prose writer prompt
├── battle/
│   ├── state.py                 # Session state, conditions, effects model
│   └── orchestrator.py          # Turn loop, parallel execution, prose overlap
├── retrieval/
│   ├── tools.py                 # Retrieval functions + thread-safe cache
│   ├── character_lookup.py      # Character name resolution (FTS5 + fuzzy)
│   └── embeddings.py            # FAISS semantic search interface
└── db/
    └── connection.py            # SQLite connection management
    
data/
├── pipeline/
│   ├── export_clean_json.py     # JSON repair + clean export
│   ├── ingest.py                # SQLite ingestion pipeline
│   └── embed.py                 # FAISS embedding generation
├── vskill.db                    # SQLite database (local build)
├── abilities.faiss              # FAISS index (local build)
└── faiss_id_map.json            # FAISS ID mapping (local build)

Configuration & Environment Setup

This project uses dotenv to load configurations. Copy the template .env.example file and configure it:

cp .env.example .env

Option A: Standard Developer API

Ensure you have set the GEMINI_API_KEY in your .env:

GEMINI_API_KEY=your_gemini_api_key

Option B: Google Cloud Enterprise / Vertex AI (Recommended)

Enterprise mode is highly recommended to bypass standard rate limits. Configure the following variables in .env and authenticate your terminal using Google Cloud ADC (gcloud auth application-default login):

GEMINI_ENTERPRISE=true
GEMINI_PROJECT=your_gcp_project_id
GEMINI_LOCATION=asia-northeast1

Note

The Gemini Interactions API (used for stateful prose) is currently available on the standard Google AI endpoint but not on Vertex AI. When running in enterprise/Vertex mode, the prose agent automatically falls back to standard generateContent. This is handled transparently.


Installation & Setup

  1. Clone the repository (or download the source).
  2. Set up a virtual environment and install dependencies:
    python3 -m venv vskill-venv
    source vskill-venv/bin/activate
    pip install -r requirements.txt
    (Note: Ensure you place your source raw vswiki.json inside the assets/ directory if you plan to rebuild the databases).

Data Pipeline (Local Build)

Because database and FAISS index files exceed GitHub's 100MB limit, you will need to build them locally from the source dataset.

  1. Reparse & Repair Malformed JSON: Exports a clean, standard-compliant JSON array to assets/vswiki_clean.json.
    python data/pipeline/export_clean_json.py
  2. Ingest to SQLite: Populates data/vskill.db with identity tables, abilities, and notable attacks, stripping citations and fixing Nihongo templates.
    python data/pipeline/ingest.py
  3. Generate Embeddings (FAISS): Generates sentence-level embeddings using a local model and compiles data/abilities.faiss and data/faiss_id_map.json.
    python data/pipeline/embed.py

Usage

You can run character searches or trigger simulations directly from the CLI.

Character Search

To search the local database for available characters matching a query:

python -m src.main --list "Goku"

Run Battle Simulation (Pit Mode)

To simulate a fight between two characters:

python -m src.main "Goku" "Superman" --turns 15

Options:

  • --turns <int>: Set the maximum number of turns before declaring a draw (default: 15).
  • --save <path.json>: Save the canonical simulation state and narrative history to a JSON file.
  • --list <query>: List matching characters instead of starting a fight.

Analyzing Costs

After a fight, token usage is logged to vskill.log. To see per-call token counts:

grep "TOKENS" vskill.log

Cost Profile (5-Turn Fight Example)

Token Type Count Approx. Cost (USD)
Input (Standard) 107,171 $0.161
Input (Cached Read) 129,854 $0.019
Output (Candidates) 7,318 $0.066
Total 247,239 $0.246

Context caching provides ~75% cost reduction on repeated character sheet tokens across turns.


Known Limitations

  • Interactions API on Vertex AI: The Gemini Interactions API returns 400 Unsupported model interaction when called via Vertex AI enterprise endpoints. The prose agent falls back to generateContent automatically.
  • Deadlock Loops: Strategic characters may enter cyclical action patterns (A shields → B switches to utility → A attacks → B shields → ...). This can be mitigated by prompt-level loop detection instructions or resolver intervention.
  • Cache Build Time: Creating explicit context caches at fight start takes ~1-2 minutes due to sequential ability fetching and Gemini tokenization overhead. This could be parallelized.
  • Data Quality: Some character ability descriptions contain parsing artifacts from the wiki scraping pipeline.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages