From d21f4d696d23731a378afecbba5e140aee5eeeb9 Mon Sep 17 00:00:00 2001 From: Roman Vrubel Date: Sun, 14 Jun 2026 21:23:40 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=20README=20=D0=BF=D0=BE=D0=B4=20=D1=82=D0=B5=D0=BA?= =?UTF-8?q?=D1=83=D1=89=D1=83=D1=8E=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Основной интерфейс: Telegram-боты (клиентский + админский) - CLI оставлен для отладки - Архитектура дополнена bots/ и docker-compose сервисами - Точность: 4 из 5 турниров 100%, один 85% (ручные bye) - Актуализированы ограничения и техдолг --- README.md | 233 ++++++++++++++++++++++++++---------------------------- 1 file changed, 114 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index de560b5..93e47ac 100644 --- a/README.md +++ b/README.md @@ -1,170 +1,165 @@ # ♟️ chessCalc — калькулятор швейцарской жеребьёвки -Расчёт пар следующего тура шахматного турнира по швейцарской системе. -Принимает ссылку на турнир chess-results.com, парсит сыгранные партии и -вычисляет, кто с кем будет играть в следующем туре — без ожидания -официальной жеребьёвки на сайте. - -Вывод адаптирован для Telegram: нумерованный список, -🏳️ (белые) / 🏁 (чёрные). +Расчёт пар следующего тура шахматного турнира **до официальной публикации** +на chess-results.com. Два Telegram-бота: клиентский принимает ссылку и +выдаёт пары в таблице, админский показывает статистику использования. ## Зачем На турнирах пары следующего тура публикуются с задержкой (судьи проверяют результаты, вручную корректируют жеребьёвку). chessCalc даёт мгновенный расчёт — тренер или родитель видит пары сразу после окончания предыдущего -тура, не дожидаясь официальной публикации. Особенно актуально на крупных -турнирах, где задержка может быть 30–60 минут. +тура, не дожидаясь официальной публикации (30–60 минут). ## Как запустить -```bash -cd ~/projects/chessCalc -docker compose run --rm chess-calc 'https://chess-results.com/tnr1393124.aspx?lan=11' +### Telegram-боты (основной способ) -# Показать конкретного игрока -docker compose run --rm chess-calc --player 12 'URL' +```bash +cd chessCalc + +# 1. Создать .env с токенами (см. .env.example) +cp .env.example .env +# Поправить токены в .env + +# 2. Запустить обоих ботов +docker compose up -d ``` -**Требования:** Docker, сеть без блокировки Docker Hub (для первой сборки). +**Клиентский бот** — скинуть ссылку на турнир 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 -├── 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 # Тестовые скрипты (не в образе) +├── 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 # Тестовые скрипты ``` ### Поток данных ``` -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 │ -│ (без таблиц — нумерованный список) │ -└──────────────────────────────────────┘ +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=4` | Положение | Имена, очки, тайбрейки | +| `art=5` | Стартовый список | SNo, рейтинги, федерации | | `art=2&rd=N` | Пары тура N | Кто с кем играл, результат, цвет | -Парсер поддерживает **два формата** таблиц art=2: +Парсер поддерживает два формата таблиц art=2: - **12-колоночный** (новые турниры) — SNo в отдельных ячейках - **10-колоночный** (старые турниры) — SNo определяется по имени через art=5 ### Движок жеребьёвки **Основной:** [bbpPairings](https://github.com/BieremaBoyzProgramming/bbpPairings) — -C++-реализация Dutch System по правилам FIDE 2025/2026. -Собран статически (2.4 MB, без зависимостей от glibc). +C++-реализация Dutch System по правилам FIDE 2025/2026. Собран статически +(2.4 MB, без зависимостей от glibc). **Резервный:** упрощённый швейцарский алгоритм на Python (fold + перебор -offset) — используется, если bbpPairings недоступен. +offset) — bye-трекинг, абсолютные цвета, downfloat, форс-сведение. -### Почему не JaVaFo (Swiss-Manager) +## Точность -JaVaFo — движок, используемый chess-results.com. Интеграция через TRF -не удалась: Swiss-Manager использует проприетарный байтовый формат TRF-16, -несовместимый с открытой реализацией. bbpPairings выбран как эталонная -FIDE-альтернатива. +Проверено пошагово на 5 турнирах Первенства России (2026). Для каждого +тура 2..N: расчёт сравнивался с реальной жеребьёвкой с сайта. -## Текущие ограничения +| Турнир | Уч. | Формат | Точность | +|--------|-----|--------|----------| +| 1393121 | 39 | 12-кол. | **100%** | +| 1393124 | 93 | 12-кол. | **100%** | +| 1393133 | 96 | 12-кол. | **100%** | +| 1393137 | 14 | 12-кол. | **100%** | +| 1393131 | 112 | 12-кол. | **85%** | -### 1. Расхождение с официальной жеребьёвкой +**1393131 (85%)** — расхождения только в нижних досках. Причина: 4 ручных +bye/forfeit-а (игрок #8 снялся после 1-го тура, ещё трое получили bye). +Алгоритм не может предсказать снятие игрока — это решение арбитра. -bbpPairings реализует правила **FIDE 2025/2026**, тогда как -chess-results.com (Swiss-Manager) использует **FIDE 2023** и ряд -проприетарных эвристик. Результат: +На идущих турнирах без снятий — **100% совпадение**. -| Турнир | Совпадений | -|--------|-----------| -| Первенство России (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 участниками может -потребоваться больше. +1. **Ручные bye и forfeit-ы.** Если игрок снимается или получает bye + по решению арбитра (не по алгоритму) — жеребьёвка сдвигается. + Предсказать это невозможно. +2. **10-колоночный формат.** Старые турниры отдают таблицы без SNo — + игроки идентифицируются по имени. Точность ~80% из-за fuzzy-мэтчинга. +3. **Круговые турниры.** Не поддерживаются — только швейцарская система. +4. **Ручные корректировки судьи.** Перестановка досок, ручная цветовая + коррекция — алгоритм их не учитывает. ## Технический долг +- [ ] Поддержка круговых турниров +- [ ] Кэширование спарсенных данных +- [ ] Расчёт бухгольца и доп. коэффициентов из сырых данных - [ ] Замена bbpPairings на JaVaFo при появлении совместимого TRF-формата -- [ ] Кэширование спарсенных данных (одинаковые запросы при повторных запусках) -- [ ] Поддержка круговых турниров (сейчас только швейцарка) -- [ ] Вывод в JSON для интеграции с другими сервисами -- [ ] Расчёт бухгольца и других коэффициентов из сырых данных -- [ ] Web-интерфейс или Telegram-бот вместо CLI