Skip to content

Instantly share code, notes, and snippets.

@luispedro
Created November 7, 2025 06:51
Show Gist options
  • Select an option

  • Save luispedro/2175390e0d17ddbf203a53d8fd6a7888 to your computer and use it in GitHub Desktop.

Select an option

Save luispedro/2175390e0d17ddbf203a53d8fd6a7888 to your computer and use it in GitHub Desktop.
from collections import namedtuple
import re
# From https://live.chessbase.com/en/CrossTable?id=fide-world-cup-2025&displayTitle=FIDE%20World%20Cup%202025%2C%20Goa
DATA = '''
Kovalev,V2557½1------1.5
Velten,P2530½0------0.5
Mekhitarian,K25450½------0.5
Petrov,M25401½------1.5
Can,E2552½00½----1
Maksimovic,B2532½11½----3
Subelj,J2545½00½----1
Kuybokarov,T2535½11½----3
Bogner,S2555½½0½----1.5
Stremavicius,T2531½½1½----2.5
Nogerbek,K2543½101011½5
Raja Rithvik R2541½010100½3
Piorun,K2557½1½½----2.5
Lobanov,S2526½0½½----1.5
Flores,D255810½0----1.5
Makhnev,D252501½1----2.5
Kantor,G2559½1------1.5
Rodrigue-Lemieux,S2524½0------0.5
Ganguly,S257311------2
Ahmadzada,A252300------0
Supi,L2575½½1½----2.5
Galaviz Medina,S2515½½0½----1.5
Ghosh,D2573½11½----3
Peng,X2521½00½----1
Lodici,L2560½1------1.5
Samadov,R2523½0------0.5
Jobava,B25730½11----2.5
Cardoso Cardoso,J25181½00----1.5
Bartel,M2575001001--2
Aronyak Ghosh2514110110--4
Lupulescu,C25770½------0.5
Blohberger,F25131½------1.5
Petrov,N25761-------1
Ghazarian,K25130-------0
Fier,A2577½1------1.5
Parligras,M2510½0------0.5
Brkic,A2578½10½½00½3
Oro,F2509½01½½11½5
Karthik Venkataraman25791½------1.5
Garcia Pantoja,R25020½------0.5
Kourkoulos-Arditis,S258311------2
Divya Deshmukh249800------0
Tin,J2583½11½----3
Lashkin,J2497½00½----1
Motylev,A258611------2
Avila Pavas,S249000------0
Warmerdam,M2582½011011½5
Lalit Babu M R2502½100100½3
Neiksans,A2585½½0½----1.5
Suleymenov,A2491½½1½----2.5
Nesterov,A25841½------1.5
Atabayev,S24940½------0.5
Huschenbeth,N25871½------1.5
Bellahcene,B24850½------0.5
Abasov,N258711------2
Suyarov,M248700------0
Adly,A2589½10½½1½-4
Grigoryan,K2481½01½½0½-3
Henriquez Villagra,C26051½10----2.5
Agibileg,U24480½01----1.5
Bai,J2590½1------1.5
Fawzy,A2476½0------0.5
Yilmaz,M2591½1------1.5
Harshavardhan G B2476½0------0.5
Woodward,A259011------2
Gusain,H247600------0
Cori,J2594½11½----3
Ansat,A2470½00½----1
Idani,P260111------2
Cori Quispe,K245100------0
Najer,E26101½------1.5
Rakotomaharo,F24350½------0.5
Dardha,D260511------2
Banh Gia Huy244000------0
Lagarde,M2617½1------1.5
Amartuvshin,G2415½0------0.5
Daneshvar,B2602½1------1.5
Salinas Herrera,P2451½0------0.5
Cheng,B25961½------1.5
Siddharth,J24670½------0.5
Narayanan S L2617½11½----3
Rojas Salas,S2413½00½----1
Zemlyanskii,I25961½------1.5
Pantsulaia,L24570½------0.5
Indjic,A2618½1------1.5
Schnaider,I2413½0------0.5
Suleymanli,A261411------2
Laohawirapap,P241700------0
Meier,G2596½1------1.5
Neelash Saha2466½0------0.5
Iniyan,P2599½1------1.5
Berdayes Ason,D2453½0------0.5
Erdogmus,Y265111------2
Abugenda,N197200------0
Mendonca,L26200½------0.5
Wang,S24021½------1.5
Ivanchuk,V2616½1------1.5
Mohammad Fahad,R2416½0------0.5
Grebnev,A26111½------1.5
Quizon,D24200½------0.5
Xiong,J2648½1------1.5
Li,Y1994½0------0.5
Yuffa,D2621½½½½½11½5
Thavandiran,S2402½½½½½00½3
Rodshtein,M264711------2
Qin,O207800------0
Sargissian,G2624½1------1.5
Liyanage,R2401½0------0.5
Adams,M264611------2
Alrehaili,A210900------0
Cheparinov,I2626½1------1.5
Cordoba Roa,A2392½0------0.5
Artemiev,V264611------2
Ndahangwapo,H209300------0
Martirosyan,H26261½------1.5
Cahaya,S23930½------0.5
Svane,R2614½1------1.5
Vazquez,F2419½0------0.5
Grandelius,N2645½1------1.5
Allam,M2112½0------0.5
Bacrot,E26271-------1
Mwali,C23920-------0
Martinez Alcantara,J264411------2
Huh,I213900------0
Ivic,V262811------2
Orozbaev,E237500------0
Gledura,B264311------2
Amdouni,Z218600------0
Amin,B26291-------1
Mandizha,F23750-------0
Kollars,D264211------2
Salih,A221200------0
Pranesh M2630½1------1.5
Akhmedinov,S2372½0------0.5
Salem,A262011------2
Tran,T240700------0
Sadhwani,R26411½------1.5
Barrish,D22840½------0.5
Hovhannisyan,R2633½½1½----2.5
Kavin Mohan2346½½0½----1.5
Pranav,V264111------2
Boulrens,A221400------0
Tari,A26311½------1.5
Manon,R23690½------0.5
Donchenko,A264111------2
Kigigha,B226300------0
Anton Guijarro,D2631½1------1.5
Silva,D2347½0------0.5
Vokhidov,S264011------2
Ilkhomi,J228900------0
Chigaev,M2634½1------1.5
Elbilia,J2332½0------0.5
Svane,F263811------2
Husbands,O229100------0
Gurel,E263411------2
Efimov,I233100------0
Keymer,V277311---------2
Kovalev,V255700---------0
Erigaisi Arjun276911---------2
Petrov,M254000---------0
Giri,A2769½1---------1.5
Maksimovic,B2532½0---------0.5
Praggnanandhaa R2768½½½½0111---5
Kuybokarov,T2535½½½½1000---3
So,W2764½0---------0.5
Stremavicius,T2531½1---------1.5
Gukesh D2763½1---------1.5
Nogerbek,K2543½0---------0.5
Wei,Y275311---------2
Piorun,K255700---------0
Abdusattorov,N2750½½½1-------2.5
Makhnev,D2525½½½0-------1.5
Mamedyarov,S27421011-------3
Kantor,G25590100-------1
Vachier-Lagrave,M27401½---------1.5
Ganguly,S25730½---------0.5
Rapport,R274011---------2
Supi,L257500---------0
Nepomniachtchi,I2732½0---------0.5
Ghosh,D2573½1---------1.5
Niemann,H2729½½0½-------1.5
Lodici,L2560½½1½-------2.5
Le,Q2729½1---------1.5
Jobava,B2573½0---------0.5
Aronian,L27281½---------1.5
Aronyak Ghosh25140½---------0.5
Yu,Y2726½½01½½1½---4.5
Blohberger,F2513½½10½½0½---3.5
Sindarov,J27211½---------1.5
Petrov,N25760½---------0.5
Fedoseev,V27171½---------1.5
Fier,A25770½---------0.5
Vidit,S2715½½½1-------2.5
Oro,F2509½½½0-------1.5
Aravindh,C2713½0---------0.5
Karthik Venkataraman2579½1---------1.5
Nihal Sarin2704½½0½-------1.5
Kourkoulos-Arditis,S2583½½1½-------2.5
Sevian,S2701½½11-------3
Tin,J2583½½00-------1
Maghsoodloo,P2701½1---------1.5
Motylev,A2586½0---------0.5
Liang,A270111---------2
Warmerdam,M258200---------0
Van Foreest,J2693½1---------1.5
Suleymenov,A2491½0---------0.5
Harikrishna,P2690½1---------1.5
Nesterov,A2584½0---------0.5
Yakubboev,N2689½1---------1.5
Huschenbeth,N2587½0---------0.5
Esipenko,A268111---------2
Abasov,N258700---------0
Bluebaum,M26801½---------1.5
Adly,A25890½---------0.5
Sarana,A2675½1---------1.5
Henriquez Villagra,C2605½0---------0.5
Dubov,D2674½½½½11-----4
Bai,J2590½½½½00-----2
Bu,X2667½½½0-------1.5
Yilmaz,M2591½½½1-------2.5
Alekseenko,K2666½1---------1.5
Woodward,A2590½0---------0.5
Sargsyan,S2664½½011½-----3.5
Cori,J2594½½100½-----2.5
Karthikeyan,M2662½½10½½½0---3.5
Idani,P2601½½01½½½1---4.5
Christiansen,J26610½---------0.5
Najer,E26101½---------1.5
Oparin,G2660½½0½-------1.5
Dardha,D2605½½1½-------2.5
Wojtaszek,R2660½½½1-------2.5
Lagarde,M2617½½½0-------1.5
Saric,I26601½---------1.5
Daneshvar,B26020½---------0.5
Leko,P2660½1---------1.5
Cheng,B2596½0---------0.5
Vitiugov,N2657½½½½½½00---3
Narayanan S L2617½½½½½½11---5
Robson,R26570½---------0.5
Zemlyanskii,I25961½---------1.5
Theodorou,N2656½½½1-------2.5
Indjic,A2618½½½0-------1.5
Deac,B2655½½1½-------2.5
Suleymanli,A2614½½0½-------1.5
Murzin,V26550½---------0.5
Meier,G25961½---------1.5
Nguyen,T2652½1---------1.5
Iniyan,P2599½0---------0.5
Erdogmus,Y265111---------2
Wang,S240200---------0
Shankland,S264911---------2
Ivanchuk,V261600---------0
Navara,D2648½0---------0.5
Grebnev,A2611½1---------1.5
Xiong,J264811---------2
Yuffa,D262100---------0
Rodshtein,M2647½½½½00-----2
Sargissian,G2624½½½½11-----4
Adams,M2646½½½½10½½11-6
Cheparinov,I2626½½½½01½½00-4
Artemiev,V2646½½½1-------2.5
Martirosyan,H2626½½½0-------1.5
Mamedov,R2646½½½½01011005
Svane,R2614½½½½10100116
Grandelius,N264501½1-------2.5
Bacrot,E262710½0-------1.5
Martinez Alcantara,J2644½½½½½½1½---4.5
Ivic,V2628½½½½½½0½---3.5
Gledura,B2643½1---------1.5
Amin,B2629½0---------0.5
Kollars,D2642½½00-------1
Pranesh M2630½½11-------3
Mishra,A2642½0---------0.5
Salem,A2620½1---------1.5
Sadhwani,R2641½½00-------1
Hovhannisyan,R2633½½11-------3
Pranav,V2641101½-------2.5
Tari,A2631010½-------1.5
Donchenko,A2641½1---------1.5
Anton Guijarro,D2631½0---------0.5
Vokhidov,S2640½½11-------3
Chigaev,M2634½½00-------1
Svane,F2638½1---------1.5
Gurel,E2634½0---------0.5
'''
Player = namedtuple('Player', ['name', 'rating'])
Match = namedtuple('Match', ['player1', 'player2', 'result'])
RESULT = r'^(\D+)(\d\d\d\d)([0-9½])([0-9½])'
def _value(r):
if r == '1':
return 1.0
elif r == '0':
return 0.0
elif r == '½':
return 0.5
else:
raise ValueError(f'Invalid result: {r}')
def read_results():
for line in DATA.strip().splitlines():
line = line.strip()
if match := re.match(RESULT, line):
name, rating, r1, r2 = match.groups()
yield Player(name, int(rating)), _value(r1), _value(r2)
def expected_score(delta):
return 1 / (1 + 10**(-delta / 400))
def prob_one_game(delta):
assert delta >= 0
expected1 = expected_score(delta)
# assume that wins are twice as likely as draws
p_win = expected1 * 2/3
p_draw = expected1 * 1/3
p_lose = 1 - expected1
return (p_win, p_draw, p_lose)
def match_probabilities(delta):
assert delta >= 0
p_win1, p_draw1, p_lose1 = prob_one_game(delta)
p_win = p_win1**2 + 2 * p_win1 * p_draw1
p_draw = p_draw1**2 + 2 * p_win1 * p_lose1
p_lose = p_lose1**2 + 2 * p_draw1 * p_lose1
return (p_win, p_draw, p_lose)
matches = []
results = read_results()
while True:
try:
p1, r1, r2 = next(results)
except StopIteration:
break
p2, r3, r4 = next(results)
assert (r1 + r3 == 1.0), f'Incompatible results: {r1} vs {r3}'
assert (r2 + r4 == 1.0), f'Incompatible results: {r2} vs {r4}'
matches.append(Match(p1, p2, (r1, r2)))
delta_results = []
for m in matches:
delta = m.player1.rating - m.player2.rating
delta_results.append((delta, m.result[0]+m.result[1]))
expected_upsets = 0
total_upsets = 0
LIMIT = 200
for m in matches:
delta = m.player1.rating - m.player2.rating
if delta >= LIMIT:
p_win, p_draw, p_lose = match_probabilities(delta)
is_upset = (m.result[0] + m.result[1]) < 1.0
if is_upset:
print(f'Upset: {m.player1.name} ({m.player1.rating}) vs {m.player2.name} ({m.player2.rating}) => {m.result[0]}-{m.result[1]} (p={p_lose:.3f})')
expected_upsets += p_lose
total_upsets += is_upset
print(f'''
Total matches with delta >= {LIMIT}: {len([m for m in matches if (m.player1.rating - m.player2.rating) >= LIMIT])}
Expected upsets: {expected_upsets:.1f}
Actual upsets: {total_upsets}
Difference: {total_upsets - expected_upsets:.1f}
''')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment