''' Divegrass Sim - 01/23/18   This model assumes that gameplay can be broken down into a fixed number of potential attacks. Seizing Ability describes a team’s ability to mount an attack, i.e. the highest number on their die. An attack is awarded if one team has a higher die roll than the other. If the dice rolls are equal, the attack is not awarded to either team. Implications of an attack’s duration and outcome are considered to be negible. Success Rate describes the quality and thus the chances of a goal during an average attack. '''   from random import randint from random import random     # Team stats home_seizing_ability = 10 away_seizing_ability = 10 home_success_rate = 0.25 away_success_rate = 0.25 home_penalty_ability = 1 away_penalty_ability = 1   # Simulation settings potential_attacks = 15 games = 50 overtime = False  # TODO: implement properly penalties = False  # TODO: implement properly   # Evaluation settings mislead_threshold = 0.05  # Deviation from the average at which a test run can be considered misleading     # Simulate the matches def simulate():     results = []     for game in range(0, games):         attacks_home = 0         attacks_away = 0         goals_home = 0         goals_away = 0         for attack in range(0, potential_attacks):             home_roll = randint(1, home_seizing_ability)             away_roll = randint(1, away_seizing_ability)             if home_roll > away_roll:                 # Home starts an attack                 attacks_home += 1                 if home_success_rate > random():                     goals_home += 1             elif home_roll < away_roll:                 # Away starts an attack                 attacks_away += 1                 if away_success_rate > random():                     goals_away += 1             else:                 # No attack                 pass           # Overtime         if overtime and goals_home == goals_away:             for attack in range(0, int(potential_attacks / 3)):  # A third of the game time                 home_roll = randint(1, home_seizing_ability)                 away_roll = randint(1, away_seizing_ability)                 if home_roll > away_roll:                     # Home starts an attack                     attacks_home += 1                     if home_success_rate > random():                         goals_home += 1                 elif home_roll < away_roll:                     # Away starts an attack                     attacks_away += 1                     if away_success_rate > random():                         goals_away += 1                 else:                     # No attack                     pass           # Penalties         if penalties and goals_home == goals_away:             home_roll = random() * home_penalty_ability             away_roll = random() * away_penalty_ability             if home_roll >= away_roll:  # Note: this is almost never equal, so this simplification is okay                 goals_home += 1             else:                 goals_away += 1           result = {             'attacks_home': attacks_home,             'attacks_away': attacks_away,             'goals_home': goals_home,             'goals_away': goals_away}         results.append(result)         #print(goals_home, '-', goals_away)     return results     if __name__ == '__main__':     runs = 1000     print('Games:', games)     print('Potential attacks:', potential_attacks)     print('Runs:', runs)     print('Home Seizing Ability:', home_seizing_ability)     print('Away Seizing Ability:', away_seizing_ability)     print('Home Success Rate:', home_success_rate)     print('Away Success Rate', away_success_rate)       win_chances = []     for _ in range(0, runs):         results = simulate()         # Tally results         wins = sum(1 for result in results if result['goals_home'] > result['goals_away'])         losses = sum(1 for result in results if result['goals_home'] < result['goals_away'])         draws = sum(1 for result in results if result['goals_home'] == result['goals_away'])         goals_home = sum(result['goals_home'] for result in results)         goals_away = sum(result['goals_away'] for result in results)         goal_delta = goals_home - goals_away         avg_goals_home = goals_home / games         avg_goals_away = goals_away / games         avg_goal_delta = avg_goals_home - avg_goals_away         win_chance = wins / games         draw_chance = draws / games         loss_chance = losses / games           # Output results         print('Win Chance:', '{:.2%}'.format(win_chance),               'Wins:', wins,               'Losses:', losses,               'Draws:', draws,               'Avg. goal delta:', '{:.2f}'.format(avg_goal_delta))         win_chances.append(win_chance)       mean_win_chance = sum(win_chances) / len(win_chances)     temp = 0     misleading_runs = 0     for win_chance in win_chances:         temp += abs(win_chance - mean_win_chance)         if abs(win_chance - mean_win_chance) >= mislead_threshold:             misleading_runs += 1     mean_absolute_difference = temp / runs     mislead_rate = misleading_runs / runs       print('Mean win rate:', '{:.2%}'.format(mean_win_chance))     print('Mean absolute difference (percentile points):', mean_absolute_difference * 100)     print('Ratio of misleading test runs ({:.2%} points deviation from the average)'.format(mislead_threshold),           '{:.2%}'.format(mislead_rate))