import numpy as np
import pandas as pd
class Player():
def __init__(self, name, fsp_in, fsp_win, ssp_win):
self.name = name
self.fsp_in = fsp_in # first serve percentage in
self.fsp_win = fsp_win # first serve percentage win
self.ssp_win = ssp_win # second serve percentage win
rafa = Player("Rafa", .73, .69, .59)
fedr = Player("Fed" , .65, .73, .57)
rafa.fsp_in, fedr.ssp_win
def serving_game(serving_player):
server_score = 0
receiver_score = 0
score_max = np.max((server_score, receiver_score))
score_min = np.min((server_score, receiver_score))
score_dif = score_max - score_min
# count:
# first serves in
# first serves win
# second serve wins
# total points/player
# receiving points won
# break points
# faced
# converted
while score_max < 4 or score_dif < 2:
if np.random.rand() < serving_player.fsp_in:
if np.random.rand() < serving_player.fsp_win:
server_score += 1
else:
receiver_score += 1
else:
if np.random.rand() < serving_player.ssp_win:
server_score += 1
else:
receiver_score += 1
score_max = np.max((server_score, receiver_score))
score_min = np.min((server_score, receiver_score))
score_dif = score_max - score_min
hold_serve = [1 if server_score > receiver_score else 0][0]
return hold_serve, server_score, receiver_score
for i in range(10):
print(serving_game(fedr))
def tiebreak(serving_list, serving_cnt):
orig_serving_turn = serving_cnt % 2
serving_turn = serving_cnt % 2
serving_player = serving_list[serving_turn]
orig_receiving_turn = 1 - serving_turn
receiving_turn = 1 - serving_turn
player_score_list = ['p1_tiebreak_score', 'p2_tiebreak_score']
player_score_dict = {'p1_tiebreak_score': 0, 'p2_tiebreak_score': 0}
score_max = np.max((player_score_dict['p1_tiebreak_score'], player_score_dict['p2_tiebreak_score']))
score_min = np.min((player_score_dict['p1_tiebreak_score'], player_score_dict['p2_tiebreak_score']))
score_dif = score_max - score_min
pts_played = np.sum((player_score_dict['p1_tiebreak_score'], player_score_dict['p2_tiebreak_score']))
while score_max < 7 or score_dif < 2:
if np.random.rand() < serving_player.fsp_in:
if np.random.rand() < serving_player.fsp_win:
player_score_dict[player_score_list[serving_turn]] += 1
else:
player_score_dict[player_score_list[receiving_turn]] += 1
else:
if np.random.rand() < serving_player.ssp_win:
player_score_dict[player_score_list[serving_turn]] += 1
else:
player_score_dict[player_score_list[receiving_turn]] += 1
pts_played += 1
if pts_played % 2 > 0:
serving_turn = 1 - serving_turn
receiving_turn = 1 - serving_turn
score_max = np.max((player_score_dict['p1_tiebreak_score'], player_score_dict['p2_tiebreak_score']))
score_min = np.min((player_score_dict['p1_tiebreak_score'], player_score_dict['p2_tiebreak_score']))
score_dif = score_max - score_min
hold_serve = [1 if player_score_dict[player_score_list[orig_serving_turn]] > player_score_dict[player_score_list[orig_receiving_turn]] else 0][0]
return hold_serve, player_score_dict['p1_tiebreak_score'], player_score_dict['p2_tiebreak_score']
for i in range(10):
print(tiebreak([fedr, rafa], 1))
def serve_alternating(serving_cnt, serving_list, player_score_list, player_score_dict):
serving_turn = serving_cnt % 2
serving_player = serving_list[serving_turn]
serve_held = serving_game(serving_player)[0]
serve_broken = 1 - serve_held
player_score_dict[player_score_list[serving_turn]] += serve_held
serving_cnt += 1
serving_turn = serving_cnt % 2
player_score_dict[player_score_list[serving_turn]] += serve_broken
score_max = np.max((player_score_dict['player1_score'], player_score_dict['player2_score']))
score_min = np.min((player_score_dict['player1_score'], player_score_dict['player2_score']))
score_dif = score_max - score_min
return score_max, score_min, score_dif, serving_cnt
# VALID_SERVING_ORDER = {0, 1}
def set_(player1, player2, serving_cnt = 0, tiebreaker=True):
# if serving_order not in VALID_SERVING_ORDER:
# raise ValueError("set_: serving_order must be one of %r." % VALID_SERVING_ORDER)
serving_list = [player1, player2]
serving_cnt = serving_cnt
player_score_list = ['player1_score', 'player2_score']
player_score_dict = {'player1_score': 0, 'player2_score': 0}
score_max = np.max((player_score_dict['player1_score'], player_score_dict['player2_score']))
score_min = np.min((player_score_dict['player1_score'], player_score_dict['player2_score']))
score_dif = score_max - score_min
if tiebreaker:
while score_max < 6:
score_max, score_min, score_dif, serving_cnt = serve_alternating(serving_cnt, serving_list, player_score_list, player_score_dict)
# print(serving_cnt)
if score_dif < 2:
score_max, score_min, score_dif, serving_cnt = serve_alternating(serving_cnt, serving_list, player_score_list, player_score_dict)
# print(serving_cnt)
if score_max == score_min == 6:
tiebreak_1st_to_serve = serving_cnt % 2
tiebreak_1st_to_receive = 1 - tiebreak_1st_to_serve
tiebreak_hold = (tiebreak(serving_list, serving_cnt))[0]
tiebreak_break = 1 - tiebreak_hold
player_score_dict[player_score_list[tiebreak_1st_to_serve]] += tiebreak_hold
player_score_dict[player_score_list[tiebreak_1st_to_receive]] += tiebreak_break
serving_cnt += 1
# print("TIEBREAKER!")
# print("Starting the tiebreaker, serving: %s" % serving_list[tiebreak_1st_to_serve].name)
# print(serving_cnt)
else:
while score_max < 6 or score_dif < 2:
score_max, score_min, score_dif, serving_cnt = serve_alternating(serving_cnt, serving_list, player_score_list, player_score_dict)
# print(serving_cnt)
return player_score_dict, serving_cnt
set_(fedr, rafa, 0, tiebreaker=True)
VALID_SERVING_ORDER = {0, 1}
def match_(player1, player2, best_of = 5, serving_order = 0, final_set_tiebreaker = True, verbose = False):
if serving_order not in VALID_SERVING_ORDER:
raise ValueError("set_: serving_order must be one of %r." % VALID_SERVING_ORDER)
serving_cnt = serving_order
player1_games = {x:0 for x in range(1, best_of + 1)}
player2_games = {x:0 for x in range(1, best_of + 1)}
player1_sets = 0
player2_sets = 0
cur_set = 1
set_max = np.max((player1_sets, player2_sets))
while set_max < (best_of // 2) + 1:
if cur_set == best_of and final_set_tiebreaker == False:
set_outcome = set_(player1, player2, serving_cnt, tiebreaker=False)
set_score, serving_cnt = set_outcome[0], set_outcome[1]
else:
set_outcome = set_(player1, player2, serving_cnt)
set_score, serving_cnt = set_outcome[0], set_outcome[1]
player1_games[cur_set], player2_games[cur_set] = set_score['player1_score'], set_score['player2_score']
if player1_games[cur_set] > player2_games[cur_set]:
player1_sets += 1
else:
player2_sets += 1
set_max = np.max((player1_sets, player2_sets))
cur_set += 1
if verbose:
if player1_sets > player2_sets:
return player1_games, player2_games, player1.name, cur_set - 1
else:
return player1_games, player2_games, player2.name, cur_set - 1
return player1_games, player2_games
match_(fedr, rafa, final_set_tiebreaker=False)
match_temp = match_(fedr, rafa, final_set_tiebreaker=False, verbose=True)
match_temp
pd.DataFrame([match_temp[0], match_temp[1]])
match_temp[2:]
np.random.seed(8)
match_list = []
for i in range(10000):
match_list.append(match_(fedr, rafa, final_set_tiebreaker=False, verbose=True))
match_list[:10], match_list[-10:]
# match_temp = match_(fedr, rafa, final_set_tiebreaker=False)
pd.DataFrame.from_dict({'fedr': list(match_temp[0].values()), 'rafa': list(match_temp[1].values())}, orient='index')
df_match_list = pd.DataFrame(match_list)
df_match_list.columns = ['fedr_games', 'rafa_games', 'winner', 'sets_needed']
df_match_list.head(5)
df_match_list.winner.value_counts()
np.array(df_match_list.winner == 'Fed').astype(int).reshape(100,100)
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
%matplotlib inline
plt.figure(figsize=(12,12))
ax = sns.heatmap(np.array(df_match_list.winner == 'Fed').astype(int).reshape(100,100), cbar=False)
# Turn off tick labels
ax.set_yticklabels([])
ax.set_xticklabels([])
df_match_list.groupby(['winner', 'sets_needed']).count()['fedr_games']#.plot.bar()
df_match_list.pivot_table(index='sets_needed', columns='winner', aggfunc='count')['fedr_games'].plot.bar(figsize=(16,8), title="Win Count by Sets Needed")
df_match_list.winner.head()
df_match_list['streak'] = 1
df_match_list.head()
for i, row in df_match_list.iterrows():
if i != 0:
if row['winner'] == df_match_list.loc[i-1, 'winner']:
df_match_list.loc[i, 'streak'] = df_match_list.loc[i-1, 'streak'] + 1
df_match_list.head(20)
df_match_list['end_of_streak'] = 0
df_match_list.head()
final_row_num = df_match_list.shape[0] - 1
for i, row in df_match_list.iterrows():
if i != final_row_num:
if row['winner'] != df_match_list.loc[i+1, 'winner']:
df_match_list.loc[i, 'end_of_streak'] = 1
else:
df_match_list.loc[i, 'end_of_streak'] = 1
df_match_list.head(20)
df_match_list.tail(20)
df_match_list.loc[(df_match_list.end_of_streak==1) & (df_match_list.winner=='Fed')][['winner', 'streak']].hist()
df_match_list.loc[(df_match_list.end_of_streak==1) & (df_match_list.winner=='Rafa')][['winner', 'streak']].hist()
df_match_list[df_match_list.end_of_streak==1].groupby(['winner', 'streak']).count()['end_of_streak']
sims = 10
matches = 10000
for i in range(sims):
np.random.seed(i)
match_list = []
fedr_wins = 0
for j in range(matches):
match_list.append(match_(fedr, rafa, final_set_tiebreaker=False, verbose=True))
for k in range(len(match_list)):
if match_list[k][2] == "Fed":
fedr_wins += 1
print("run %d, Fed win rate: %.4f" % (i, fedr_wins/float(matches)))
rafa07 = Player("Rafa", .70, .68, .57)
fedr07 = Player("Fed" , .71, .71, .62)
rafa07.fsp_in, fedr07.ssp_win
np.random.seed(8)
match_list07 = []
for i in range(10000):
match_list07.append(match_(fedr07, rafa07, final_set_tiebreaker=False, verbose=True))
def sim_win_count(match_list, player, winner_col):
player_wins = 0
for i in range(len(match_list)):
if match_list[i][winner_col] == player:
player_wins += 1
return player_wins
print("Fed wins %d out of %d" % (sim_win_count(match_list07, "Fed", 2), len(match_list07)))