def __init__(self, states = None, league = None, all_dates = None): self.DIVISIONAL_GAMES = 4 self.CONF_OPPONENTS_GAMES = 4 self.REMAINING_CONF_GAMES = 3 self.INTERCONF_GAMES = 2 self.TOTAL_GAMES = self.DIVISIONAL_GAMES*4 + self.CONF_OPPONENTS_GAMES*6 + self.REMAINING_CONF_GAMES*4 + self.INTERCONF_GAMES*15 self.states = {} if states is None else states self.domains = "domains" self.selected = "selected" self.master_dates = "master_dates" self.current_cost = 'current_cost' if len(self.states) == 0: domains = {} selected = {} for team in league.teams(): #formulate a list of the games it has to play games = [] for opponent in league.teams(): if opponent is not team: games += [opponent]*constraint.total_games(self, team, opponent) random.shuffle(games) domains[team] = games for i in range(1, self.TOTAL_GAMES+1): selected[(team, i)] = None self.states[self.domains] = domains self.states[self.selected] = selected if self.current_cost not in self.states: self.states[self.current_cost] = 1230 if self.master_dates not in self.states: self.states[self.master_dates] = {}
def ordered_domain(self, dk, sk): t1, t2 = dk home_games, away_games, num_games = constraint.home_away_num_game_dicts(sk, self.states[self.selected]) if dk not in num_games or num_games[dk] < constraint.total_games(self, t1, t2): total = (self.TOTAL_GAMES + 1)/2 t1_home = home_games[t1] if t1 in home_games else 0 t1_away = away_games[t1] if t1 in away_games else 0 t2_home = home_games[t2] if t2 in home_games else 0 t2_away = away_games[t2] if t2 in away_games else 0 ''' If there's enough of home or away games only try the other Check that it's in the domain, the opponents where you play an odd number of games against can tilt the number of home/away games on inconsistent states ''' if (t1_home == total or t2_away == total) and False in self.states[self.domains][dk]: return self.domain_dates(False, sk, True) elif (t1_away == total or t2_home == total) and True in self.states[self.domains][dk]: return self.domain_dates(True, sk, True) ''' We want to try the T/F that there's more of in the domain first Check for reverse because DFS takes states in reverse order ''' T_num = len([x for x in self.states[self.domains][dk] if x]) F_num = len(self.states[self.domains][dk]) - T_num if (T_num == F_num): t_first = random.choice([True,False]) else: t_first = T_num < F_num return self.order_TF(dk, t_first, sk) else: return []
def __init__(self, states=None, league=None, all_dates=None): self.DIVISIONAL_GAMES = 4 self.CONF_OPPONENTS_GAMES = 4 self.REMAINING_CONF_GAMES = 3 self.INTERCONF_GAMES = 2 self.TOTAL_GAMES = self.DIVISIONAL_GAMES * 4 + self.CONF_OPPONENTS_GAMES * 6 + self.REMAINING_CONF_GAMES * 4 + self.INTERCONF_GAMES * 15 self.states = {} if states is None else states self.domains = "domains" self.selected = "selected" self.master_dates = "master_dates" self.current_cost = 'current_cost' if len(self.states) == 0: domains = {} selected = {} for team in league.teams(): #formulate a list of the games it has to play games = [] for opponent in league.teams(): if opponent is not team: games += [opponent] * constraint.total_games( self, team, opponent) random.shuffle(games) domains[team] = games for i in range(1, self.TOTAL_GAMES + 1): selected[(team, i)] = None self.states[self.domains] = domains self.states[self.selected] = selected if self.current_cost not in self.states: self.states[self.current_cost] = 1230 if self.master_dates not in self.states: self.states[self.master_dates] = {}
def ordered_domain(self, dk, sk): t1, t2 = dk home_games, away_games, num_games = constraint.home_away_num_game_dicts( sk, self.states[self.selected]) if dk not in num_games or num_games[dk] < constraint.total_games( self, t1, t2): total = (self.TOTAL_GAMES + 1) / 2 t1_home = home_games[t1] if t1 in home_games else 0 t1_away = away_games[t1] if t1 in away_games else 0 t2_home = home_games[t2] if t2 in home_games else 0 t2_away = away_games[t2] if t2 in away_games else 0 ''' If there's enough of home or away games only try the other Check that it's in the domain, the opponents where you play an odd number of games against can tilt the number of home/away games on inconsistent states ''' if (t1_home == total or t2_away == total) and False in self.states[self.domains][dk]: return self.domain_dates(False, sk, True) elif (t1_away == total or t2_home == total) and True in self.states[self.domains][dk]: return self.domain_dates(True, sk, True) ''' We want to try the T/F that there's more of in the domain first Check for reverse because DFS takes states in reverse order ''' T_num = len([x for x in self.states[self.domains][dk] if x]) F_num = len(self.states[self.domains][dk]) - T_num if (T_num == F_num): t_first = random.choice([True, False]) else: t_first = T_num < F_num return self.order_TF(dk, t_first, sk) else: return []
def create_schedule(self): if self.debug: print "{}: Starting".format(dg.datetime.datetime.today()) matchups = None initialState = dom.Matchups(None, self.data.league, self.data.game_indices) if self.matchups_json: matchups = read_output(self.data.league, "Matchups") else: matchups, matchups_statesExplored = self.DFS(initialState) if self.debug: print "{}: Matchups, {} states explored, {}".format( dg.datetime.datetime.today(), matchups_statesExplored, matchups is not None) if matchups is not None: write_output(matchups, "Matchups") #now we have our matchups which is a dict of (team, game_num) -> [opponent] #we next want to assign home/away so we create a map of the form: #(team1, team2, game_num) -> [True/False] #where True indicates it's played at team1's venue #also team1 < team2 alphabetically venues_dates = None if self.venues_dates_json: venues_dates = {} venues = read_output(self.data.league, "Venues") dates = read_output(self.data.league, "Dates") self.data.game_indices = read_output(self.data.league, "I2D") for state in venues: venues_dates[state] = (dates[state], venues[state]) else: venues_domains = {} venues_selected = {} master_dates = {} for state in matchups: t, g_n = state o = matchups[state] sk = (t, o, g_n) if t.name < o.name else (o, t, g_n) dk = (t, o) if t.name < o.name else (o, t) if sk not in venues_selected: venues_selected[sk] = None dates_true = date_ranges(2, dk[0], g_n) dates_false = date_ranges(2, dk[1], g_n) master_dates[(sk, True)] = dates_true master_dates[(sk, False)] = dates_false venues_domains[dk] = [True, False] * ( (dom.constraint.total_games(initialState, t, o) + 1) / 2) venueState = dom.Venues({initialState.domains: venues_domains, initialState.selected: venues_selected,\ initialState.master_dates: master_dates}) venues_dates, venues_statesExplored = self.DIJKSTRAS( venueState) #venues_dates, venues_statesExplored = self.DFS(venueState) if self.debug: print "{}: Venues, {} states explored, {}".format( dg.datetime.datetime.today(), venues_statesExplored, venues_dates is not None) if venues_dates is not None: venues = {} dates = {} #write_output(venu, "Dates") #create a master map of everything of the form: #(team, game_num) -> (opponent, home, dateindex) #where home is true if played at team's venue sched_map = {} for state in venues_dates: t1, t2, g_n = state date, home = venues_dates[state] sched_map[(t1, g_n)] = (t2, home, date) sched_map[(t2, g_n)] = (t1, not home, date) venues[state] = home dates[state] = date #sanity check that everything is right for team in self.data.league.teams(): date = -20 opponent_venue = {} for i in range(1, 83): opponent, t_f, d = sched_map[(team, i)] if date >= d: print "invalid date. game {} has date {}, game {} has date {}".format( i - 1, date, i, d) date = d key = (opponent, t_f) constraint.add(opponent_venue, key) for opponent in self.data.league.teams(): if opponent is not team: if opponent_venue[( opponent, True)] < (constraint.total_games( initialState, team, opponent)) / 2: print "not enough home games" if opponent_venue[ (opponent, False)] < (constraint.total_games( initialState, team, opponent)) / 2: print "not enough away games" if opponent_venue[ (opponent, True)] + opponent_venue[ (opponent, False)] != constraint.total_games( initialState, team, opponent): print "not enough games" write_output(venues, "Venues") write_output(dates, "Dates") write_output(self.data.i2d, "I2D") return sched_map return None
def min_key(self): ''' Goal is to first assign the games for teams with opponents they have an even number of games with. Then once they are all assigned we try to fit the remaining games, there's a little more flexibility there as it's not strict split ''' teams_selected_even = {} teams_selected_odd = {} #calculate number of games against even-gamed opponents total_even_games = 4*self.DIVISIONAL_GAMES + 6*self.CONF_OPPONENTS_GAMES + 15*self.INTERCONF_GAMES total_odd_games = self.TOTAL_GAMES - total_even_games begin_search = True for state in self.states[self.selected]: if self.states[self.selected][state] is not None: begin_search = False t1, t2, g_n = state total_games = total_even_games if constraint.total_games(self, t1, t2)%2==0 else total_odd_games teams_selected = teams_selected_even if constraint.total_games(self, t1, t2)%2==0 else teams_selected_odd constraint.add(teams_selected, t1) constraint.add(teams_selected, t2) if teams_selected[t1] == total_games: teams_selected.pop(t1, None) if teams_selected[t2] == total_games: teams_selected.pop(t2, None) #teams_selected_* stores how many teams have selected their home/away for games #removing those that have had all their games scheduled with home/away #if it's empty then no team has had a selected home/away yet if begin_search: for k in self.states[self.domains]: if len(self.states[self.domains][k]) > 0 and constraint.total_games(self, k[0], k[1])%2==0: return (k, self.min_key_helper(k)) elif len(teams_selected_even) == 0 and len(teams_selected_odd) == 0: for k in self.states[self.domains]: if len(self.states[self.domains][k]) > 0 and constraint.total_games(self, k[0], k[1])%2!=0: return (k, self.min_key_helper(k)) #choose the team with the most selected home/away venues, i.e. least domain size remaining min_k = None if len(teams_selected_even) > 0: min_k = sorted(teams_selected_even.keys(), key=lambda x: -teams_selected_even[x])[0] elif len(teams_selected_odd) > 0: min_k = sorted(teams_selected_odd.keys(), key=lambda x: -teams_selected_odd[x])[0] else: return (None, None) best_k_even = None best_len_even = float("-inf") best_k_odd = None best_len_odd = float("-inf") #we have the team we want to next choose a venue for, but domains are pairs of teams, #so choose the pair with the largest remaining games to choose from for k in self.states[self.domains]: t1, t2 = k #store best_k for opponents u play even/odd # of games w/ l = len(self.states[self.domains][k]) if t1 is min_k or t2 is min_k: if constraint.total_games(self, t1, t2)%2 == 0: if l > 0 and l > best_len_even: best_len_even = l best_k_even = k else: if l > 0 and l > best_len_odd: best_len_odd = l best_k_odd = k #want to first assign games with teams you play even # of games with if best_k_even is not None: return (best_k_even, self.min_key_helper(best_k_even)) elif best_k_odd is not None: return (best_k_odd, self.min_key_helper(best_k_odd)) else: return (None, None)
def create_schedule(self): if self.debug: print "{}: Starting".format(dg.datetime.datetime.today()) matchups = None initialState = dom.Matchups(None, self.data.league, self.data.game_indices) if self.matchups_json: matchups = read_output(self.data.league, "Matchups") else: matchups, matchups_statesExplored = self.DFS(initialState) if self.debug: print "{}: Matchups, {} states explored, {}".format(dg.datetime.datetime.today(), matchups_statesExplored, matchups is not None) if matchups is not None: write_output(matchups, "Matchups") #now we have our matchups which is a dict of (team, game_num) -> [opponent] #we next want to assign home/away so we create a map of the form: #(team1, team2, game_num) -> [True/False] #where True indicates it's played at team1's venue #also team1 < team2 alphabetically venues_dates = None if self.venues_dates_json: venues_dates = {} venues = read_output(self.data.league, "Venues") dates = read_output(self.data.league, "Dates") self.data.game_indices = read_output(self.data.league, "I2D") for state in venues: venues_dates[state] = (dates[state], venues[state]) else: venues_domains = {} venues_selected = {} master_dates = {} for state in matchups: t, g_n = state o = matchups[state] sk = (t, o, g_n) if t.name < o.name else (o, t, g_n) dk = (t, o) if t.name < o.name else (o, t) if sk not in venues_selected: venues_selected[sk] = None dates_true = date_ranges(2, dk[0], g_n) dates_false = date_ranges(2, dk[1], g_n) master_dates[(sk,True)] = dates_true master_dates[(sk,False)] = dates_false venues_domains[dk] = [True, False] * ((dom.constraint.total_games(initialState, t, o) + 1)/2) venueState = dom.Venues({initialState.domains: venues_domains, initialState.selected: venues_selected,\ initialState.master_dates: master_dates}) venues_dates, venues_statesExplored = self.DIJKSTRAS(venueState) #venues_dates, venues_statesExplored = self.DFS(venueState) if self.debug: print "{}: Venues, {} states explored, {}".format(dg.datetime.datetime.today(), venues_statesExplored, venues_dates is not None) if venues_dates is not None: venues = {} dates = {} #write_output(venu, "Dates") #create a master map of everything of the form: #(team, game_num) -> (opponent, home, dateindex) #where home is true if played at team's venue sched_map = {} for state in venues_dates: t1, t2, g_n = state date, home = venues_dates[state] sched_map[(t1, g_n)] = (t2, home, date) sched_map[(t2, g_n)] = (t1, not home, date) venues[state] = home dates[state] = date #sanity check that everything is right for team in self.data.league.teams(): date = -20 opponent_venue = {} for i in range(1, 83): opponent, t_f, d = sched_map[(team, i)] if date >= d: print "invalid date. game {} has date {}, game {} has date {}".format(i-1, date, i, d) date = d key = (opponent, t_f) constraint.add(opponent_venue, key) for opponent in self.data.league.teams(): if opponent is not team: if opponent_venue[(opponent, True)] < (constraint.total_games(initialState, team, opponent))/2: print "not enough home games" if opponent_venue[(opponent, False)] < (constraint.total_games(initialState, team, opponent))/2: print "not enough away games" if opponent_venue[(opponent, True)] + opponent_venue[(opponent, False)] != constraint.total_games(initialState, team, opponent): print "not enough games" write_output(venues, "Venues") write_output(dates, "Dates") write_output(self.data.i2d, "I2D") return sched_map return None
def min_key(self): ''' Goal is to first assign the games for teams with opponents they have an even number of games with. Then once they are all assigned we try to fit the remaining games, there's a little more flexibility there as it's not strict split ''' teams_selected_even = {} teams_selected_odd = {} #calculate number of games against even-gamed opponents total_even_games = 4 * self.DIVISIONAL_GAMES + 6 * self.CONF_OPPONENTS_GAMES + 15 * self.INTERCONF_GAMES total_odd_games = self.TOTAL_GAMES - total_even_games begin_search = True for state in self.states[self.selected]: if self.states[self.selected][state] is not None: begin_search = False t1, t2, g_n = state total_games = total_even_games if constraint.total_games( self, t1, t2) % 2 == 0 else total_odd_games teams_selected = teams_selected_even if constraint.total_games( self, t1, t2) % 2 == 0 else teams_selected_odd constraint.add(teams_selected, t1) constraint.add(teams_selected, t2) if teams_selected[t1] == total_games: teams_selected.pop(t1, None) if teams_selected[t2] == total_games: teams_selected.pop(t2, None) #teams_selected_* stores how many teams have selected their home/away for games #removing those that have had all their games scheduled with home/away #if it's empty then no team has had a selected home/away yet if begin_search: for k in self.states[self.domains]: if len( self.states[self.domains][k] ) > 0 and constraint.total_games(self, k[0], k[1]) % 2 == 0: return (k, self.min_key_helper(k)) elif len(teams_selected_even) == 0 and len(teams_selected_odd) == 0: for k in self.states[self.domains]: if len( self.states[self.domains][k] ) > 0 and constraint.total_games(self, k[0], k[1]) % 2 != 0: return (k, self.min_key_helper(k)) #choose the team with the most selected home/away venues, i.e. least domain size remaining min_k = None if len(teams_selected_even) > 0: min_k = sorted(teams_selected_even.keys(), key=lambda x: -teams_selected_even[x])[0] elif len(teams_selected_odd) > 0: min_k = sorted(teams_selected_odd.keys(), key=lambda x: -teams_selected_odd[x])[0] else: return (None, None) best_k_even = None best_len_even = float("-inf") best_k_odd = None best_len_odd = float("-inf") #we have the team we want to next choose a venue for, but domains are pairs of teams, #so choose the pair with the largest remaining games to choose from for k in self.states[self.domains]: t1, t2 = k #store best_k for opponents u play even/odd # of games w/ l = len(self.states[self.domains][k]) if t1 is min_k or t2 is min_k: if constraint.total_games(self, t1, t2) % 2 == 0: if l > 0 and l > best_len_even: best_len_even = l best_k_even = k else: if l > 0 and l > best_len_odd: best_len_odd = l best_k_odd = k #want to first assign games with teams you play even # of games with if best_k_even is not None: return (best_k_even, self.min_key_helper(best_k_even)) elif best_k_odd is not None: return (best_k_odd, self.min_key_helper(best_k_odd)) else: return (None, None)