🧩 Расширение 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_idplayer_typeusername
Наша сторона (наш бот играет)bot:<algorithm> напр. bot:aggressivebot«<Algorithm> Bot»
Наша сторона, но сыграл человек (markHumanPlayed())хост-аккаунт (String(id))humanимя аккаунта
Сайт-бот (id < 0)String(id) — нативный отрицательный idbotSITE_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 Идентичность, источники и дедупликация.


Связанное