🧭 Каталог вариантов усиления бота

О чём это

Полное меню направлений усиления игрового бота, составленное после KCP-эксперимента (см. Rezultaty-usileniya-modeli-i-roster-oracle-botov). Каждый вариант — с механикой, ценой стройки, ожидаемым приростом и рисками. Основано на сплошном обзоре четырёх репо (движок, ev, analytics, доки) 2026-07-11. Исходный план арки фич — Plan-usileniya-value-modeli.

Точка отсчёта: 2-ply rich = 57.8% (задеплоенный чемпион oracle-3), 1-ply KCP = 60.8% (сильнее, но ~6.6 с/ход — не live). Гейт = win-rate против aggressive, seeded арена.

Три факта, которые определяют стратегию

  1. Генерация self-play почти бесплатна. 1-ply rich играет ~27 партий/сек на M3 Pro → 100 000 партий ≈ 1 час ≈ 1.66M обучающих строк. Кости дают встроенную эксплорацию: два одинаковых бота не повторяют партии, 56 мультисетов на ход диверсифицируют дебюты сами — никакой epsilon-greedy механики не нужно. Это условия TD-Gammon (тоже кости, тоже value-модель).
  2. Недостающий кирпич один — дампер обучающих строк. BotMatchRunner.simulateGame видит на каждом ходу всё (позиция до броска, кости, ходящий, выбранный путь), но возвращает только исход. Мини-экспортёр (callback per turn → буфер строк → штамп исхода → CSV в формате экспортёра) ≈ 0.5–1 день. Все примитивы есть: FenParser.serialize (DFEN с пулом костей), UCI-нотация в OpeningBookBot.uci, фичи через *Features.extract. При генерации 2-ply дампить и корневое значение поиска — оно уже посчитано, это бесплатные TreeStrap-метки.
  3. Измерительный прибор надо усилить до итераций. Гейт на 400 партий шумит ±2.5пп (1σ) — принимать на нём 15–20 решений о промоушене нельзя (промоутится шум). Но 1-ply гейт на 10 000 партий стоит ~6 минут и даёт σ=0.5пп; парные сиды (кандидат и чемпион на одинаковых наборах костей) режут дисперсию разницы ещё в 2–3×.

Инварианты любого обучающего цикла

Три прибора, без которых цикл самообмана не заметит

  1. Парный 10k-гейт (1-ply, vs aggressive, одинаковые сиды у кандидата и чемпиона) — рефери никогда не участвует в тренировочных данных. Промоушен только при ≥ чемпион + 1.5пп.
  2. Второй замороженный рефери (например 1-ply material, 41.5%): если win-rate против основного рефери растёт, а против второго — нет, идёт стилевой оверфит.
  3. Held-out logloss на человеческих партиях: если гейт растёт, а human-logloss деградирует — модель усиливает собственные слепые пятна (амплификация ошибок поиска-учителя).

Milestone-замер: 2-ply гейт на 1600 партий (~72 мин) на каждом значимом промоушене.


Ось A — Самообучение

A1. Дистилляция KCP→rich — ✅ ВЫПОЛНЕН 07-11 (частичный успех, ёмкостный потолок найден)

Идея: никаких новых партий. У нас есть дорогой сильный учитель (KCP-модель, 60.8%) и дешёвый студент (rich-фичи, 9 штук). Перекладываем сигнал учителя в студента: новая метка = λ·предсказание учителя + (1−λ)·исход партии на существующих 1.3M человеческих строк (KCP-обогащение уже есть — training_data_kcp.csv.gz содержит оба набора фич).

Механика: учитель предсказывает на 13 фичах (секунды на 1.3M строк), студент учится на 9 rich-фичах с бленд-меткой; свип λ ∈ {0, 0.25, 0.5, 0.75, 1.0}; retrain 15 с; оценка на тесте против настоящих исходов. Лучшую λ — в ONNX и на арену.

Почему может сработать: 4 KCP-колонки несут ~12.8пп сигнала (60.8 vs 48.0 на 1-ply). Дистилляция просит mobility/king-safety выразить этот сигнал там, где они коррелируют. Прямая аналогия — NNUE-сети, обучаемые имитировать более медленный сильный эвалуатор.

Результат: offline — плоский ноль, арена — маленький, но реальный эффект

