From 1fce30afbc63955d92b2915013c99418b9c0f649 Mon Sep 17 00:00:00 2001 From: mjam03 Date: Wed, 12 Jan 2022 10:37:14 +0000 Subject: [PATCH] Update prob_match function to fix bug with serving change over --- src/tennisim/match.py | 196 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 3 deletions(-) diff --git a/src/tennisim/match.py b/src/tennisim/match.py index 6b05e7b..91ffb16 100644 --- a/src/tennisim/match.py +++ b/src/tennisim/match.py @@ -74,7 +74,7 @@ def prob_match( g_b (int, optional): games in curr set won by 'b'. Defaults to 0. pt_a (int, optional): points in curr game won by 'a'. Defaults to 0. pt_b (int, optional): points in curr game won by 'b'. Defaults to 0. - sets (int, optional): how many sets match si 'best of'. Defaults to 3. + sets (int, optional): how many sets match is 'best of'. Defaults to 3. Returns: float: probability that player 'a' wins the match @@ -110,8 +110,13 @@ def prob_match( # by adding prob we win set if we win this game # with prob we win this set if we lose this game p_this_game = prob_game(p_a, pt_a, pt_b) - p_this_set_if_w = p_this_game * prob_set(p_a, p_b, g_a + 1, g_b) - p_this_set_if_l = (1 - p_this_game) * prob_set(p_a, p_b, g_a, g_b + 1) + # we want prob we win the set here if we win the game + # but if we serve this game then they serve the next + # so we need to view it from ther perspective + p_this_set_if_w = p_this_game * (1 - prob_set(p_b, p_a, g_b, g_a + 1)) + p_this_set_if_l = (1 - p_this_game) * ( + 1 - prob_set(p_b, p_a, g_b + 1, g_a) + ) p_this_set = p_this_set_if_w + p_this_set_if_l # so now we have the probability of winning the set we are in @@ -126,3 +131,188 @@ def prob_match( )[0] p_match = p_match_w_set + p_match_l_set return p_match + + +def reformat_match(match_data: list, p_a: float, p_b: float) -> list: + """Reformats data generated by match simulation for ease of analysis + + Args: + match_data (list): output of match simulation function sim_match + p_a (float): prob that player 'a' wins a given point on serve + p_b (float): prob that player 'b' wins a given point on serve + + Returns: + list: list of points in chronological order of the sim'ed tennis match + """ + + points = [] + game_count = 0 + point_count = 0 + set_prog = match_data[1] + game_progs = match_data[2] + point_progs = match_data[3] + + for i, set_score in enumerate(set_prog): + if i == 0: + # then we haven't played a set yet + # so set score is 0,0 + st_a = 0 + st_b = 0 + else: + # grab previous set score + st_a = set_prog[i - 1][0] + st_b = set_prog[i - 1][1] + + # grab the games in the set + games = game_progs[i] + # for each game in that set + for j, g in enumerate(games): + if j == 0: + # then we haven't played any games yet + g_a = 0 + g_b = 0 + else: + g_a = games[j - 1][0] + g_b = games[j - 1][1] + + # now grab the point score in the game + pts = point_progs[i][j] + pts = [(0, 0)] + pts[:-1] + for k, p in enumerate(pts): + if k == 0: + # we haven't played any points yet + pt_a = 0 + pt_b = 0 + else: + # if we have an even game count + # then 'a' is serving so keep as is + if game_count % 2 == 0: + pt_a = p[0] + pt_b = p[1] + else: + # else reverse them + pt_a = p[1] + pt_b = p[0] + + # now compute win probab for player 'a' + # first check special situation of being in tiebreak + if g_a == 6 and g_b == 6: + # then in tiebreak + if game_count % 2 == 0: + # then 'a' should serve first point of tiebreak + # e.g. if we've played 1,2 points in tb then 'b' serve + if (pt_a + pt_b) % 4 == 1 or (pt_a + pt_b) % 4 == 1: + p = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b, pt_a + ) + p_w = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b, pt_a + 1 + ) + p_l = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b + 1, pt_a + ) + else: + # then 'a' is serving + p = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a, pt_b + ) + p_w = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a + 1, pt_b + ) + p_l = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a, pt_b + 1 + ) + else: + # we have the reverse where b serves first point of tb + if (pt_a + pt_b) % 4 == 1 or (pt_a + pt_b) % 4 == 1: + # then 'a' is serving + p = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a, pt_b + ) + p_w = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a + 1, pt_b + ) + p_l = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a, pt_b + 1 + ) + else: + p = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b, pt_a + ) + p_w = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b, pt_a + 1 + ) + p_l = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b + 1, pt_a + ) + else: + # we're not in the tiebreak so much easier + if game_count % 2 == 0: + # then 'a' serving + p = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a, pt_b + ) + p_w = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a + 1, pt_b + ) + p_l = prob_match( + p_a, p_b, st_a, st_b, g_a, g_b, pt_a, pt_b + 1 + ) + else: + p = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b, pt_a + ) + p_w = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b, pt_a + 1 + ) + p_l = 1 - prob_match( + p_b, p_a, st_b, st_a, g_b, g_a, pt_b + 1, pt_a + ) + + # now add this data to a map + p_map = { + "st_a": st_a, + "st_b": st_b, + "g_a": g_a, + "g_b": g_b, + "pt_a": pt_a, + "pt_b": pt_b, + "p_a": p_a, + "p_b": p_b, + "pc": point_count, + "gc": game_count, + "sc": st_a + st_b, + "prob": p, + "prob_w": p_w, + "prob_l": p_l, + } + + point_count += 1 + points.append(p_map) + + # bump game count up + game_count += 1 + + # add on final score + st_a = set_prog[-1][0] + st_b = set_prog[-1][1] + p = prob_match(p_a, p_b, st_a, st_b) + p_map = { + "st_a": set_prog[-1][0], + "st_b": set_prog[-1][1], + "g_a": 0, + "g_b": 0, + "pt_a": 0, + "pt_b": 0, + "p_a": p_a, + "p_b": p_b, + "pc": point_count, + "gc": game_count, + "sc": st_a + st_b, + "prob": p, + "prob_w": p, + "prob_l": p, + } + points.append(p_map) + + return points