Основные изменения: - 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).
170 lines
9.4 KiB
Markdown
170 lines
9.4 KiB
Markdown
# ♟️ chessCalc — калькулятор швейцарской жеребьёвки
|
||
|
||
Расчёт пар следующего тура шахматного турнира по швейцарской системе.
|
||
Принимает ссылку на турнир chess-results.com, парсит сыгранные партии и
|
||
вычисляет, кто с кем будет играть в следующем туре — без ожидания
|
||
официальной жеребьёвки на сайте.
|
||
|
||
Вывод адаптирован для Telegram: нумерованный список,
|
||
🏳️ (белые) / 🏁 (чёрные).
|
||
|
||
## Зачем
|
||
|
||
На турнирах пары следующего тура публикуются с задержкой (судьи проверяют
|
||
результаты, вручную корректируют жеребьёвку). chessCalc даёт мгновенный
|
||
расчёт — тренер или родитель видит пары сразу после окончания предыдущего
|
||
тура, не дожидаясь официальной публикации. Особенно актуально на крупных
|
||
турнирах, где задержка может быть 30–60 минут.
|
||
|
||
## Как запустить
|
||
|
||
```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 уч.) | ~1–4% |
|
||
|
||
Обе жеребьёвки **корректны** по своим редакциям правил. Пары валидны:
|
||
нет повторов, самоматчей, нарушений цветового баланса. Доска 1 совпадает
|
||
практически всегда.
|
||
|
||
### 2. Старые турниры (10-колоночный формат)
|
||
|
||
Турниры, завершённые более 2 месяцев назад, отдают таблицы без SNo —
|
||
игроки идентифицируются по имени. Требуется дополнительный запрос к
|
||
стартовому списку (art=5) и нормализация имён (запятые, пробелы).
|
||
|
||
### 3. Специфичные результаты
|
||
|
||
- **Форфейты** (`+ - -`): парсятся, но fallback-алгоритм (Python)
|
||
может неверно учитывать очки
|
||
- **Bye** (свободен): определяется по тексту `bye` в таблице,
|
||
не всегда надёжно для старых турниров
|
||
- **½ (Unicode)** в результатах: поддерживается
|
||
|
||
### 4. Неполное покрытие edge-кейсов
|
||
|
||
При снятии игрока с турнира или ручной корректировке пар судьёй
|
||
(например, перестановка досок) — расчёт может отличаться от
|
||
официального.
|
||
|
||
### 5. Производительность
|
||
|
||
4 последовательных HTTP-запроса к chess-results.com + запуск bbpPairings.
|
||
Полный цикл: ~5–7 секунд. Для турниров с >200 участниками может
|
||
потребоваться больше.
|
||
|
||
## Технический долг
|
||
|
||
- [ ] Замена bbpPairings на JaVaFo при появлении совместимого TRF-формата
|
||
- [ ] Кэширование спарсенных данных (одинаковые запросы при повторных запусках)
|
||
- [ ] Поддержка круговых турниров (сейчас только швейцарка)
|
||
- [ ] Вывод в JSON для интеграции с другими сервисами
|
||
- [ ] Расчёт бухгольца и других коэффициентов из сырых данных
|
||
- [ ] Web-интерфейс или Telegram-бот вместо CLI
|