Offline-свип идеально плоский: AUC 0.7022–0.7025 на всех λ против контрольных 0.7023 (λ=0). Сигнал учителя (его собственный AUC 0.7109) не переносится — mobility/king-safety не могут реконструировать вероятность взятия короля. Ожидаемо задним числом: дистилляция помогает студенту, которому не хватает данных или оптимизации; здесь 9-фичевый GBDT на 1.2M строк упирается в ёмкость, а перелейблинг тех же фич ёмкости не добавляет.

Но парные seeded-арены (общие сиды кандидат/контроль) показали реальный, хоть и малый эффект: 1-ply, λ=0 vs λ=1 — 10k партий 50.0% vs 51.2%; 20k партий 50.0% vs 51.1% (10211W/9789L). Сглаживание меток (непрерывная вероятность учителя вместо шумных 0/1 исходов) даёт ~+1.1пп даже при неизменном AUC — это эффект регуляризации/сглаживания, отдельный от переноса нового сигнала. 2-ply парный гейт (проверка, доживает ли эффект до задеплоенного чемпиона) — см. PR.

Вывод для дорожной карты: узкое место — набор фич, не метки и не объём данных (третий раз подряд в истории проекта: все большие скачки давали именно фичи). Приоритет смещается на C2 (новые дешёвые фичи) и B1 (гибрид 2-ply+KCP-рескоринг) — эффект нужно вносить фичами, не переразметкой существующих.

ev PR #6 (Closes #5): distill.py — teacher-scoring + бленд-меток + свип, один проход извлечения фич на сплит (rich-матрица = срез kcp-матрицы → учитель и студент видят побайтово идентичные строки).

СтройкаФактИтог
~0.5 дня, свип < 1 часа (реально 31с)offline: плоско; арена: +1.1пп при λ=1 (20k партий, парный сид)Дёшевый информативный результат, как и планировалось — пошли дальше по каталогу с чётким приоритетом (фичи, не метки)

A2. Flywheel на исходах (TD-Gammon)

Идея: классическое самообучение. Текущий бот играет сам с собой, позиции лейблятся исходом, модель переобучается, цикл повторяется. Каждая итерация — шаг policy iteration: выучили V^π, играем жадно по ней.

Механика:

  • Генерация: 1-ply rich чекпойнты в пуле оппонентов: 50% текущий-vs-текущий, 30% против ~5 прошлых чекпойнтов, 20% против встроенных ботов кроме aggressive (рефери остаётся внешним). 100k партий/итерацию ≈ 62 мин на ноуте, облако не нужно.
  • Метки: исход, mover-perspective (ничьих нет — чистые 0/1). Опционально лёгкое дисконтирование ранних ходов к 0.5 (дисперсия костей).
  • Смесь: human:self-play = 1:1 в первой итерации → до 1:2; не ниже 25% human. Человеческие строки — якорь распределения состояний. ⚠️ ~16.6 строк партии делят один исход → эффективный размер выборки = партии, не строки; сабсэмплить ≤2 строк на фазу партии или взвешивать 1/длину.
  • Цикл: генерация (~1 ч) → retrain (15 с) → парный 10k-гейт (6 мин). Итерация ≈ 1.5 ч → 4–5/день, ~15–20 итераций за неделю (можно ночами).
СтройкаОжиданиеРиски
~1 день (дампер + скрипт цикла)+2–6пп к 1-ply, большинство в первых ~5 итерациях; 2-ply двигается примерно в тандемеКоллапс распределения (митиграция: пул+human-якорь+кости); стилевой оверфит (приборы №2–3); плато ёмкости GBDT — история проекта говорит, что скачки давали фичи, не объём данных, так что честный нижний край = +1–2пп

Бонус: лучший нарратив для презентации хакатона — «бот научился, играя сам с собой всю ночь». И дампер, построенный здесь, нужен всем остальным вариантам.

A3. TreeStrap — перемётка человеческих позиций поиском

Идея: метка позиции = не исход партии (шумный, один на 16.6 позиций), а значение 2-ply expectimax под текущей моделью (pre-roll, ожидание по 56 мультисетам). Это рецепт NNUE-сообщества (Stockfish-сети учатся на оценках поиска) и TreeStrap (Veness et al. — мастерский уровень в классических шахматах бутстрапом поиска).

