- Клиентский бот: принимает ссылку на chess-results.com, проверяет наличие результатов/жеребьёвки, считает пары следующего тура, выводит в HTML-формате - Админский бот: /stat — уникальные пользователи, всего запросов, запросов за сегодня - Статистика через SQLite (volume bot-data) - Оба бота в docker-compose с автоперезапуском - .env.example с тестовыми токенами - .env в .gitignore |
||
|---|---|---|
| bots | ||
| swiss_calc | ||
| .env.example | ||
| .gitignore | ||
| bbpPairings.exe | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
| test_all_rounds.py | ||
| test_round_by_round.py | ||
♟️ chessCalc — калькулятор швейцарской жеребьёвки
Расчёт пар следующего тура шахматного турнира по швейцарской системе. Принимает ссылку на турнир chess-results.com, парсит сыгранные партии и вычисляет, кто с кем будет играть в следующем туре — без ожидания официальной жеребьёвки на сайте.
Вывод адаптирован для Telegram: нумерованный список, 🏳️ (белые) / 🏁 (чёрные).
Зачем
На турнирах пары следующего тура публикуются с задержкой (судьи проверяют результаты, вручную корректируют жеребьёвку). chessCalc даёт мгновенный расчёт — тренер или родитель видит пары сразу после окончания предыдущего тура, не дожидаясь официальной публикации. Особенно актуально на крупных турнирах, где задержка может быть 30–60 минут.
Как запустить
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 — 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