Initial commit: 2125_GCE project

This commit is contained in:
GlyphRunner System
2026-07-09 12:54:44 -04:00
parent c3a826b65c
commit ae13f78c22
299 changed files with 124289 additions and 1031 deletions
+244
View File
@@ -0,0 +1,244 @@
"""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