1# -*- coding: UTF-8 -*- 2 3from pychess.Utils.const import WHITE, WHITEWON, BLACK, BLACKWON, DRAW 4 5 6def get_elo_rating_change(model, overridden_welo, overridden_belo): 7 """ http://www.fide.com/fide/handbook.html?id=197&view=article (§8.5, July 2017) """ 8 9 def individual_elo_change(elo_player, elo_opponent, blitz): 10 result = {} 11 12 # Adaptation of the inbound parameters 13 pprov = '?' in elo_player 14 try: 15 pval = int(elo_player.replace("?", "")) 16 except ValueError: 17 pval = 0 18 try: 19 oval = int(elo_opponent.replace("?", "")) 20 except ValueError: 21 oval = 0 22 if pval == 0 or oval == 0: 23 return None 24 25 # Development coefficient - We ignore the age of the player and we assume that 26 # he is already rated. The provisional flag '?' just denotes that he has not 27 # played his first 30 games, but it may also denotes that he never had any 28 # ranking. The calculation being based on the current game only, we can't 29 # handle that second specific case 30 if blitz: 31 k = 20 32 else: 33 if pprov: 34 k = 40 35 else: 36 if pval >= 2400: 37 k = 10 38 else: 39 k = 20 40 41 # Probability of gain 42 d = pval - oval 43 d = max(-400, d) 44 d = min(400, d) 45 # The approximate formula should not be used : result["pd"] = 1.0/(1+10**(-d/400)) 46 pd = [50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 53, 53, 47 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 48 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 49 58, 58, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 50 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 64, 51 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 52 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 69, 69, 53 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 54 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 55 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 76, 56 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 57 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 58 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 59 82, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 60 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, 61 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 62 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 63 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 64 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 65 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 66 92][abs(d)] / 100.0 67 result["pd"] = pd if pval >= oval else 1.0 - pd 68 69 # New difference in Elo for loss, draw, win 70 for score in [0, 1, 2]: 71 result["diff%d" % score] = round(k * ([0, 0.5, 1][score] - result["pd"]), 1) 72 73 # Result 74 return result 75 76 # Gathering of the data 77 welo = model.tags["WhiteElo"] if overridden_welo is None else overridden_welo 78 belo = model.tags["BlackElo"] if overridden_belo is None else overridden_belo 79 blitz = model.timemodel.isBlitzFide() 80 81 # Result 82 result = [None, None] 83 result[WHITE] = individual_elo_change(welo, belo, blitz) 84 result[BLACK] = individual_elo_change(belo, welo, blitz) 85 return None if result[WHITE] is None or result[BLACK] is None else result 86 87 88def get_elo_rating_change_str(model, player, overridden_welo, overridden_belo): 89 """ Determination of the ELO rating change """ 90 91 erc = get_elo_rating_change(model, overridden_welo, overridden_belo) 92 if erc is None: 93 return "" 94 erc = erc[player] 95 96 # Status of the game 97 if (model.status == WHITEWON and player == WHITE) or (model.status == BLACKWON and player == BLACK): 98 d = 2 99 else: 100 if (model.status == WHITEWON and player == BLACK) or (model.status == BLACKWON and player == WHITE): 101 d = 0 102 else: 103 if model.status == DRAW: 104 d = 1 105 else: 106 return "%.0f%%, %.1f / %.1f / %.1f" % (100 * erc["pd"], erc["diff0"], erc["diff1"], erc["diff2"]) 107 108 # Result 109 return "%s%.1f" % ("+" if erc["diff%d" % d] > 0 else "", erc["diff%d" % d]) 110 111 112def get_elo_rating_change_pgn(model, player): 113 # One move must occur to validate the rating 114 if model.ply == 0: 115 return "" 116 117 # Retrieval of the statistics for the player 118 data = get_elo_rating_change(model, None, None) 119 if data is None: 120 return "" 121 data = data[player] 122 123 # Status of the game 124 if (model.status == WHITEWON and player == WHITE) or (model.status == BLACKWON and player == BLACK): 125 d = 2 126 else: 127 if (model.status == WHITEWON and player == BLACK) or (model.status == BLACKWON and player == WHITE): 128 d = 0 129 else: 130 if model.status == DRAW: 131 d = 1 132 else: 133 return "" 134 135 # Result is rounded to the nearest integer 136 r = int(round(data["diff%s" % d], 0)) 137 return "+%d" % r if r > 0 else str(r) 138