'''
    Season Simulation
    
    2017 Jacob Grishey

    For the purpose of simulating sports seasons
    and determining regular season standings.
'''

# IMPORTS

import json
import statistics
import numpy
from operator import itemgetter
import copy
from pathlib import Path

# Read JSON file

with open("./../data/season.json") as jsonfile:
    SEASON = json.load(jsonfile)

# Teams of the NHL

BRENT = ["Cape Cod Bluefins", "Trevor Phillips Industries",
        "Inglorious Basterds", "Crack Smoking Monkeys",
        "Moose Knuckles", "Hood Rich"]

DREW = ["Small Sample Size", "The Bearded Bandits",
        "Row Row Fight the Powah", "Motor City Machine Guns",
        "Suck Our Dekes", "Tenacious V"]

teamsData = [{'name': team, 'w': 0, 'l': 0, 'otl': 0, 'elo': 1500, 'aw': 0,
        'al': 0, 'aotl': 0, 'seed1': 0, 'seed2': 0, 'seed3': 0, 'seed4': 0, 'seed5': 0,
        'seed6': 0, 'seed7': 0, 'seed8': 0, 'r2': 0, 'r3': 0, 'cup': 0}
         for team in BRENT + DREW]

# Get last season's results.

if Path("./../data/resultsS32.json").is_file():
    with open("./../data/resultsS32.json") as lastSeason:
        lastSeason = json.load(lastSeason)
        for team in lastSeason:
            lastElo = team['elo']
            nowElo = (lastElo - 1500) * (2 / 3) + 1500
            next(item for item in teamsData if item["name"] == team["name"])['elo'] = nowElo

# Separate past from future games

past = [game for game in SEASON if game['resultType'] != "TBD"]
pastReg = [game for game in past if game['gameType'] == "R"]
pastPO = [game for game in past if game['gameType'] == "P"]

future = [game for game in SEASON if game['resultType'] == "TBD"]
futureReg = [game for game in future if game['gameType'] == "R"]
futurePO = [game for game in future if game['gameType'] == "P"]

# Expected Score function
#
# Given elo of team A and team B, calculate expected score of team A.

def expectedScoreA (eloA, eloB):
    return 1 / (1 + 10 ** ((eloB - eloA) / 400))

# New Rating Function
#
# Given Elo, actual score, expected score, and goal differential and calculate the team's new Elo rating.

def newRating (eloA, eloB, scoreActual, scoreExpected, goalDifferential, gameType):
    # K-Factor
    K = 32

    # Importance
    I = 1.5 if gameType == "P" else 1.0
    
    # Calculate for goal differential and autocorrelation
    marginMult = numpy.log(goalDifferential + 1) * (2.2 / (abs(eloA - eloB) * 0.01 + 2.2))

    # Return new rating
    return eloA + (marginMult * K * I) * (scoreActual - scoreExpected)

# Process Game Function

def processGame (game, teamList, future):
    homeTeam = next(item for item in teamList if item["name"] == game['homeTeam'])
    awayTeam = next(item for item in teamList if item["name"] == game['awayTeam'])

    # Current Elo ratings
    currentEloA = homeTeam['elo']
    currentEloB = awayTeam['elo']

    # Get Expected Scores
    eA = expectedScoreA(currentEloA, currentEloB)
    eB = 1 - eA

    # Get scores
    homeGoals = game['homeGoals']
    awayGoals = game['awayGoals']
    goalDifferential = abs(homeGoals - awayGoals)

    if not future:
        # Get Actual Scores
        if homeGoals > awayGoals:
            if game['resultType'] != "REG":
                sA = 1.0
                sB = 0.5
                if game['resultType'] == "SO":
                    homeTeam['w'] += 1
                    awayTeam['otl'] += 1
                else:
                    homeTeam['w'] += 1
                    awayTeam['otl'] += 1
            else:
                sA = 1.0
                sB = 0.0
                homeTeam['w'] += 1
                awayTeam['l'] += 1
        else:
            if game['resultType'] != "REG":
                sB = 1.0
                sA = 0.5
                if game['resultType'] == "SO":
                    homeTeam['otl'] += 1
                    awayTeam['w'] += 1
                else:
                    homeTeam['otl'] += 1
                    awayTeam['w'] += 1
            else:
                sB = 1.0
                sA = 0.0
                awayTeam['w'] += 1
                homeTeam['l'] += 1

        # Calculate new Elo ratings
        newA = newRating(currentEloA, currentEloB, sA, eA, goalDifferential, "R")
        newB = newRating(currentEloB, currentEloA, sB, eB, goalDifferential, "R")

        # Apply Elo ratings
        homeTeam['elo'] = newA
        awayTeam['elo'] = newB
    
    else:
        # Random number between 0 and 1 to decide who wins.
        decideWin = numpy.random.random()

        # Random number between 0 and 1 to decide if it goes into Overtime.
        decideOT = numpy.random.random()
        decideSO = numpy.random.random()

        if decideOT <= 0.233:
            if decideSO <= 0.579:
                if decideWin <= eA:
                    homeTeam['w'] += 1
                    awayTeam['otl'] += 1
                else:
                    homeTeam['otl'] += 1
                    awayTeam['w'] += 1
            else:
                if decideWin <= eA:
                    homeTeam['w'] += 1
                    awayTeam['otl'] += 1
                else:
                    homeTeam['otl'] += 1
                    awayTeam['w'] += 1
        else:
            if decideWin <= eA:
                homeTeam['w'] += 1
                awayTeam['l'] += 1
            else:
                homeTeam['l'] += 1
                awayTeam['w'] += 1

