> **Дисклеймер:** Статья полностью написана автором проекта от первого лица. В ней описывается разработка математического алгоритма процедурной генерации речи на чистом TypeScript без использования LLM, внешних фреймворков и нейросетей ("dependencies": {}).
В прошлых статьях про инженерную архитектуру ГИГАХРУЩА мы разбирали, как в одном браузерном файле на чистом TypeScript держится тороидальный клеточный мир 1024x1024, кастомный WebGL-рэйкастер, симуляция A-Life и объемный генератор MESH PASS.
Сегодня мы расскажем историю создания речевого ядра проекта — историю о том, как попытка уйти от хардкода привела нас к теории категорий, дистрибутивной семантике и ошеломляющим открытиям: оказывается, если взглянуть на художественные тексты через чистую линейную алгебру, математика сама начинает «понимать» смысл слов и строить сюжетные конструкции.
В этой статье мы покажем, как с помощью векторного анализа корпусов, классов эквивалентности и PCA-осей нам удалось заставить Марковское ядро генерировать живую, контекстную речь для тысяч НПС — без сторонних фреймворков ("dependencies": {}), с задержкой 0 миллисекунд и 100% детерминизмом по SeedRng.
## 1. Откровение дистрибутивной алгебры: Как математика видит слова в тексте
Когда мы только подступили к задаче озвучки НПС (диалоги, баблы над головами, лог речи, посты в локальную соцсеть «Инфосеть Демос»), перед нами встала дилемма:
1. **Жесткий хардкод (If-Else / шаблоны):** Написать 5000 заготовок `"{npc} пришел в {place}"`. Скучно, видно швы, игроку надоедает через полчаса.
2. **Наивный Марков по сырому тексту:** Выдает колоссальное разнообразие, но страдает «синтаксической шизофренией» — НПС придумывает ложные факты, путает термины и уходит в бессвязный бред.
3. **LLM в облаке:** Недопустимо. Контракт ГИГАХРУЩА — автономный браузерный билд, который должен работать 60 FPS даже на кассовом терминале без доступа к сети.
Мы задались вопросом: можно ли обойтись без захардкоженных словарей и регексов, заставив математику саму извлечь структуру человеческого языка из литературы?
Мы взяли 11 обучающих корпусов (16,900+ предложений из «Пикника на обочине», «Самосбор-тредов», «Метро 2033», «S.T.A.L.K.E.R.», «Града обреченного», «Соляриса», советских инструкций Гражданской Обороны и ГОСТов) и написали офлайн-исследователь `scripts/discover_categories.ts`.
Мы не кормили алгоритму готовые словари. Вместо этого алгоритм скользил окном контекста по тысячам страниц и строил вектор окружения для каждого слова.
Затем мы ввели на словаре отношение эквивалентности, и произошло то, что поразило нас самих: математика без знания русского языка сама сгруппировала слова в идеальные смысловые кластеры!
Вот реальный фрагмент вывода нашего скрипта `scripts/discover_categories.ts`:
[Категория #20] (Векторный узел: ОБОРУДОВАНИЕ / ПАТРОНЫ)
патронов/патроны (+0.43), оборудования/оборудование (-0.20),
огонь (-0.11), заблокировать (-0.21)
## 2. Теория категорий и синтаксический граф
Из получившихся векторов и фактор-множеств мы построили динамический синтаксический граф. В нём узлы — это не просто слова, а категории эквивалентности, а рёбра — вероятности перехода между ними с учётом математических ограничений контекста.
Когда НПС оказывается в определенной ситуации (например, ремонт гермодвери на -88 этаже при отключенном питании), движок формирует вектор игрового контекста:
roomType: 'трансформаторная',
occupation: 'слесарь-параноик',
Этот вектор проецируется на построенный синтаксический граф. Выбор следующего атома реплики определяется прохождением по ребрам графа с максимальным скалярным произведением вектора состояния НПС и вектора контекста слова.
## 3. Примеры процедурной генерации речи в игре
### Пример 1: Находка ценного хабара (Гаусс-пушка)
- **Игровой контекст:** `floorZ: -12`, `roomType: кровавый_тупик`, `occupation: проходчик`, `dangerLevel: 60`, `foundItemValue: 5000`.
- **Результат генерации:**
> *«Проходчик долго осматривал гаусс-пушку. За такой хабар бригадир смены выставит три нормы пайкового концентрата у шлюза.»*
- **Разбор ядра:** Параметр ценности предмета `foundItemValue: 5000` дал максимальный векторный отклик, выбрав топовое вооружение и бытовой контекст ценностей.
### Пример 2: Слесарь-параноик у мокрого шва гермодвери
- **Игровой контекст:** `floorZ: 88`, `roomType: трансформаторная`, `powerStatus: 'off'`, `occupation: слесарь-параноик`, `dangerLevel: 40`.
- **Результат генерации:**
> *«Слесарь-параноик не верил тишине: у гермы шов мокрый. Сначала уплотнитель, потом разговор.»*
- **Разбор ядра:** Профессиональные теги гильдии слесарей `repair` вытащили из корпуса специфические бытовые действия ремонта и проверки герметичности.
### Пример 3: Лаборант НИИ в продувочной прачечной
- **Игровой контекст:** `floorZ: 104`, `roomType: продувочная_прачечная`, `occupation: лаборант НИИ`, `dangerLevel: 15`.
- **Результат генерации:**
> *«Лаборант НИИ установил дозиметр ДП-5В у продувочной прачечной. Реагенты в норме, инспекционный шлюз чист.»*
- **Разбор ядра:** Категория `science` подтянула профильные приборы (дозиметр ДП-5В) и канцелярит НИИ «Щит».
### Пример 4: Раненый домком просит помощи
- **Игровой контекст:** `floorZ: 3`, `roomType: жилая_ячейка`, `hp: 15`, `thirst: 95`, `recentTrauma: true`, `occupation: домком`.
- **Результат генерации:**
> *«Домком едва дышал на полу: грязный бинт и талон на воду решают быстрее любого разговора.»*
- **Разбор ядра:** Критический уровень потребностей сфокусировал ядро на предметах первой помощи, проигнорировав оружие и хабар.
### Пример 5: Мясник у Кровавого Алтаря (Пси-излучение)
- **Игровой контекст:** `floorZ: -69`, `roomType: алтарь_чернобожия`, `karma: -100`, `occupation: мясник`, `dangerLevel: 95`.
- **Результат генерации:**
> *«Пси-излучение заполнило кровавый тупик. Мясник шептал слова бетона, пока процедурная аномалия не закрыла шлюз.»*
## 4. Ограничения и главное откровение
Для нас эта работа стала доказательством того, что математическая элегантность дистрибутивной алгебры избавляет инди-разработчика от написания тысяч строк хардкода.
Конечно, у этого подхода есть свои границы. Если вы ищете в процедурных репликах глубокого философского романа или многоходовых логических цепочек — вы их там не найдёте. В своей основе это математические вероятностные Марковские цепи.
Но в мире с сотнями тысяч процедурных НПС этот метод, как никакой иной, создаёт осязаемый, живой, независимый от игрока, а главное — контекстно осмысленный слой текстов. Теперь у жителей мегаструктуры есть рот, и они наконец могут выразить то, как из их собственной точки отсчёта выглядит бесконечный ГИГАХРУЩ.
### Полезные ссылки проекта:
- **Telegram-паблик разработчиков (обновления и девлоги):** https://t.me/gigah_rush
- **Онлайн-версия игры (прямо в браузере):** https://gigahrush.github.io/