🧩 Расширение dicechess-extension
Внутреннее
Расширение играет нашим алгоритмом на живом аккаунте dicechess.com и подменяет игрока в постящейся партии. Знание о подмене существует только здесь и из сырья не восстановимо. Не выноси наружу токены и логику доступа к сайту.
dicechess-extension — браузерное расширение (MV3), которое запускает наш Scala.js-движок прямо во вкладке залогиненного dicechess.com, играет им против сайт-движка, и по завершении партии постит её в аналитику. Это четвёртый писатель в POST /api/games (наряду с observer, sync и beturanga-observer), но у него есть уникальная роль, которой нет у остальных: подмена игрока.
1. Зачем оно — измерение силы алгоритма
Observer и sync записывают партии как есть: кто реально играл (человек или сайт-бот). Расширение решает другую задачу — проверить, насколько силён наш алгоритм. Для этого оно играет нашим ботом на обычном аккаунте и записывает партию так, будто за нашу сторону играл не хост-аккаунт, а конкретный алгоритм. Тогда в аналитике можно посчитать win-rate именно bot:aggressive, bot:monte-carlo и т.д.
Алгоритм выбирается в popup и передаётся в content.js через startBot(algorithm) (по умолчанию aggressive); одной строкой он конструирует и движок (EngineBot), и рекордер (GameRecorder).
2. Подмена игрока (GameRecorder._player)
Вся суть — в том, как рекордер строит PlayerInput для каждой стороны (src/gameRecorder.js, метод _player(id)):
| Случай | external_id | player_type | username |
|---|---|---|---|
| Наша сторона (наш бот играет) | bot:<algorithm> напр. bot:aggressive | bot | «<Algorithm> Bot» |
Наша сторона, но сыграл человек (markHumanPlayed()) | хост-аккаунт (String(id)) | human | имя аккаунта |
Сайт-бот (id < 0) | String(id) — нативный отрицательный id | bot | SITE_BOT_NAMES[id] (-40…-43 = DC Coach) |
| Прочие люди | String(id) | human | из наблюдаемой карты игроков |
То есть наша сторона записывается как алгоритм, а не как аккаунт, на котором он крутился. Это и есть «подмена». Сайт-боты при этом остаются под своими нативными отрицательными id — расширение НЕ перекодирует их в bot:*. Синтетический id bot:<algorithm> зарезервирован исключительно за нашим собственным движком (см. 08 Идентичность, источники и дедупликация).
Перехват человеком
Если человек сходил, пока бот был на паузе,
connector.jsвызываетrecorder.markHumanPlayed(), и подмена отключается: наша сторона записывается как человеческий аккаунт, а не как алгоритм. Иначе мы приписали бы алгоритму ход, которого он не делал.
3. Идентичность партии и гонка first-writer-wins
Расширение использует нативный gameId сайта (UUID) напрямую как первичный ключ партии, source = "dicechess.com". Значит партию, сыгранную нашим ботом, видит и observer (она же шла «живьём» на сайте) — и оба постят её под одним и тем же UUID.
sequenceDiagram autonumber participant EXT as 🧩 extension participant OBS as 🛰️ observer participant G as 📊 analytics Note over EXT,OBS: оба видят одну партию (один UUID) EXT->>G: POST /api/games · наша сторона = bot:aggressive G-->>EXT: 201 created (строка зафиксирована) OBS->>G: POST /api/games · наша сторона = хост-аккаунт G-->>OBS: 200 exists (НЕ перезаписывает)
Это и есть гонка first-writer-wins: кто первым запостил gameId, тот и определил идентичности игроков. Если первым успел observer — наша сторона осядет как хост-аккаунт, а не как bot:<algorithm>, и партия для анализа силы алгоритма «потеряется». Поэтому, когда нужна корректная атрибуция алгоритма, важно, чтобы расширение опередило observer (либо чтобы observer не смотрел этот аккаунт).
4. Почему это знание невосстановимо
Сайт видит только человеческий аккаунт — он не знает, что за него ходил наш алгоритм. Подмену хост-аккаунт → bot:<algorithm> нельзя восстановить из сырья (game-move-history не содержит этой информации). Поэтому:
- расширение — постоянный специсточник, не дублируемый observer’ом или sync;
- raw-архив (bronze) для него отложен: расширение живёт в браузере, у него нет ни PG-доступа, ни сырого JSON-конверта, который кладут в bronze другие писатели. Это известная дыра.
5. Историческая правка дедупликации (V6)
Ранняя версия расширения писала четыре сайт-бота «DC Coach» под синтетическими id bot:dc-coach-*, создавая по второй строке игрока на каждого сайт-бота. Миграция аналитики V6 (V6__dedupe_site_bots.sql) слила каждую такую строку в её нативный отрицательный «двойник» (bot:dc-coach-rookie → -40, -beginner → -41, -amateur → -42, -master → -43), перенаправив партии и удалив дубликат. Наш собственный bot:<algorithm> намеренно не тронут — это реальный отдельный игрок. В текущем коде SITE_BOT_NAMES уже ключует этих ботов по нативным id -40…-43, так что баг исправлен и на стороне писателя. Подробнее — 08 Идентичность, источники и дедупликация.
Связанное
- 08 Идентичность, источники и дедупликация — конвенция
external_id, нативный id ботов противbot:<algorithm>, гонка first-writer-wins, дедуп V6. - 03 Синхронизация — обзор и архитектура — место расширения среди четырёх писателей.
- 07 Контракт ingest и валидация движком — контракт
POST /api/games, который расширение делит со всеми. - Игра против роботов, Созданная структура — связанная история с
dicechess-bots(наш бот против сайт-движка через STOMP).