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:
@@ -143,6 +143,10 @@ HP деградирует в RP. У эонов возможен откат к п
|
||||
|
||||
При колонизации планеты сначала списываются по типам и владельцам, затем суммарное списание каждого владельца распределяется между стадиями колоний. Каждая стадия получает одну суммарную выдачу. Общие целые квоты, число списанных планет и количество колоний сохраняются точно. Из-за объединения распределений единичные остатки между владельцами могут отличаться от прежнего последовательного порядка; вероятности переходов и формулы этик не меняются.
|
||||
|
||||
В пределах цивилизационной транзакции точные общие количества передаются между фазами. Применение переходов, колонизация и коррекция ёмкости возвращают обновлённый снимок; перемещение между владельцами само по себе общую численность не меняет. Этот снимок используется для периодов, ограничений, нормализации этик и поклонения. После изменения запасов другими подсистемами снимок строится заново. Снимки не сохраняются в файл и не заменяют проверку состояния при внешних изменениях словарей.
|
||||
|
||||
Автоматическое создание нескольких типов звёзд также использует один текущий снимок. Успешная пробная операция вычитает фактически израсходованные количества; перенос ресурса в привязку сохраняет общий запас. При отказе пробной операции снимок остаётся неизменным. Новые звёзды выдаются после завершения отбора типов, как и прежде.
|
||||
|
||||
Космические стадии C2.0–C4.4 свободны от планетных привязок. Общие бюджеты звёздных семейств и структур учитывают уже занятые резервы. Неподдерживаемый промежуточный переход блокируется в операторе; конечные положительные запросы делят свободную ёмкость одновременно. Отклонённые количества возвращаются к источнику. Освободившаяся при переходах ёмкость доступна следующему шагу. Если внешние изменения уменьшили ёмкость ниже существующего населения, избыток последовательно деградирует с приоритетом старших стадий. При возвращении в планетные стадии используются реальные карликовые звёзды, при отсутствии хозяина население остаётся свободным.
|
||||
|
||||
Порядок полного шага: производство за интервал → непрерывные потоки существующих цивилизаций → поколения звёзд и биологической жизни и бласты → итоговые ограничения среды → тренды. Возникшие из C0.1 цивилизации C0.2 получают равномерный профиль, смешанный с существующим населением, и начинают непрерывную эволюцию со следующего шага. Разные размеры полного шага могут отличаться из-за этого порядка, ограничений среды и нелинейных эффектов; чистая непрерывная цепочка без ограничений согласована при разделении интервала.
|
||||
|
||||
@@ -240,6 +240,10 @@ godot --path Owniverse.GI --script res://tests/benchmark_civilizations.gd -- ui
|
||||
|
||||
`test_flow_commit.gd` проверяет консолидированное применение переходов и колонизации: точные квоты получателей, сохранение количества у каждого владельца, профили этик, статистику создания и отсутствие привязок у Ct. Проверки используют несколько владельцев и численность 103, 10^40 + 103 и 10^400 + 103.
|
||||
|
||||
Дополнительно проверяется точное совпадение обновляемых общих количеств с полным пересчётом после переходов, колонизации и каскадной деградации при нехватке ёмкости. Входные снимки остаются неизменными.
|
||||
|
||||
`test_production_transaction.gd` проверяет, что успешное создание обновляет общий снимок, привязка сохраняет общий ресурс, а отклонённая операция сохраняет и состояние, и снимок.
|
||||
|
||||
```sh
|
||||
godot --headless --path Owniverse.GI --script res://tests/test_flow_commit.gd
|
||||
```
|
||||
|
||||
@@ -412,3 +412,32 @@ godot --headless --path . --script res://tests/benchmark_many_objects.gd -- cycl
|
||||
godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -- res://tests/artifacts/all_objects_speed6.adventure.json 0.2 profile cycles=20
|
||||
godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -- res://tests/artifacts/many_objects_fragmented_stars_cycle.adventure.json 0.2 profile cycles=20
|
||||
```
|
||||
|
||||
## Повторное использование общих количеств — 2026-10-08
|
||||
|
||||
Второй этап: общий точный снимок передаётся между фазами цивилизационных потоков, колонизации и коррекции ёмкости. Переходы и выдачи обновляют только затронутые количества; перенос между владельцами сохраняет общую численность. Периоды стадий, дрейф и поклонение используют актуальные количества этого интервала. Автоматическое создание разных типов звёзд также использует общий снимок: успешная пробная операция вычитает расходы, отказ сохраняет исходный снимок. При внешних изменениях используется обычный пересчёт. Снимки не записываются в сохранения.
|
||||
|
||||
Сравнение с версией после первого этапа. На двух одинаковых диагностических сохранениях выполнены пары до → после → после → до: по 20 циклов на прогон, по 40 циклов на модель и сохранение. Скорость 6, интервал 0,2 секунды, CPU-профиль включён, UI отсутствует. Каждый прогон полностью обработал 400 000 игровых лет. Состояния валидны, входные SHA-256 не изменились.
|
||||
|
||||
| Сохранение | До, мс | После, мс | Сокращение | Полных циклов/с после |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Все объекты | 377,185 | 367,326 | 2,61% | 2,722 |
|
||||
| Много объектов и партий | 443,163 | 422,973 | 4,56% | 2,364 |
|
||||
|
||||
Тяжёлое сохранение:
|
||||
|
||||
| Метрика | До | После |
|
||||
|---|---:|---:|
|
||||
| Полных сборок общих количеств за цикл | 15 | 7 |
|
||||
| Сборка общих количеств, мс/цикл | 37,498 | 17,767 |
|
||||
| Производство, мс/цикл | 37,554 | 20,316 |
|
||||
| Применение потоков, мс/цикл | 91,626 | 89,259 |
|
||||
| Завершение потоков, мс/цикл | 9,821 | 6,881 |
|
||||
|
||||
Время сборки общих количеств вложено в остальные фазы и не прибавляется к ним повторно. Проверка цивилизационной части отдельно дала небольшой выигрыш; основную экономию обеспечивает исключение повторных сборок при автоматическом создании звёзд.
|
||||
|
||||
Полные состояния обеих версий совпали после 10 последовательных сравнений: по пять на каждое сохранение, интервалы 0,2 / 0,53 / 0,31 / 0,2 / 0,2 секунды. Дополнительные проверки сравнивают снимки с полным пересчётом, контролируют неизменность входных снимков, отказ производства и каскадную деградацию. Выполнено 4321 целевая проверка без ошибок; полная регрессия всех наборов не запускалась.
|
||||
|
||||
Пять полных циклов в секунду пока не достигнуты: тяжёлый цикл составляет 423.0 мс; до 200 мс нужно ещё около 2.11-кратного ускорения. Следующая крупная статья — подготовка потоков цивилизаций, около 115.2 мс/цикл.
|
||||
|
||||
Полные результаты и контрольные суммы: `physical_totals_step2_results.json`. Базовая версия: `9357f757a95db96274d4e115c7e498b58eca6902`. Сохранение игрока не использовалось.
|
||||
|
||||
@@ -0,0 +1,870 @@
|
||||
{
|
||||
"delta_seconds": 0.2,
|
||||
"input_sha256": {
|
||||
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
|
||||
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
|
||||
},
|
||||
"order": "before-after-after-before",
|
||||
"profile_enabled": true,
|
||||
"runs": [
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 409.924,
|
||||
"mean_ms": 374.42165,
|
||||
"min_ms": 321.955,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23286,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120325,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
49609,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
167957,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
890864,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2156571,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
59939,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
667995,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
338483,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
186561,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
530120,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2635468,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
112680,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
118343,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
488368,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
543976,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
348509,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 396.498,
|
||||
"mean_ms": 366.8874,
|
||||
"min_ms": 316.948,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23679,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
121355,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52357,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
134122,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
867762,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2171014,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
60589,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
672927,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
313233,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
188758,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
537217,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2691969,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113057,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
119560,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
283727,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
371874,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
352124,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 400.433,
|
||||
"mean_ms": 367.76455,
|
||||
"min_ms": 317.346,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23679,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120585,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
52508,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
133714,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
873014,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2178049,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
60872,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
674461,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
312963,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
189817,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
538355,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2697406,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
113617,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
119833,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
283294,
|
||||
200
|
||||
],
|
||||
"production": [
|
||||
370613,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
354430,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 412.947,
|
||||
"mean_ms": 379.94895,
|
||||
"min_ms": 327.79,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
23817,
|
||||
80
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
120810,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
50245,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
170562,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
901208,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2186592,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61551,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
676571,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
342898,
|
||||
80
|
||||
],
|
||||
"generation_products": [
|
||||
190410,
|
||||
40
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
538869,
|
||||
60
|
||||
],
|
||||
"generations": [
|
||||
2675372,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
114216,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
120558,
|
||||
3016
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
495969,
|
||||
360
|
||||
],
|
||||
"production": [
|
||||
554663,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
353982,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "all_objects_speed6",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 463.763,
|
||||
"mean_ms": 441.44965,
|
||||
"min_ms": 386.031,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17806,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136652,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
230107,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
196342,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1825154,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2292865,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62146,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
680101,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
288185,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
77590,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
800537,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2298719,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141855,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54592,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
745858,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
747261,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
356732,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 445.055,
|
||||
"mean_ms": 423.0315,
|
||||
"min_ms": 374.104,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17840,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
137440,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
231537,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137928,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1782223,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2310391,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61962,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
687903,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
238360,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
77666,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
802637,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2360346,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141317,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54930,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
356207,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
405364,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
357407,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 1,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 441.891,
|
||||
"mean_ms": 422.9136,
|
||||
"min_ms": 376.819,
|
||||
"model": "after",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17821,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136949,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
231992,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
137312,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1788156,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2297486,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
61993,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683317,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
238430,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78170,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
804912,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2364256,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
141451,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54900,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
354476,
|
||||
140
|
||||
],
|
||||
"production": [
|
||||
407287,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
358298,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
},
|
||||
{
|
||||
"cycles": 20,
|
||||
"max_ms": 465.049,
|
||||
"mean_ms": 444.87595,
|
||||
"min_ms": 392.884,
|
||||
"model": "before",
|
||||
"profile": {
|
||||
"assign_homes": [
|
||||
17906,
|
||||
60
|
||||
],
|
||||
"civilization_flow_capacity": [
|
||||
136963,
|
||||
20
|
||||
],
|
||||
"civilization_flow_colonies": [
|
||||
229784,
|
||||
20
|
||||
],
|
||||
"civilization_flow_finish": [
|
||||
196495,
|
||||
20
|
||||
],
|
||||
"civilization_flow_ledger": [
|
||||
1839904,
|
||||
20
|
||||
],
|
||||
"civilization_flow_plan": [
|
||||
2302987,
|
||||
20
|
||||
],
|
||||
"civilization_operator_build": [
|
||||
62518,
|
||||
20
|
||||
],
|
||||
"civilization_operator_solve": [
|
||||
683660,
|
||||
860
|
||||
],
|
||||
"enforce_capacity": [
|
||||
289846,
|
||||
60
|
||||
],
|
||||
"generation_products": [
|
||||
78882,
|
||||
20
|
||||
],
|
||||
"generation_withdrawal": [
|
||||
808021,
|
||||
40
|
||||
],
|
||||
"generations": [
|
||||
2328372,
|
||||
20
|
||||
],
|
||||
"initialize": [
|
||||
142755,
|
||||
20
|
||||
],
|
||||
"newborn_batches": [
|
||||
54963,
|
||||
1490
|
||||
],
|
||||
"physical_amounts_build": [
|
||||
754056,
|
||||
300
|
||||
],
|
||||
"production": [
|
||||
754898,
|
||||
20
|
||||
],
|
||||
"trends": [
|
||||
358634,
|
||||
20
|
||||
]
|
||||
},
|
||||
"run": 2,
|
||||
"save": "many_objects_fragmented_stars_cycle",
|
||||
"validation": "",
|
||||
"years": 400000
|
||||
}
|
||||
],
|
||||
"speed": 6,
|
||||
"reference_commit": "9357f757a95db96274d4e115c7e498b58eca6902",
|
||||
"exact_differential": {
|
||||
"cycles": 10,
|
||||
"inputs": [
|
||||
"all_objects_speed6",
|
||||
"many_objects_fragmented_stars_cycle"
|
||||
],
|
||||
"delta_seconds": [
|
||||
0.2,
|
||||
0.53,
|
||||
0.31,
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
"equal_full_states": true
|
||||
},
|
||||
"checks": {
|
||||
"flow_commit": 1971,
|
||||
"civilization_flows": 1895,
|
||||
"civilization_state": 70,
|
||||
"frame_evolution": 38,
|
||||
"production_transaction": 14,
|
||||
"stellar_rules": 333
|
||||
},
|
||||
"summary": {
|
||||
"all_objects_speed6": {
|
||||
"before": {
|
||||
"mean_ms": 377.1853,
|
||||
"cycles_per_second": 2.651216789201488,
|
||||
"profile_ms": {
|
||||
"assign_homes": 1.177575,
|
||||
"civilization_flow_capacity": 6.028375,
|
||||
"civilization_flow_colonies": 2.49635,
|
||||
"civilization_flow_finish": 8.462975,
|
||||
"civilization_flow_ledger": 44.8018,
|
||||
"civilization_flow_plan": 108.579075,
|
||||
"civilization_operator_build": 3.03725,
|
||||
"civilization_operator_solve": 33.61415,
|
||||
"enforce_capacity": 17.034525000000002,
|
||||
"generation_products": 9.424275,
|
||||
"generation_withdrawal": 26.724725,
|
||||
"generations": 132.771,
|
||||
"initialize": 5.6724,
|
||||
"newborn_batches": 5.972524999999999,
|
||||
"physical_amounts_build": 24.608425,
|
||||
"production": 27.465975,
|
||||
"trends": 17.562275000000003
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 4.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 4.0,
|
||||
"generation_products": 2.0,
|
||||
"generation_withdrawal": 3.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 150.8,
|
||||
"physical_amounts_build": 18.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"mean_ms": 367.32597499999997,
|
||||
"cycles_per_second": 2.7223775830173733,
|
||||
"profile_ms": {
|
||||
"assign_homes": 1.18395,
|
||||
"civilization_flow_capacity": 6.0485,
|
||||
"civilization_flow_colonies": 2.621625,
|
||||
"civilization_flow_finish": 6.6959,
|
||||
"civilization_flow_ledger": 43.519400000000005,
|
||||
"civilization_flow_plan": 108.726575,
|
||||
"civilization_operator_build": 3.036525,
|
||||
"civilization_operator_solve": 33.6847,
|
||||
"enforce_capacity": 15.6549,
|
||||
"generation_products": 9.464375,
|
||||
"generation_withdrawal": 26.8893,
|
||||
"generations": 134.734375,
|
||||
"initialize": 5.66685,
|
||||
"newborn_batches": 5.984825,
|
||||
"physical_amounts_build": 14.175525,
|
||||
"production": 18.562175,
|
||||
"trends": 17.66385
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 4.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 4.0,
|
||||
"generation_products": 2.0,
|
||||
"generation_withdrawal": 3.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 150.8,
|
||||
"physical_amounts_build": 10.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"reduction_percent": 2.613920797019398
|
||||
},
|
||||
"many_objects_fragmented_stars_cycle": {
|
||||
"before": {
|
||||
"mean_ms": 443.1628,
|
||||
"cycles_per_second": 2.2565070894939736,
|
||||
"profile_ms": {
|
||||
"assign_homes": 0.8927999999999999,
|
||||
"civilization_flow_capacity": 6.840375,
|
||||
"civilization_flow_colonies": 11.497275,
|
||||
"civilization_flow_finish": 9.820924999999999,
|
||||
"civilization_flow_ledger": 91.62644999999999,
|
||||
"civilization_flow_plan": 114.8963,
|
||||
"civilization_operator_build": 3.1166,
|
||||
"civilization_operator_solve": 34.094025,
|
||||
"enforce_capacity": 14.450775,
|
||||
"generation_products": 3.9118000000000004,
|
||||
"generation_withdrawal": 40.21395,
|
||||
"generations": 115.677275,
|
||||
"initialize": 7.11525,
|
||||
"newborn_batches": 2.738875,
|
||||
"physical_amounts_build": 37.49785,
|
||||
"production": 37.553975,
|
||||
"trends": 17.88415
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 3.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 3.0,
|
||||
"generation_products": 1.0,
|
||||
"generation_withdrawal": 2.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 74.5,
|
||||
"physical_amounts_build": 15.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"mean_ms": 422.97254999999996,
|
||||
"cycles_per_second": 2.364219616615783,
|
||||
"profile_ms": {
|
||||
"assign_homes": 0.891525,
|
||||
"civilization_flow_capacity": 6.859725,
|
||||
"civilization_flow_colonies": 11.588225,
|
||||
"civilization_flow_finish": 6.881,
|
||||
"civilization_flow_ledger": 89.25947500000001,
|
||||
"civilization_flow_plan": 115.19692500000001,
|
||||
"civilization_operator_build": 3.098875,
|
||||
"civilization_operator_solve": 34.2805,
|
||||
"enforce_capacity": 11.91975,
|
||||
"generation_products": 3.8959,
|
||||
"generation_withdrawal": 40.188725,
|
||||
"generations": 118.11505,
|
||||
"initialize": 7.0691999999999995,
|
||||
"newborn_batches": 2.74575,
|
||||
"physical_amounts_build": 17.767075000000002,
|
||||
"production": 20.316275,
|
||||
"trends": 17.892625
|
||||
},
|
||||
"profile_calls_per_cycle": {
|
||||
"assign_homes": 3.0,
|
||||
"civilization_flow_capacity": 1.0,
|
||||
"civilization_flow_colonies": 1.0,
|
||||
"civilization_flow_finish": 1.0,
|
||||
"civilization_flow_ledger": 1.0,
|
||||
"civilization_flow_plan": 1.0,
|
||||
"civilization_operator_build": 1.0,
|
||||
"civilization_operator_solve": 43.0,
|
||||
"enforce_capacity": 3.0,
|
||||
"generation_products": 1.0,
|
||||
"generation_withdrawal": 2.0,
|
||||
"generations": 1.0,
|
||||
"initialize": 1.0,
|
||||
"newborn_batches": 74.5,
|
||||
"physical_amounts_build": 7.0,
|
||||
"production": 1.0,
|
||||
"trends": 1.0
|
||||
}
|
||||
},
|
||||
"reduction_percent": 4.5559442263655825
|
||||
}
|
||||
},
|
||||
"candidate_script_sha256": {
|
||||
"civilization_flow.gd": "5d2b5f5a90fc9182ac1f933d473441457be28b47800a0081b1409614dd28aab5",
|
||||
"civilization_state.gd": "ae0832531c7ce244e6939793007b3d7be5bd32246be6c209a75fdb5e3e6ffe7a",
|
||||
"civilization_evolution.gd": "24efc14eedee5e8a28422262e37e6c54e1a0c403a164bd8f4c96d749f89ac61f",
|
||||
"evolution_coordinator.gd": "0b8cec7e53f047698545e9b97c986b99e0ea5bfc61a6b0177335aedb442e6048",
|
||||
"object_ledger.gd": "f7fbfeb4be905b525ad91648167ed1e32c4d3b77553549c69c3ebb808ac99b4b",
|
||||
"manual_production.gd": "138a94d3bb7d63470f2ca2eb841badef910ec27f3e247c5c9ed8e925c0971fa4",
|
||||
"automatic_production.gd": "dba9f98cbecc90ad9935653c39f7fcfad53edb3ca2670bbce4e1594b58b399a5",
|
||||
"universe_environment.gd": "e31c87b38a4ec0052f3d43f7be2af51ee7612a48fadcf0b5072ba678107e576e"
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,10 @@ func run():
|
||||
var profiles: Dictionary = {}
|
||||
for id in counts: profiles[id] = profile
|
||||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
world.civilization_flow._commit(world.state, physical, {"C0.2": {"counts": counts, "profiles": profiles}}, rules, 100)
|
||||
var initial_physical: Dictionary = physical.duplicate()
|
||||
var committed: Dictionary = world.civilization_flow._commit(world.state, physical, {"C0.2": {"counts": counts, "profiles": profiles}}, rules, 100)
|
||||
check(E.eq(committed, world.ledger.physical_amounts(world.state)), "Commit snapshot matches a full physical rebuild")
|
||||
check(E.eq(physical, initial_physical), "Commit does not mutate its input snapshot")
|
||||
for id in counts:
|
||||
check(E.eq(world.ledger.physical_amount(world.state, id), counts[id]), "Commit preserves exact destination quota: " + id)
|
||||
for owner in ["RD", "OD"]:
|
||||
@@ -33,6 +36,14 @@ func run():
|
||||
check(E.eq(world.state.binds.RD.get("Ct", 0), 0), "Transcendent population cannot retain a stellar owner")
|
||||
dispose(world)
|
||||
world = fixture()
|
||||
world.state.amount["C2.0"] = count
|
||||
var initial_capacity: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var frozen_capacity: Dictionary = initial_capacity.duplicate()
|
||||
var constrained: Dictionary = State.enforce_capacity(world, world.state, rules, 100, initial_capacity)
|
||||
check(E.eq(constrained, world.ledger.physical_amounts(world.state)), "Cascading capacity changes maintain exact physical totals")
|
||||
check(E.eq(initial_capacity, frozen_capacity), "Capacity correction does not mutate the previous snapshot")
|
||||
dispose(world)
|
||||
world = fixture()
|
||||
var original_owners: Dictionary = {}
|
||||
for owner in ["RD", "OD", "YD", "WYD"]:
|
||||
world.state.binds[owner] = {}
|
||||
@@ -43,7 +54,13 @@ func run():
|
||||
"C1.4": {"counts": {"C1.4": E.multiply(count, 3)}, "profiles": {"C1.4": profile}},
|
||||
"C1.5": {"counts": {"C1.5": E.multiply(count, 5)}, "profiles": {"C1.5": profile}}
|
||||
}
|
||||
world.civilization_flow._colonize(world.state, colonies, 100, rules)
|
||||
var physical_before: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var untouched: Dictionary = physical_before.duplicate()
|
||||
var colonized: Dictionary = world.civilization_flow._colonize(world.state, colonies, 100, rules, physical_before)
|
||||
check(E.eq(colonized, world.ledger.physical_amounts(world.state)), "Colonization snapshot matches a full physical rebuild")
|
||||
check(E.eq(physical_before, untouched), "Colonization does not mutate its input snapshot")
|
||||
var capacity: Dictionary = State.enforce_capacity(world, world.state, rules, 100, colonized)
|
||||
check(E.eq(capacity, world.ledger.physical_amounts(world.state)), "Capacity snapshot matches a full physical rebuild")
|
||||
check(E.eq(world.ledger.physical_amount(world.state, "C1.4"), E.multiply(count, 3)), "Colonization credits exact first quota")
|
||||
check(E.eq(world.ledger.physical_amount(world.state, "C1.5"), E.multiply(count, 5)), "Colonization credits exact second quota")
|
||||
check(E.eq(world.state.stats.total_amount["C1.4"], E.multiply(count, 3)), "Consolidated credit preserves creation statistics")
|
||||
|
||||
@@ -59,11 +59,16 @@ func run() -> void:
|
||||
world.state.auto_resources = {"H": 10}
|
||||
world.state.batches.H = [{"amount": 10, "start_year": 1, "end_year": 2}]
|
||||
var before: Dictionary = world.state.duplicate(true)
|
||||
check((not world.production.try_production(world.state, world.catalog.entities.N, 1, true)), "Binding failure rejects production")
|
||||
var physical: Dictionary = world.ledger.physical_amounts(world.state)
|
||||
var frozen_physical: Dictionary = physical.duplicate()
|
||||
check((not world.production.try_production(world.state, world.catalog.entities.N, 1, true, physical)), "Binding failure rejects production")
|
||||
check(E.eq(physical, frozen_physical), "Failed production preserves the shared quantity snapshot")
|
||||
check(E.eq(world.state, before), "Failure preserves costs, batches and budget")
|
||||
world.state.amount.S = 10
|
||||
physical = world.ledger.physical_amounts(world.state)
|
||||
var old_batches: Array = world.state.batches.H
|
||||
check(world.production.try_production(world.state, world.catalog.entities.N, 2, true), "Funded production succeeds")
|
||||
check(world.production.try_production(world.state, world.catalog.entities.N, 2, true, physical), "Funded production succeeds")
|
||||
check(E.eq(physical, world.ledger.physical_amounts(world.state)), "Successful production updates exact totals without changing bound-resource totals")
|
||||
check((E.eq(world.state.amount.H, 6) and E.eq(world.state.amount.S, 4)), "Exact cost and binding")
|
||||
check(E.eq(world.state.auto_resources.H, 6), "Exact automatic budget")
|
||||
check((E.eq(old_batches[0].amount, 10) and E.eq(world.state.batches.H[0].amount, 6)), "Consumed batches isolated")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, {}))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
Reference in New Issue
Block a user