#!/usr/bin/env python3 """Validate arithmetic and single-authority boundaries in product contracts.""" from __future__ import annotations import math import re from pathlib import Path ROOT = Path(__file__).resolve().parents[1] PRODUCT = ROOT / "docs" def cache_loss(lines: list[tuple[int, bool]], fraction: float) -> tuple[int, list[int]]: total = sum(value for value, _ in lines) target = math.ceil(total * fraction) removed = 0 selected: list[int] = [] remaining: list[tuple[int, int]] = [] for index, (value, splittable) in enumerate(lines): if splittable: take = min(value, max(0, target - removed)) removed += take if take: selected.append(index) else: remaining.append((index, value)) for index, value in remaining: current_error = abs(target - removed) next_error = abs(target - (removed + value)) if next_error <= current_error: removed += value selected.append(index) if target > 0 and removed == 0 and remaining: index, value = min(remaining, key=lambda pair: (abs(pair[1] - target), pair[0])) removed = value selected = [index] return removed, selected def maximum_gap(duration: int, timestamps: list[int]) -> int: points = [0, *timestamps, duration] return max(right - left for left, right in zip(points, points[1:])) def main() -> None: # The six-authority contract is intentionally consolidated in the current # review set; keep this validator pointed at the public authority files # instead of the retired docs/product split. economy = (PRODUCT / "02_GAMEPLAY_AND_BALANCE.md").read_text() battle = economy product = (PRODUCT / "01_GAME_DESIGN.md").read_text() save = economy region_totals = [ int(match.group(1)) for match in re.finditer( r"^\| [1-8] \| [0-9]+ min \| [0-9]+ min \| ([0-9]+) min \| .*? \|$", economy, re.MULTILINE, ) ] assert len(region_totals) == 8, region_totals assert sum(region_totals) == 972 traces = { "normal": (1200, [25, 90, 175, 280, 325, 390, 475, 580, 625, 690, 775, 880, 925, 990, 1075, 1180], 105), "active": (1200, [20, 75, 135, 205, 310, 350, 410, 475, 535, 640, 680, 740, 805, 865, 970, 1015, 1070, 1135], 105), "efficient": (1200, [35, 125, 240, 295, 385, 500, 555, 645, 760, 815, 905, 1020, 1075, 1165], 115), "adversarial": (1200, [30, 80, 145, 270, 320, 375, 435, 590, 640, 700, 755, 920, 970, 1030, 1095], 165), "returning": (1200, [25, 85, 135, 250, 300, 355, 415, 560, 620, 680, 755, 900, 950, 1015, 1085, 1190], 145), } for name, (duration, timestamps, expected_gap) in traces.items(): timestamp_literal = "[" + ",".join(str(value) for value in timestamps) + "]" assert f"| {name.title()} |" in economy assert f"`{timestamp_literal}` | {expected_gap} s |" in economy observed_gaps = { name: maximum_gap(duration, timestamps) for name, (duration, timestamps, _) in traces.items() } assert observed_gaps == {name: expected for name, (*_, expected) in traces.items()} recruit_cost = min(250, 90 + 12 * 8 + 5 * max(0, 29 - 6)) counter_cost = min(180, 55 + 12 * 8 + 8 * 7 + 10 * 2) first_clear_xp = math.floor((50 + 25 * 2 + 10 * 3 + 35 + 20) * 1.15) assert (recruit_cost, counter_cost, first_clear_xp) == (250, 180, 212) assert math.floor(14 / 2) - 4 == 3 assert 4 * (14 - 1) == 52 cache_vectors = [ ([(30, False), (30, False), (40, False)], 0.25, 30), ([(100, True)], 0.25, 25), ([(40, False)], 0.25, 40), ([(18, False), (18, False)], 0.50, 18), ([(10, True), (30, False), (40, False)], 0.50, 40), ] for lines, fraction, expected in cache_vectors: actual, _ = cache_loss(lines, fraction) assert actual == expected, (lines, fraction, actual, expected) raw = 125 mitigated = raw * (100 / (100 + 500)) * 0.10 damage = max(1, math.ceil(0.04 * raw), math.floor(mitigated)) assert math.isclose(mitigated, 2.0833333333333335) assert damage == 5 failure_rows = ( "| Wayfinder | 0 Coin | `min(2,current Coin)` | `min(4,current Coin)` |", "| Standard | `min(2,current Coin)` | `min(4,current Coin)` | `min(6,current Coin)` |", "| Oathbound | `min(4,current Coin)` | `min(6,current Coin)` | `min(8,current Coin)` |", ) assert all(value in save for value in failure_rows) assert "status: pending | applied | rejected" in save assert "immediate child" not in save assert "crit = can_crit and critical_roll_bp < CritChanceBp" in battle assert "DamageFloor = ceil(0.04 * RawDamage)" in battle assert "Every 20-minute profile trace stays within its stated decision-gap bound." in product print( "ABG_DESIGN_CONTRACTS_OK " f"contract_minutes={sum(region_totals)} " f"gaps={observed_gaps} " f"recruit_cap={recruit_cost} counter_cap={counter_cost} " f"cache_vectors={len(cache_vectors)} damage_floor={damage}" ) if __name__ == "__main__": main()