235 lines
5.8 KiB
Python
235 lines
5.8 KiB
Python
|
|
"""Superpower Registry for 152 Superpowers.
|
||
|
|
|
||
|
|
Provides:
|
||
|
|
- load_all_superpowers() - Load all 152 superpowers
|
||
|
|
- get_superpower(id) - Get single superpower by ID
|
||
|
|
- get_superpowers_by_band(band) - Get all superpowers in band
|
||
|
|
- calculate_boost(superpower_ids) - Calculate aggregate boost
|
||
|
|
- search_superpowers(query) - Search superpowers by name/description
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Optional, List, Dict, Any
|
||
|
|
|
||
|
|
# Module-level cache
|
||
|
|
_superpowers: Optional[Dict[int, dict]] = None
|
||
|
|
_superpowers_list: Optional[List[dict]] = None
|
||
|
|
_loaded = False
|
||
|
|
_load_path: Optional[Path] = None
|
||
|
|
|
||
|
|
|
||
|
|
def load_all_superpowers(path: Optional[str] = None) -> None:
|
||
|
|
"""Load all 152 superpowers from JSON file.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
path: Optional path to superpowers.json file.
|
||
|
|
Defaults to /home/dave/superdave/glyphs/superpowers.json
|
||
|
|
"""
|
||
|
|
global _superpowers, _superpowers_list, _loaded, _load_path
|
||
|
|
|
||
|
|
if _loaded:
|
||
|
|
return
|
||
|
|
|
||
|
|
if path is None:
|
||
|
|
path = "/home/dave/superdave/glyphs/superpowers.json"
|
||
|
|
|
||
|
|
filepath = Path(path)
|
||
|
|
if not filepath.exists():
|
||
|
|
raise FileNotFoundError(f"Superpowers file not found: {filepath}")
|
||
|
|
|
||
|
|
_load_path = filepath
|
||
|
|
|
||
|
|
with open(filepath) as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
# Validate count
|
||
|
|
if data.get("total") != 152:
|
||
|
|
raise ValueError(f"Expected 152 superpowers, got {data.get('total')}")
|
||
|
|
|
||
|
|
# Build cache by ID
|
||
|
|
_superpowers = {}
|
||
|
|
for sp in data.get("superpowers", []):
|
||
|
|
_superpowers[sp["id"]] = sp
|
||
|
|
|
||
|
|
# Build sorted list
|
||
|
|
_superpowers_list = sorted(_superpowers.values(), key=lambda x: x["id"])
|
||
|
|
|
||
|
|
_loaded = True
|
||
|
|
|
||
|
|
|
||
|
|
def get_superpower(id: int) -> Optional[dict]:
|
||
|
|
"""Get a single superpower by ID.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
id: Superpower ID (1-152)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Superpower dict with id, name, boost_percent, description, band
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
return _superpowers.get(id) if _superpowers else None
|
||
|
|
|
||
|
|
|
||
|
|
def list_superpower_ids() -> List[int]:
|
||
|
|
"""List all superpower IDs (sorted).
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of 152 superpower IDs [1, 2, ..., 152]
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
return [sp["id"] for sp in _superpowers_list] if _superpowers_list else []
|
||
|
|
|
||
|
|
|
||
|
|
def get_superpowers_by_band(band: str) -> List[dict]:
|
||
|
|
"""Get all superpowers in a band.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
band: Band identifier ("A", "B", "C", "D")
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of superpower dicts in that band
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
if not _superpowers_list:
|
||
|
|
return []
|
||
|
|
|
||
|
|
return [sp for sp in _superpowers_list if sp.get("band") == band]
|
||
|
|
|
||
|
|
|
||
|
|
def get_superpowers_by_bands(bands: List[str]) -> List[dict]:
|
||
|
|
"""Get all superpowers in multiple bands.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
bands: List of band identifiers
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of superpower dicts in those bands
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
if not _superpowers_list:
|
||
|
|
return []
|
||
|
|
|
||
|
|
return [sp for sp in _superpowers_list if sp.get("band") in bands]
|
||
|
|
|
||
|
|
|
||
|
|
def calculate_boost(superpower_ids: List[int]) -> float:
|
||
|
|
"""Calculate aggregate boost from superpower IDs.
|
||
|
|
|
||
|
|
Formula: power_boost = 1.0 + Σ(boost_percent) / 100.0
|
||
|
|
|
||
|
|
Args:
|
||
|
|
superpower_ids: List of superpower IDs
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Aggregate boost multiplier (e.g., 2.0 = 200% effectiveness)
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
total_boost = 0.0
|
||
|
|
for id in superpower_ids:
|
||
|
|
sp = _superpowers.get(id)
|
||
|
|
if sp:
|
||
|
|
total_boost += sp.get("boost_percent", 0) / 100.0
|
||
|
|
|
||
|
|
return 1.0 + total_boost
|
||
|
|
|
||
|
|
|
||
|
|
def search_superpowers(query: str, fields: Optional[List[str]] = None, limit: int = 20) -> List[dict]:
|
||
|
|
"""Search superpowers by query.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
query: Search string (case-insensitive substring)
|
||
|
|
fields: Fields to search (default: ["name", "description"])
|
||
|
|
limit: Maximum results
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of matching superpower dicts
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
if not _superpowers_list:
|
||
|
|
return []
|
||
|
|
|
||
|
|
if fields is None:
|
||
|
|
fields = ["name", "description"]
|
||
|
|
|
||
|
|
query_lower = query.lower()
|
||
|
|
results = []
|
||
|
|
|
||
|
|
for sp in _superpowers_list:
|
||
|
|
for field in fields:
|
||
|
|
value = sp.get(field, "")
|
||
|
|
if isinstance(value, str) and query_lower in value.lower():
|
||
|
|
results.append(sp)
|
||
|
|
break
|
||
|
|
|
||
|
|
if len(results) >= limit:
|
||
|
|
break
|
||
|
|
|
||
|
|
return results
|
||
|
|
|
||
|
|
|
||
|
|
def super_stats() -> dict:
|
||
|
|
"""Get statistics about the superpower registry.
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Dict with total, bands, loaded status, etc.
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
if not _superpowers_list:
|
||
|
|
return {
|
||
|
|
"total": 0,
|
||
|
|
"bands": {},
|
||
|
|
"loaded": False,
|
||
|
|
}
|
||
|
|
|
||
|
|
# Count by band
|
||
|
|
band_counts = {}
|
||
|
|
for sp in _superpowers_list:
|
||
|
|
band = sp.get("band")
|
||
|
|
band_counts[band] = band_counts.get(band, 0) + 1
|
||
|
|
|
||
|
|
return {
|
||
|
|
"total": len(_superpowers_list),
|
||
|
|
"band_counts": band_counts,
|
||
|
|
"loaded": _loaded,
|
||
|
|
"load_path": str(_load_path) if _load_path else None,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
# Global singleton instance
|
||
|
|
_adapter_instance: Optional[Any] = None
|
||
|
|
|
||
|
|
|
||
|
|
def get_superpower_names(superpower_ids: List[int]) -> List[str]:
|
||
|
|
"""Get names for a list of superpower IDs.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
superpower_ids: List of IDs
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
List of superpower names
|
||
|
|
"""
|
||
|
|
if not _loaded:
|
||
|
|
load_all_superpowers()
|
||
|
|
|
||
|
|
names = []
|
||
|
|
for id in superpower_ids:
|
||
|
|
sp = _superpowers.get(id)
|
||
|
|
if sp:
|
||
|
|
names.append(sp.get("name", f"SP{id}"))
|
||
|
|
|
||
|
|
return names
|