ChessCalcNextTour/README.md
Roman Vrubel d21f4d696d Переписан README под текущую реализацию
- Основной интерфейс: Telegram-боты (клиентский + админский)
- CLI оставлен для отладки
- Архитектура дополнена bots/ и docker-compose сервисами
- Точность: 4 из 5 турниров 100%, один 85% (ручные bye)
- Актуализированы ограничения и техдолг
2026-06-14 21:23:40 +00:00

165 lines
9.5 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 минут).
## Как запустить
### Telegram-боты (основной способ)
```bash
cd chessCalc
# 1. Создать .env с токенами (см. .env.example)
cp .env.example .env
# Поправить токены в .env
# 2. Запустить обоих ботов
docker compose up -d
```
**Клиентский бот** — скинуть ссылку на турнир 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'
```
## Архитектура
```
chessCalc/
├── 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 # Тестовые скрипты
```
### Поток данных
```
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: нумерованный список │
└─────────────────────────────────────────────────────┘
```
### Источники данных на 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) — bye-трекинг, абсолютные цвета, downfloat, форс-сведение.
## Точность
Проверено пошагово на 5 турнирах Первенства России (2026). Для каждого
тура 2..N: расчёт сравнивался с реальной жеребьёвкой с сайта.
| Турнир | Уч. | Формат | Точность |
|--------|-----|--------|----------|
| 1393121 | 39 | 12-кол. | **100%** |
| 1393124 | 93 | 12-кол. | **100%** |
| 1393133 | 96 | 12-кол. | **100%** |
| 1393137 | 14 | 12-кол. | **100%** |
| 1393131 | 112 | 12-кол. | **85%** |
**1393131 (85%)** — расхождения только в нижних досках. Причина: 4 ручных
bye/forfeit-а (игрок #8 снялся после 1-го тура, ещё трое получили bye).
Алгоритм не может предсказать снятие игрока — это решение арбитра.
На идущих турнирах без снятий — **100% совпадение**.
## Ограничения
1. **Ручные bye и forfeit-ы.** Если игрок снимается или получает bye
по решению арбитра (не по алгоритму) — жеребьёвка сдвигается.
Предсказать это невозможно.
2. **10-колоночный формат.** Старые турниры отдают таблицы без SNo —
игроки идентифицируются по имени. Точность ~80% из-за fuzzy-мэтчинга.
3. **Круговые турниры.** Не поддерживаются — только швейцарская система.
4. **Ручные корректировки судьи.** Перестановка досок, ручная цветовая
коррекция — алгоритм их не учитывает.
## Технический долг
- [ ] Поддержка круговых турниров
- [ ] Кэширование спарсенных данных
- [ ] Расчёт бухгольца и доп. коэффициентов из сырых данных
- [ ] Замена bbpPairings на JaVaFo при появлении совместимого TRF-формата