go-learn/docs/decisions/ADR-0002-group-algorithm.md
sab.code.lab 1c85091186 chore: восстановление репозитория из снапшота v0.3.1
Прежняя git-история утрачена при переносе проекта на машину владельца
(снапшот без .git). Хэши коммитов в docs/reports/* относятся к утраченной
истории.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 09:34:02 +02:00

57 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-0002: алгоритм поиска групп и дамэ — flood fill
Дата: 2026-08-05 | Статус: принято (этап 1)
## Контекст
Движку правил (packages/core) на каждый ход нужно находить группы камней и их
дамэ: при проверке захватов, самоубийства, при подсчёте и в эвристике мёртвых
групп. План этапа 1 предписывает выбрать между flood fill и union-find по
микро-бенчмарку и зафиксировать выбор в ADR.
## Альтернативы
1. **Flood fill** — обход группы стеком от стартового камня, дамэ собираются в
множество. Простая реализация, локальный обход (только затронутые группы).
2. **Union-find** — массив parent на всю доску, объединение соседей одного
цвета, дамэ агрегируются по корням. Теоретически эффективен при
инкрементальных обновлениях, но наша позиция неизменяема: структуру пришлось
бы перестраивать на каждый ход, что обнуляет его главное преимущество.
## Бенчмарк
`packages/core/bench/group-bench.mts` (запуск: `npx tsx ...`): оба алгоритма
находят все группы и дамэ на случайных позициях 19×19 (плотности 0.35/0.55/0.75,
40 позиций × 300 итераций на плотность, детерминированный RNG). Среда:
Node.js 20, x86-64. Контрольные суммы результатов совпали.
| Плотность | flood fill | union-find | u/f |
| --------- | ---------- | ---------- | ---- |
| 0.35 | 230.4 мс | 373.1 мс | 1.62 |
| 0.55 | 288.5 мс | 505.6 мс | 1.75 |
| 0.75 | 328.3 мс | 596.8 мс | 1.82 |
| Итого | 847.1 мс | 1475.5 мс | 1.74 |
Union-find проигрывает на всех плотностях (в 1.61.8 раза): на маленькой доске
(361 клетка) стоимость полного прохода union-find и аллокаций Map/Set по корням
не окупается, а flood fill обходится дешёвыми операциями над массивом и одним
множеством дамэ на группу.
## Решение
Оставляем **flood fill** (реализован в `groupAt`, packages/core/src/board.ts).
## Причины
- Быстрее на целевом профиле (доски 919, обходы только затронутых групп).
- Существенно проще код и меньше аллокаций; неизменяемость BoardState всё равно
не даёт переиспользовать структуру union-find между ходами.
- Производительности достаточно с запасом: полный пересчёт всех групп 19×19 —
десятки микросекунд, а движок обходит лишь соседние с ходом группы.
## Компромисс
Если этапы 34 (ИИ) покажут профилировкой, что пересчёт групп — узкое место
(например, массовые симуляции MCTS), решение пересматривается: кандидат —
инкрементальный union-find с копированием при записи или кэш групп в узлах
симуляции. Точка пересмотра — бенчмарк на реальной нагрузке ИИ, не раньше.