def runSeason (teams, pastPO):
    # Update elo from regular season games

    for game in pastReg:
        processGame(game, teams, False)

    
    def simRound (roundSeries, roundGames):
        # Simulate scheduled games
        for game in roundGames:
            homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
            awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

            # Current Elo ratings of both teams
            homeElo = homeTeam['elo']
            awayElo = awayTeam['elo']

            # Win probabilities
            eA = expectedScoreA(homeElo, awayElo)
            eB = 1 - eA

            # Random number between 0 and 1 to decide who wins.
            decideWin = numpy.random.random()

            # Random number between 0 and 1 to decide if it goes into Overtime.
            decideOT = numpy.random.random()

            # Get series data
            series = next(item for item in roundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

            # For scheduling purposes
            previousLow = min(series['hWins'], series['aWins'])

            # Simulate game
            if decideOT <= 0.233:
                if decideWin <= eA:
                    series['hWins'] += 1
                    if min([series['hWins'], series['aWins']]) > previousLow:
                        roundGames.append({
                            'homeTeam': game['homeTeam'],
                            'awayTeam': game['awayTeam']
                        })
                    sA = 1.0
                    sB = 0.5
                else:
                    series['aWins'] += 1
                    if min([series['hWins'], series['aWins']]) > previousLow:
                        roundGames.append({
                            'homeTeam': game['homeTeam'],
                            'awayTeam': game['awayTeam']
                        })
                    sA = 0.5
                    sB = 1.0
            else:
                if decideWin <= eA:
                    series['hWins'] += 1
                    if min([series['hWins'], series['aWins']]) > previousLow:
                        roundGames.append({
                            'homeTeam': game['homeTeam'],
                            'awayTeam': game['awayTeam']
                        })
                    sA = 1.0
                    sB = 0.0
                else:
                    series['aWins'] += 1
                    if min([series['hWins'], series['aWins']]) > previousLow:
                        roundGames.append({
                            'homeTeam': game['homeTeam'],
                            'awayTeam': game['awayTeam']
                        })
                    sA = 0.0
                    sB = 1.0

    # Sim rest of regular season, if necessary

    if len(futureReg) > 0:
        
        for team in teams:
            next(item for item in teamsData if item["name"] == team['name'])['elo'] = team['elo']
            next(item for item in teamsData if item["name"] == team['name'])['w'] = team['w']
            next(item for item in teamsData if item["name"] == team['name'])['l'] = team['l']
            next(item for item in teamsData if item["name"] == team['name'])['otl'] = team['otl']

        for game in futureReg:
            processGame(game, teams, True)
        
        # Sort teams into divisions
        brent = []
        drew = []

        # Collect teams, calculate points.
        for team in teams:
            if team['name'] in BRENT:
                brent.append({"name": team['name'], "pts": team['w'] * 2 + team['otl'], "w": team['w']})
                next(item for item in teamsData if item["name"] == team['name'])['aw'] += team['w']
                next(item for item in teamsData if item["name"] == team['name'])['al'] += team['l']
                next(item for item in teamsData if item["name"] == team['name'])['aotl'] += team['otl']
            elif team['name'] in DREW:
                drew.append({"name": team['name'], "pts": team['w'] * 2 + team['otl'], "w": team['w']})
                next(item for item in teamsData if item["name"] == team['name'])['aw'] += team['w']
                next(item for item in teamsData if item["name"] == team['name'])['al'] += team['l']
                next(item for item in teamsData if item["name"] == team['name'])['aotl'] += team['otl']
        
        # Sort by points
        brent = sorted(brent, key=itemgetter('pts', 'w'), reverse=True)
        drew = sorted(drew, key=itemgetter('pts', 'w'), reverse=True)
        divisionWinners = sorted(brent[:1] + drew[:1], key=itemgetter('pts', 'w'), reverse=True)
        divisionRunners = [brent[1], drew[1]]
        restOfPlayoffs = sorted(divisionRunners + brent[2:] + drew[2:], key=itemgetter('pts', 'w'), reverse=True)[:6]
        playoffs = divisionWinners + restOfPlayoffs

        # Add Results
        for i in range(1, 8):
            next(item for item in teamsData if item["name"] == playoffs[i-1]['name'])['seed{0}'.format(i)] += 1
        
        # Schedule First Round
        firstRoundSeries = []
        firstRoundGames = []

        for i in range(0, 7):
            firstRoundSeries.append({
                'home': playoffs[i]['name'],
                'away': playoffs[7-i]['name'],
                'hWins': 0,
                'aWins': 0
            })
            for k in range(0, 4):
                firstRoundGames.append({
                    'homeTeam': playoffs[i]['name'],
                    'awayTeam': playoffs[7-i]['name']
                })
        
        # Simulate Round
        simRound(firstRoundSeries, firstRoundGames)
        
        # Get winners
        winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], firstRoundSeries))
        playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

        # Add Results
        for winner in winners:
            next(item for item in teamsData if item["name"] == winner)['r2'] += 1

        # Schedule Second Round
        secondRoundSeries = []
        secondRoundGames = []

        for i in range(0, 3):
            secondRoundSeries.append({
                'home': playoffs[i]['name'],
                'away': playoffs[3-i]['name'],
                'hWins': 0,
                'aWins': 0
            })
            for k in range(0, 4):
                secondRoundGames.append({
                    'homeTeam': playoffs[i]['name'],
                    'awayTeam': playoffs[3-i]['name']
                })
        
        # Simulate round
        simRound(secondRoundSeries, secondRoundGames)
        
        # Get winners
        winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], secondRoundSeries))
        playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

        # Add Results
        for winner in winners:
            next(item for item in teamsData if item["name"] == winner)['r3'] += 1

        # Schedule Third Round
        thirdRoundSeries = []
        thirdRoundGames = []

        for i in range(0, 1):
            thirdRoundSeries.append({
                'home': playoffs[i]['name'],
                'away': playoffs[1-i]['name'],
                'hWins': 0,
                'aWins': 0
            })
            for k in range(0, 4):
                thirdRoundGames.append({
                    'homeTeam': playoffs[i]['name'],
                    'awayTeam': playoffs[1-i]['name']
                })
        
        # Simulate Round
        simRound(thirdRoundSeries, thirdRoundGames)
        
        # Get winners
        winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], thirdRoundSeries))

        # Add Results
        for winner in winners:
            next(item for item in teamsData if item["name"] == winner)['cup'] += 1

    else:
        # Sort teams into divisions
        brent = []
        drew = []

        # Collect teams, calculate points.
        for team in teams:
            if team['name'] in BRENT:
                brent.append({"name": team['name'], "pts": team['w'] * 2 + team['otl'], "w": team['w']})
                next(item for item in teamsData if item["name"] == team['name'])['w'] = team['w']
                next(item for item in teamsData if item["name"] == team['name'])['l'] = team['l']
                next(item for item in teamsData if item["name"] == team['name'])['otl'] = team['otl']
                next(item for item in teamsData if item["name"] == team['name'])['aw'] += team['w']
                next(item for item in teamsData if item["name"] == team['name'])['al'] += team['l']
                next(item for item in teamsData if item["name"] == team['name'])['aotl'] += team['otl']
            elif team['name'] in DREW:
                drew.append({"name": team['name'], "pts": team['w'] * 2 + team['otl'], "w": team['w']})
                next(item for item in teamsData if item["name"] == team['name'])['w'] = team['w']
                next(item for item in teamsData if item["name"] == team['name'])['l'] = team['l']
                next(item for item in teamsData if item["name"] == team['name'])['otl'] = team['otl']
                next(item for item in teamsData if item["name"] == team['name'])['aw'] += team['w']
                next(item for item in teamsData if item["name"] == team['name'])['al'] += team['l']
                next(item for item in teamsData if item["name"] == team['name'])['aotl'] += team['otl']
        
        # Sort by points
        brent = sorted(brent, key=itemgetter('pts', 'w'), reverse=True)
        drew = sorted(drew, key=itemgetter('pts', 'w'), reverse=True)
        divisionWinners = sorted(brent[:1] + drew[:1], key=itemgetter('pts', 'w'), reverse=True)
        divisionRunners = [brent[1], drew[1]]
        restOfPlayoffs = sorted(divisionRunners + brent[2:] + drew[2:], key=itemgetter('pts', 'w'), reverse=True)[:6]
        playoffs = divisionWinners + restOfPlayoffs

        # Add Results
        for i in range(1, 9):
            next(item for item in teamsData if item["name"] == playoffs[i-1]['name'])['seed{0}'.format(i)] += 1

        # Schedule first round games
        firstRoundGames = []
        firstRoundSeries = []

        for i in range(0, 4):
            firstRoundSeries.append({
                'home': playoffs[i]['name'],
                'away': playoffs[7-i]['name'],
                'hWins': 0,
                'aWins': 0
            })

        def checkFinished (series):
            for serie in series:
                if serie['hWins'] != 4 and serie['aWins'] != 4:
                    return False
            return True
        
        def checkSeriesExists (series, home, away):
            for serie in series:
                if serie['home'] == home or serie['home'] == away:
                    if serie['away'] == home or serie['away'] == away:
                        return True
            return False
        
        for game in pastPO:
            homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
            awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

            if checkSeriesExists(firstRoundSeries, game['homeTeam'], game['awayTeam']):
                # Current Elo ratings of both teams
                homeElo = homeTeam['elo']
                awayElo = awayTeam['elo']

                # Win probabilities
                eA = expectedScoreA(homeElo, awayElo)
                eB = 1 - eA

                # Get scores
                homeGoals = game['homeGoals']
                awayGoals = game['awayGoals']
                goalDifferential = abs(homeGoals - awayGoals)

                # Get series data
                series = next(item for item in firstRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                isHomeTrue = game['homeTeam'] == series['home']

                # Simulate game
                if game['resultType'] != "REG":
                    if homeGoals > awayGoals:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        sA = 1.0
                        sB = 0.5
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        sA = 0.5
                        sB = 1.0
                else:
                    if homeGoals > awayGoals:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        sA = 1.0
                        sB = 0.0
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        sA = 0.0
                        sB = 1.0

                # Calculate new Elo ratings
                newA = newRating(homeElo, awayElo, sA, eA, goalDifferential, "P")
                newB = newRating(awayElo, homeElo, sB, eB, goalDifferential, "P")

                # Apply Elo ratings
                homeTeam['elo'] = newA
                awayTeam['elo'] = newB

                pastPO = [gm for gm in pastPO if (gm['date'] != game['date'] or gm['homeTeam'] != homeTeam['name'])]
        
        # If first round finished with past games
        if checkFinished(firstRoundSeries):

            # Get winners
            winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], firstRoundSeries))
            playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

            # Add results
            for winner in winners:
                next(item for item in teamsData if item["name"] == winner)['r2'] += 1

            # Schedule second round
            secondRoundGames = []
            secondRoundSeries = []

            for i in range(0, 2):
                secondRoundSeries.append({
                    'home': playoffs[i]['name'],
                    'away': playoffs[3-i]['name'],
                    'hWins': 0,
                    'aWins': 0
                })

            # Go through past games of second round
            for game in pastPO:
                homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                if checkSeriesExists(secondRoundSeries, game['homeTeam'], game['awayTeam']):
                    # Current Elo ratings of both teams
                    homeElo = homeTeam['elo']
                    awayElo = awayTeam['elo']

                    # Win probabilities
                    eA = expectedScoreA(homeElo, awayElo)
                    eB = 1 - eA

                    # Get scores
                    homeGoals = game['homeGoals']
                    awayGoals = game['awayGoals']
                    goalDifferential = abs(homeGoals - awayGoals)

                    # Get series data
                    series = next(item for item in secondRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                    isHomeTrue = game['homeTeam'] == series['home']

                    # Simulate game
                    if game['resultType'] != "REG":
                        if homeGoals > awayGoals:
                            if isHomeTrue:
                                series['hWins'] += 1
                            else:
                                series['aWins'] += 1
                            sA = 1.0
                            sB = 0.5
                        else:
                            if isHomeTrue:
                                series['aWins'] += 1
                            else:
                                series['hWins'] += 1
                            sA = 0.5
                            sB = 1.0
                    else:
                        if homeGoals > awayGoals:
                            if isHomeTrue:
                                series['hWins'] += 1
                            else:
                                series['aWins'] += 1
                            sA = 1.0
                            sB = 0.0
                        else:
                            if isHomeTrue:
                                series['aWins'] += 1
                            else:
                                series['hWins'] += 1
                            sA = 0.0
                            sB = 1.0

                    # Calculate new Elo ratings
                    newA = newRating(homeElo, awayElo, sA, eA, goalDifferential, "P")
                    newB = newRating(awayElo, homeElo, sB, eB, goalDifferential, "P")

                    # Apply Elo ratings
                    homeTeam['elo'] = newA
                    awayTeam['elo'] = newB

                    pastPO = [gm for gm in pastPO if (gm['date'] != game['date'] or gm['homeTeam'] != homeTeam['name'])]

            # If second round finished with past games
            if checkFinished(secondRoundSeries):
                # Get winners
                winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], secondRoundSeries))
                playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

                # Add results
                for winner in winners:
                    next(item for item in teamsData if item["name"] == winner)['r3'] += 1

                # Schedule third round
                thirdRoundGames = []
                thirdRoundSeries = []

                thirdRoundSeries.append({
                    'home': playoffs[0]['name'],
                    'away': playoffs[1]['name'],
                    'hWins': 0,
                    'aWins': 0
                })
                
                # Go through past games of third round
                for game in pastPO:
                    homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                    awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                    if checkSeriesExists(thirdRoundSeries, game['homeTeam'], game['awayTeam']):
                        # Current Elo ratings of both teams
                        homeElo = homeTeam['elo']
                        awayElo = awayTeam['elo']

                        # Win probabilities
                        eA = expectedScoreA(homeElo, awayElo)
                        eB = 1 - eA

                        # Get scores
                        homeGoals = game['homeGoals']
                        awayGoals = game['awayGoals']
                        goalDifferential = abs(homeGoals - awayGoals)

                        # Get series data
                        series = next(item for item in thirdRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                        isHomeTrue = game['homeTeam'] == series['home']

                        # Simulate game
                        if game['resultType'] != "REG":
                            if homeGoals > awayGoals:
                                if isHomeTrue:
                                    series['hWins'] += 1
                                else:
                                    series['aWins'] += 1
                                sA = 1.0
                                sB = 0.5
                            else:
                                if isHomeTrue:
                                    series['aWins'] += 1
                                else:
                                    series['hWins'] += 1
                                sA = 0.5
                                sB = 1.0
                        else:
                            if homeGoals > awayGoals:
                                if isHomeTrue:
                                    series['hWins'] += 1
                                else:
                                    series['aWins'] += 1
                                sA = 1.0
                                sB = 0.0
                            else:
                                if isHomeTrue:
                                    series['aWins'] += 1
                                else:
                                    series['hWins'] += 1
                                sA = 0.0
                                sB = 1.0

                        # Calculate new Elo ratings
                        newA = newRating(homeElo, awayElo, sA, eA, goalDifferential, "P")
                        newB = newRating(awayElo, homeElo, sB, eB, goalDifferential, "P")

                        # Apply Elo ratings
                        homeTeam['elo'] = newA
                        awayTeam['elo'] = newB

                        pastPO = [gm for gm in pastPO if (gm['date'] != game['date'] or gm['homeTeam'] != homeTeam['name'])]
                
                # If third round finished with past games
                if checkFinished(thirdRoundSeries):

                    # Get winners
                    winner = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], thirdRoundSeries))

                    # Add results
                    next(item for item in teamsData if item["name"] == winner[0])['cup'] += 1

                # If third round partially finished
                else:
                    for team in teams:
                        next(item for item in teamsData if item["name"] == team['name'])['elo'] = team['elo']

                    for series in thirdRoundSeries:
                        if series['hWins'] != 4 or series['aWins'] != 4:
                            # Schedule needed games
                            numToBeScheduled = min(series['hWins'], series['aWins']) + 4 - (series['hWins'] + series['aWins'])
                            for i in range(numToBeScheduled):
                                thirdRoundGames.append({'homeTeam': series['home'], 'awayTeam': series['away']})

                    # Simulate scheduled games
                    for game in thirdRoundGames:
                        homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                        awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                        # Current Elo ratings of both teams
                        homeElo = homeTeam['elo']
                        awayElo = awayTeam['elo']

                        # Win probabilities
                        eA = expectedScoreA(homeElo, awayElo)
                        eB = 1 - eA

                        # Random number between 0 and 1 to decide who wins.
                        decideWin = numpy.random.random()

                        # Random number between 0 and 1 to decide if it goes into Overtime.
                        decideOT = numpy.random.random()

                        # Get series data
                        series = next(item for item in thirdRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                        isHomeTrue = game['homeTeam'] == series['home']

                        previousLow = min(series['aWins'], series['hWins'])

                        # Simulate game
                        if decideOT <= 0.233:
                            if decideWin <= eA:
                                if isHomeTrue:
                                    series['hWins'] += 1
                                else:
                                    series['aWins'] += 1
                                if min([series['hWins'], series['aWins']]) > previousLow:
                                    thirdRoundGames.append({
                                        'homeTeam': game['homeTeam'],
                                        'awayTeam': game['awayTeam']
                                    })
                                sA = 1.0
                                sB = 0.5
                            else:
                                if isHomeTrue:
                                    series['aWins'] += 1
                                else:
                                    series['hWins'] += 1
                                if min([series['hWins'], series['aWins']]) > previousLow:
                                    thirdRoundGames.append({
                                        'homeTeam': game['homeTeam'],
                                        'awayTeam': game['awayTeam']
                                    })
                                sA = 0.5
                                sB = 1.0
                        else:
                            if decideWin <= eA:
                                if isHomeTrue:
                                    series['hWins'] += 1
                                else:
                                    series['aWins'] += 1
                                if min([series['hWins'], series['aWins']]) > previousLow:
                                    thirdRoundGames.append({
                                        'homeTeam': game['homeTeam'],
                                        'awayTeam': game['awayTeam']
                                    })
                                sA = 1.0
                                sB = 0.0
                            else:
                                if isHomeTrue:
                                    series['aWins'] += 1
                                else:
                                    series['hWins'] += 1
                                if min([series['hWins'], series['aWins']]) > previousLow:
                                    thirdRoundGames.append({
                                        'homeTeam': game['homeTeam'],
                                        'awayTeam': game['awayTeam']
                                    })
                                sA = 0.0
                                sB = 1.0
                    
                    # Get winners
                    winner = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], thirdRoundSeries))

                    # Add results
                    next(item for item in teamsData if item["name"] == winner[0])['cup'] += 1

            # If second round partially finished
            else:
                for team in teams:
                    next(item for item in teamsData if item["name"] == team['name'])['elo'] = team['elo']

                for series in secondRoundSeries:
                    if series['hWins'] != 4 or series['aWins'] != 4:
                        # Schedule needed games
                        numToBeScheduled = min(series['hWins'], series['aWins']) + 4 - (series['hWins'] + series['aWins'])
                        for i in range(numToBeScheduled):
                            secondRoundGames.append({'homeTeam': series['home'], 'awayTeam': series['away']})

                # Simulate scheduled games
                for game in secondRoundGames:
                    homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                    awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                    # Current Elo ratings of both teams
                    homeElo = homeTeam['elo']
                    awayElo = awayTeam['elo']

                    # Win probabilities
                    eA = expectedScoreA(homeElo, awayElo)
                    eB = 1 - eA

                    # Random number between 0 and 1 to decide who wins.
                    decideWin = numpy.random.random()

                    # Random number between 0 and 1 to decide if it goes into Overtime.
                    decideOT = numpy.random.random()

                    # Get series data
                    series = next(item for item in secondRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                    isHomeTrue = game['homeTeam'] == series['home']

                    previousLow = min(series['aWins'], series['hWins'])

                    # Simulate game
                    if decideOT <= 0.233:
                        if decideWin <= eA:
                            if isHomeTrue:
                                series['hWins'] += 1
                            else:
                                series['aWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                secondRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 1.0
                            sB = 0.5
                        else:
                            if isHomeTrue:
                                series['aWins'] += 1
                            else:
                                series['hWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                secondRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 0.5
                            sB = 1.0
                    else:
                        if decideWin <= eA:
                            if isHomeTrue:
                                series['hWins'] += 1
                            else:
                                series['aWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                secondRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 1.0
                            sB = 0.0
                        else:
                            if isHomeTrue:
                                series['aWins'] += 1
                            else:
                                series['hWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                secondRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 0.0
                            sB = 1.0
                
                # Get winners
                winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], secondRoundSeries))
                playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

                # Add results
                for winner in winners:
                    next(item for item in teamsData if item["name"] == winner)['r3'] += 1

                # Schedule Third Round
                thirdRoundSeries = []
                thirdRoundGames = []

                thirdRoundSeries.append({
                    'home': playoffs[0]['name'],
                    'away': playoffs[1]['name'],
                    'hWins': 0,
                    'aWins': 0
                })
                for k in range(0, 4):
                    thirdRoundGames.append({
                        'homeTeam': playoffs[0]['name'],
                        'awayTeam': playoffs[1]['name']
                    })
                
                # Simulate scheduled games
                for game in thirdRoundGames:
                    homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                    awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                    # Current Elo ratings of both teams
                    homeElo = homeTeam['elo']
                    awayElo = awayTeam['elo']

                    # Win probabilities
                    eA = expectedScoreA(homeElo, awayElo)
                    eB = 1 - eA

                    # Random number between 0 and 1 to decide who wins.
                    decideWin = numpy.random.random()

                    # Random number between 0 and 1 to decide if it goes into Overtime.
                    decideOT = numpy.random.random()

                    # Get series data
                    series = next(item for item in thirdRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                    isHomeTrue = game['homeTeam'] == series['home']

                    previousLow = min(series['aWins'], series['hWins'])

                    # Simulate game
                    if decideOT <= 0.233:
                        if decideWin <= eA:
                            if isHomeTrue:
                                series['hWins'] += 1
                            else:
                                series['aWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                thirdRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 1.0
                            sB = 0.5
                        else:
                            if isHomeTrue:
                                series['aWins'] += 1
                            else:
                                series['hWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                thirdRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 0.5
                            sB = 1.0
                    else:
                        if decideWin <= eA:
                            if isHomeTrue:
                                series['hWins'] += 1
                            else:
                                series['aWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                thirdRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 1.0
                            sB = 0.0
                        else:
                            if isHomeTrue:
                                series['aWins'] += 1
                            else:
                                series['hWins'] += 1
                            if min([series['hWins'], series['aWins']]) > previousLow:
                                thirdRoundGames.append({
                                    'homeTeam': game['homeTeam'],
                                    'awayTeam': game['awayTeam']
                                })
                            sA = 0.0
                            sB = 1.0
                
                # Get winners
                winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], thirdRoundSeries))

                # Add results
                for winner in winners:
                    next(item for item in teamsData if item["name"] == winner)['cup'] += 1

        # If first round partially finished
        else:
            for team in teams:
                next(item for item in teamsData if item["name"] == team['name'])['elo'] = team['elo']

            for series in firstRoundSeries:
                if series['hWins'] != 4 or series['aWins'] != 4:
                    # Schedule needed games
                    numToBeScheduled = min(series['hWins'], series['aWins']) + 4 - (series['hWins'] + series['aWins'])
                    for i in range(numToBeScheduled):
                        firstRoundGames.append({'homeTeam': series['home'], 'awayTeam': series['away']})

            # Simulate scheduled games
            for game in firstRoundGames:
                homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                # Current Elo ratings of both teams
                homeElo = homeTeam['elo']
                awayElo = awayTeam['elo']

                # Win probabilities
                eA = expectedScoreA(homeElo, awayElo)
                eB = 1 - eA

                # Random number between 0 and 1 to decide who wins.
                decideWin = numpy.random.random()

                # Random number between 0 and 1 to decide if it goes into Overtime.
                decideOT = numpy.random.random()

                # Get series data
                series = next(item for item in firstRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])


                isHomeTrue = game['homeTeam'] == series['home']

                previousLow = min(series['aWins'], series['hWins'])

                # Simulate game
                if decideOT <= 0.233:
                    if decideWin <= eA:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            firstRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 1.0
                        sB = 0.5
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            firstRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 0.5
                        sB = 1.0
                else:
                    if decideWin <= eA:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            firstRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 1.0
                        sB = 0.0
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            firstRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 0.0
                        sB = 1.0
            
            # Get winners
            winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], firstRoundSeries))
            playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

            # Add results
            for winner in winners:
                next(item for item in teamsData if item["name"] == winner)['r2'] += 1

            # Schedule Second Round
            secondRoundSeries = []
            secondRoundGames = []

            for i in range(0, 2):
                secondRoundSeries.append({
                    'home': playoffs[i]['name'],
                    'away': playoffs[3-i]['name'],
                    'hWins': 0,
                    'aWins': 0
                })
                for k in range(0, 4):
                    secondRoundGames.append({
                        'homeTeam': playoffs[i]['name'],
                        'awayTeam': playoffs[3-i]['name']
                    })
            
            # Simulate scheduled games
            for game in secondRoundGames:
                homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                # Current Elo ratings of both teams
                homeElo = homeTeam['elo']
                awayElo = awayTeam['elo']

                # Win probabilities
                eA = expectedScoreA(homeElo, awayElo)
                eB = 1 - eA

                # Random number between 0 and 1 to decide who wins.
                decideWin = numpy.random.random()

                # Random number between 0 and 1 to decide if it goes into Overtime.
                decideOT = numpy.random.random()

                # Get series data
                series = next(item for item in secondRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                previousLow = min(series['aWins'], series['hWins'])

                isHomeTrue = game['homeTeam'] == series['home']

                # Simulate game
                if decideOT <= 0.233:
                    if decideWin <= eA:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            secondRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 1.0
                        sB = 0.5
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            secondRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 0.5
                        sB = 1.0
                else:
                    if decideWin <= eA:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            secondRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 1.0
                        sB = 0.0
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            secondRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 0.0
                        sB = 1.0
            
            # Get winners
            winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], secondRoundSeries))
            playoffs = list(filter(lambda team: team['name'] in winners, playoffs))

            # Add results
            for winner in winners:
                next(item for item in teamsData if item["name"] == winner)['r3'] += 1

            # Schedule Third Round
            thirdRoundSeries = []
            thirdRoundGames = []

            thirdRoundSeries.append({
                'home': playoffs[0]['name'],
                'away': playoffs[1]['name'],
                'hWins': 0,
                'aWins': 0
            })
            for k in range(0, 4):
                thirdRoundGames.append({
                    'homeTeam': playoffs[0]['name'],
                    'awayTeam': playoffs[1]['name']
                })
            
            # Simulate scheduled games
            for game in thirdRoundGames:
                homeTeam = next(item for item in teams if item["name"] == game['homeTeam'])
                awayTeam = next(item for item in teams if item["name"] == game['awayTeam'])

                # Current Elo ratings of both teams
                homeElo = homeTeam['elo']
                awayElo = awayTeam['elo']

                # Win probabilities
                eA = expectedScoreA(homeElo, awayElo)
                eB = 1 - eA

                # Random number between 0 and 1 to decide who wins.
                decideWin = numpy.random.random()

                # Random number between 0 and 1 to decide if it goes into Overtime.
                decideOT = numpy.random.random()

                # Get series data
                series = next(item for item in thirdRoundSeries if item['home'] == game['homeTeam'] or item['home'] == game['awayTeam'])

                previousLow = min(series['aWins'], series['hWins'])

                isHomeTrue = game['homeTeam'] == series['home']

                # Simulate game
                if decideOT <= 0.233:
                    if decideWin <= eA:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            thirdRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 1.0
                        sB = 0.5
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            thirdRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 0.5
                        sB = 1.0
                else:
                    if decideWin <= eA:
                        if isHomeTrue:
                            series['hWins'] += 1
                        else:
                            series['aWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            thirdRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 1.0
                        sB = 0.0
                    else:
                        if isHomeTrue:
                            series['aWins'] += 1
                        else:
                            series['hWins'] += 1
                        if min([series['hWins'], series['aWins']]) > previousLow:
                            thirdRoundGames.append({
                                'homeTeam': game['homeTeam'],
                                'awayTeam': game['awayTeam']
                            })
                        sA = 0.0
                        sB = 1.0
            
            # Get winners
            winners = list(map(lambda s: s['home'] if s['hWins'] == 4 else s['away'], thirdRoundSeries))

            # Add results
            for winner in winners:
                next(item for item in teamsData if item["name"] == winner)['cup'] += 1
        
# Run simulation 100,000 times.

blankData = copy.deepcopy(teamsData)

for i in range(0, 100000):
    runSeason(copy.deepcopy(blankData), copy.deepcopy(pastPO))
    print(i)

# Calculate average season.
for team in teamsData:
    team['aw'] /= 100000
    team['al'] /= 100000
    team['aotl'] /= 100000
    if team['name'] in BRENT:
        team['division'] = "Brent"
    else:
        team['division'] = "Drew"

# Output data to file.

with open("./../data/results.json", "w") as outfile:
    json.dump(teamsData, outfile, indent=4)