ChessCalcNextTour/swiss_calc/__main__.py
Roman Vrubel cf7fafd2ba chessCalc: парсер chess-results.com + FIDE Swiss + Docker
Принимает URL турнира, показывает пары следующего тура в Telegram-формате.
- parser.py: парсинг chess-results.com
- swiss.py: FIDE Dutch System
- display.py: форматирование для Telegram
- Docker: docker compose run --rm chess-calc 'URL'
2026-06-14 13:57:32 +00:00

132 lines
5 KiB
Python
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.

"""
Chess tournament Swiss system pairing calculator.
Fetches data from chess-results.com and calculates next round pairings.
Usage:
python -m swiss_calc <URL>
python -m swiss_calc --standings <URL>
"""
import sys
import argparse
from .parser import fetch_tournament
from .swiss import calculate_next_round
from .display import format_pairings_table, format_player_standings
def main():
parser = argparse.ArgumentParser(
description='Chess tournament pairing calculator for chess-results.com'
)
parser.add_argument('url', nargs='?', help='chess-results.com tournament URL')
parser.add_argument('--standings', '-s', action='store_true',
help='Show standings instead of next round')
parser.add_argument('--top', type=int, default=20,
help='Number of top players to show in standings (default: 20)')
parser.add_argument('--player', '-p', type=int, default=None,
help='Player rank to highlight')
parser.add_argument('--json', action='store_true',
help='Output JSON instead of formatted text')
args = parser.parse_args()
url = args.url
if not url:
url = input('URL турнира chess-results.com: ').strip()
if not url:
print('❌ URL не указан', file=sys.stderr)
sys.exit(1)
print(f'⏳ Загружаю: {url}', file=sys.stderr)
try:
tournament = fetch_tournament(url)
except Exception as e:
print(f'❌ Ошибка: {e}', file=sys.stderr)
sys.exit(1)
print(f'{tournament["name"]}', file=sys.stderr)
print(f'📅 Тур {tournament["current_round"]} из {tournament["num_rounds"]}', file=sys.stderr)
print(f'👥 {len(tournament["standings"])} участников', file=sys.stderr)
print(file=sys.stderr)
if tournament['current_round'] >= tournament['num_rounds']:
if args.json:
import json
json.dump({'status': 'completed', 'round': tournament['current_round'], 'total_rounds': tournament['num_rounds']}, sys.stdout, ensure_ascii=False)
return
print('🏁 Турнир завершён!')
print()
print(format_player_standings(tournament, args.top))
return
if args.standings:
output = format_player_standings(tournament, args.top)
if args.json:
import json
json.dump({'type': 'standings', 'data': tournament['standings'][:args.top]}, sys.stdout, ensure_ascii=False)
return
print(output)
return
# Calculate next round
print('🧮 Считаю следующий тур...', file=sys.stderr)
next_round = calculate_next_round(tournament)
# Cross-check against pre-calculated if available
precalc = sum(1 for s in tournament['standings'] if s.get('next_opponent'))
if precalc > 0:
print(f'📊 На сайте уже рассчитан {precalc} пар', file=sys.stderr)
output = format_pairings_table(next_round, tournament['name'])
if args.json:
import json
pairs = []
for pairing in next_round['pairings']:
if len(pairing) == 3:
p1, p2, color = pairing
pairs.append({
'white': {'name': p1.name, 'rank': p1.sno, 'rating': p1.rating, 'points': p1.points},
'black': {'name': p2.name, 'rank': p2.sno, 'rating': p2.rating, 'points': p2.points},
'color': color,
})
elif len(pairing) == 5:
pairs.append({
'white': {'name': pairing[1] if pairing[2] == 'w' else pairing[4], 'name2': ...},
})
json.dump({
'round': next_round['round'],
'pairings': pairs,
'source': next_round.get('source', 'algorithm'),
}, sys.stdout, ensure_ascii=False)
return
print(output)
# Player highlight
if args.player:
rank = args.player
for p in tournament['standings']:
if p['rank'] == rank:
# Find this player's pairing
for pairing in next_round['pairings']:
if len(pairing) == 3:
p1, p2, color = pairing
if p1.sno == rank or p2.sno == rank:
if p1.sno == rank:
opp = p2
player_color = color # color belongs to p1
else:
opp = p1
player_color = 'b' if color == 'w' else 'w' # opposite for p2
print()
print(f"🔍 **{p['name']}** (#{rank}, {p['points']} очков):")
print(f" {'🏳️' if player_color == 'w' else '🏁'} vs **{opp.name}**")
print(f" Рейтинг: {opp.rating} | Очки: {opp.points}")
break
if __name__ == '__main__':
main()