56093b3a55
The stage contract and profiles are in 4b013fb. This commit adds the measured balance (the #120 grid on identical seeds and the 12-seed raid), the replay/export video with four stills, and updates the user and handoff documentation. The visual files are provided for human review; the agent that captured them has no image input and checked only the encoded parameters, shot boundaries and the first large plume. Co-Authored-By: Kimi Code <noreply@kimi.com>
475 lines
51 KiB
Markdown
475 lines
51 KiB
Markdown
# Передача разработки AirDef
|
||
|
||
Это журнал фактически сделанных изменений и ограничений проверки, а не список обещанных функций.
|
||
Рабочая ветка `kimi-work`; issues не закрывать, публиковать комментарии с коммитом и проверками.
|
||
Движок — отдельный `chexit/horizon-airdef`, ветка `airdef`, подключён точным gitlink `horizon/`.
|
||
|
||
## Текущее состояние: 4 ноября 2026
|
||
|
||
Последнее продолжение пяти задач: [CONTINUATION-2026-11-04.md](CONTINUATION-2026-11-04.md).
|
||
Движок `57f8b2853c4443f7e3ff5701600cbb262b53118f`, API29. В этом файле ниже сохранён
|
||
исторический журнал предыдущих серий; старые SHA и числа тестов не заменяют новую контрольную точку.
|
||
|
||
След #91 продолжен: `catalog/specs.json` — единственная копия числовых строк дронов, ракет и
|
||
комплексов; `python3 tools/gen_catalog.py` генерирует `specs_generated.rs` и
|
||
`unit_specs_generated.rs`, `--check` (CI) валит расхождение. Числа не менялись; сверка с литералами
|
||
подтвердила 1.6 как фактический голос P1-SUN (недостижимый `P1sun => 1.7` в старом match не в счёт);
|
||
на это добавлена Rust-регрессия. Остаток: варианты комплексов и игровая приёмка.
|
||
|
||
### Продолжение #129: стадии холодного пуска и разгон (6 ноября 2026)
|
||
|
||
Ветка `agent/issue-129-cold-launch`, кодовый коммит `4b013fb7f30dbaa87a266b27a31911937fc523b4`
|
||
(документация, измерения и кадры — следующим коммитом этой же ветки).
|
||
Игра/движок не менялись: pin `278f356aa604fa21c2d15d0081823bbe0f891df2`, API30. Push не выполнялся,
|
||
issues не закрывались.
|
||
|
||
**Контракт стадий.** У `MissileSpec` появились `eject` и `eject_drag`; `cold` — тики физического
|
||
выброса, `boost` — тики departure. `guided(age) = age >= cold + boost`; основная ветка наведения,
|
||
самонаведение и облёт рельефа больше не могут включиться до зажигания (раньше было `age >= boost`,
|
||
что при большом `cold` включало наведение до мотора). Зажигание — по-прежнему `age > cold`;
|
||
событие `MotorIgnition`, звук, факел и голос используют текущую позицию ракеты. `departure_ramp`
|
||
растёт 0→1 за `boost`; темп поворота в departure — `turn·(0.2 + 0.8·ramp)`. Тяга ракетных
|
||
профилей — `accel·(0.45 + 0.55·ramp)`, на guidance — полная. P1-SUN (`flame=0`) сохраняет
|
||
полный разгон без ракетной струи.
|
||
|
||
**Профили.** `M48n6`: `cold=20`, `eject=2.4`, `eject_drag=0.07` — 1,00 с; `M77n6`: `cold=24`,
|
||
`eject=2.6`, `eject_drag=0.07` — 1,20 с; `IrisT`/`M9m338`: `cold=8`, `eject=1.8/1.6`,
|
||
`eject_drag=0.1` — 0,40 с. Рельсовые профили (`cold=0`) получили только spool тяги; ранний
|
||
поворот на рельсовом пуске был проверен и отменён: он уводил Pac3 в землю на холмах и валил
|
||
`an_interceptor_over_rolling_land_does_not_hit_the_ground`. `speed`, `accel`, `turn`, `pk`,
|
||
`fuse` и правила столкновений не менялись.
|
||
|
||
**RM48U.** `EJECT_TICKS` 5 → 20. Выброс неосвещённый и замедляется; зажигание на `EJECT_TICKS`,
|
||
факел/звук/голос с этого момента; насчитанное время полёта включает выброс; дым в записи
|
||
начинается от точки зажигания, а не от горловины. Live, repeat и export читают одно и то же
|
||
состояние `lit`/записанное `MotorIgnition`.
|
||
|
||
**Проверки.** core release **206 + 8 сценариев**, 8 ignored; адаптер **67 + 1 boundary +
|
||
5 сценариев**; настоящий `.hmod` — **30 шагов** `wasm_contract.toml`; `gen_catalog.py --check` и
|
||
`gen_models.py --check` без расхождений; `test_gen_assets.py` — 10 тестов; `git diff --check` чист.
|
||
Новые регрессии: стадии по порядку и отсутствие powered-эффектов до зажигания; `guided` не раньше
|
||
`cold` для всех профилей; непрерывность скорости/поворота и throttled первый powered тик; P1-SUN
|
||
без факела; записанное зажигание через `cold` тиков и `missile_is_lit` по нему; RM48U без
|
||
голоса/факела до зажигания и ровно один звук зажигания.
|
||
|
||
**Баланс.** Сетка #120 (6 вариантов × 8 seed, 2208 целей на сторону, baseline `dc36d67`):
|
||
сбито 1988 → 1939, прорывов 216 → 258, ракет 3461 → 3467; `near200` 318 → 311, `far1500`
|
||
338 → 334, `hills20` 334 → 319; `energy_lost` 0 → 0, `expired` 1 → 0, scrub 2 → 4.
|
||
`SAMS=20 SIZE=12`, 12 seed: 203 → 201 сбитых, 1 → 3 прорыва, 355 → 360 ракет. Отдельные CSV:
|
||
[сетка](measurements/issue-129-grid.csv), [рейды](measurements/issue-129-balance.csv).
|
||
Это намеренное следствие более длинного старта; скрытого повышения `pk` или максимальной скорости нет.
|
||
|
||
**Съёмка.** Scratch `HORIZON_HOME=/private/tmp/issue-129-visual`, плоский мир `ColdLaunch129`,
|
||
Metal/offscreen, мод и клиент из `4b013fb`. Экспорт `cold129d`: 1280×720, 30 fps, 403 кадра,
|
||
13,43 с, h264 + AAC 48 кГц (mean −15,7 dB). План «Пуск» — кадры 94–152: выброс на 94, крупный
|
||
факел на 128 (≈1,13 с, правило 1,00 с), разгон до 152. Файл и кадры:
|
||
[docs/media/cold-launch](media/cold-launch). **Ограничение:** у агента нет ввода изображений,
|
||
поэтому композиция и красота кадров человеком не оценены; проверены только факт/параметры
|
||
съёмки, границы планов и появление факела. Отдельный very-close статичный план выброса и live-MP4
|
||
не сняты (`--offscreen` без окна); это не полная визуальная приёмка.
|
||
|
||
### Продолжение #122 (второй налёт из UI)
|
||
|
||
Точный лог Windows: `stopped in UI event (fuel budget 1000000000, remaining Some(0))`,
|
||
backtrace `known_units ← order_raid ← launch_wave ← ui_select` — другой класс, чем тик
|
||
`1f28f83`. Причина: `order_raid` пересчитывал снимок известных комплексов на каждом дроне,
|
||
а сам снимок был квадратичен по журналу и группировке. Исправление: один снимок на приказ
|
||
(+ O(журнал+группировка) через множества, `price_grid_on` переиспользует свой снимок);
|
||
семантика целеуказания и выбора носителей не менялась, сверка с прежним предикатом в
|
||
нативном тесте, `/stats` в WASM совпал побайтово. Лимиты не трогали, HD/запись не отключали,
|
||
размер волны не ограничивали.
|
||
|
||
Воспроизведение в настоящем WASM (измеренный fuel): до — второй приказ 111 дронов исчерпывает
|
||
ровно 1e9 в событии (failures=1), тот же приказ 11 дронов — 247805865; после — тот же сценарий
|
||
целиком: max command 32946177/1e9, tick 236581288/300M, ошибок 0. Диагностический гость
|
||
(`trigger.patch` вызывает обработчик `ui_select(BTN_LAUNCH)`) и точный сценарий:
|
||
[issue-122-ui-fuel](measurements/issue-122-ui-fuel/summary.json). Воспроизвести удалось только
|
||
с разнесённой группировкой: стек в одной точке гибнет от одной боеголовки (`known=0`, приказ
|
||
дёшев) — у пользователя силы дожили до второго приказа.
|
||
|
||
Закреплённые регрессии: нативные «эквивалентность снимка» и «второй умный приказ с толстым
|
||
журналом принят и запущен»; тяжёлый WASM fixture расширен двумя умными приказами через
|
||
`airdef preset` + `airdef raid` (функция кнопки ПУСК) — 4 принятых приказа, SHA fixture
|
||
обновлён; лёгкий контракт 30 шагов с `preset` и отказом пустой волны. Новая команда
|
||
`airdef preset <n>` — script-эквивалент кнопки готового состава.
|
||
|
||
Последний общий прогон: core release **195 + 8 сценариев** / 7 ignored; адаптер **65 + 1
|
||
boundary + 5 сценариев**; лёгкий WASM **30 шагов**; тяжёлый WASM **283 шага** (tick max
|
||
182413686/300M, command max 6939810/1e9, failures=0), отчёт
|
||
[flat-wasm-raid-budget](measurements/flat-wasm-raid-budget.json); 6 тестов отчёта budget
|
||
и build-тест прошли. Ни одно issue не закрыто; пуш не выполнялся.
|
||
|
||
### Контрольная точка 3 ноября
|
||
|
||
Код предыдущих четырёх задач интегрирован до `6e2d661`. Продолжение #102/обратного вывоза
|
||
опубликовано до `aab3e78`; последнее продолжение расследования налётов — `2932364`.
|
||
Текущий движок — `f95f03c08ec2916a0e5bd3b2d4a2a790e7937b47`, API 27.
|
||
README описывает доступное поведение; `IMPLEMENTATION-STATUS.md` отделяет реализацию от приёмки.
|
||
Ни одно issue не закрыто. Участники: Codex (интеграция, #119, ревью, документация) и три
|
||
GPT-6.1 Terra (энергетика, типизированная доставка, излучение). Новые числа — только игровые.
|
||
|
||
| Issue | Опубликованные контрольные коммиты |
|
||
|---|---|
|
||
| #119 | `c96676f`: укрытия, read-only geometry, четыре проверки |
|
||
| #118 | `c775f31`, `952a10f`: доставка ТЗМ, возврат снятой ракеты ПВО, реальное движение и save/restore |
|
||
| #121 | `26b258f`, `0df225a`, `88f9405`, `9e1e0f5`, `b377a5d`, `ce76f20`: излучение, UI/save/replay, наблюдения, last-known SEAD |
|
||
| #120 | `143d9b2` → `e38ca0d`, затем `a7b8cb9`: конечное горение, coast, swept impact, диагностика, звук, исправленное ветвление фаз |
|
||
| WASM | `6e2d661`: проверка On/Off и запрета управления отсутствующим радаром, 24 шага |
|
||
|
||
Для обновления существующей установки сначала `git switch kimi-work`, затем `git pull --ff-only`,
|
||
`python3 tools/airdef.py all`, `python3 tools/airdef.py play`. `all` обновляет точный сабмодуль,
|
||
собирает игру/мод и устанавливает пакет. На Windows обычно используется `python` вместо `python3`.
|
||
|
||
### Продолжение #102 и обратного вывоза RM48U
|
||
|
||
- `d3378b3`: `fork()` синхронизирует URL из `.gitmodules` даже на fast path совпавшего pin.
|
||
Реальная Git-регрессия воспроизводит старый upstream URL и новый коммит, отсутствующий в локальном clone.
|
||
Это исправляет наблюдавшееся на Windows `upload-pack: not our ref 86b1014…`; пользователь подтвердил
|
||
ручное исправление через `git submodule sync --recursive` до публикации автоматизации.
|
||
- `bb931ab`, `e9fbaba`, `b0085cb`: `ground-unload` вдали от склада резервирует одну RM48U и вызывает
|
||
ТЗМ. `ground-cancel` отменяет ещё ожидающий вывоз/доставку; после забора груз продолжает путь на склад.
|
||
Проверены физическое движение и полный save/restore до забора и после забора, без телепортации запаса.
|
||
- `910f02c`: потеря зарезервированной ракеты вместе с ячейками сбрасывает заявку на вывоз.
|
||
Иначе можно было создать сохранение, которое loader сам отвергал как несовместимое состояние.
|
||
- Двигатель: `f4c1131d3`, затем `c89c78c47` опубликованы в `horizon-airdef:airdef` до обновления gitlink.
|
||
API27 добавляет acoustics-status и capture-owner-status, scoped к запуску мода и поколению мира.
|
||
Остановка capture проверяет владельца; смена мира снимает незапущенный запрос или посылает cancel.
|
||
- `910f02c`: команда `/airdef capabilities`; дальний звуковой fallback сохраняется до подготовки
|
||
акустического пути, экспорт принимает только своё подтверждённое состояние; Arming ждёт до 600 тиков.
|
||
`prepared` — milestone хоста, не GPU fence, не результат каждой акустической трассы и не готовый MP4.
|
||
- `4dddea2`, `8d5d0d1`: opt-in `test-budgets`, отдельный ручной workflow и JSON provenance;
|
||
тесты ошибок сборки, stale identity и ненулевого runtime exit. Native success не заменяет actual WASM.
|
||
- `aab3e78`: repo-local packer использует target-cache закреплённого движка; `CARGO_TARGET_DIR`
|
||
не переопределяется для сборки самого мода и не уводит `.hmod` в чужое дерево.
|
||
|
||
Проверки объединённого состояния: core release **190 + 4 сценария**, 7 ignored;
|
||
адаптер **60 + 1 boundary + 4 сценария**; лёгкий WASM **26 шагов**, тяжёлый **69 шагов**;
|
||
Git-регрессия и три теста failure report прошли. Исходный профиль запрашивал 64 ПВО + 160 целей,
|
||
602 tick / 300 frame; JSON фиксирует clean `aab3e78`, pin API27 и совпадающий build identity.
|
||
Полные данные: [budget report](measurements/api27-wasm-budget.json).
|
||
**Исправление оценки покрытия:** второй запрос исходного 69-шагового сценария отклонялся во время
|
||
подготовки первого. Его успешный exit не подтверждает 160 запущенных или одновременно летящих целей.
|
||
В архивный JSON добавлена поправка; исправленный сценарий проверяет ответы. Для настоящего stress
|
||
также нужны ответы render-world: прежний sandbox их не давал и часто проверял лишь ожидание земли.
|
||
|
||
Дополнительно два свежих профиля игры на Metal/no-audio: smoke off не ready, HD smoke ready;
|
||
при HORIZON_ACOUSTICS=1 + HORIZON_NO_AUDIO=1 акустика enabled/request=true, prepared=false,
|
||
awaiting-backend. Выход через quit подтверждён. Это не прослушивание, не benchmark и не новый MP4.
|
||
Правила API, пределы проверок и команды: [ENGINE.md](ENGINE.md).
|
||
|
||
## Двойные налёты и плотность следов
|
||
|
||
Перед продолжением читать [RAID-STABILITY.md](RAID-STABILITY.md): два соответствующих класса
|
||
отключения теперь воспроизведены и исправлены — тик (`1f28f83`) и событие UI на втором
|
||
приказе (#122, `order_raid`/known_units); приёмка конкретного Windows-сеанса остаётся.
|
||
Текущий исправленный WASM stress проверяет две принятые волны, обслуживаемую плоскую геометрию
|
||
и 184 воздушные цели в конкретный момент; новый [JSON](measurements/flat-wasm-raid-budget.json)
|
||
заменяет прежнее недостаточное доказательство покрытия. Лимиты fuel/memory не увеличивались.
|
||
`86e1d272e` в движке улучшает callback/fuel/error-chain diagnostics; `ed7615d36` добавляет только
|
||
необязательную flat-world среду тестов и отказывает неожиданным ошибкам команд в Script.
|
||
Основная клиентская логика сцены этим test helper не заменяется.
|
||
|
||
`692ae77`: HD-треки ракет оптически плотнее на 12% через общий `rocket()` и cruise style.
|
||
SAM/RM48U, крылатые/сверхзвуковые/баллистические треки покрыты; сегментов, радиусов и времени не добавлено.
|
||
Пожары и дымка винтовых дронов не менялись. Обычные ванильные fallback-частицы этим множителем не меняются.
|
||
Пять существующих smoke-тестов прошли; прирост FPS и приёмка новой плотности на Windows не заявлены.
|
||
|
||
Последний общий прогон: core release190 + 6 сценариев, 7 ignored;
|
||
адаптер60 + 1 boundary + 4 сценария; лёгкий WASM26; тяжёлый WASM271; 5 тестов отчёта.
|
||
Нативные overlap-тесты — `airdef-core/tests/scenarios.rs`:
|
||
два стандартных массовых пресета и два налёта с разделением «Орешников», оба со встроенной записью.
|
||
|
||
Продолжение по пользовательскому HD-отключению: `1f28f83` заменяет полный `known.retain` при
|
||
каждом взрыве на удаление прежнего квадрата; семантическая эквивалентность проверена.
|
||
В диагностическом WASM max-menu ×2 раньше был trap tick1835 на 300M; после фикса полный
|
||
3602-tick прогон прошёл, max135273690. Гость отличается от baseline только временным trigger hook
|
||
состава меню и исправлением. Это измерение fuel, не прирост FPS. Артефакты/ограничения — в RAID-STABILITY.
|
||
`cc48302` добавляет публичный native max-menu regression; `3b470dd` закрепляет опциональный
|
||
engine `test --fuel-profile` (не новый SDK API и не увеличение лимитов).
|
||
|
||
`cbd6067` исправляет сообщение о пустом соседнем складе, показывает типизированные запасы на M,
|
||
уточняет подсказку разведки и убирает устаревшую строку README о якобы отсутствующей доставке.
|
||
[GROUND-STRIKES.md](GROUND-STRIKES.md) — инструкция с различием номеров своего С-400/склада и вражеской ПУ.
|
||
Default RM48U=0; `ground-stock` задаёт остаток, `ground-load` грузит одну; поздний трек не даёт fix.
|
||
`ab737e7`: actual WASM теперь 27 шагов с проверкой ошибки пустого соседнего склада.
|
||
|
||
После фикса полный core: 192 passed + 6 сценариев / 7 ignored; новый max-menu сценарий отдельно
|
||
прошёл седьмым. Адаптер60 + 1 boundary + 4 сценария; light WASM27; engine /spin fuel-profile test прошёл.
|
||
|
||
## Исходная контрольная точка этой серии
|
||
|
||
Исходное состояние этой серии: `2689d93`; движок закреплён на
|
||
`86b1014a9218e68041444cbf52db02a85107c815`, уровень API 26. Рабочее дерево было чистым,
|
||
коммиты опубликованы. Текущий точный pin всегда смотреть в `git ls-files --stage horizon`,
|
||
а происхождение собранного пакета — в `airdef-mod/target/horizon-mod/build-identity.json`.
|
||
|
||
Четыре задачи были заказаны владельцем отдельно:
|
||
|
||
| Issue | Заказанный остаток |
|
||
|---|---|
|
||
| #120 | Конечная работа двигателя перехватчика, coast, энергия манёвра, согласованные эффекты |
|
||
| #118 | Типизированная доставка RM48U через ТЗМ; не создавать неподтверждённые носители |
|
||
| #121 | Переключение излучения, сохранение разрешённого внешнего целеуказания, разведка излучателей |
|
||
| #119 | Ослабление непрямого урона известным укрытием, ограниченная политика Unknown |
|
||
|
||
Исполнители работали в отдельных worktree; их проверенные коммиты перенесены в `kimi-work`.
|
||
В ветках `codex/issue-*` сохранены промежуточные версии; продолжать нужно от `kimi-work`,
|
||
где разрешены конфликты общего формата сохранений и сигнатур боевых функций.
|
||
|
||
## Что уже сделано в предыдущей серии Codex
|
||
|
||
- `airdef-core` компилирует исходники `airdef-mod/src/sim/mod.rs` через `#[path]`.
|
||
Это единый источник симуляции, но разделение домена и представления ещё не завершено (#87).
|
||
- Исправлены остаточные терминальные коллизии ответных ударов и скачок после world-query (#106/#117).
|
||
Восстановление ответов мира не даёт права телепортироваться к плановой точке.
|
||
- Доработаны запись ответных ударов, launch one-shot и live-след (#109), lifecycle показа и часы
|
||
отложенного звука (#88/#89). Полная унификация всех потребителей сцены ещё не закончена.
|
||
- Движок вынесен в отдельную репу; сборщик берётся из закреплённого форка, а не случайного PATH.
|
||
Есть build identity, настоящий WASM contract и handshake доступности дыма API26 (#102/#104).
|
||
- Обновлён upstream Horizon 0.2.97, исправления очереди/инвалидации акустики сохранены.
|
||
Измерительные fixtures, notices и лицензии добавлены; прослушивание и массовый CPU-тест ещё нужны (#96).
|
||
- Pipeline поддерживает authored overrides, manifest и экспорт итоговых габаритов в
|
||
`sim/generated_bounds.rs`. Навигация учитывает footprint. Generated output вручную не редактировать.
|
||
- Улучшены модели наземной техники; степень сходства и материалы ещё промежуточные (#115).
|
||
Галерея: `MODEL-PILOTS.md`, `VEHICLE-MODELS.md`; это не приёмка всех летательных аппаратов.
|
||
- `966b52f`: раздельные ПУ С-400/С-500, роли Radar Search/Engagement/Height/Multi.
|
||
Чужая подсказка разрешает огневому радару уточнение, но не подменяет собственное измерение.
|
||
- `966b52f`: резерв конкретной ячейки, отдельные крышки ТПК, выброс до включения двигателя.
|
||
RM48U — условный игровой профиль С-400; AA и ground rounds делят четыре физические ячейки.
|
||
Боеприпас списывается при выбросе, не при выдаче отложенного приказа; отмена не расходует его.
|
||
- Конфигурация, маска использованных ячеек и положение крышек включены в save/replay.
|
||
Smoke начинается в точке зажигания. С-500 ограничен двумя ракетами по числу ТПК;
|
||
старое сохранение с четырьмя загруженными ракетами при загрузке ограничивается двумя.
|
||
- `2689d93`: документированы источники архитектуры батареи, команды, скриншоты и границы визуальной проверки.
|
||
|
||
## Проверки и воспроизведение
|
||
|
||
На `966b52f`: 169 тестов ядра + 4 сценария прошли, 7 ignored; адаптер — 53 теста,
|
||
1 boundary и 4 сценария прошли; WASM contract — 21 шаг. Это исходная контрольная точка,
|
||
не результат новых четырёх задач. На `2689d93` был собран чистый пакет (`airdef_dirty=false`).
|
||
|
||
На итоговом объединённом коде `6e2d661` прошли:
|
||
|
||
- `airdef-core --release`: **187 passed, 7 ignored**, плюс **4 сценария**.
|
||
- `airdef-mod`: **56 passed**, **1 boundary**, **4 сценария**.
|
||
- Настоящий WASM-пакет: **24 шага**, включая команды излучения и typed ammunition.
|
||
- `gen_models.py --check`: различий нет; в этой серии геометрия ассетов не менялась.
|
||
|
||
Полный core здесь запускался с `--release`; это не прогон всех семи ignored-тестов.
|
||
Отдельно выполнен сравнительный ignored-тест баланса (см. ниже).
|
||
|
||
```sh
|
||
cargo test --release --manifest-path airdef-core/Cargo.toml
|
||
cargo test --manifest-path airdef-mod/Cargo.toml
|
||
python3 tools/airdef.py test-wasm
|
||
python3 tools/gen_models.py --check
|
||
python3 tools/test_gen_assets.py
|
||
git diff --check
|
||
```
|
||
|
||
На этой macOS cargo находится в `~/.cargo/bin/cargo`. GUI-проверки запускать с отдельным
|
||
`HORIZON_HOME` и рабочим каталогом тестового мира; не использовать реальные сохранения владельца.
|
||
Metal-проверки не подтверждают Windows/Linux, HDR всех платформ или прослушивание звука.
|
||
Скриншоты крышек подтверждают только их позу: полного видео выброса/зажигания пока нет.
|
||
|
||
## Технические детали новой серии
|
||
|
||
### #120: энергетика и единственный исход перехвата
|
||
|
||
`MissileKind::burn_ticks` задаёт конечное время горения: простой профиль равен 2/3 игрового
|
||
`spec.life`; для электрического P1-SUN используется весь срок жизни. Это не отдельная модель
|
||
топлива и не реальные кривые тяги. `Missile::engine_lit` — общий источник состояния мотора.
|
||
|
||
Три ветки скорости разделены явно: cold (`age <= cold`, множитель 0.97 с прежним минимумом 0.4),
|
||
powered (`+accel`, ограничение `spec.speed`), coast (множитель 0.995 без холодного минимума).
|
||
После выгорания доступный поворот масштабируется текущей скоростью; при падении ниже 25%
|
||
игрового максимума перехват заканчивается диагностикой `MotorEnergyLost`. Срок жизни остаётся
|
||
отдельным ограничением. Полноценной аэродинамики, гравитационной баллистики и расхода топлива по тяге нет.
|
||
|
||
Swept fuse учитывает движение обоих объектов за тик; `Impact` получает точку closest approach
|
||
и относительную скорость того же проверенного отрезка, включая удержание на Unknown.
|
||
Геометрию не заменяет отдельный бонус «встречная/догонная». Один остаточный pk зависит от
|
||
фактической скорости перехватчика и существующего состояния носителя/дальности.
|
||
Повторный штраф по stealth и старый штраф по абсолютной скорости цели удалены из этого броска;
|
||
обнаружение/сопровождение по-прежнему учитывают stealth в своих местах.
|
||
|
||
`MotorBurnout` и `MotorEnergyLost` записываются в scene journal; tally отличает потерю энергии.
|
||
Live и replay прекращают факел и новый дым по одному состоянию. `Out::engine_stops` немедленно
|
||
снимает голос мотора на burnout, не ждёт обычной трёхтиковой grace пропавшего источника;
|
||
при переходе на replay/export live-команды остановки отбрасываются вместе с live-выводом.
|
||
Перемотка восстанавливает моторное состояние из записи, включая возврат до выгорания.
|
||
|
||
При ревью был найден и исправлен недостижимый coast: ранний прототип проверял `!lit`,
|
||
что перехватывало и cold, и coast. Не переносить один `143d9b2` без последующих исправлений.
|
||
`a7b8cb9` содержит отдельный тест точных правил скорости всех трёх фаз.
|
||
|
||
Сравнительная выборка **только изменения #120**, отдельно от #119/#118/#121:
|
||
baseline `2689d93`, агентская итоговая ветка `508f9a0` (её исправление перенесено как `a7b8cb9`).
|
||
12 одинаковых seed, `SAMS=20 SIZE=12`: 204 цели в каждой версии;
|
||
baseline — 202 сбитых, 2 прорыва, 367 ракет; новое — 203, 1, 355.
|
||
Это небольшой регрессионный ориентир, не доказательство баланса всех типов/дальностей.
|
||
Данные каждого seed: [CSV](measurements/issue-120-balance.csv).
|
||
|
||
Расширенная сетка остатка #120 (ветка `agent/issue-120-balance`, коммит см. в git):
|
||
baseline `2689d93` против текущего `e6a7000` (то есть с #118/#119/#121 и сменами движка, не только #120),
|
||
6 вариантов × 8 одинаковых seed, рейд `SIZE=36` (46 целей: Gerbera 10, Shahed 13, Geran3 5, Х-101 5,
|
||
«Калибр» 4, Х-22 2, «Циркон» 1, «Искандер» 4, «Кинжаль» 2; «Орешник» harness исключает, как в `one_raid`):
|
||
`batt10/20/50` (10/20/50 ЗРК, дистанция 500), `near200` (20 ЗРК, пуск в 200), `far1500` (20 ЗРК, пуск в 1500),
|
||
`hills20` (20 ЗРК, холмы, 500). Кластер тот же `MIX` из 10 систем: `SAMS=10` — по одному комплексу каждого типа.
|
||
Итого 2208 целей на сторону, 96 строк: [CSV](measurements/issue-120-grid.csv). Команда (игнорируемый тест
|
||
`sim::balance::issue_120_grid`, только harness-поля в `Result`, поведение симуляции не менялось):
|
||
|
||
```sh
|
||
SEEDS=1-8 cargo test --release --manifest-path airdef-core/Cargo.toml issue_120_grid -- --ignored --nocapture
|
||
```
|
||
|
||
Факты сетки (суммы по 8 seed каждого варианта, baseline → текущий): сбитые 297→301 (batt10), 331→334 (batt20),
|
||
357→363 (batt50), 313→318 (near200), 328→338 (far1500), 328→334 (hills20); прорывы 70→66, 37→34, 11→5,
|
||
53→48, 40→30, 39→33. Промахов по взрывателю заметно меньше (например far1500: 70→38). Расход ракет близок
|
||
(516→504 … 597→583). На холмах потери у рельефа: разбито 14 и 14, самоуничтожений у хребта 6→2.
|
||
Прорывы во всех вариантах — преимущественно баллистические и быстрые классы («Искандер», «Кинжаль», Х-22,
|
||
«Циркон»); у медленных Shahed/Gerbera прорывов почти нет (0–2 на вариант), у ложных Gerbera — ноль ни разу.
|
||
`MotorEnergyLost` не сработал ни в одном из 48 сценариев текущего кода, `expired` ≤ 1 на вариант: coast
|
||
существует, но бои на этих дистанциях заканчиваются раньше, чем сопротивление 0.995/тик опустит скорость
|
||
ниже порога 25% (и раньше выработки ресурса). Это измеренное свойство профиля, не подтверждённый дефект,
|
||
поэтому параметры не менялись. Путь потери энергии покрыт юнит-тестом регресса (`regress.rs`), а не этой сеткой.
|
||
|
||
```sh
|
||
SAMS=20 SIZE=12 cargo test --release --manifest-path airdef-core/Cargo.toml sim::balance::one_raid -- --ignored --nocapture
|
||
```
|
||
|
||
### #118: один физический груз и восстанавливаемая заявка
|
||
|
||
`ground_request` у С-400 — заявка на **одну** RM48U. Рядом со складом остаётся прямая передача;
|
||
вне радиуса склада та же команда `ground-load` создаёт заявку, если есть запас и живая ТЗМ.
|
||
ТЗМ забирает одну ракету из конечного запаса, едет к исправному развёрнутому носителю и передаёт
|
||
её в общие ячейки. Если вытеснена ракета ПВО, ТЗМ везёт её обратно на склад. Generic reload
|
||
не создаёт RM48U. Ударная заявка имеет приоритет перед generic reload.
|
||
|
||
Поля `ground_stock` / `air_48n6_stock` на складе — остатки, на ТЗМ — физический груз двух типов.
|
||
Заявка, груз и марш сохраняются; оперативный `job` реконструируется после загрузки.
|
||
Передача запрещена во время движения, подготовки пуска, перезарядки, отказа ПУ и при отсутствии
|
||
вместимости. Разрушение очищает заявку/груз; repair не восстанавливает уничтоженный typed stock.
|
||
Проверены настоящее движение `Sim.step` по плоскому WorldSource, передача/возврат, истощение,
|
||
прерывание, несовместимость, заполненные ячейки, unload и полный `state_text → apply_state`
|
||
с продолжением маршрута. GUI-доставка на сложном рельефе ещё не проверена.
|
||
|
||
Новых носителей вроде Бука не добавляли: один публичный игровой профиль не даёт основания
|
||
приписывать его всем ЗРК. Обратный вывоз из удалённого носителя автоматизирован в продолжении выше.
|
||
Новая команда `ground-cancel` отменяет ожидающую заявку, но не возвращает уже забранный груз в ПУ.
|
||
ТЗМ вмещает два типизированных места RM48U/48Н6; прежние абстрактные generic reload-пакеты учитываются отдельно.
|
||
Вывезенная ракета возвращается в конечный запас; ПВО восстанавливается при заборе только из реально
|
||
имеющейся у ТЗМ ракеты 48Н6 и свободной ячейки. Резерв одной RM48U учитывается при выборе носителя удара.
|
||
|
||
### #121: излучение отдельно от сети
|
||
|
||
`radar_on` — сохраняемая настройка, default=true для старых миров. Команда
|
||
`/airdef radar <номер> on|off` и кнопка карты меняют её; у launcher-only нет своего активного радара.
|
||
`radar_up` учитывает этот флаг. Off запрещает новые own measurements, но старые треки стареют
|
||
обычно и разрешённый сетевой приём сохраняется. Это не выключение корпуса/связи/ГСН ракеты.
|
||
Огневой радар, обязательный для отдельной ПУ, в Off не авторизует новые пуски.
|
||
|
||
Условный наблюдатель-противник раз в 20 тиков обнаруживает работающие радары в 1600 блоках.
|
||
Это упрощённый игровой датчик по расстоянию, без LOS/радиопеленгации и ошибки оценки позиции.
|
||
Контакт хранится как последняя наблюдённая точка и стареет за 6000 тиков. Off не стирает
|
||
контакт, не телепортирует его вслед за носителем и не предотвращает раскрытие пуском/РЭБ.
|
||
|
||
В `raid::radar_strikes` удалён отдельный обход: раньше SEAD выбирал любую Radar и её настоящий
|
||
`pos`, даже если общий planner уже работал по наблюдениям. Теперь SEAD берёт те же last-known
|
||
контакты и их возраст. Явный sandbox-режим `Aim::ObjectsAndSams` по-прежнему выбирает расстановку
|
||
напрямую; не путать его с умным противником. Проверки покрывают необнаруженную молчащую РЛС,
|
||
смену позиции без наблюдения, повторное раскрытие, старение, relay freshness и seek.
|
||
|
||
### Совместимость текстового состояния
|
||
|
||
Сохраняем строки `sam` расширением справа; индексы ниже включают слово `sam` как поле 0.
|
||
|
||
| Индекс | Значение |
|
||
|---|---|
|
||
| 22–24 | ground_rounds, ground_stock, air_48n6_stock |
|
||
| 25–27 | mode, radar_role, spent_cells |
|
||
| 28 | ground_request: 0/1, только С-400 |
|
||
| 29 | radar_on: true/false, отсутствие означает true |
|
||
| 30 | ground_return: true/false, резерв вывоза одной RM48U из С-400 |
|
||
| 31 | return_cargo: true/false, возвращаемая RM48U на ТЗМ |
|
||
|
||
Две агентские ветки сначала заняли поле 28 одновременно; при интеграции излучение перенесено
|
||
в 29, опубликованная совместимость заявки сохранена. Проверка полного roundtrip одновременно
|
||
содержит ожидающую доставку и выключенный радар. Новые значения входят в durable mark;
|
||
настройка излучения также в `SamKey` и ghost replay. Не переиспользовать эти позиции для нового поля.
|
||
|
||
### #119: непрямой взрыв и укрытия
|
||
|
||
`WorldCache::blast_cover` читает текущую occupancy без запросов мира и без загрузки чанков.
|
||
Три луча к репрезентативным точкам корпуса дают общий множитель для корпуса и узлов;
|
||
для отмеченного объекта считается своя тройка. Известное укрытие оставляет 15% игрового
|
||
воздействия по перекрытому лучу, открытый — 100%; края/проёмы дают промежуточное значение.
|
||
Все лучи этого вызова читаются до отправки разрушения в движок. Исходный воксель пропускается,
|
||
чтобы контакт на поверхности стены не закрывал взрыв сразу во все стороны.
|
||
|
||
Unknown, stale и слишком старые секции считаются **неразрешёнными**. Ожидание равно нулю:
|
||
для них сохраняется прежний неослабленный урон как консервативная игровая политика,
|
||
а не утверждение, что там воздух. После ответа мира этот взрыв не пересчитывается.
|
||
Нет очереди отложенного урона и повторного применения на world reply. Частичные блоки
|
||
на этом этапе считаются занятым кубом; тонкая collision shape для blast отдельно не запрашивается.
|
||
Прямой вооружённый контакт не ослабляется этим множителем.
|
||
|
||
Проверки `sim::damage::cover_tests`: стена/проём для корпуса, узлов и объекта;
|
||
Unknown и поздний ответ без повторного урона/запросов; прямое попадание; устаревшая геометрия;
|
||
пропуск исходного блока. Все четыре прошли в объединённом наборе.
|
||
|
||
### #127: устойчивая очередь маршрутов после массового перенацеливания
|
||
|
||
Реализована только узкая часть issue: `sim::ew::RouteJob` — задание с ID дрона, новой целью,
|
||
ID цели-юнита, версией знаний и общим для тика снимком `known_units` (`Arc`). `adapt` по-прежнему
|
||
выбирает новые цели раз в 40 тиков, но маршруты теперь достраивает `replan_retargets` **каждый
|
||
тик**: одно задание или несколько, пока не исчерпан `WORK_PER_TICK`. Незавершённое задание
|
||
остаётся в `Sim::retargets` (FIFO) и не теряется; повторный выбор того же дрона заменяет задание
|
||
на месте, поэтому очередь не растёт.
|
||
|
||
Старый маршрут не очищается при перенацеливании: дрон следует готовому безопасному участку,
|
||
пока новое задание не получит маршрут. Если задание отменяется (дрон погиб, цель изменена снова,
|
||
юнит уничтожен), маршрут очищается как fallback — дрон летит прямо до следующего адаптивного тика.
|
||
Lancet, баллистика и decoy, как и раньше, летят прямо: для них задание не создаётся. В полёте
|
||
следующая известная цель берётся только если до неё есть разворот; цель сзади внутри радиуса
|
||
разворота пропускается, и дрон завершает текущий заход, а не получает невыполнимый маршрут.
|
||
Повторный план использует снимок разведки исходного выбора цели и известный рельеф **той же версии
|
||
задания**: `KnownRelief` (сетка и высоты) лежит в `Arc` внутри `RouteJob`, `Sim::known_relief` хранит
|
||
тот же `Arc` только для новых заданий. Завершение другой умной волны заменяет глобальный указатель,
|
||
но не снимок ожидающего job. Выборка рельефа, цены и кэш маршрутов в `replan_retargets` ключуются
|
||
версией знаний целиком — adapt tick плюс указатели на снимки known units и рельефа, — а не только
|
||
рельефом: два job с одинаковым рельефом, но разными known units, не делят price/route grid. За
|
||
пределами известной сетки — Unknown, новая карта не раскрывается.
|
||
|
||
Очередь — рантайм-состояние планировщика. Дроны и очередь запуска в текущий `state_text` не входят,
|
||
поэтому формат save не менялся и старые saves читаются как прежде; запись replay по-прежнему хранит
|
||
траектории, а не задания. Регрессии: `mass_retarget_finishes_its_route_jobs_over_the_budget`
|
||
(40+ ретаргетов, прогресс каждый бюджетный тик, старый маршрут до нового, нет пустых маршрутов)
|
||
и `mass_retarget_is_deterministic_and_never_duplicates_a_drone` (тот же seed — те же цели и
|
||
маршруты; повторный `adapt` не создаёт дублей). Полный EnemyPicture, общая модель возможностей,
|
||
честное разделение наблюдения/истины и структурированное намерение остаются за #127.
|
||
|
||
## Что следующему агенту проверить в первую очередь
|
||
|
||
1. На Windows: обновление сабмодуля через `all`, сохранение/перезаход, рабочий цикл игры.
|
||
2. В GUI: полный холодный пуск крупным планом; карточка On/Off; маршрут ТЗМ по сложной местности;
|
||
разрушение стены одним взрывом и защита от того же взрыва по исходной геометрии.
|
||
3. Прослушать прекращение голоса двигателя при burnout и seek/экспорт вокруг этой границы.
|
||
4. Расширенная балансировка #120 (батареи/дальности/классы) записана в `measurements/issue-120-grid.csv`;
|
||
остаются прослушивание голоса мотора и платформенная приёмка.
|
||
5. Продолжить оставшиеся архитектурные #87/#88/#89/#91/#102 и художественный #115 по статусной таблице.
|
||
|
||
Это незавершённая платформенная/визуальная приёмка, а не отсутствие перечисленного выше кода.
|
||
Прогон четырёх задач не запускал GUI/MP4. В продолжении API27 есть два коротких игровых smoke,
|
||
но новый MP4, прослушивание и платформенная приёмка Windows/Linux по-прежнему не выполнены.
|
||
|
||
## Правила продолжения
|
||
|
||
- Не закрывать issue даже после исправления; сообщать проверенный объём и остаток в комментарии.
|
||
- Не переписывать gitlink на плавающую ветку. Изменения движка сначала публикуются отдельно.
|
||
- Не путать наблюдённый трек с настоящим положением цели и `own_tick` с relay.
|
||
- Не менять live-мир при seek; replay воспроизводит последствия, не применяет урон повторно.
|
||
- Не считать Unknown свободным пространством или подтверждённой стеной без явной игровой политики.
|
||
- Новые числа — игровой масштаб, не реальные характеристики вооружения.
|
||
- Токен Gitea читать из локального файла во время запроса; не коммитить и не печатать.
|