sharp.go
← Назад

· Sharp.go Team

Внутри движка: как sharp.go рендерит 361 камень на 60fps

Пять лет назад доска для Го на 60 кадрах в секунду с полноценной 3D-инспекцией была мечтой рендерера. Сегодня это его дневная задача. Этот пост — тот самый разбор с комментариями, которого нам не хватало в начале: архитектурные решения, тупики и мелкие детали, которые всё изменили.

Проблема

01

Почему это сложно

Три ограничения, которые сформировали 3D-движок, который мы выпускаем сегодня.

Игра в Го — это дерево, а не линия. В любой момент игрок может разветвиться на тысячи альтернативных продолжений, и у каждого своя расстановка камней на сетке 19×19. Мы хотели всё три сразу: интерактивную 3D-камеру, задержку анализа меньше 100 мс и партию, которая не разваливается после 200-го хода.

Каждое из этих ограничений решаемо по отдельности. Проблема в том, что все они затрагивают один и тот же горячий путь — момент, когда камень касается доски — и каждому нужно разное время.

Кодирование ходов

02

21-байтный проводной формат

Компактный, детерминированный, воспроизводимый между версиями.

Каждый ход в sharp.go — это 21-байтная полезная нагрузка. Мы перепробовали JSON, CBOR и protobuf, прежде чем остановиться на самописном бинарном формате. Формат настолько компактен, что 400 ходов реальной игры занимают 8,4 КБ — примерно столько же, сколько сжатый JPEG самой доски.

  • 01.

    Байт 0 — Цвет

    0x00 для чёрных, 0x01 для белых. Один байт. Без согласований.

  • 02.

    Байты 1–2 — Координата

    Два байта в little-endian: строка (0–18) и столбец (0–18). 361 допустимая позиция на сетке 19×19.

  • 03.

    Байты 3–6 — Номер хода

    32-битный индекс хода без знака. Последовательный внутри партии, детерминированный между воспроизведениями.

  • 04.

    Байты 7–10 — Метка реального времени

    миллисекунды от начала игры. Используется для перемотки воспроизведения.

  • 05.

    Байты 11–14 — Хеш позиции

    32-битный хеш Zobrist. Позволяет обнаруживать дублирующиеся позиции и суперко без полного сканирования доски.

  • 06.

    Байты 15–20 — Зарезервировано для сигналов движка

    Захваченные камни, маркер ко, флаг пасса. Зарезервировано для прямой совместимости.

Раздел

"

Мы не собирались изобретать бинарный формат. Мы хотели сделать анализ мгновенным, и число байтов оказалось самым простым ограничением, которое можно ослабить последним.

"

Naomi S. / Ведущий инженер движка
Рука ставит чёрный камень на доску 19×19 в середине партии, с группами камней в формировании у верхнего правого и нижнего краёв.

Ход 217 — чёрные играют расширение снизу. Один байт цвета, два байта координаты, восемнадцать байт контекста.

Конвейер рендеринга

03

Три прохода, один холст

Порядок важнее стоимости.

Рендерер — это строгий трёхпроходный конвейер. Мы не сортируем, не смешиваем и не отражаем между проходами — каждый проход пишет в свою текстуру, а следующий её читает.

Tabs

Каждый камень — это единичный инстансный куб с PBR-материалом. Мы рендерим все 361 позицию в закадровую текстуру глубины доски. Пустые позиции пропускаются полностью; занятые получают индивидуальную трансформацию.
Частота кадров в установившемся режиме 60fps
Пропускная способность декодирования формата ходов 1.1GB/s
Байт на ход в проводном формате 21B

Дизайн-решения

04

Почему мы выпускаем 3D первым

Логика нашего первого продукта, а не приговор 2D.

Comparison

Сравнение движков

Feature sharp.go 2D-клиенты Мобильные приложения
3D-инспекция доски
Отклик ИИ быстрее 100 мс
Открытый формат партий
Совместный разбор
Ссылки для шеринга в два клика
Режим только в браузере
  • Инстансный рендеринг по всей доске

    361 инстанс, один вызов отрисовки. Всё остальное было медленнее.

  • Кешированные отражения, а не реалтайм

    Перерисовка при повороте камеры больше 5°. Игроки не замечают.

  • Бинарный формат ходов на диске

    Компактный, детерминированный — партии 2026 года загружаются и в 2027.

  • Глубокие ссылки как URL первого класса

    У каждой позиции стабильный URL. Поделиться доской — это копипаст, а не скриншот.

Часто задаваемые вопросы

Часто задаваемые вопросы

01 Почему движок использует Zobrist-хеширование для позиций?

Хеши Zobrist обновляются за O(1) на каждый ход (xor изменённых битов) и позволяют обнаруживать суперко и дублирующиеся позиции за одно 32-битное сравнение. Полное сканирование доски на каждом ходу нормально работает на 19×19, но не масштабируется на 25×25, которые мы планируем поддерживать.

02 Можно ли воспроизвести партию по URL на другом устройстве?

Да. У каждой партии есть стабильный URL с последовательностью ходов, закодированной в пути. Откройте URL на любом устройстве в любом браузере — и воспроизведение начнётся с той же позиции.

03 Играет ли ИИ на том же уровне офлайн?

Да. Локальный ИИ работает полностью офлайн и по умолчанию откалиброван на уровень начинающего. В меню можно поднять уровень сильнее — никакого сетевого запроса не требуется.

04 Почему бинарный формат ходов не описан в публичной спецификации?

Стабильность. Формат — часть нашего обещания совместимости воспроизведений. Мы опубликуем стабильную спецификацию v1, когда выйдет движок 1.0. До этого момента формат может меняться между минорными версиями.

Что дальше

05

Дорожная карта, а не обещания

Над чем мы работаем сейчас, в порядке ожидаемой стоимости.

Следующие шесть месяцев — о переносимости воспроизведений. Каждая сыгранная вами партия должна пережить клиент, на котором была сыграна.

Попробовать движок

Сыграть на настоящей 3D-доске

Без установки. Откройте игру в браузере, сыграйте против откалиброванного ИИ, поделитесь позицией одним кликом.

"

Лучший интерфейс для Го — тот, который исчезает. Вы смотрите на доску, а не на приложение.

"

Команда sharp.go / Гонконг, май 2026
enginerenderingperformance