Механика: eval_{t+1} = distill(search(eval_t)) — лестница. Каждая ступень: перелейблить подвыборку (300–500k строк, ~1–9 ч на 64 OCPU облака, <$10) → retrain → гейт. Метка = λ·search + (1−λ)·исход, λ≈0.5–0.75. Одна ступень в день, 2–3 ступени реалистично.

Про «потолок слабого учителя»

Студент может превысить учителя: учитель (57.8%) ограничивает одну ступень, а не лестницу — дистиллированного студента снова оборачивают в 2-ply поиск, и search(eval_{t+1}) > search(eval_t), пока дистилляция достаточно точна. Реальный потолок — представительная ёмкость фич, и он общий для всей оси A.

СтройкаОжиданиеРиски
1–1.5 дня (батч-лейблер на движке + облачный скрипт)+4–6пп на 2-ply за 2–3 ступениЦена 2-ply на строку не замерена (0.16–2 core-s — ×12 неопределённость, замерить до коммита); амплификация слепых пятен (прибор №3); стухание ступеней к 3-й

A4. AlphaZero-lite (эскалация)

Идея: объединить A2 и A3 — self-play 2-ply ботом (сильнейшая доступная генерация) + бесплатные search-метки (корневое значение уже посчитано при выборе хода) + лига чекпойнтов.

Механика: лига {текущий, лучший, 2 прошлых, 1 встроенный}; 5–10k партий/итерация (2–4 ч на 64 OCPU); метка = 0.7·корневое значение + 0.3·исход; replay-окно последних ~3 итераций + human-строки с весом ~0.3; итерация 3–5 ч → 1–2 в день.

СтройкаОжиданиеРиски
2–3 дня до первой итерации; ~$30–100 облакапотолок самый высокий: 2-ply 62–66%Все риски A2 + сильнее амплификация (и состояния, и метки от текущей модели); мало партий → шумный исход (несёт search-метка); оркестрация ест дефицитные дни

Вердикт: только как эскалация второй недели, если A1+A2 застопорились — переиспользует их дампер и гейт-инфраструктуру.


Ось B — Поиск

B1. Гибрид: 2-ply rich + KCP-рескоринг корневых финалистов — ❌ ВЫПОЛНЕН 07-12/13, ЭФФЕКТА НЕТ

