3fcc7211ce
- Added `civilization_flow.gd` to manage continuous expected flows and resource allocation within civilizations. - Introduced `allocate_profiled` and `whole_quotas` functions in `quantity_math.gd` for precise stock allocation based on float shares. - Enhanced `credit` logic in `object_ledger.gd` to handle production remainders more effectively. - Updated `append_batch` in `object_batches.gd` to prevent batch creation for invalid IDs. - Introduced `ratio_approx` function in `exact.gd` for better handling of float profiles. - Added a benchmark script `benchmark_civilization_flows.gd` to evaluate the performance of civilization flow mechanics. - Refactored various functions to improve clarity and efficiency in handling resource flows and allocations.
112 lines
6.1 KiB
GDScript
112 lines
6.1 KiB
GDScript
extends "res://tests/benchmark_large_star_cohorts.gd"
|
|
## Fresh all-object stress world with many valid cohorts sharing death cycles.
|
|
const Fixture = preload("res://tests/fixtures/civilization_benchmark.gd")
|
|
var death_cycles: int = 40
|
|
var fragments_per_cycle: int = 8
|
|
|
|
func _initialize():
|
|
artifact_prefix = "many_objects_fragmented_stars"
|
|
cycles = 40
|
|
call_deferred("run")
|
|
|
|
func make_state(_initial: Dictionary, use_cycles: bool) -> Dictionary:
|
|
var world = load("res://world/owniverse/owniverse.gd").new()
|
|
world.catalog.load_definitions(root.get_node("Global").OwniverseEntities)
|
|
var global: Node = root.get_node("Global")
|
|
Fixture.seed(world, global)
|
|
world.batches.cycle_stars = use_cycles
|
|
var anchor: Variant = E.of("1e12")
|
|
var stock: Variant = E.of("1e60")
|
|
var planets: Variant = E.of("1e28")
|
|
world.state.time = {"datetime": anchor, "year": anchor}
|
|
world.state.energy = E.of("1e100")
|
|
world.state.batches = {}
|
|
world.state.batch_origins = {}
|
|
world.state.binds = {}
|
|
world.state.ethics = {}
|
|
for id in world.catalog.entities:
|
|
world.state.amount[id] = 0 if world.catalog.is_group(id) else (planets if world.catalog.entities[id].group_as == "Planet" else stock)
|
|
world.state.stats.total_amount[id] = world.state.amount[id]
|
|
# Keep the synthetic universe sparse enough for blast damage to coexist
|
|
# with all life stages instead of sterilizing them on the first cycle.
|
|
world.state.amount.S = E.of("1e300")
|
|
world.state.amount.H = E.of("1e80")
|
|
world.state.amount.He = E.of("1e79")
|
|
world.state.amount.Met = E.of("1e79")
|
|
var dwarfs: Array = ["RD", "OD", "YD", "WYD"]
|
|
var stages: int = 0
|
|
for id in global.CivilizationRules:
|
|
world.state.amount[id] = planets
|
|
var weights: Array = [0.35, 0.2, 0.15, 0.12, 0.1, 0.08]
|
|
var profile: Dictionary = {}
|
|
var ethics: Array = ["m", "l", "w", "d", "c", "b"]
|
|
for e in 6: profile[ethics[e]] = weights[(e + stages) % 6]
|
|
world.state.ethics[id] = profile
|
|
stages += 1
|
|
# Planetary stages are hosted by all four kinds of dwarf stars.
|
|
for id in world.catalog.entities:
|
|
if world.catalog.entities[id].group_as != "Planet" or world.catalog.is_group(id): continue
|
|
if global.CivilizationRules.has(id) and str(global.CivilizationRules[id].home) != "": continue
|
|
var held: Variant = E.divide(world.state.amount[id], 4)
|
|
world.state.amount[id] = 0
|
|
for dwarf in dwarfs:
|
|
if not world.state.binds.has(dwarf): world.state.binds[dwarf] = {}
|
|
world.state.binds[dwarf][id] = held
|
|
# Bound stars remain physical and continue producing inside structures.
|
|
var structures: Array = ["SCD", "SCM", "SCG", "GEM", "GSD"]
|
|
var stellar_types: int = 0
|
|
for id in world.catalog.entities:
|
|
if world.catalog.entities[id].group_as != "Star" or E.le(world.catalog.entities[id].lifespan, 0): continue
|
|
var held: Variant = E.divide(stock, 4)
|
|
var owner: String = structures[stellar_types % structures.size()]
|
|
if not world.state.binds.has(owner): world.state.binds[owner] = {}
|
|
world.state.binds[owner][id] = held
|
|
world.state.amount[id] = E.subtract(stock, held)
|
|
stellar_types += 1
|
|
world.ledger.refresh_groups(world.state)
|
|
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
|
var active_types: int = 0
|
|
for id in world.catalog.entities:
|
|
if world.catalog.is_group(id) or E.le(physical.get(id, 0), 0): continue
|
|
active_types += 1
|
|
world.state.stats.total_amount[id] = physical[id]
|
|
var entity = world.catalog.entities[id]
|
|
if E.le(entity.lifespan, 0) or global.CivilizationRules.has(id): continue
|
|
if entity.group_as != "Star":
|
|
world.batches.append_batch(world.state, id, physical[id], anchor, anchor)
|
|
continue
|
|
# Valid but deliberately fragmented historical age cohorts. The test
|
|
# stresses merge eligibility, rather than claiming these are produced
|
|
# by the ordinary steady-state creation path with one fixed origin.
|
|
var span: Variant = world.batches.effective_lifespan(world.state, id)
|
|
var count: int = death_cycles * fragments_per_cycle
|
|
var assigned: Variant = 0
|
|
world.state.batches[id] = []
|
|
for n in count:
|
|
var cycle: int = n / fragments_per_cycle
|
|
var fragment: int = n % fragments_per_cycle
|
|
var start: Variant = E.add(anchor, cycle * 20000 + (fragment + 1) * 1800)
|
|
var end: Variant = E.add(start, 500)
|
|
var quota: Variant = E.floor_value(E.mul_div(physical[id], n + 1, count))
|
|
world.state.batches[id].append({"amount": E.subtract(quota, assigned), "created_from": E.subtract(start, span), "created_to": E.subtract(end, span), "start_year": start, "end_year": end})
|
|
assigned = quota
|
|
assert(E.eq(assigned, physical[id]))
|
|
world.state.batch_origins[id] = world.state.batches[id][0].created_from
|
|
var raw_cohorts: int = cohort_count(world)
|
|
world.batches.coarsen(world.state)
|
|
var payload: Dictionary = {"format_version": 6, "app_version": "0.7.0", "ui": {"current_page": 0}, "state": world.state,
|
|
"simulation": {"active_clock": {}, "requested_clock": {}, "productions": [], "speed": 6}}
|
|
var error: String = root.get_node("SaveManager").validate_data(payload)
|
|
assert(error.is_empty(), error)
|
|
var suffix: String = "cycle" if use_cycles else "legacy"
|
|
var path: String = "res://tests/artifacts/%s_%s.adventure.json" % [artifact_prefix, suffix]
|
|
var output = FileAccess.open(path, FileAccess.WRITE)
|
|
assert(output != null)
|
|
output.store_string(JSON.stringify(E.encode(payload), " ", true, true))
|
|
output.close()
|
|
var restored: Dictionary = E.decode(restore_json_integers(JSON.parse_string(FileAccess.get_file_as_string(path))))
|
|
assert(E.eq(restored.state, world.state), "Save roundtrip changed the stress state")
|
|
print("Generated %s: active_types=%d civilization_stages=%d stellar_types=%d raw_cohorts=%d stored_stellar_cohorts=%d" % [suffix, active_types, stages, stellar_types, raw_cohorts, cohort_count(world)])
|
|
dispose(world)
|
|
return restored.state
|