Refactor capacity enforcement and production logic

- Updated `enforce_capacity` function to accept an optional `physical` parameter, allowing for more flexible handling of physical amounts.
- Modified `initialize_life_batches` to return physical amounts after enforcing capacity, improving data flow.
- Enhanced `try_production` to utilize shared physical amounts, reducing redundant calculations.
- Adjusted `stellar_environment` to accept an optional `quantities` parameter, streamlining its usage.
- Added new test results for physical totals to validate performance improvements across various scenarios.
This commit is contained in:
Kirill
2026-10-08 21:10:36 +03:00
parent 9357f757a9
commit c42940369a
13 changed files with 994 additions and 45 deletions
+3 -2
View File
@@ -116,7 +116,8 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
var order: Array = candidates.keys()
order.sort_custom(func ( a : String , b : String ) -> bool : return (E.lt(candidates[a], candidates[b]) if E.ne(candidates[a], candidates[b]) else E.lt(a, b)))
var created: Dictionary = {}
var metallicity: Variant = world.environment.stellar_environment(target).metallicity
var current_quantities: Dictionary = world.ledger.physical_amounts(target)
var metallicity: Variant = world.environment.stellar_environment(target, current_quantities).metallicity
var total_created: Variant = 0
for id in order:
var entity: OwniverseEntity = world.catalog.entities[id]
@@ -128,7 +129,7 @@ func produce_for_period(previous: Dictionary, target: Dictionary, quantities: Di
var actual: Variant = E.minimum(desired, auto_capacity(target, entity, budget))
if E.le(actual, 0):
continue
if (not world.production.try_production(target, entity, actual, true)):
if (not world.production.try_production(target, entity, actual, true, current_quantities)):
continue
budget = ordinary_star_budget(target)
created[id] = actual
+2 -2
View File
@@ -6,9 +6,9 @@ const ETHICS = State.ETHICS
const OPPOSITE = {"m": "l", "l": "m", "w": "d", "d": "w", "c": "b", "b": "c"}
# Cache the weighted float worship rate multiplied by the exact population.
static func worship(world, target: Dictionary, rules: Dictionary) -> Variant:
static func worship(world, target: Dictionary, rules: Dictionary, physical: Dictionary = {}) -> Variant:
var result: Variant = 0
var physical: Dictionary = world.ledger.physical_amounts(target)
if physical.is_empty(): physical = world.ledger.physical_amounts(target)
for id in rules:
if not rules[id].worships: continue
var pop: Dictionary = State.normalize_profile(target.ethics.get(id, {}))
+33 -21
View File
@@ -10,7 +10,7 @@ var _generator_key: String = ""
var _generator: Dictionary = {}
var _solutions: Dictionary = {}
func _edges(target: Dictionary, rules: Dictionary) -> Dictionary:
func _edges(target: Dictionary, rules: Dictionary, physical: Dictionary = {}) -> Dictionary:
var modifiers: Dictionary = Parameters.life_modifiers(target)
var order: float = maxf(1e-12, E.approx(Parameters.value(target, "Order")))
var stages: Array = rules.keys()
@@ -18,13 +18,13 @@ func _edges(target: Dictionary, rules: Dictionary) -> Dictionary:
var rates: Dictionary = {}
var spans: Dictionary = {}
var progression: Dictionary = {}
var budget: Dictionary = State.stellar_budget(world, target, rules)
var budget: Dictionary = State.stellar_budget(world, target, rules, physical)
var signature: String = str(modifiers) + ":" + str(order)
for id in stages.duplicate():
if rules[id].ethics.is_empty(): continue
rates[id] = Civ.coefficients(world, rules, id, modifiers)
progression[id] = _can_progress(id, rules[id].next, budget, rules)
spans[id] = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules)))
spans[id] = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules, physical)))
signature += id + str(spans[id]) + str(rates[id]) + str(rules[id]) + str(progression[id])
for row: Dictionary in rules[id].ethics.values():
for destination in Q.rule_weights(str(row.fall)):
@@ -171,11 +171,11 @@ func _solve_basis(generator: Dictionary, source: String, years: float) -> Dictio
for node in range(e, size, 6): occupation[node] *= factor
return {"mass": mass, "occupation": occupation}
func _drift(target: Dictionary, rules: Dictionary, generator: Dictionary, years: float) -> void:
func _drift(target: Dictionary, rules: Dictionary, generator: Dictionary, years: float, physical: Dictionary = {}) -> void:
for id in rules:
if rules[id].ethics.is_empty() or not target.ethics.has(id): continue
var profile: Dictionary = State.normalize_profile(target.ethics[id])
var span: float = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules)))
var span: float = maxf(1e-12, E.approx(State.effective_span(world, target, id, rules, physical)))
var duration: float = years / span
var pair_decay: float = exp(-0.03 * duration)
var opposite_decay: float = exp(-0.02 * duration)
@@ -307,11 +307,16 @@ func _limit(plans: Dictionary, budget: Dictionary, rules: Dictionary) -> void:
_merge(plans[source], source, rejected, plans[source].profiles[destination])
## Apply one conserved source-by-owner table, then credit each destination once.
func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules: Dictionary, year: Variant) -> void:
func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules: Dictionary, year: Variant) -> Dictionary:
var credits: Dictionary = {}
var contributions: Dictionary = {}
var births: Dictionary = {}
var ownership: Dictionary = {}
# Each source quota replaces its old physical population. Transfers between
# owners do not change these exact totals, and credits are applied below.
var final: Dictionary = physical.duplicate()
for source in plans:
if final.has(source): final[source] = plans[source].counts.get(source, 0)
var space_stages: Dictionary = {"Ct": true}
for id in rules: space_stages[id] = id == "Ct" or str(rules[id].home) != ""
for source in plans:
@@ -368,7 +373,9 @@ func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules:
if world.catalog.entities[id].event_group == "DwStar" and E.gt(physical[id], 0): hosts[id] = physical[id]
var host_shares: Array = _host_shares(hosts) if not hosts.is_empty() else []
for destination in credits:
var created: Variant = world.ledger.credit(target, destination, Q.sum(credits[destination]))
var requested: Variant = Q.sum(credits[destination])
var created: Variant = world.ledger.credit(target, destination, requested)
if final.has(destination): final[destination] = E.add(final[destination], created)
var bound: Variant = 0
for owner in credits[destination]:
var count: Variant = credits[destination][owner]
@@ -385,7 +392,6 @@ func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules:
bound = E.add(bound, count)
target.amount[destination] = E.subtract(target.amount[destination], bound)
if not rules.has(destination): births[destination] = created
var final: Dictionary = world.ledger.physical_amounts(target)
for destination in contributions:
if not rules.has(destination) or E.le(final.get(destination, 0), 0): continue
var profile: Dictionary = {}
@@ -400,6 +406,7 @@ func _commit(target: Dictionary, physical: Dictionary, plans: Dictionary, rules:
for ethic in ETHICS: profile[ethic] += entry.profile[ethic] * ratio
target.ethics[destination] = State.normalize_profile(profile)
for destination in births: world.batches.append_batch(target, destination, births[destination], year, year)
return final
func _host_shares(hosts: Dictionary) -> Array:
var total: Variant = Q.sum(hosts)
@@ -407,16 +414,16 @@ func _host_shares(hosts: Dictionary) -> Array:
for value in hosts.values(): result.append(E.ratio_approx(value, total))
return result
func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: Dictionary) -> void:
if colonies.is_empty(): return
var physical: Dictionary = world.ledger.physical_amounts(target)
func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: Dictionary, physical: Dictionary = {}) -> Dictionary:
if physical.is_empty(): physical = world.ledger.physical_amounts(target)
if colonies.is_empty(): return physical
var stocks: Dictionary = {}
for id in ["GG", "AB", "RP", "HP"]: stocks[id] = physical.get(id, 0)
var requests: Dictionary = {}
for id in colonies: requests[id] = colonies[id].counts.get(id, 0)
var granted: Dictionary = Q.allocate_profiled(requests, Q.sum(stocks))
var total: Variant = Q.sum(granted)
if E.le(total, 0): return
if E.le(total, 0): return physical
var taken_resources: Dictionary = Q.allocate_profiled(stocks, total)
var removed_by_owner: Dictionary = {}
for resource in taken_resources:
@@ -435,6 +442,7 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
removed_by_owner[owner] = E.add(removed_by_owner.get(owner, 0), amount)
var remaining: Dictionary = granted.duplicate()
var placements: Dictionary = {}
var final: Dictionary = physical.duplicate()
var owners: Array = removed_by_owner.keys()
for i in owners.size():
var owner: String = owners[i]
@@ -449,7 +457,8 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
for destination in granted:
var count: Variant = granted[destination]
if E.le(count, 0): continue
world.ledger.credit(target, destination, count)
var created: Variant = world.ledger.credit(target, destination, count)
if final.has(destination): final[destination] = E.add(final[destination], created)
var bound: Variant = 0
for owner in placements.get(destination, {}):
var held: Variant = placements[destination][owner]
@@ -466,6 +475,9 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
drifted[ethic] += profile[source] * (0.96 if source == ethic else (0.004 if Civ.OPPOSITE[source] == ethic else 0.009))
target.ethics[id] = State.merge_profiles(target.ethics.get(id, {}), physical.get(id, 0), drifted, granted[id])
target.civilization_stats.colonies = E.add(target.civilization_stats.get("colonies", 0), total)
for resource in taken_resources:
final[resource] = E.subtract(final.get(resource, 0), taken_resources[resource])
return final
func evolve(target: Dictionary, from_year: Variant, to_year: Variant, timer: RefCounted = null) -> void:
var keep_alive: RefCounted = self
@@ -476,12 +488,12 @@ func evolve(target: Dictionary, from_year: Variant, to_year: Variant, timer: Ref
# Bound the Poisson mean, processing the entire requested interval.
var maximum_years: float = years
var cursor: Variant = from_year
var physical: Dictionary = world.ledger.physical_amounts(target)
while E.lt(cursor, to_year):
if timer != null and not await timer.pause(): return
generator = _edges(target, rules)
generator = _edges(target, rules, physical)
maximum_years = 32.0 / generator.lambda if generator.lambda > 0.0 else years
var end: Variant = E.minimum(to_year, E.add(cursor, E.of(maximum_years)))
var physical: Dictionary = world.ledger.physical_amounts(target)
var started: int = world.profile_start()
var transaction: Dictionary = _plans(target, physical, rules, generator, E.approx(E.subtract(end, cursor)))
world.profile_end("civilization_flow_plan", started)
@@ -509,18 +521,18 @@ func evolve(target: Dictionary, from_year: Variant, to_year: Variant, timer: Ref
_limit(plans, State.stellar_budget(world, target, rules, physical), rules)
world.profile_end("civilization_flow_capacity", started)
started = world.profile_start()
_commit(target, physical, plans, rules, end)
physical = _commit(target, physical, plans, rules, end)
world.profile_end("civilization_flow_ledger", started)
if timer != null and not await timer.pause(): return
started = world.profile_start()
_colonize(target, transaction.colonies, end, rules)
physical = _colonize(target, transaction.colonies, end, rules, physical)
world.profile_end("civilization_flow_colonies", started)
started = world.profile_start()
_drift(target, rules, generator, E.approx(E.subtract(end, cursor)))
_drift(target, rules, generator, E.approx(E.subtract(end, cursor)), physical)
target.civilization_stats.wars = E.add(target.civilization_stats.get("wars", 0), transaction.wars)
target.civilization_stats.collateral = E.add(target.civilization_stats.get("collateral", 0), damage_total)
State.enforce_capacity(world, target, rules, end)
State.reconcile(world, target, rules)
target.civilization_stats.worship = Civ.worship(world, target, rules)
physical = State.enforce_capacity(world, target, rules, end, physical)
State.reconcile(world, target, rules, physical)
target.civilization_stats.worship = Civ.worship(world, target, rules, physical)
world.profile_end("civilization_flow_finish", started)
cursor = end
+10 -7
View File
@@ -133,10 +133,11 @@ static func admit_transition(budget: Dictionary, rules: Dictionary, source: Stri
# Resolve shortages from the most advanced stages downwards. This gives existing
# advanced populations priority and lets a demotion cascade in the same cycle.
static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year: Variant) -> void:
static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year: Variant, physical: Dictionary = {}) -> Dictionary:
var profile_started: int = world.profile_start()
detach_space_civilizations(target, rules)
var physical: Dictionary = world.ledger.physical_amounts(target)
if physical.is_empty(): physical = world.ledger.physical_amounts(target)
else: physical = physical.duplicate()
reconcile(world, target, rules, physical)
var ethical_totals: Dictionary = physical.duplicate()
var budget: Dictionary = stellar_budget(world, target, rules, physical)
@@ -163,7 +164,9 @@ static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year:
var previous: String = (stages[E.subtract(index, 1)] if E.gt(index, 0) else "C1.9")
world.batches.scale_batches(target, id, E.divide(supported, count))
target.amount[id] = supported
world.ledger.credit(target, previous, excess)
var credited: Variant = world.ledger.credit(target, previous, excess)
physical[id] = E.maximum(0, supported)
physical[previous] = E.add(physical.get(previous, 0), credited)
world.batches.append_batch(target, previous, excess, year, year)
var previous_count: Variant = ethical_totals.get(previous, physical.get(previous, target.amount.get(previous, 0)))
var previous_profile: Dictionary = target.ethics.get(previous, empty_profile())
@@ -174,7 +177,7 @@ static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year:
# Capacity losses are proportional, so the surviving source keeps its profile.
if E.eq(previous, "C1.9"):
var stars: Dictionary = {}
var current_physical: Dictionary = world.ledger.physical_amounts(target)
var current_physical: Dictionary = physical
for star in current_physical:
if (E.eq(world.catalog.entities[star].event_group, "DwStar") and E.gt(current_physical[star], 0)):
stars[star] = current_physical[star]
@@ -192,10 +195,10 @@ static func enforce_capacity(world, target: Dictionary, rules: Dictionary, year:
target.binds[owner][previous] = E.add(target.binds[owner].get(previous, 0), amount)
target.amount[previous] = E.subtract(target.amount[previous], amount)
if changed:
physical = world.ledger.physical_amounts(target)
reconcile(world, target, rules, physical)
assign_homes(world, target, rules, physical)
world.profile_end("enforce_capacity", profile_started)
return physical
# Unity's structure chain: direct Claim resets the required structure count;
# intervening stages require ten times more units of the preceding structure.
@@ -247,13 +250,13 @@ static func assign_homes(world, target: Dictionary, rules: Dictionary, physical:
world.profile_end("assign_homes", profile_started)
static func effective_span(world, target: Dictionary, id: String, rules: Dictionary) -> Variant:
static func effective_span(world, target: Dictionary, id: String, rules: Dictionary, physical: Dictionary = {}) -> Variant:
if (not rules.has(id)): return world.catalog.entities[id].lifespan
var base: Variant = rules[id].span
if (not is_space_civilization(id, rules)): return base
var population: Variant = target.amount.get(id, 0)
if E.le(population, 0): return base
var homes: Dictionary = stage_homes(world, target, id, world.catalog.civilization_layout(rules))
var homes: Dictionary = stage_homes(world, target, id, world.catalog.civilization_layout(rules), physical)
target.civilization_homes[id] = homes
var multiplier: float = 1.0
for home in homes:
+5 -5
View File
@@ -5,13 +5,12 @@ const SimulationTime = preload("res://world/owniverse/simulation_time.gd")
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
const QuantityMath = preload("res://world/owniverse/quantity_math.gd")
func initialize_life_batches() -> void:
func initialize_life_batches() -> Dictionary:
for id in Global.CivilizationRules:
world.state.batches.erase(id)
world.state.batch_origins.erase(id)
CivilizationState.detach_space_civilizations(world.state, Global.CivilizationRules)
CivilizationState.enforce_capacity(world, world.state, Global.CivilizationRules, world.state.time.datetime)
var physical: Dictionary = world.ledger.physical_amounts(world.state)
var physical: Dictionary = CivilizationState.enforce_capacity(world, world.state, Global.CivilizationRules, world.state.time.datetime)
for id in world.catalog.entities:
if world.catalog.entities[id].life_rule.is_empty():
continue
@@ -19,6 +18,7 @@ func initialize_life_batches() -> void:
for batch : Dictionary in world.state.batches.get(id, []):
tracked = E.add(tracked, batch.amount)
world.batches.append_batch(world.state, id, E.maximum(0, E.subtract(physical.get(id, 0), tracked)), world.state.time.datetime, world.state.time.datetime)
return physical
func run_frame(clock: Dictionary, budget: RefCounted) -> void:
@@ -31,10 +31,11 @@ func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefC
# Reconcile only when simulation advances.
# Fractional frame updates cannot change capacity or populations.
var processed_year: Variant = SimulationTime.processed_year(datetime)
var quantities: Dictionary = {}
if E.gt(processed_year, world.state.time.year):
if budget != null: budget.call("set_phase", "initialize_life_batches")
var profile_initialize: int = world.profile_start()
initialize_life_batches()
quantities = initialize_life_batches()
world.profile_end("initialize", profile_initialize)
var previous: Dictionary = world.state
if budget != null: budget.call("set_phase", "deep_copy_and_coarsen")
@@ -46,7 +47,6 @@ func evolute_to_datetime(datetime: Variant, clock: Dictionary = {}, budget: RefC
var from_year: Variant = previous.time.year
var to_year: Variant = processed_year
if E.gt(to_year, from_year):
var quantities: Dictionary = world.ledger.physical_amounts(previous)
var distribution: Dictionary = QuantityMath.normalize_distribution(quantities)
next.distribution = distribution
+6 -2
View File
@@ -3,7 +3,7 @@ const E = preload("res://world/owniverse/math/exact.gd")
const CivilizationState = preload("res://world/owniverse/civilization_state.gd")
const QuantityMath = preload("res://world/owniverse/quantity_math.gd")
func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant, automatic: bool = false) -> bool:
func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant, automatic: bool = false, physical: Dictionary = {}) -> bool:
if (E.le(amount, 0) or E.ne(amount, E.floor_value(amount))):
return false
var trial: Dictionary = target.duplicate()
@@ -13,7 +13,9 @@ func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant
trial.batches = target.batches.duplicate()
# Physical object totals do not change while resources are withdrawn from
# this transaction. Keep one snapshot instead of scanning every resource.
var physical: Dictionary = world.ledger.physical_amounts(trial)
var shared_physical: bool = not physical.is_empty()
if not shared_physical: physical = world.ledger.physical_amounts(trial)
var next_physical: Dictionary = physical.duplicate() if shared_physical else {}
var copied_batches: Dictionary = {}
for resource in entity.cost:
var cost: Variant = E.round_value(E.multiply(E.maximum(0, entity.cost[resource]), amount))
@@ -28,6 +30,7 @@ func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant
if automatic:
trial.auto_resources[resource] = E.maximum(0, E.subtract(trial.auto_resources.get(resource, 0), cost))
for id in taken:
if shared_physical and next_physical.has(id): next_physical[id] = E.subtract(next_physical[id], taken[id])
# Isolate only batches actually touched by this transaction.
if (trial.batches.has(id) and (not copied_batches.has(id))):
trial.batches[id] = trial.batches[id].duplicate(true)
@@ -41,6 +44,7 @@ func try_production(target: Dictionary, entity: OwniverseEntity, amount: Variant
for field in ["amount", "binds", "batches", "auto_resources"]:
if trial.has(field):
target[field] = trial[field]
if shared_physical: physical.merge(next_physical, true)
return true
+2 -2
View File
@@ -2,8 +2,8 @@ extends "res://world/owniverse/simulation_module.gd"
const E = preload("res://world/owniverse/math/exact.gd")
const WorldParameters = preload("res://world/owniverse/world_parameters.gd")
func stellar_environment(source: Dictionary) -> Dictionary:
var quantities: Dictionary = world.ledger.physical_amounts(source)
func stellar_environment(source: Dictionary, quantities: Dictionary = {}) -> Dictionary:
if quantities.is_empty(): quantities = world.ledger.physical_amounts(source)
# SMBH is a self-group in GI; its quantity stores mass, not object count.
var mass: Variant = source.amount.get("SMBH", 0)
# Geometric volume includes a 1e22 scale per void and can exceed E27.