No description
Find a file
vrubel 07b016bff9
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 7s
perf: SQL LIKE query instead of Python loop; skip warmup if cache >1000
2026-06-20 09:06:11 +00:00
.forgejo/workflows Fix CI test step: use docker run instead of compose 2026-06-14 21:47:37 +00:00
bots perf: SQL LIKE query instead of Python loop; skip warmup if cache >1000 2026-06-20 09:06:11 +00:00
swiss_calc Парсер: поддержка латиницы (международные турниры) + таблица: колонки Рейт и Доп1 2026-06-19 14:51:15 +00:00
.env.example Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
.gitignore Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
bbpPairings.exe chessCalc: исправлена точность жеребьёвки до 100% 2026-06-14 20:29:27 +00:00
docker-compose.prod.yml Add CI/CD pipeline (Forgejo) and prod docker-compose 2026-06-14 21:41:07 +00:00
docker-compose.yml Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
Dockerfile Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
README.md Переписан README под текущую реализацию 2026-06-14 21:23:40 +00:00
requirements.txt Добавлены Telegram-боты (клиентский + админский) 2026-06-14 20:39:19 +00:00
test_all_rounds.py chessCalc: исправлена точность жеребьёвки до 100% 2026-06-14 20:29:27 +00:00
test_round_by_round.py chessCalc: исправлена точность жеребьёвки до 100% 2026-06-14 20:29:27 +00:00

♟️ chessCalc — калькулятор швейцарской жеребьёвки

Расчёт пар следующего тура шахматного турнира до официальной публикации на chess-results.com. Два Telegram-бота: клиентский принимает ссылку и выдаёт пары в таблице, админский показывает статистику использования.

Зачем

На турнирах пары следующего тура публикуются с задержкой (судьи проверяют результаты, вручную корректируют жеребьёвку). chessCalc даёт мгновенный расчёт — тренер или родитель видит пары сразу после окончания предыдущего тура, не дожидаясь официальной публикации (3060 минут).

Как запустить

Telegram-боты (основной способ)

cd chessCalc

# 1. Создать .env с токенами (см. .env.example)
cp .env.example .env
# Поправить токены в .env

# 2. Запустить обоих ботов
docker compose up -d

Клиентский бот — скинуть ссылку на турнир chess-results.com, получить пары следующего тура в таблице (MarkdownV2).

Админский бот — команда /stat: уникальные пользователи, всего запросов, запросов за сегодня.

CLI (для отладки)

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 — 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-формата