Механика: обычный 2-ply rich поиск даёт топ-K финалистов; KCP-модель дооценивает только их позиции один раз, итог = (1−w)·search + w·rescore. Реализовано в движке (RootRescore, engine PR #478, Closes #477): loss-taint по точному флагу на уровне броска — линия с потерей короля никогда не спасается рескорингом при любом весе; ~130 мс/ход подтверждено (кандидат даже быстрее baseline). live-viable, как и ожидалось по скорости.

Результат: рескоринг не усиливает 2-ply

Свип 200 партий намекал (w=0.2=62% vs 59.5% baseline), но решающий парный прогон 1600 партий (фиксированные сиды → автопарно): baseline 58.1% vs w=0.2 58.2% = +0.1пп = одна партия из 1600 = статистический ноль. Рескоринг реально менял ходы (W/B сплит 486/444→493/438), но не в плюс по силе.

Почему — ключевой вывод: KCP это заменитель ГЛУБИНЫ поиска, а не ортогональный сигнал. 1-ply material 41.5%→1-ply KCP 60.8% (+19пп) — KCP дал одноплайному поиску форсайт, которого у него не было. Но 2-ply УЖЕ имеет форсайт (просмотр броска соперника + его лучшего ответа + LossValue на взятие короля), поэтому KCP-сигнал избыточен. Это объясняет, почему обе попытки влить KCP в live 2-ply бот дали плоско: A1 (дистилляция в фичи) и B1 (рескоринг на корне) — по одной причине.

Следствие для дорожной карты: KCP исчерпан как рычаг усиления при наличии глубины. Приоритет уходит на ортогональный сигнал — C2 (новые дешёвые фичи, не дублирующие поиск), B5 (candidateLimit, почти бесплатно), A2 (self-play, меняет распределение данных). engine PR #478 корректен и покрыт тестами, но мотивирующий эксперимент пуст → решение о мёрже (оставить как спящий инструмент vs закрыть) за пользователем.

Методическая заметка: арена играет партии последовательно (BotMatchRunner.runMatch) → облако не ускоряет (параллелится только ONNX intra-op, ~6 ядер); M3 Pro быстрее Oracle A1 на ядро → тяжёлые арены гнать локально. ~3.5 с/партия на 2-ply.

B2. Policy-модель для top-K ответов соперника

В ExpectimaxSearch.opponentMinValue ответы соперника сейчас не прунятся вовсе («сотни-тысячи ответов на бросок») — единственное непрунированное место и естественный слот для policy: дешёвая модель предвыбирает top-K ответов. Листья уже материализуются как Array[GameState] — батч-форма готова. Это и самостоятельное ускорение 2-ply, и пререквизит 3-ply. Корневой пре-ранкер тоже захардкожен на материал (SearchScoring.scorePath(…, evaluateMaterial)) — второй слот.

B3. 3-ply expectimax

Глубина захардкожена (2); naive 3-ply ≈ 8 × 56 × R × 56 × R′ ≈ 10⁶–10⁸ листьев/ход. Требует: B2 (прунинг ответов), дедлайн-чеки внутри chance node (сейчас проверяется только между кандидатами), дешёвый лист. Большая стройка, неизвестный прирост. После хакатона.

B4. Реанимация MCTS

История: UCT MCTS v1 проиграл flat-MC 30:70 (журнал турниров 06-22) — случайное расширение без прунинга тратит итерации впустую. Список фиксов записан в 03-Idei-po-uluchsheniyu-algoritmov §1–4 (прунинг кандидатов, safe root fallback, weighted chance nodes, ранний обрыв роллаутов + maxPlies-баг). Код жив на ветке feat/392-opening-book-bot. Естественная форма реанимации — MCTS с value-моделью вместо роллаутов (закрывающий TIP в 2-ply-Expectimax-poverkh-value-modeli). Спекулятивно; после хакатона.

B5. candidateLimit↑ — ✅ ВЫПОЛНЕН 07-13, ВЫИГРЫШ +4.8пп

Рычаг силы/времени: было записано K=2→46.5%, K=8→55+%. Проверили K=16 (тот же чемпион rich_distilled, 2-ply, парный прогон 1600 партий, сиды фиксированы).

K=8 → K=16 = 58.1% → 62.9% (+4.8пп, ~4σ), время всего +16% (не 2×)

Ожидали +1–2пп — получили +4.8пп, самый крупный выигрыш после rich-фич. И это параметр конфига, ноль изменений кода.

Урок: узкое место — грубая ПРЕ-РАНЖИРОВКА кандидатов (только материал), не глубина и не фичи. Лучший по полной 2-ply-оценке ход часто оказывается за пределами топ-8 по материалу; K=16 его ловит. Прямое следствие — перспективная идея: лучший пре-ранкер (rich-фичи или сама value-модель вместо материала при отборе кандидатов) — вероятно, позволит меньшим K брать тот же ход.

⚠️ Замер untimed (арена перебирает все 16). В live под часами выигрыш реализуется, только если бот успевает дойти за 8 кандидатов в бюджете хода — но downside нет (anytime: топ-кандидат всегда оценивается первым), так что K=16 — строго-лучший кап. Деплой = мелкая правка house-bots (сделать candidateLimit конфигурируемым; движок уже принимает ExpectimaxConfig).

✅ ЗАДЕПЛОЕН 07-13 — house-bots PR #6 (ORACLE_CANDIDATE_LIMIT, дефолт берётся из ExpectimaxConfig().candidateLimit, не хардкод — по замечанию ревью), oracle-3 в проде на K=16. Смоук чист, oracle-1/2/4 не тронуты.

Follow-up: sweep K=24/32 — ✅ ЗАВЕРШЁН 07-15, насыщение на K=24

Тот же чемпион (rich_distilled_l100.onnx), тот же парный сетап 1600 партий/сиды.

candidateLimitWin-rateΔ от пред.Время (1600 партий)
858.1%~3.6ч
1662.9%+4.8пп~4ч
2466.7% (1067W/533L)+3.8пп~7.4ч
3266.9% (1071W/529L)+0.2пп (шум)~10.7ч

Сладкая точка = K=24 (66.7%); дальше плато (K=32 = +0.2пп = 4 партии = ноль, за +45% времени). Рычаг реален и крупен до 24, затем выдыхается. Сильно подтверждает урок B5: главное узкое место — грубость материального пре-ранкера (тупое расширение K вытаскивало недооценённые ходы вплоть до 24, потом все хорошие уже в наборе). ⚠️ Цена растёт СВЕРХлинейно (не только больше кандидатов — бот сильнее → партии длиннее → больше ходов). Замер untimed; в live под часами прирост меньше (бот не всегда доходит до K кандидатов в бюджете хода), downside нет (anytime). Открытые решения: (а) деплой K=24 в oracle-3 (сейчас K=16) — взвесить против бюджета хода на rpi4; (б) пре-ранкер (ниже) может дать 66.7% при малом K дешевле.

Урок процесса: такой sweep впредь гнать в облаке параллельными процессами (см. Oracle Cloud) — на маке 4 конфига заняли ~26ч суммарно последовательно.

Follow-up: лучший пре-ранкер кандидатов — ✅ ЗАВЕРШЁН 07-15, +1.4–1.8пп (полировка, не рычаг)

Прямое следствие урока B5: если преранжировка кандидатов (сейчас — только материал) грубая, надо не расширять K, а улучшить сам преранкер. engine issue #481 → PR #482 (Closes #481): ExpectimaxSearch теперь принимает инъектируемый батчевый преранкер (preRank), дефолт = материал (побайтово то же поведение, все существующие тесты прошли без изменений). Отбор кандидатов переведён с построчного SearchScoring.scorePath (по одному вызову на кандидата) на единый батч-вызов над результирующими позициями всех легальных ходов — тот же паттерн батчинга, что уже используется в chance-node (opponentMinValue) и в root-рескоринге (RootRescore). Цена не выросла: applyTurn и раньше считался по разу на кандидата внутри scorePath; теперь считается один раз заранее и переиспользуется в цикле топ-K (нет повторного replay).

OnnxExpectimaxSearch получил preRankWithModel: Boolean — при включении переиспользует уже загруженную ONNX-сессию (батчево) вместо материала для преранжировки, без второй модели: та же модель, что уже оценивает листья chance-node, теперь решает, какие корневые ходы вообще туда попадут. OnnxExpectimaxArenaRunner даёт это как 8-й CLI-аргумент — испытать в арене без переобучения.

CI зелёный (14/14), покрытие 86.72% (порог 85%), sbt doc чист.

Результат арены (07-15, Oracle, 2 VM параллельно — первое применение облачного паттерна из sweep-урока). Тот же чемпион rich_distilled, 2-ply, 1600 партий/конфиг, сиды фиксированы, vs aggressive. Каждая VM гоняла один K с preRankWithModel:

candidateLimitmaterial-преранкmodel-преранкΔвремя
K=858.1%59.9%+1.8пп9373 с
K=1662.9%64.3%+1.4пп18412 с (~2× K=8)

Модельный преранк даёт стабильные +1.4–1.8пп при фиксированном K — качество отбора кандидатов действительно двигает силу, гипотеза урока B5 подтверждена направленно.

Но это полировка, а не рычаг: острый преранкер на малом K НЕ заменяет большой K.

  • model K=8 (59.9%) ниже material K=16 (62.9%) — преранкер на половинном K не догоняет материал на двойном;
  • model K=16 (64.3%) ниже material K=24 (66.7%). Сырой candidateLimit остаётся доминирующим рычагом (material K=8→24 = +8.6пп против +1.5пп от преранкера). Стоимость модельного преранка над материалом мала (+~4% времени: лишний батч-инференс по всем легальным ходам на ход), downside по-прежнему нет (anytime, топ-кандидат первым).

Открытый хвост (не начат): model-преранк при K=24 не мерян — теоретически может пробить плато материала 66.7–66.9% (расширяет топ-24 по модели, а не по материалу, → может вытащить ходы, которые материал не поднимает даже при K=32). Но отдача заведомо предельная (диминишинг), а гейт давно взят (55% → уже 66.7%) → припаркован, отдельный платный прогон пока не оправдан.

Деплой oracle-3: material K=24 ✅ ЗАДЕПЛОЕН 07-16 (house-bots PR #13, env-only ORACLE3_CANDIDATE_LIMIT 16→24, без пересборки образа — verified ORACLE_CANDIDATE_LIMIT=24 в живом контейнере на rpi4). Выбран как strength-оптимум (66.7%); модельный преранк при K=16 (+1.4пп над material K=16) отклонён — не окупает лишнюю live-латентность ONNX-ранжировки, а material K=24 и так strength-оптимум. Anytime под часами → на медленной rpi4 downside нет (топ-кандидат оценивается первым, строго ≥ K=16).


Ось C — Оценка и фичи

C1. Удешевить KCP (engine epic #385)

Сделано: 1A (вынос ply-0) + 1B (int-кодирование пула костей) = 1.6–1.72×. В спеках: 1C (#390, bitmask-шорткат прямых взятий, «~1× на тихих позициях») и 2A (#391, mutable scratch board make/undo, «~2–3× на JVM, 5× оптимистично»,高 риск — вариант A уже был реверчен по регрессии). Реалистичный итог 3–5× от исходного, НЕ порядок. Следствие: 6.6 с/ход → ~1.5–2 с/ход — oracle-4 становится почти live (3+ мин контроли), гибрид B1 и TreeStrap A3 дешевеют пропорционально. Отвергнуто внутри эпика (не переделывать): прунинг мультисетов (неверен для AAA/AAB), кэш транспозиций (hit-rate≈0), внутривызовный параллелизм.

C2. Новые дешёвые фичи

Пешечная структура (сдвоенные/изолированные/проходные), укрытие короля, контроль центра, темпо-фичи — всё µs-класса, т.е. можно в листья 2-ply (в отличие от mobility=2 прохода movegen и KCP=216-DFS). История проекта: скачки давали именно фичи (+6.5пп rich, +12.8пп KCP на 1-ply). Паттерн готов: *Features в движке → enrichment → features.py → арена. ~1 день на набор.

C3. Модель посильнее (MLP вместо GBDT)

Если докажется потолок ёмкости GBDT (например, A1 покажет, что 9 фич не выражают KCP, и C2 не поможет). Сейчас пайплайн LightGBM-only: export_onnx завязан на convert_lightgbm + .txt-сериализацию; MLP потребует диспатч конвертера (skl2onnx или torch.onnx.export). Скала-сторона (FloatTensorType, вход “input”) не меняется. Средняя стройка; только по доказанной необходимости.


Ось D — Данные

D1. Больше человеческих партий + веса

Сейчас: 79k партий (оба ≥1800) из ~185k+ в БД. Расширение = чистая CLI-команда (mise run db:export-training out.csv.gz 1500 или 0 classic king_captured), рейтинги уже в CSV → взвешивание по силе игроков = один параметр sample_weight до LGBMRegressor.fit (мелкая правка ev). Риск: слабые игроки = шумные метки — потому и веса, а не просто больше данных.

D2. Книга дебютов для модельных ботов — ❌ ЗАВЕРШЁН 07-17, ДЛЯ ЧЕМПИОНА ОТРИЦАТЕЛЕН

Конвейер готов сегодня: mise run db:export-book (analytics) → JSON → OpeningBookBot.decorate(baseBot, book) (движок). Арена-валидация на крошечной книге уже дала +5пп aggressive-book vs aggressive (шумно, но позитивно). Требование: книга только от сильных игроков (≥2000), боты исключены (иначе петля «боты играют книгу → книга учится на ботах», см. Kniga-debyutov-dlya-robotov). ~0.5 дня на подключение к oracle-ботам.

Прогресс 07-16. База выросла до 788 931 партии (classic+king_captured = 505 634; both≥1800 = 102 669, both≥2000 = 79 780). Экспортированы 4 варианта книги под свип (лёгкий запрос, ~20–30 с на конфиг, план hash-join — прод не заметил):

minGames / minRatingзаписей
100 / 2000245 (июньский экспорт на той же настройке был 147)
50 / 2000580
100 / 1800304
50 / 1800686

Сотни записей — норма для dice chess: 56 мультисетов на ход разносят дерево быстро, книга покрывает первые ~2–3 хода, где статистика плотная. Файлы в приватном dicechess-ev/books/ (gitignored; в public-репо не кладём). Гейт-инструмент: engine issue #485 → PR #486 (merged) — аргумент bookPath в OnnxExpectimaxArenaRunner, декорация OpeningBookBot.decorate; впервые позволяет мерить «oracle-3 + книга vs aggressive» сидированной парной ареной.

Свип-результат 07-17 (Oracle, 4 VM параллельно, ночь; парные арены 1600 партий, K=24, rich_distilled, vs aggressive):

книга (minGames/minRating)записейwin-rateΔ vs бескнижный 66.7%
100 / 200024566.2%−0.5пп
50 / 200058064.9%−1.8пп
100 / 180030463.4%−3.3пп (~2.8σ)
50 / 180068666.3%−0.4пп

Книга НЕ усиливает чемпиона — все 4 конфига ≤ бескнижного базлайна (средний эффект ≈ −1.5пп)

Механизм: каждый book-hit подменяет решение 2-ply/K=24-поиска (уровень 66.7%) ходом из статистики людей 1800–2000 — а бот в дебюте уже сильнее этих людей. Худшая книга — из самых слабых партий (r1800/g100, −3.3пп значимо). Ранний позитив «aggressive+book = +5пп» не противоречие: слабому боту человеческая книга помогает, чемпиону — вредит. Это четвёртое подтверждение сквозного урока (после A1-дистилляции, B1-рескоринга и объёма данных): данные/метки — не рычаг, когда поиск уже хорош.

Бонус свипа: кросс-машинный детерминизм арены ДОКАЗАН

Бескнижный контроль K=24 на той же A1-VM: 1067 (563/504) / 533 (237/296) = 66.7% — побитово идентичен локальному прогону на M3 Pro (другая ОС, другой билд ONNX Runtime; оба aarch64). Сиды фиксированы ⇒ партии воспроизводятся между машинами точно → будущие свипы можно сравнивать с локально записанными базлайнами без контрольных прогонов (в пределах aarch64-парка: мак / A1 / rpi4).

Решение: книгу в oracle-3 НЕ деплоим. Возможная жизнь для книги: слабые house-боты (greedy/oracle-1 — там плюс) и слайд презентации («проверили книгу из 800k партий — бот уже сильнее людей, писавших её»). Инфраструктура (экспортёр, парсер, декоратор, арена-гейт) остаётся готовой и проверенной.


Отвергнуто ранее (не переделывать)

ИдеяРезультатГде записано
Aggressive-преселекция кандидатов MC46.7% < 50.8% базы01-Analiz-Monte-Karlo-protiv-Aggressive, журнал Match 3
Epsilon-greedy heavy rollouts (40% greedy)49.2% < 50.8%; «случайные роллауты работают лучше»там же, Match 4
UCT MCTS v1проиграл flat-MC 30:7002-Zhurnal-turnirov 06-22
Прунинг 56 мультисетов KCPматематически неверен (AAB/AAA, изломанные пути слайдеров)epic #385
Кэш транспозиций в KCP (plies≥1)hit-rate ≈ 0 на случайных блужданияхepic #385
Dice-conditioned модель на корне«лукахеда не даёт, слепоту к рекапче не лечит»2-ply-Expectimax-poverkh-value-modeli §7

Рекомендованный план (12 дней до хакатона, с запасом на UI-работу)

  1. День 1: A1 (дистилляция) + парный 10k-гейт как инструмент. Даже нулевой результат дёшев и информативен — сразу меряет ёмкость rich-фич.
  2. День 2: дампер self-play строк (движок) — открывает A2/A3/A4; сразу с дампом корневого значения при 2-ply генерации.
  3. Дни 3–5: A2 flywheel (итерации фоном/ночами) с тремя приборами-инвариантами.
  4. Параллельно по желанию: B1 гибрид — самый вероятный путь донести силу KCP до живого бота; B5 — бесплатный замер.
  5. После хакатона: C1 (KCP 2A), B2→B3, D1/D2, при доказанном потолке — C3.

Честная сводная проекция по оси A: 2-ply live-бот 57.8% → ~61–65% к хакатону; ~50% уверенности в приросте >+3пп, ~20% риска упереться в потолок фич на +1–2пп.

Связанные заметки