#!/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" / "product" 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: economy = (PRODUCT / "ECONOMY_AND_BALANCE.md").read_text() battle = (PRODUCT / "SYSTEMS_AND_BATTLE.md").read_text() product = (PRODUCT / "GAME_PRODUCT_CONTRACT.md").read_text() save = (PRODUCT / "SAVE_AND_FAILURE_CONTRACT.md").read_text() chapter_totals = [ int(match.group(1)) for match in re.finditer( r"^\| [1-8] \| .*? \| ([0-9]+) \| [0-9]+ \| [0-7](?:-[0-7])? \|$", economy, re.MULTILINE, ) ] assert len(chapter_totals) == 8, chapter_totals assert sum(chapter_totals) == 1700 traces = { "normal": (1200, [90, 240, 390, 540, 690, 840, 990, 1140], 150), "active": (1200, [60, 170, 280, 390, 500, 610, 720, 830, 940, 1050, 1160], 110), "efficient": (1200, [120, 280, 440, 600, 760, 920, 1080], 160), "adversarial": (1200, [90, 220, 350, 480, 610, 740, 870, 1000, 1130], 130), "returning": (720, [60, 200, 340, 480, 620], 140), } for duration, timestamps, expected_gap in traces.values(): timestamp_literal = "[" + ",".join(str(value) for value in timestamps) + "]" assert f"| {duration:,} | `{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 = 120 mitigated = raw * (100 / (100 + 500)) * 0.10 damage = max(1, math.ceil(0.05 * raw), math.floor(mitigated)) assert math.isclose(mitigated, 2.0) assert damage == 6 failure_values = ("Lose 15%", "Lose 25%", "Lose 30%", "Lose 50%", "Lose 60%", "Lose 100%") assert all(value in save for value in failure_values) assert all(value not in economy for value in failure_values) assert "status: pending | applied | rejected" not in save assert "immediate child" not in save assert "ResolvedCritMultiplier" in battle assert "DamageFloor = ceil(0.05 * Raw)" in battle assert "active profile's\nmaximum gap" in product print( "ABG_DESIGN_CONTRACTS_OK " f"chapter_minutes={sum(chapter_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()