Files
2125_GCE/glyphs/superpower_assigner.py
T
2026-07-09 12:54:44 -04:00

244 lines
7.0 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Superpower Assignment Algorithm.
Assigns superpowers to glyphs based on:
1. Specialized type (if any)
2. Dynamic metrics calculation (5-25 range for G002-G600)
3. Band eligibility
4. Scoring and ranking
"""
import hashlib
from typing import Dict, List, Any, Optional
from .superpower_registry import (
load_all_superpowers,
get_superpower,
get_superpowers_by_band,
get_superpowers_by_bands,
)
from .specialized_types import get_specialized_type, get_type_config, SPECIALIZED_GLYPH_TYPES
def calculate_power_count(metrics: Dict[str, Any], specialized_type: str = "") -> int:
"""Calculate number of superpowers for a glyph (5-25 range).
Formula: power_count = 5 + int((avg_metric / 100) * 20)
Args:
metrics: Glyph metrics dict (power, complexity, resonance, stability, connectivity, affinity)
specialized_type: Optional specialized type name
Returns:
Power count (5-25)
"""
# Get metric values
values = [
metrics.get("power", 50),
metrics.get("complexity", 50),
metrics.get("resonance", 50),
metrics.get("stability", 50),
metrics.get("connectivity", 50),
metrics.get("affinity", 50),
]
avg_metric = sum(values) / len(values)
# Calculate base count (5-25 range)
base_count = 5 + int((avg_metric / 100) * 20)
# Clamp to range
base_count = max(5, min(25, base_count))
# Override for specialized types
if specialized_type:
type_config = get_type_config(specialized_type)
min_powers = type_config.get("min_powers", 5)
max_powers = type_config.get("max_powers", 25)
# For aether_node, always 152
if specialized_type == "aether_node":
return 152
# Clamp to type's range
base_count = max(min_powers, min(max_powers, base_count))
return base_count
def get_eligible_bands(glyph_id: str, specialized_type: str = "") -> List[str]:
"""Get eligible superpower bands for a glyph.
Args:
glyph_id: Glyph ID
specialized_type: Optional specialized type
Returns:
List of band identifiers (e.g., ["A", "B"])
"""
# Aether node gets all bands
if specialized_type == "aether_node":
return ["A", "B", "C", "D"]
# Monument grade gets all bands
if specialized_type == "monument_grade_equilibrium":
return ["A", "B", "C", "D"]
# Get type config
if specialized_type:
type_config = get_type_config(specialized_type)
preferred_ids = type_config.get("preferred_superpower_ids", [])
# Determine bands from preferred IDs
bands = set()
for id in preferred_ids:
if id <= 15:
bands.add("A")
elif id <= 45:
bands.add("B")
elif id <= 76:
bands.add("C")
else:
bands.add("D")
return list(bands)
# Default: based on glyph ID (family/tier)
glyph_num = int(glyph_id[1:]) if glyph_id.startswith("G") else 1
tier = ((glyph_num - 1) // 10) + 1
if tier <= 15:
return ["A", "B"]
elif tier <= 30:
return ["B", "C"]
elif tier <= 45:
return ["C", "D"]
else:
return ["D", "C"]
def score_superpower(
superpower: Dict[str, Any],
glyph_id: str,
metrics: Dict[str, Any],
specialized_type: str = ""
) -> float:
"""Score a superpower for a glyph.
Formula: score = 0.45 × metrics + 0.35 × type_bias + 0.15 × boost% + 0.05 × hash
Args:
superpower: Superpower dict
glyph_id: Glyph ID
metrics: Glyph metrics
specialized_type: Optional specialized type
Returns:
Score (0-100)
"""
# Metrics component (45%)
avg_metric = sum(metrics.values()) / len(metrics) if metrics else 50
metrics_score = avg_metric * 0.45
# Type bias component (35%)
type_bias = 50 # Default neutral
if specialized_type:
type_config = get_type_config(specialized_type)
preferred_ids = type_config.get("preferred_superpower_ids", [])
if superpower["id"] in preferred_ids:
type_bias = 100 # Preferred
else:
type_bias = 25 # Not preferred
type_score = type_bias * 0.35
# Boost component (15%)
boost = superpower.get("boost_percent", 0)
boost_score = (boost / 100) * 0.15 * 100 # Normalize to 0-100 scale
# Hash component (5%) - deterministic variety
hash_input = f"{glyph_id}_{superpower['id']}"
hash_value = int(hashlib.md5(hash_input.encode()).hexdigest()[:8], 16)
hash_score = (hash_value / 0xFFFFFFFF) * 100 * 0.05
return metrics_score + type_score + boost_score + hash_score
def assign_superpowers(
glyph_id: str,
metrics: Dict[str, Any],
specialized_type: str = "",
category: str = ""
) -> List[int]:
"""Assign superpowers to a glyph.
Args:
glyph_id: Glyph ID (e.g., "G001")
metrics: Glyph metrics dict
specialized_type: Optional specialized type
category: Optional category
Returns:
List of superpower IDs
"""
# Load superpowers if not loaded
try:
load_all_superpowers()
except FileNotFoundError:
# If superpowers not loaded, return empty
return []
# Special case: G001 / aether_node gets all 152
if glyph_id == "G001" or specialized_type == "aether_node":
return list(range(1, 153)) # 1-152
# Determine specialized type if not provided
if not specialized_type:
specialized_type = get_specialized_type(glyph_id, metrics, category)
# Calculate power count
power_count = calculate_power_count(metrics, specialized_type)
# Get eligible bands
eligible_bands = get_eligible_bands(glyph_id, specialized_type)
# Get eligible superpowers
eligible_powers = get_superpowers_by_bands(eligible_bands)
# Score and rank
scored = []
for sp in eligible_powers:
score = score_superpower(sp, glyph_id, metrics, specialized_type)
scored.append((score, sp))
# Sort by score descending
scored.sort(key=lambda x: x[0], reverse=True)
# Take top N
result = [sp["id"] for score, sp in scored[:power_count]]
return result
def assign_all_glyphs(glyphs: List[Dict[str, Any]]) -> Dict[str, List[int]]:
"""Assign superpowers to all glyphs.
Args:
glyphs: List of glyph dicts
Returns:
Dict mapping glyph_id to superpower IDs
"""
result = {}
for glyph in glyphs:
glyph_id = glyph.get("id", "")
metrics = glyph.get("originalMetrics", {})
category = glyph.get("category", "")
# Get specialized type
specialized_type = get_specialized_type(glyph_id, metrics, category)
# Assign superpowers
superpower_ids = assign_superpowers(glyph_id, metrics, specialized_type, category)
result[glyph_id] = superpower_ids
return result