#!/usr/bin/env python3
"""
BattleCalc: Damage calculator.
Attack x Defense x Affinity x Crit = Damage. O(1) per hit.

No dependencies. Pure Python.
"""
import time
import random

# === THE LATTICE: damage matrix ===
AFFINITY = {
    "fire":      {"strong": ["nature", "ice"],      "weak": ["water", "earth"],     "neutral": ["lightning", "holy", "dark", "arcane", "physical"]},
    "ice":       {"strong": ["nature", "fire"],     "weak": ["fire", "lightning"],  "neutral": ["water", "earth", "holy", "dark", "arcane", "physical"]},
    "lightning": {"strong": ["water", "earth"],     "weak": ["earth", "lightning"], "neutral": ["fire", "ice", "holy", "dark", "arcane", "physical"]},
    "holy":      {"strong": ["dark", "shadow"],     "weak": ["dark", "arcane"],     "neutral": ["fire", "ice", "water", "earth", "lightning", "physical"]},
    "dark":      {"strong": ["holy", "arcane"],     "weak": ["holy", "light"],      "neutral": ["fire", "ice", "water", "earth", "lightning", "physical"]},
    "earth":     {"strong": ["lightning", "ice"],   "weak": ["water", "wind"],      "neutral": ["fire", "holy", "dark", "arcane", "physical"]},
    "water":     {"strong": ["fire", "earth"],      "weak": ["lightning", "ice"],   "neutral": ["holy", "dark", "arcane", "physical"]},
    "wind":      {"strong": ["earth", "lightning"], "weak": ["ice", "earth"],       "neutral": ["fire", "holy", "dark", "arcane", "physical"]},
    "nature":    {"strong": ["water", "earth"],     "weak": ["fire", "ice"],        "neutral": ["lightning", "holy", "dark", "arcane", "physical"]},
    "arcane":    {"strong": ["holy", "dark"],       "weak": ["dark", "physical"],   "neutral": ["fire", "ice", "water", "earth", "lightning"]},
    "shadow":    {"strong": ["holy", "light"],      "weak": ["holy", "light"],      "neutral": ["fire", "ice", "water", "earth", "lightning", "physical"]},
    "physical":  {"strong": ["arcane"],             "weak": ["arcane"],             "neutral": ["fire", "ice", "water", "earth", "lightning", "holy", "dark"]}
}

CRIT_TIERS = {
    "common":    {"chance": 0.05, "mult": 1.5},
    "uncommon":  {"chance": 0.10, "mult": 2.0},
    "rare":      {"chance": 0.20, "mult": 2.5},
    "epic":      {"chance": 0.30, "mult": 3.0},
    "legendary": {"chance": 0.50, "mult": 4.0}
}

def affinity_modifier(attacker: str, defender: str) -> float:
    """O(1) affinity modifier lookup."""
    if attacker not in AFFINITY:
        return 1.0
    if defender in AFFINITY[attacker]["strong"]:
        return 2.0
    if defender in AFFINITY[attacker]["weak"]:
        return 0.5
    return 1.0

def calc_damage(attacker_atk: int, defender_def: int, attacker_aff: str, defender_aff: str, crit_tier: str = "common") -> dict:
    """O(1) damage calculation."""
    base = max(1, attacker_atk - defender_def // 2)
    aff_mod = affinity_modifier(attacker_aff, defender_aff)
    crit_data = CRIT_TIERS.get(crit_tier, CRIT_TIERS["common"])
    crit_roll = random.random()
    is_crit = crit_roll < crit_data["chance"]
    crit_mult = crit_data["mult"] if is_crit else 1.0
    final = int(base * aff_mod * crit_mult)

    return {
        "base": base,
        "affinity_modifier": aff_mod,
        "is_crit": is_crit,
        "crit_mult": crit_mult,
        "final": max(1, final),
        "effect": "CRITICAL HIT!" if is_crit else None
    }

def describe_affinity(attacker: str, defender: str) -> str:
    """O(1) affinity matchup description."""
    mod = affinity_modifier(attacker, defender)
    if mod == 2.0:
        return f"{attacker.upper()} is SUPER EFFECTIVE against {defender}!"
    if mod == 0.5:
        return f"{attacker.upper()} is WEAK against {defender}."
    return f"{attacker.upper()} is neutral against {defender}."

# === DEMO ===
def demo():
    print("=" * 50)
    print("  BattleCalc: Damage calculator, O(1) per hit")
    print("=" * 50)
    print()
    print("  Combat scenario: Hero vs Dragon")
    print("  Hero: ATK 50, AFF fire, Sword (epic crit)")
    print("  Dragon: DEF 30, AFF ice")
    print()

    scenarios = [
        ("fire", "ice", 2.0, "super effective"),
        ("fire", "water", 0.5, "resisted"),
        ("fire", "fire", 1.0, "neutral"),
        ("holy", "dark", 2.0, "super effective"),
        ("dark", "holy", 0.5, "resisted"),
        ("ice", "fire", 0.5, "resisted"),
        ("lightning", "water", 2.0, "super effective"),
        ("shadow", "holy", 0.5, "resisted")
    ]

    for aff_a, aff_d, expected, desc in scenarios:
        start = time.perf_counter_ns()
        mod = affinity_modifier(aff_a, aff_d)
        elapsed = time.perf_counter_ns() - start

        result = "OK" if mod == expected else "MISMATCH"
        print(f"  {aff_a:10s} vs {aff_d:10s} = x{mod}  ({desc:15s}) [{elapsed}ns] [{result}]")

    print()
    print("  Battle rounds:")
    print()
    for i in range(8):
        start = time.perf_counter_ns()
        result = calc_damage(attacker_atk=50, defender_def=30, attacker_aff="fire", defender_aff="ice", crit_tier="epic")
        elapsed = time.perf_counter_ns() - start
        crit_str = " [CRIT!]" if result["is_crit"] else ""
        print(f"  Round {i+1}: {result['final']:4d} damage{crit_str}  (base:{result['base']} x{result['affinity_modifier']} x{result['crit_mult']}) [{elapsed}ns]")

    print()
    print("=" * 50)
    print("  12 affinities, 5 crit tiers. O(1) per hit. No formulas.")
    print("=" * 50)

if __name__ == "__main__":
    demo()
