extends SceneTree const E = preload("res://world/owniverse/math/exact.gd") const State = preload("res://world/owniverse/civilization_state.gd") var checks: int = 0 var failures: int = 0 func _initialize(): call_deferred("run") func check(value: bool, message: String) -> void: checks += 1 if not value: failures += 1 push_error(message) func fixture(): var world = load("res://world/owniverse/owniverse.gd").new() world.catalog.load_definitions(root.get_node("Global").OwniverseEntities) for id in world.catalog.entities: world.state.amount[id] = 0 world.state.stats.total_amount[id] = 0 for id in ["RD", "OD", "YD", "WYD", "BG", "TWD", "SNS", "MGT", "BH25", "SMBH", "SCD", "SCS", "SCM", "SCL", "SCG", "GED", "GES", "GEM", "GEL", "GEG", "LGG", "LSC", "LGF", "LGW", "LUB"]: if world.state.amount.has(id): world.state.amount[id] = E.of("1e60") return world func dispose(world): for entity in world.catalog.entities.values(): entity.free() world.free() func population(world) -> Variant: var result: Variant = 0 for id in world.ledger.physical_amounts(world.state): if root.get_node("Global").CivilizationRules.has(id) or world.catalog.entities[id].group_as == "Planet": result = E.add(result, world.ledger.physical_amount(world.state, id)) return result func invariants(world) -> void: var physical: Dictionary = world.ledger.physical_amounts(world.state) for id in physical: check(E.ge(physical[id], 0) and E.eq(physical[id], E.floor_value(physical[id])), "Physical counts remain nonnegative whole: " + id) for id in world.state.ethics: var total: float = 0.0 for value in world.state.ethics[id].values(): check(is_finite(value) and value >= 0.0 and value <= 1.0, "Finite normalized ethic shares") total += value check(absf(total - 1.0) < 1e-12, "Ethic shares sum to one: " + id) func configure(rules: Dictionary, progress: float, degradation: float, span: int = 100) -> void: for id in rules: if rules[id].ethics.is_empty(): continue rules[id].span = span rules[id].fights = false rules[id].colonizes = false rules[id].diplomacy = false for row in rules[id].ethics.values(): row.progress = progress row.degradation = degradation row.war = 0 row.colonization = 0 row.fall = rules[id].previous + ":1" func run(): var global: Node = root.get_node("Global") var baseline: Dictionary = global.CivilizationRules.duplicate(true) configure(global.CivilizationRules, 1.0, 0.0) var world = fixture() var count: Variant = E.of("10000000000000000000000000000000000000007") world.state.amount["C0.2"] = count world.state.ethics["C0.2"] = State.empty_profile() world.civilization_flow.evolve(world.state, 0, 100) check(E.eq(population(world), count), "Continuous chain conserves arbitrary-size whole population") check(absf(E.approx(E.divide(world.state.amount["C0.2"], count)) - exp(-1.0)) < 1e-12, "Survival follows the exponential interval law") check(E.gt(world.state.amount["C0.4"], 0), "One interval includes multiple successive stages") check(not world.state.batches.has("C0.2") and not world.state.batches.has("C0.3"), "Civilizations do not create age batches") invariants(world) var half = fixture() half.state.amount["C0.2"] = count half.state.ethics["C0.2"] = State.empty_profile() half.civilization_flow.evolve(half.state, 0, 50) half.civilization_flow.evolve(half.state, 50, 100) for id in global.CivilizationRules: check(E.approx(E.divide(E.absolute(E.subtract(world.state.amount.get(id, 0), half.state.amount.get(id, 0))), count)) < 1e-12, "Splitting the interval preserves continuous dynamics: " + id) dispose(half) dispose(world) configure(global.CivilizationRules, 0.0, 0.0) world = fixture() world.state.amount["C0.2"] = 10001 world.state.ethics["C0.2"] = {"m": 1.0, "l": 0.0, "w": 0.0, "d": 0.0, "c": 0.0, "b": 0.0} world.civilization_flow.evolve(world.state, 0, 100) check(E.eq(world.state.amount["C0.2"], 10001), "Disabled stage movement retains every individual") check(world.state.ethics["C0.2"].m < 1.0 and world.state.ethics["C0.2"].m > 0.9, "Analytical drift changes shares without materializing populations") invariants(world) dispose(world) configure(global.CivilizationRules, 1.0, 0.0) world = fixture() for id in ["RD", "OD", "YD", "WYD"]: world.state.amount[id] = 0 world.state.amount["C1.9"] = 101 world.civilization_flow.evolve(world.state, 0, 100) check(E.eq(world.state.amount["C1.9"], 101), "Unfunded interstellar flows return to their source") check(E.eq(population(world), 101), "Resource admission conserves exact population") dispose(world) configure(global.CivilizationRules, 0.0, 0.0, 1) world = fixture() world.state.amount["C1.6"] = 60000 world.state.binds.RD = {"HP": 5, "RP": 5, "GG": 5, "AB": 5} global.CivilizationRules["C1.6"].colonizes = true for row in global.CivilizationRules["C1.6"].ethics.values(): row.colonization = 1 world.civilization_flow.evolve(world.state, 0, 1) check(E.eq(world.state.civilization_stats.colonies, 20), "Integrated colonization is limited by real planet stock") check(E.eq(world.state.binds.RD.get("C1.4", 0), 20), "Colonies retain the target stellar owner") check(E.eq(population(world), 60020), "Colonization creates precisely the consumed number of occupied worlds") var physical: Dictionary = world.ledger.physical_amounts(world.state) for resource in ["HP", "RP", "GG", "AB"]: check(E.eq(physical[resource], 0), "Planet stock consumed once: " + resource) invariants(world) dispose(world) global.CivilizationRules = baseline.duplicate(true) world = fixture() world.state.amount["C2.0"] = 60000 world.state.amount["C1.9"] = 10000 world.civilization_flow.evolve(world.state, 0, 20000) check(E.gt(world.state.civilization_stats.wars, 0), "War rewards integrate encounters during the interval") check(E.gt(world.state.civilization_stats.collateral, 0), "Collateral damage reaches previous stages") check(E.eq(population(world), 70000), "War outcomes and collateral conserve occupied-world counts") invariants(world) var restored = fixture() restored.state = E.decode(E.encode(world.state)) world.civilization_flow.evolve(world.state, 20000, 30000) restored.civilization_flow.evolve(restored.state, 20000, 30000) check(E.eq(world.state, restored.state), "Save resume reproduces flow populations and carries") dispose(restored) dispose(world) global.CivilizationRules = baseline print("Civilization flows: %d checks, %d failures" % [checks, failures]) quit(1 if failures else 0)