Files
owniverse/simulation_rules.md
T

209 lines
36 KiB
Markdown
Raw Normal View History

# Эволюция звёзд Owniverse.GI
2026-10-01 07:48:52 +03:00
Числа симуляции хранят целую часть в двух блоках int64, дробный остаток — в
float. Весь порядок E27 сохраняет целые единицы. Возраст разделён на целые
годы и дробную часть с шагом 10^-9 года; большие промежуточные произведения
распределений вычисляются через `mul_div`. Границы точности, допуск переноса
дробей и несовместимый со старым формат сохранения 4 описаны в
[документации математики](world/owniverse/math/README.md).
Механики перенесены из Owniverse.U с согласованными изменениями GI: постепенные партии, детерминированные дробные остатки, уязвимость цивилизаций до C1.2 и передача выживших планет преемнику. Баланс таблицы GI сохраняется.
## Порядок расчёта
1. Накопить игровое время; обработать новые десятые доли года.
2. Рассчитать производство существующих объектов за интервал с учётом их постепенного исчезновения; выполнить автоматическое создание звёзд и их планет.
3. Для очередного поколения определить долю заканчивающих жизнь звёзд. Применить локальное разрушение их планет до списания партий.
4. Снять целые объекты из партий, рассчитать основные и альтернативные выходы с накоплением дробей.
5. Передать выжившие планеты полученным звёздам/остаткам. Если преемников несколько, распределить по их фактическим количествам; если преемника нет — освободить. Остальные связанные объекты используют существующие правила GI.
6. Создать партии преемников. Учесть производство новорождённых за оставшуюся часть длинного шага, включая излучение короткоживущих сверхновых.
7. Применить суммарный бласт поколения; повторять обработку поколений, пока в интервале остаются переходы.
8. Пересчитать группы и опубликовать состояние.
## Автоматическое создание бурых карликов
На горячие бурые карлики `HBD` выделяется 0,1% **нового фактически произведённого автоматического водорода** за расчётный интервал. По текущей таблице автоматическую долю имеют STN (`auto = 0.5`) и ASTN (`auto = 0.65`). Обычный водород, ручные покупки и неиспользованный запас прошлых интервалов повторно в эту долю не входят.
При стоимости `c = 0.1 H` квота объектов увеличивается на `ΔQ = ΔH_auto × 0.001 / c`. Она хранится между циклами и в сохранении в `auto_star_deficits.HBD`. Доступность ограничивается автоматическим запасом и уже оплаченным топливом: `Q ≤ (auto_resources.H + HBD_fuel) / c`.
Карлики появляются по одному. Для `q = max(0, Q)` создаётся `floor(q)` объектов и ещё один с вероятностью `q - floor(q)`, с ограничением по доступному топливу. Созданное количество вычитается из Q; отрицательный остаток компенсирует досрочное появление на следующих шагах. Повторного случайного округления без нового автоматического водорода нет. Это исключение из общего детерминированного округления GI. Состояние генератора `auto_star_deficits.HBD_random` сохраняется, поэтому загрузка продолжает ту же последовательность.
Водород списывается целыми единицами авансом: `charge = ceil(max(0, count × c - HBD_fuel))`. Неиспользованный остаток `HBD_fuel += charge - count × c` сохраняется в `auto_star_deficits.HBD_fuel` и больше не входит в свободный запас или бюджет обычных звёзд. Например, 500 автоматических H позволяют создать 5 HBD, списав 1 H и оставив 0,5 H оплаченного топлива. Следующие 5 HBD используют этот остаток без повторного списания. Численность всегда целая, но не обязана быть кратной 10. При отсутствии обычных звёзд их доля остаётся в запасе, а производство HBD всё равно работает.
Новые HBD получают обычные жизненные партии. По таблице их срок — 5 миллиардов лет, затем они постепенно превращаются в холодные `CBD`. На экране оба типа суммируются в `BD`. Ручного рецепта у общего счётчика BD нет. Миграция сохранений не требуется: квота использует существующий словарь, отсутствующее значение равно нулю.
## Постепенные партии
### Короткоживущие звёздные события внутри длинного шага
Для SN, DSN, BSN, HN и GRB-событий полностью завершившиеся окна рождения не создают отдельные словари партий. Их целые количества суммируются в очередь завершений текущего расчёта; обычные партии сохраняются только для окон, срок которых ещё не закончился. Состояние этой очереди не записывается в сохранение: она полностью обрабатывается до публикации нового состояния.
Окно завершено, если `window_end + lifespan ≤ to_year`. Его доля количества рассчитывается прежним методом разности накопленных целых значений. Для производства пространства интеграл исчезновения вычитается по окнам в прежнем порядке, чтобы сохранить округление. Здесь остаётся лёгкий арифметический проход по окнам, но отсутствуют создание, копирование, сортировка и последующая обработка тысяч временных словарей.
Ресурсы, остатки звёзд и бласты от завершившейся части применяются на следующем поколении расчёта, как раньше. Поэтому сохраняются порядок передачи привязок, среда бластов и ограничения цивилизаций. Ускорение допускается только для событий группы `SNGRBB` с линейным выходом и без преемников с ненулевым сроком жизни. При изменении таблиц остальные случаи автоматически используют обычный путь. Сроки жизни и ширина окон не увеличиваются, формат сохранения не меняется.
На сохранении возраста 140,55 млн лет, при шестой скорости и шаге 1/60 секунды: максимальное временное число партий SN/GRB уменьшилось с 1668 до 2; средний расчёт кадра без отрисовки — примерно с 15 до 10 мс. За 120 шагов результаты старого и нового путей совпали по всем полям состояния. Эти значения характеризуют конкретное сохранение, а не гарантированную частоту кадров на всех устройствах.
### Общие окна рождения и эволюции
Окно рождения шириной `w = lifespan / 40` при `lifespan > 10`, иначе `w = lifespan`. Окно превращения сдвинуто на `lifespan`. При частичном списании:
```
expected = remainder + (remaining_count - remainder) * elapsed / remaining_window
converted = floor(expected)
remainder = expected - converted
```
Конец окна списывает остаток. Переход не уничтожает всю партию в начале окна. Начала и длительности сохраняют модель GI, включая годовую длительность событий SN/GRB.
## Планеты
При ручном и автоматическом создании звёзд:
```
expected = stars_created * planet_amount * planet_chance * metallicity + remainder
planets_created = floor(expected)
remainder = expected - planets_created
```
Остаток независим для каждой пары звезда/планета и сохраняется. Планеты сразу связаны со звездой. Эволюционный переход сам по себе не создаёт новую планетную систему. Шансы планет в таблице — доли, а проценты альтернативных выходов звёзд делятся на 100.
## Среда: адаптация формул Unity к состоянию GI
GI не хранит накопленные счётчики первичной материи Unity. Поэтому `stellar_environment()` вычисляет среду из текущего физического состава, включая связанные объекты, без суммирующих групп интерфейса. Это приближение для баланса GI, а не побитовое воспроизведение Unity.
- `M` — свободные и связанные H/He/Met плюс табличная масса конкретных объектов и масса SMBH.
- `V` — физическое S плюс табличный объём объектов и `voids * 1e22`.
- `Z = max(0, 0.4 * ln(100 * free_Met / M + 0.11) + 0.88)`; при `M = 0` металличность нулевая.
- `B = 65536 * f(M)`, где `f(0)=1`, `f(M)=M^0.1` при `0<M<1e10`, иначе `10`.
- `density = 1.23456e-7 * sqrt(B + M) / max(1, V)^(1/6)`.
- `blast_factor = clamp(free_S / V, 0, 1)`; при нулевом объёме `1`.
Планетные бонусы сил пока не перенесены. Параметры мира Bur/Exp/Agg имеют собственные сохраняемые шкалы и эффективные множители (см. раздел индикаторов ниже). Металличность для производства фиксируется до расходования ресурсов на звёзды. Среда бласта фиксируется для поколения до добавления его продуктов.
## Разрушение и бласты
Локальное воздействие: `ratio = dying_stars / physical_stars`, без множителя плотности.
Бласт: `hits = floor(power * blast_factor + saved_remainder)`. Распределение экспозиции между типами звёзд, имеющими планетные системы в таблице, пропорционально их физическому количеству. Для типа: `ratio = hits * star_share / physical_stars * density`.
Для каждого связанного типа планет накапливается `held_planets * min(1, ratio)`. Уничтожается целая часть, не больше существующего количества. Остатки локального воздействия и бластов раздельны. Это позволяет слабым воздействиям работать при постепенной эволюции.
Уровень 1 превращает только `blastability=1` в RP (HP, EON, цивилизации до C1.2). C1.3 и выше защищены от этого уровня. Уровень 2 уничтожает все планеты, включая защищённые цивилизации, без создания RP. Уничтоженные объекты удаляются из жизненных партий, чтобы не возникать заново.
Единственный источник мощности — поле `blast` сущности: `power = expired_count × max(0, entity.blast)`. При `blast = 0` внешнего воздействия нет. Старые вхождения `blast:N` в `output` игнорируются, включая альтернативные выходы; двойного суммирования нет. Внутренний выход `blast` остаётся событием, а не предметом. `destroy_level` независимо задаёт уровень локального разрушения у умирающего объекта; внешние бласты всегда действуют на уровне 1.
## Границы этапа
Рассчитываются агрегаты по типам, а не отдельные звёздные системы; конкретная принадлежность каждой планеты отдельной звезде не моделируется. Распределения целых объектов детерминированы. Порядок внутри длинного шага — по поколениям, поэтому нелинейное воздействие бластов и среды не обязано совпадать с серией более коротких шагов. Эволюция HP и эонов подключена ниже; эволюция цивилизаций и бонусы структур описаны ниже. Глобальная система сил остаётся отдельной механикой.
Проверки: `tests/test_time.gd`, `tests/test_stellar_rules.gd`. Сохранения игрока тестами не затрагиваются.
## Первичная жизнь: HP → EON1 → … → EON5 → C0.0
2026-10-03 11:06:52 +03:00
Правила встроены в `data/owniverse_life_rule_loader.gd` и загружаются через `OwniverseLifeRuleLoader`; исходные сроки и коэффициенты взяты из Unity `civs.txt`. В каждой обработанной части партии:
- прогресс: `count * progress + carry_progress`;
- деградация: `count * degradation + carry_degradation`;
- остальная жизнь остаётся на прежней стадии и получает новую партию для следующей попытки.
Прогресс и деградация округляются вниз с сохранением остатков. Если оба накопленных исхода конкурируют за последнюю планету, распределяются только существующие объекты, а неиспользованный остаток переносится на следующую попытку. Деградация распределяется по весам `fall`; знаковые ошибки распределения также сохраняются.
Для всех шести стадий базовые доли за попытку — `progress=0.00025`, `degradation=0.0025`. Интервалы HP/EON1/EON2/EON3/EON4/EON5: 5 млн, 10 млн, 10 млн, 1 млн, 1 млн, 500 тыс. лет. Используются постепенные окна GI шириной `span / 40`. Случайные множители Unity не применяются. Агрессия, порядок и просвещение изменяют вероятности по формулам из раздела индикаторов ниже.
HP деградирует в RP. У эонов возможен откат к предыдущим стадиям по таблице. RP не развивается сам. C0.0 и C0.1 также развиваются: период 2000 лет, прогресс 0.0175, деградация 0.025. Переходы C0.0 → C0.1 → C0.2, откат C0.0 → EON5 и C0.1 → C0.0. Развитие C0.2 и выше подключено через этические правила. Конфликт civ_bonus устранён повторным импортом: бонусы структур совпадают с Unity. Для существующих этических цивилизаций пользователь выбрал равномерное распределение между шестью этиками с детерминированным целым остатком.
Переходы сохраняют количество планет и их хозяев, включая передачу преемнику звезды и разрушение бластом. Повторная попытка той же стадии не увеличивает статистику созданных объектов. Партии новых планет регистрируются при создании; недостающие партии старых HP/эонов — однократно при загрузке с текущим возрастом вселенной, без выдумывания их прошлого возраста. Состояние содержит `life_version=2` (версия 1 также загружается) и `life_remainders`.
Миграция версии жизни 1 → 2 добавляет только недостающие партии C0.0/C0.1, сохраняя уже существующие партии HP и эонов.
## Этические и космические цивилизации
2026-10-03 11:06:52 +03:00
`data/owniverse_civilization_rule_loader.gd` содержит 43 стадии по шесть этик из Unity. `OwniverseCivilizationRuleLoader` группирует строки по стадиям, формирует связи между ними и передаёт готовый словарь в `Global.CivilizationRules`. После C4.4 следует Ct: конечная стадия без новых жизненных партий.
Состояние `ethics[stage][ethic]` хранит целые количества. Для существующих цивилизаций без этик количество распределяется поровну в порядке m, l, w, d, c, b; остаток распределяется по одному в этом порядке. Существующее распределение сохраняется. При внешних потерях оно пропорционально сокращается.
Цивилизации с непустым `home` (C2.0–C4.4) освобождаются от прежних привязок без изменения общего количества. Общий механизм переходов больше не переносит планетную привязку на такого преемника. Структуры назначаются отдельно в `civilization_homes`; при возвращении к планетным стадиям цивилизации размещаются у имеющихся карликовых звёзд.
Правило среды согласовано: число космических цивилизаций ограничено звёздным ресурсом. Для стадии `i` расход ёмкости — `population[i] * max(1, Stars[i])`. Каждое семейство `Home` имеет общий бюджет для всех использующих его стадий; звёзды внутри структур также учитываются, но повторно не суммируются. SMBH учитывается по суммарной массе в эквиваленте звёзд, согласно решению пользователя.
Переход переиспользует резерв исходной стадии. При нехватке ёмкости неподдержанные переходы остаются на исходной стадии. Лимит применяется к общему механизму выходов партий и ручному созданию; расчёт прогресса C0.2+ использует тот же бюджет.
При загрузке и после эволюционных поколений избыток деградирует последовательно до поддерживаемой стадии. Приоритет получают существующие более развитые цивилизации; остаток бюджета достаётся младшим. Деградация переносит количество и этики, сокращает старые партии и регистрирует новые, не уничтожая цивилизации. C2.0 при избытке становится C1.9 и распределяется по имеющимся звёздам-карликам. Если карликов нет, C1.9 остаётся свободной — отсутствующая звезда не создаётся. Планетные стадии до C2.0 не расходуют отдельную звёздную ёмкость: у одной звезды могут быть несколько планет.
Резерв ёмкости логический: звёзды не расходуются и продолжают производить ресурсы. Общий бюджет пересчитывается из физических количеств, отдельная копия бронирований в сохранении не хранится.
### Расчёт поколения C0.2–C4.4
1. Из истёкшей части партии пропорционально выбираются представители шести этик. Для каждой этики читаются `progress` и `degradation`; целые исходы получаются с сохранением дробных остатков. Неудавшийся из-за лимита переход возвращается на исходную стадию.
2. Прогресс ведёт на следующую стадию; деградация распределяется по весам `fall` конкретной этики. Повторные попытки не увеличивают статистику созданных цивилизаций.
3. При флаге `fights` рассчитываются войны. Веса исходов — 1:2:3:3, доли прогресса победителей — 0:0.25:0.5:0.75; первый исход деградирует атакующих. Потери противников — 2:1:1.5:2 на войну. Два последних исхода дают 10 и 30 единиц побочного ущерба предыдущей стадии. Выбор участников ограничен живой популяцией, включая ещё не сработавшие партии; их партии сокращаются вместе с населением.
4. Для обычного дрейфа веса своей/противоположной/каждой другой этики равны 196/0/1. Дипломатия умножает исходные численности на `diplomacy_rate`, затем нормирует к прежней численности. Флаг дипломатии действует на остающихся на стадии. При переходе в Ct этика сохраняется без дрейфа.
5. При флаге `colonizes` создаются колонии на две стадии ниже отправителя. Цели — существующие GG, AB, RP, HP; колонизация заменяет цель и сохраняет её звёздную привязку. Веса дрейфа колонистов — 960/4/9. Исходная цивилизация не расходуется. Новая партия начинается в текущий игровой момент.
6. После применения переходов пересчитываются этики, поклонение (`Σ population × worship`), бласты и ограничения среды. Число операций зависит от числа типов и партий, а не от числа цивилизаций.
Окна исходных партий сохраняют время переходов; несколько частей одного типа обрабатываются агрегированно между минимальным и максимальным временем. Побочный ущерб неистёкшим партиям применяется в текущий момент. Все остатки округления сохраняются в `civilization_remainders`, счётчики — в `civilization_stats`.
### Структуры и периоды
Прямой `claim` задаёт структуру стадии и норму 1; каждая промежуточная стадия умножает норму на 10 до следующего `claim`. Начиная с C3.0 требуется соответствующий запас структур, общий для использующих его стадий. При потере запаса избыток деградирует. Этот бюджет действует одновременно со звёздным.
Размещение выбирает структуры в порядке убывания `civ_bonus`, начиная с соответствующего стадии `claim`. Прямое размещение ограничено числом структур; остальные разрешённые структуры при наличии дают общий бонус, как в Unity. Период новых партий равен `span / средневзвешенный civ_bonus`, для неразмещённой части множитель равен 1. Уже назначенные сроки партий не переписываются при изменении бонуса.
Ct не получает часы. Инициализация отсутствующих партий жизни выполняется при загрузке и перед продвижением симуляции. Параметры мира и тенденции описаны ниже; интерфейс применения сил в этот перенос не входит.
## Параметры мира, тенденции и индикаторы
Источник формул — Unity `OwniverseInstance`, `Life`, `LifeActions`, `Trends` и классы индикаторов. Расчёты находятся в `world_parameters.gd`; показ — в `ui/indicators/world_indicators.gd`. Шкалы обновляются каждые 0,1 реальной секунды и сразу при создании/загрузке вселенной. Подписи используют текущую локаль и шаблоны.
### Энергия и плотность
Энергия сохраняет экономику GI: запас расходуется на H/S. Заполнение индикатора — `clamp(energy / 1e14, 0, 1)`. Это не коэффициент производства Unity; дополнительное замедление производства при уменьшении запаса не вводится. В описании выводятся остаток и процент.
Плотность берётся из `stellar_environment`. Положение указателя ограничено диапазоном 0…1:
- `density ≤ 1e-5`: шкала разрыва, `(log10(max(density, 1e-7)) + 7) / 2`;
- `1e-5 < density < 0.9`: обычная шкала, `ln(density × 1e5) / ln(9e4)`;
- `density ≥ 0.9`: шкала сжатия, `(density − 0.9) × 10`.
В Unity в первой формуле было `−7`; это ошибка, не позволяющая указателю двигаться. Здесь исправлено на `+7`. Переключение шкалы меняет и заголовок/описание индикатора. Само наступление апокалипсиса индикатор не запускает.
2026-09-28 22:32:52 +03:00
Общая рамка `Background/WarningBorder` получает цвета Unity `WarningUI` в формате RGBA: при плотности ≤1e-5 — `C040407F`, ≤1e-6 — `DF2020BF`, ≤1e-7 — `FF0000FF`; при плотности ≥0,9 — `40A0C07F`, ≥0,97 — `20B0DFBF`, ≥1 — `00C0FFFF`. В нормальном диапазоне рамка прозрачна. Она обновляется вместе с индикаторами, не перехватывает нажатия и снимается при восстановлении плотности. Необратимая последовательность апокалипсиса Unity сюда не входит.
### Горючесть, взрывчатость и агрессия
`world_parameters` хранит Bur, Exp и Agg в диапазоне 1…19, начальное значение — 10. Эффективный множитель `M(x) = 10^((x−10)/9)`: крайние значения 0,1 и 10, нейтральное — 1.
- Bur делит срок жизни новых звёзд на `M(Bur)`. `modify_world_parameter("Bur", delta)` также пересчитывает оставшееся время существующих звёзд с коэффициентом `старый M / новый M`, сохраняя количество и остатки округления. Производство звёзд умножается на `M`, если `M ≤ 1`, иначе на `sqrt(M)`.
- Exp умножает вероятность альтернативного исхода (с ограничением до 1), мощность `entity.blast` и производство событий группы SNGRBB. Локальный `destroy_level` от него не меняется.
- Для Agg обозначим `a = M(Agg)`. Вероятность прогресса умножается на `a`, деградации — на `(1 − (1−a)/20) / Order`, войны — на `1 − (1−a)/5`, колонизации — на `1 − (1−a)/2`. Если сумма прогресса и деградации превышает 1, они пропорционально нормируются. Вероятности войны/колонизации ограничены единицей.
- Для первичной жизни дополнительный множитель прогресса — Enlightenment. Order дополнительно уменьшает потери в войнах и побочный ущерб. Начальные Enlightenment и Order равны 1.
Изменять шкалы следует через `owniverse.modify_world_parameter(key, delta)`, чтобы горючесть корректно обновляла партии. Готовых кнопок применения сил этот метод не добавляет.
### Тенденции
Пять направлений: Antitheism, Integration, Xenocide, Reconstruction, Spirituality. Участвуют космические стадии с непустым `home`; вес первой — 1, каждой следующей — вдвое больше. Для каждой тенденции:
`rate = Σ_стадий (вес стадии × Σ_этик(доля этики × вес этики))`
`прирост = rate × прошедшие игровые годы × 1e-9`.
Доли берутся на начало интервала, после проверки вместимости. Умножение на время исправляет ошибку Unity, где аргумент period не использовался. Накопление само по себе не зависит от FPS; изменение состава цивилизаций внутри крупных шагов остаётся дискретным, как остальная симуляция.
Веса этик в порядке пяти направлений:
| Этика | Антитеизм | Интеграция | Ксеноцид | Реконструкция | Духовность |
|---|---:|---:|---:|---:|---:|
| m | 0 | 2 | 2 | 0 | 6 |
| l | 6 | 1 | 1 | 2 | 0 |
| w | 2 | 0 | 6 | 0 | 2 |
| d | 0 | 6 | 0 | 1 | 3 |
| c | 1 | 3 | 3 | 3 | 0 |
| b | 3 | 0 | 0 | 6 | 1 |
При достижении 1 учитываются все целые срабатывания, дробный остаток сохраняется. Только Integration имеет последствия в исходной Unity: равновероятно выбирается Federation (+0,1 и Agg −1), Enlightenment (+0,2 и Agg −1) или Order (+0,1). Генератор сохраняет своё состояние. До 256 событий за шаг выбор выполняется по одному; для больших количеств используется нормальная аппроксимация мультиномиального распределения с сохранением точного суммарного числа событий, чтобы высокие скорости не блокировали кадр. Она может менять последовательность исходов при другом разбиении больших интервалов.
Federation сохраняется и увеличивается как в Unity, но в исходном коде не применяется к дипломатии. Дополнительное правило для него не придумано. Остальные четыре тенденции имеют счётчики срабатываний и сброс, но не последствия — так же, как в исходной реализации. Параметры и остатки тенденций сохраняются; у существующего сохранения текущего формата без этих полей используются нейтральные значения и нулевые тенденции, без изменения ресурсов или возраста.