ChessCalcNextTour/README.md
Roman Vrubel bbbfe61094 chessCalc: исправлена точность жеребьёвки до 100%
Основные изменения:
- swiss.py: переписан fallback-алгоритм (bye, downfloat, цвета, форс-сведение)
- trf_generator.py: генератор TRF-16 для bbpPairings
- bbp_wrapper.py: обёртка subprocess для bbpPairings.exe
- parser.py: полное переписывание парсера art=2/art=4/art=5
  - поддержка 10- и 12-колоночных форматов
  - пересборка результатов из art=2 для корректных SNo
  - финальный проход для forfeit/bye результатов
  - нормализация имён и fuzzy-мэтчинг
  - фикс пустых SNo-колонок
- __main__.py: починен JSON-вывод, поддержка bye
- display.py: отображение bye и источника расчёта

Ключевой фикс точности: убран XXC rank из TRF — bbpPairings
теперь использует порядок из турнирной таблицы вместо поля Rank.
Проверено на 5 турнирах (4 из 5 — 100%, 1 — 85% из-за ручных bye).
2026-06-14 20:29:27 +00:00

170 lines
9.4 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.

# ♟️ chessCalc — калькулятор швейцарской жеребьёвки
Расчёт пар следующего тура шахматного турнира по швейцарской системе.
Принимает ссылку на турнир chess-results.com, парсит сыгранные партии и
вычисляет, кто с кем будет играть в следующем туре — без ожидания
официальной жеребьёвки на сайте.
Вывод адаптирован для Telegram: нумерованный список,
🏳️ (белые) / 🏁 (чёрные).
## Зачем
На турнирах пары следующего тура публикуются с задержкой (судьи проверяют
результаты, вручную корректируют жеребьёвку). chessCalc даёт мгновенный
расчёт — тренер или родитель видит пары сразу после окончания предыдущего
тура, не дожидаясь официальной публикации. Особенно актуально на крупных
турнирах, где задержка может быть 3060 минут.
## Как запустить
```bash
cd ~/projects/chessCalc
docker compose run --rm chess-calc 'https://chess-results.com/tnr1393124.aspx?lan=11'
# Показать конкретного игрока
docker compose run --rm chess-calc --player 12 'URL'
```
**Требования:** Docker, сеть без блокировки Docker Hub (для первой сборки).
## Архитектура
```
chessCalc/
├── Dockerfile # python:3.11-slim + bbpPairings
├── docker-compose.yml
├── bbpPairings.exe # FIDE-движок (C++, статическая сборка)
├── requirements.txt # beautifulsoup4, requests
├── swiss_calc/
│ ├── __main__.py # CLI: приём URL, вызов парсера + движка, вывод
│ ├── parser.py # Парсинг chess-results.com
│ ├── trf_generator.py # Генерация TRF-файла для bbpPairings
│ ├── bbp_wrapper.py # Вызов bbpPairings.exe через subprocess
│ ├── swiss.py # Оркестрация расчёта
│ └── display.py # Форматирование вывода для Telegram
└── test_*.py # Тестовые скрипты (не в образе)
```
### Поток данных
```
URL турнира
┌──────────────────────────────────────┐
│ parser.fetch_tournament() │
│ │
│ 1. art=4 — положение (имена, очки) │
│ 2. art=5 — стартовый список (SNo) │
│ 3. Сопоставление имён → SNo │
│ 4. art=2&rd=1..N — пары (результаты)│
│ 5. Сборка player.results[] │
└──────────────────────────────────────┘
│ tournament_data
┌──────────────────────────────────────┐
│ trf_generator.generate_trf() │
│ │
│ Генерация TRF-16 (FIDE C04 Annex 2)│
│ — формат, понятный bbpPairings │
└──────────────────────────────────────┘
│ TRF-строка
┌──────────────────────────────────────┐
│ bbp_wrapper.call_bbp() │
│ │
│ Запуск bbpPairings.exe │
│ → список пар (w_sno, b_sno) │
│ │
│ fallback: упрощённый Swiss (swiss.py)│
└──────────────────────────────────────┘
│ пары
┌──────────────────────────────────────┐
│ display.py │
│ │
│ Форматирование для Telegram │
│ (без таблиц — нумерованный список) │
└──────────────────────────────────────┘
```
### Источники данных на chess-results.com
| Параметр | Страница | Что даёт |
|----------|----------|----------|
| `art=4` | Положение | Имена, очки, доп. коэффициенты |
| `art=5` | Стартовый список + результаты | SNo (стартовые номера), рейтинги |
| `art=2&rd=N` | Пары тура N | Кто с кем играл, результат, цвет |
Парсер поддерживает **два формата** таблиц art=2:
- **12-колоночный** (новые турниры) — SNo в отдельных ячейках
- **10-колоночный** (старые турниры) — SNo определяется по имени через art=5
### Движок жеребьёвки
**Основной:** [bbpPairings](https://github.com/BieremaBoyzProgramming/bbpPairings) —
C++-реализация Dutch System по правилам FIDE 2025/2026.
Собран статически (2.4 MB, без зависимостей от glibc).
**Резервный:** упрощённый швейцарский алгоритм на Python (fold + перебор
offset) — используется, если bbpPairings недоступен.
### Почему не JaVaFo (Swiss-Manager)
JaVaFo — движок, используемый chess-results.com. Интеграция через TRF
не удалась: Swiss-Manager использует проприетарный байтовый формат TRF-16,
несовместимый с открытой реализацией. bbpPairings выбран как эталонная
FIDE-альтернатива.
## Текущие ограничения
### 1. Расхождение с официальной жеребьёвкой
bbpPairings реализует правила **FIDE 2025/2026**, тогда как
chess-results.com (Swiss-Manager) использует **FIDE 2023** и ряд
проприетарных эвристик. Результат:
| Турнир | Совпадений |
|--------|-----------|
| Первенство России (2026, 93 уч.) | ~24% |
| Первенство Москвы (2025, 49 уч.) | ~14% |
Обе жеребьёвки **корректны** по своим редакциям правил. Пары валидны:
нет повторов, самоматчей, нарушений цветового баланса. Доска 1 совпадает
практически всегда.
### 2. Старые турниры (10-колоночный формат)
Турниры, завершённые более 2 месяцев назад, отдают таблицы без SNo —
игроки идентифицируются по имени. Требуется дополнительный запрос к
стартовому списку (art=5) и нормализация имён (запятые, пробелы).
### 3. Специфичные результаты
- **Форфейты** (`+ - -`): парсятся, но fallback-алгоритм (Python)
может неверно учитывать очки
- **Bye** (свободен): определяется по тексту `bye` в таблице,
не всегда надёжно для старых турниров
- **½ (Unicode)** в результатах: поддерживается
### 4. Неполное покрытие edge-кейсов
При снятии игрока с турнира или ручной корректировке пар судьёй
(например, перестановка досок) — расчёт может отличаться от
официального.
### 5. Производительность
4 последовательных HTTP-запроса к chess-results.com + запуск bbpPairings.
Полный цикл: ~57 секунд. Для турниров с >200 участниками может
потребоваться больше.
## Технический долг
- [ ] Замена bbpPairings на JaVaFo при появлении совместимого TRF-формата
- [ ] Кэширование спарсенных данных (одинаковые запросы при повторных запусках)
- [ ] Поддержка круговых турниров (сейчас только швейцарка)
- [ ] Вывод в JSON для интеграции с другими сервисами
- [ ] Расчёт бухгольца и других коэффициентов из сырых данных
- [ ] Web-интерфейс или Telegram-бот вместо CLI