ChessCalcNextTour/test_round_by_round.py

129 lines
4.1 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Test round-by-round: parse once, truncate results, calculate next round."""
import sys
sys.path.insert(0, '.')
from swiss_calc.parser import fetch_tournament, fetch_url, parse_round_pairings
from swiss_calc.trf_generator import generate_trf
from swiss_calc.bbp_wrapper import call_bbp
import re
def norm(n):
return re.sub(r'\s+', ' ', n.replace(',', '')).strip()
BASE = 'https://s1.chess-results.com/tnr1289471.aspx?lan=11'
print("=" * 60)
print("ТЕСТ: пошаговый расчёт всех туров")
print("=" * 60)
# Parse once — get full data with correct SNo, byes, forfeits
data = fetch_tournament(BASE)
total_rounds = data['current_round']
standings = data['standings']
print(f"Турнир: {data['name'][:60]}")
print(f"Сыграно туров: {total_rounds}")
print(f"Участников: {len(standings)}")
# Collect actual pairs for comparison
name_to_sno = {norm(s['name']): s['starting_sno'] for s in standings}
actual_pairs = {}
for rd in range(1, total_rounds + 1):
html = fetch_url(f'{BASE}&art=2&rd={rd}&turdet=YES')
games = parse_round_pairings(html, rd)
for g in games:
if g['white_sno'] == 0:
g['white_sno'] = name_to_sno.get(norm(g['white_name']), 0)
if g['black_sno'] == 0:
g['black_sno'] = name_to_sno.get(norm(g['black_name']), 0)
actual_pairs[rd] = games
print(f" Тур {rd}: {len(games)} пар")
print(f"\n{'='*60}")
print(f"ПОШАГОВЫЙ РАСЧЁТ")
print(f"{'='*60}")
overall_matched = 0
overall_total = 0
for next_rd in range(2, total_rounds + 1):
completed = next_rd - 1
# Truncate results to only first `completed` rounds
truncated_standings = []
for s in standings:
full_results = s.get('results', [])
full_orig = s.get('_orig_results', [])
# Take results only up to round `completed`
trunc_results = [r for r in full_results if r.get('round', 99) <= completed]
trunc_orig = [r for r in full_orig if isinstance(r, dict) and r.get('round', 99) <= completed]
points = sum(r['score'] for r in trunc_results)
truncated_standings.append({
'rank': s['rank'],
'name': s['name'],
'fed': s['fed'],
'points': points,
'starting_sno': s['starting_sno'],
'rating': s.get('rating', 0),
'results': trunc_results,
'_orig_results': trunc_orig,
'tb': s.get('tb', []),
})
truncated = {
'name': data['name'],
'num_rounds': total_rounds,
'current_round': completed,
'players': data['players'],
'standings': truncated_standings,
}
trf = generate_trf(truncated, next_rd, name=data.get('name', 'Test'),
use_rank=True, initial_color_white=True)
try:
bbp_pairs, _ = call_bbp(trf)
except Exception as e:
print(f" Тур {next_rd}: bbpPairings ОШИБКА — {e}")
continue
# Compare with actual
calc_set = set()
for w_sno, b_sno in bbp_pairs:
if b_sno == 0 or w_sno == 0:
continue
calc_set.add((min(w_sno, b_sno), max(w_sno, b_sno)))
real_set = set()
for g in actual_pairs.get(next_rd, []):
w, b = g['white_sno'], g['black_sno']
if w == 0 or b == 0:
continue
# Only include completed games (not future pairings)
if g.get('white_score') is not None:
real_set.add((min(w, b), max(w, b)))
matched = len(calc_set & real_set)
total = len(real_set)
if total == 0:
print(f" Тур {next_rd}: нет сыгранных пар для сравнения")
continue
pct = 100 * matched / total
overall_matched += matched
overall_total += total
bar = '' * int(pct / 5) + '' * (20 - int(pct / 5))
print(f" Тур {next_rd}: {matched}/{total} совпадений [{bar}] {pct:.0f}%")
print(f"\n{'='*60}")
if overall_total > 0:
print(f"ИТОГО: {overall_matched}/{overall_total} ({100*overall_matched/overall_total:.0f}%)")
else:
print(f"ИТОГО: 0 пар")
print(f"{'='*60}")