2026-06-14 20:29:27 +00:00
|
|
|
|
# ♟️ chessCalc — калькулятор швейцарской жеребьёвки
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
Расчёт пар следующего тура шахматного турнира **до официальной публикации**
|
|
|
|
|
|
на chess-results.com. Два Telegram-бота: клиентский принимает ссылку и
|
|
|
|
|
|
выдаёт пары в таблице, админский показывает статистику использования.
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
## Зачем
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
На турнирах пары следующего тура публикуются с задержкой (судьи проверяют
|
|
|
|
|
|
результаты, вручную корректируют жеребьёвку). chessCalc даёт мгновенный
|
|
|
|
|
|
расчёт — тренер или родитель видит пары сразу после окончания предыдущего
|
2026-06-14 21:23:40 +00:00
|
|
|
|
тура, не дожидаясь официальной публикации (30–60 минут).
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
## Как запустить
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
### Telegram-боты (основной способ)
|
|
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
```bash
|
2026-06-14 21:23:40 +00:00
|
|
|
|
cd chessCalc
|
|
|
|
|
|
|
|
|
|
|
|
# 1. Создать .env с токенами (см. .env.example)
|
|
|
|
|
|
cp .env.example .env
|
|
|
|
|
|
# Поправить токены в .env
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
# 2. Запустить обоих ботов
|
|
|
|
|
|
docker compose up -d
|
2026-06-14 13:57:32 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
**Клиентский бот** — скинуть ссылку на турнир chess-results.com, получить
|
|
|
|
|
|
пары следующего тура в таблице (MarkdownV2).
|
|
|
|
|
|
|
|
|
|
|
|
**Админский бот** — команда `/stat`: уникальные пользователи, всего
|
|
|
|
|
|
запросов, запросов за сегодня.
|
|
|
|
|
|
|
|
|
|
|
|
### CLI (для отладки)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker compose run --rm client-bot python3 -m swiss_calc 'URL'
|
|
|
|
|
|
|
|
|
|
|
|
# JSON-вывод
|
|
|
|
|
|
docker compose run --rm client-bot python3 -m swiss_calc --json 'URL'
|
|
|
|
|
|
|
|
|
|
|
|
# Положение после последнего тура
|
|
|
|
|
|
docker compose run --rm client-bot python3 -m swiss_calc --standings 'URL'
|
|
|
|
|
|
```
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
|
|
|
|
|
## Архитектура
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
```
|
|
|
|
|
|
chessCalc/
|
2026-06-14 21:23:40 +00:00
|
|
|
|
├── Dockerfile # python:3.11-slim + bbpPairings
|
|
|
|
|
|
├── docker-compose.yml # client-bot + admin-bot + volume bot-data
|
|
|
|
|
|
├── .env.example # шаблон токенов (коммитится)
|
|
|
|
|
|
├── .env # реальные токены (gitignored)
|
|
|
|
|
|
├── bbpPairings.exe # FIDE-движок (C++, статическая сборка)
|
|
|
|
|
|
├── requirements.txt # requests, bs4, python-telegram-bot, rich
|
|
|
|
|
|
├── swiss_calc/ # Ядро — парсинг + расчёт
|
|
|
|
|
|
│ ├── __main__.py # CLI entry point
|
|
|
|
|
|
│ ├── parser.py # Парсинг chess-results.com (art=2/4/5)
|
|
|
|
|
|
│ ├── trf_generator.py # Генерация TRF-16 для bbpPairings
|
|
|
|
|
|
│ ├── bbp_wrapper.py # Вызов bbpPairings.exe через subprocess
|
|
|
|
|
|
│ ├── swiss.py # Оркестрация + fallback Swiss-алгоритм
|
|
|
|
|
|
│ └── display.py # Форматирование вывода (CLI)
|
|
|
|
|
|
├── bots/ # Telegram-боты
|
|
|
|
|
|
│ ├── client_bot.py # Клиентский бот (ссылка → пары)
|
|
|
|
|
|
│ ├── admin_bot.py # Админский бот (/stat)
|
|
|
|
|
|
│ └── stats.py # Статистика (SQLite)
|
|
|
|
|
|
└── test_*.py # Тестовые скрипты
|
2026-06-14 13:57:32 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
### Поток данных
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
```
|
2026-06-14 21:23:40 +00:00
|
|
|
|
Telegram-бот CLI (отладка)
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ URL турнира │ URL турнира
|
|
|
|
|
|
▼ ▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ parser.fetch_tournament() │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 1. art=4 — положение (имена, очки, тайбрейки) │
|
|
|
|
|
|
│ 2. art=5 — стартовый список (SNo, рейтинги, FED) │
|
|
|
|
|
|
│ 3. Сопоставление имён → SNo (fuzzy-мэтчинг) │
|
|
|
|
|
|
│ 4. art=2&rd=1..N — пары/результаты каждого тура │
|
|
|
|
|
|
│ 5. Сборка player.results[] из art=2 + forfeit/bye │
|
|
|
|
|
|
└─────────────────────────────────────────────────────┘
|
|
|
|
|
|
│ tournament_data
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ swiss.calculate_next_round() │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ 1. Генерация TRF-16 (trf_generator) │
|
|
|
|
|
|
│ 2. bbpPairings --dutch (FIDE 2025 Dutch System) │
|
|
|
|
|
|
│ → список пар (w_sno, b_sno) │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ fallback: упрощённый Swiss (swiss.py) │
|
|
|
|
|
|
│ — если bbpPairings недоступен │
|
|
|
|
|
|
└─────────────────────────────────────────────────────┘
|
|
|
|
|
|
│ пары
|
|
|
|
|
|
▼
|
|
|
|
|
|
┌─────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ Форматирование │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
│ Telegram: MarkdownV2 + таблица в code-блоке │
|
|
|
|
|
|
│ CLI: нумерованный список │
|
|
|
|
|
|
└─────────────────────────────────────────────────────┘
|
2026-06-14 20:29:27 +00:00
|
|
|
|
```
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
### Источники данных на chess-results.com
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
| Параметр | Страница | Что даёт |
|
|
|
|
|
|
|----------|----------|----------|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
| `art=4` | Положение | Имена, очки, тайбрейки |
|
|
|
|
|
|
| `art=5` | Стартовый список | SNo, рейтинги, федерации |
|
2026-06-14 20:29:27 +00:00
|
|
|
|
| `art=2&rd=N` | Пары тура N | Кто с кем играл, результат, цвет |
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
Парсер поддерживает два формата таблиц art=2:
|
2026-06-14 20:29:27 +00:00
|
|
|
|
- **12-колоночный** (новые турниры) — SNo в отдельных ячейках
|
|
|
|
|
|
- **10-колоночный** (старые турниры) — SNo определяется по имени через art=5
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
### Движок жеребьёвки
|
|
|
|
|
|
|
|
|
|
|
|
**Основной:** [bbpPairings](https://github.com/BieremaBoyzProgramming/bbpPairings) —
|
2026-06-14 21:23:40 +00:00
|
|
|
|
C++-реализация Dutch System по правилам FIDE 2025/2026. Собран статически
|
|
|
|
|
|
(2.4 MB, без зависимостей от glibc).
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
|
|
|
|
|
**Резервный:** упрощённый швейцарский алгоритм на Python (fold + перебор
|
2026-06-14 21:23:40 +00:00
|
|
|
|
offset) — bye-трекинг, абсолютные цвета, downfloat, форс-сведение.
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
## Точность
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
Проверено пошагово на 5 турнирах Первенства России (2026). Для каждого
|
|
|
|
|
|
тура 2..N: расчёт сравнивался с реальной жеребьёвкой с сайта.
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
| Турнир | Уч. | Формат | Точность |
|
|
|
|
|
|
|--------|-----|--------|----------|
|
|
|
|
|
|
| 1393121 | 39 | 12-кол. | **100%** |
|
|
|
|
|
|
| 1393124 | 93 | 12-кол. | **100%** |
|
|
|
|
|
|
| 1393133 | 96 | 12-кол. | **100%** |
|
|
|
|
|
|
| 1393137 | 14 | 12-кол. | **100%** |
|
|
|
|
|
|
| 1393131 | 112 | 12-кол. | **85%** |
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
**1393131 (85%)** — расхождения только в нижних досках. Причина: 4 ручных
|
|
|
|
|
|
bye/forfeit-а (игрок #8 снялся после 1-го тура, ещё трое получили bye).
|
|
|
|
|
|
Алгоритм не может предсказать снятие игрока — это решение арбитра.
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
На идущих турнирах без снятий — **100% совпадение**.
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
## Ограничения
|
2026-06-14 20:29:27 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
1. **Ручные bye и forfeit-ы.** Если игрок снимается или получает bye
|
|
|
|
|
|
по решению арбитра (не по алгоритму) — жеребьёвка сдвигается.
|
|
|
|
|
|
Предсказать это невозможно.
|
|
|
|
|
|
2. **10-колоночный формат.** Старые турниры отдают таблицы без SNo —
|
|
|
|
|
|
игроки идентифицируются по имени. Точность ~80% из-за fuzzy-мэтчинга.
|
|
|
|
|
|
3. **Круговые турниры.** Не поддерживаются — только швейцарская система.
|
|
|
|
|
|
4. **Ручные корректировки судьи.** Перестановка досок, ручная цветовая
|
|
|
|
|
|
коррекция — алгоритм их не учитывает.
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 20:29:27 +00:00
|
|
|
|
## Технический долг
|
2026-06-14 13:57:32 +00:00
|
|
|
|
|
2026-06-14 21:23:40 +00:00
|
|
|
|
- [ ] Поддержка круговых турниров
|
|
|
|
|
|
- [ ] Кэширование спарсенных данных
|
|
|
|
|
|
- [ ] Расчёт бухгольца и доп. коэффициентов из сырых данных
|
2026-06-14 20:29:27 +00:00
|
|
|
|
- [ ] Замена bbpPairings на JaVaFo при появлении совместимого TRF-формата
|