Initial commit: 2125_GCE project
This commit is contained in:
Executable
+244
@@ -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
|
||||
Reference in New Issue
Block a user