Refactor civilization flow mechanics: enhance commit and colonization logic, add tests for flow application and resource allocation, and optimize performance metrics.

This commit is contained in:
Kirill
2026-10-08 20:43:51 +03:00
parent 413f2be904
commit 9357f757a9
7 changed files with 946 additions and 32 deletions
+78 -32
View File
@@ -306,53 +306,84 @@ func _limit(plans: Dictionary, budget: Dictionary, rules: Dictionary) -> void:
plans[source].counts[destination] = allowed
_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:
var credits: Dictionary = {}
var contributions: Dictionary = {}
var births: Dictionary = {}
var ownership: Dictionary = {}
var space_stages: Dictionary = {"Ct": true}
for id in rules: space_stages[id] = id == "Ct" or str(rules[id].home) != ""
for source in plans:
ownership[source] = {}
var free: Variant = target.amount.get(source, 0)
if E.gt(free, 0): ownership[source][""] = free
for owner in target.binds:
for source in target.binds[owner]:
if not ownership.has(source): continue
var held: Variant = target.binds[owner][source]
if E.gt(held, 0): ownership[source][owner] = held
for source in plans:
var plan: Dictionary = plans[source]
var slots: Dictionary = {"": target.amount.get(source, 0)}
for owner in target.binds:
var held: Variant = target.binds[owner].get(source, 0)
if E.gt(held, 0): slots[owner] = held
var remaining: Dictionary = {}
for destination in plan.counts:
var amount: Variant = plan.counts[destination]
if E.le(amount, 0): continue
remaining[destination] = amount
if not contributions.has(destination): contributions[destination] = []
contributions[destination].append({"amount": amount, "profile": plan.profiles[destination]})
if destination == source: continue
var taken: Dictionary = Q.allocate_profiled(slots, amount)
for owner in taken:
if E.le(taken[owner], 0): continue
slots[owner] = E.subtract(slots[owner], taken[owner])
var host: String = owner
if State.is_space_civilization(destination, rules) or destination == "Ct": host = ""
elif owner == "" and State.is_space_civilization(source, rules): host = "@host"
var slots: Dictionary = ownership[source]
target.amount[source] = 0
var owners: Array = slots.keys()
if owners.size() == 1 and owners[0] == "":
target.amount[source] = remaining.get(source, 0)
for destination in remaining:
if destination == source: continue
var host: String = "@host" if space_stages.get(source, false) and not space_stages.get(destination, false) else ""
if not credits.has(destination): credits[destination] = {}
credits[destination][host] = E.add(credits[destination].get(host, 0), taken[owner])
target.amount[source] = slots[""]
for owner in slots:
if owner != "": target.binds[owner][source] = slots[owner]
credits[destination][host] = E.add(credits[destination].get(host, 0), remaining[destination])
continue
for i in owners.size():
var owner: String = owners[i]
# Last row receives the remaining exact quotas; the free-only path
# therefore needs no proportional allocation or repeated subtraction.
var allocation: Dictionary = remaining if i == owners.size() - 1 else Q.allocate_profiled(remaining, slots[owner])
for destination in allocation:
var amount: Variant = allocation[destination]
if E.le(amount, 0): continue
if i != owners.size() - 1:
remaining[destination] = E.subtract(remaining[destination], amount)
if destination == source:
if owner == "": target.amount[source] = amount
else: target.binds[owner][source] = amount
continue
var host: String = owner
if space_stages.get(destination, false): host = ""
elif owner == "" and space_stages.get(source, false): host = "@host"
if not credits.has(destination): credits[destination] = {}
credits[destination][host] = E.add(credits[destination].get(host, 0), amount)
if owner != "" and E.le(allocation.get(source, 0), 0): target.binds[owner][source] = 0
var hosts: Dictionary = {}
for id in physical:
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 bound: Variant = 0
for owner in credits[destination]:
var count: Variant = credits[destination][owner]
if owner == "" or E.le(count, 0): continue
if owner == "@host":
if hosts.is_empty(): continue
var ownership: Dictionary = Q.split_profile(target.civilization_remainders, "flow:hosts:" + destination, count, hosts.keys(), _host_shares(hosts))
for host in ownership:
if E.le(ownership[host], 0): continue
var distributed: Dictionary = Q.split_profile(target.civilization_remainders, "flow:hosts:" + destination, count, hosts.keys(), host_shares)
for host in distributed:
if E.le(distributed[host], 0): continue
if not target.binds.has(host): target.binds[host] = {}
target.binds[host][destination] = E.add(target.binds[host].get(destination, 0), ownership[host])
target.amount[destination] = E.subtract(target.amount[destination], count)
target.binds[host][destination] = E.add(target.binds[host].get(destination, 0), distributed[host])
else:
target.binds[owner][destination] = E.add(target.binds[owner].get(destination, 0), count)
target.amount[destination] = E.subtract(target.amount[destination], count)
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:
@@ -387,7 +418,7 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
var total: Variant = Q.sum(granted)
if E.le(total, 0): return
var taken_resources: Dictionary = Q.allocate_profiled(stocks, total)
var remaining: Dictionary = granted.duplicate()
var removed_by_owner: Dictionary = {}
for resource in taken_resources:
var removed: Variant = taken_resources[resource]
if E.le(removed, 0): continue
@@ -401,15 +432,30 @@ func _colonize(target: Dictionary, colonies: Dictionary, year: Variant, rules: D
if E.le(amount, 0): continue
if owner == "": target.amount[resource] = E.subtract(target.amount[resource], amount)
else: target.binds[owner][resource] = E.subtract(target.binds[owner][resource], amount)
var allocation: Dictionary = Q.allocate_profiled(remaining, amount)
for destination in allocation:
var count: Variant = allocation[destination]
if E.le(count, 0): continue
remaining[destination] = E.subtract(remaining[destination], count)
var created: Variant = world.ledger.credit(target, destination, count)
if owner != "" and not State.is_space_civilization(destination, rules):
target.amount[destination] = E.subtract(target.amount[destination], created)
target.binds[owner][destination] = E.add(target.binds[owner].get(destination, 0), created)
removed_by_owner[owner] = E.add(removed_by_owner.get(owner, 0), amount)
var remaining: Dictionary = granted.duplicate()
var placements: Dictionary = {}
var owners: Array = removed_by_owner.keys()
for i in owners.size():
var owner: String = owners[i]
var allocation: Dictionary = remaining if i == owners.size() - 1 else Q.allocate_profiled(remaining, removed_by_owner[owner])
for destination in allocation:
var count: Variant = allocation[destination]
if E.le(count, 0): continue
if i != owners.size() - 1: remaining[destination] = E.subtract(remaining[destination], count)
if owner == "" or State.is_space_civilization(destination, rules): continue
if not placements.has(destination): placements[destination] = {}
placements[destination][owner] = count
for destination in granted:
var count: Variant = granted[destination]
if E.le(count, 0): continue
world.ledger.credit(target, destination, count)
var bound: Variant = 0
for owner in placements.get(destination, {}):
var held: Variant = placements[destination][owner]
target.binds[owner][destination] = E.add(target.binds[owner].get(destination, 0), held)
bound = E.add(bound, held)
target.amount[destination] = E.subtract(target.amount[destination], bound)
for id in granted:
if E.le(granted[id], 0): continue
var profile: Dictionary = colonies[id].profiles[id]