Vex Tournament Manager Algorithm

Sorry if my category is a bit off, this is my first time using the vex forums.

So, recently I’ve been coding a competition simulator for me and another team at my school, but my main roadblock is getting an accurate qualification schedule generator. I know there’s a basic few rules, like maximizing time between matches, making sure you don’t play with the same teams twice (if possible), and making sure you don’t play against the same teams twice (if possible).

I was wondering, has anyone been able to figure out the algorithm that vex tournament manager uses to generates its qualification schedules?

They say they do what you are talking about. You may get @Dave_Flowerday to respond and give you an answer. When TM runs you get some stats on how the random worked and the EP/TM operator can decide if they like that or not.

Of course some teams will tell you that it matches them up with a clawbot or pushbot against a pair of top robots. :slight_smile:

4 Likes

Does anyone have an update on weather or not the tournament manager algorithm has been publicly released?

I don’t, but I did write a VRC schedule generator that I used to simulate World’s last year. Happy to share if there’s interest.

3 Likes

That would be appreciated.

Assuming you have python (and installed pandas for it):

import math
import pandas as pd
import random

pd.options.mode.chained_assignment = None

#teams = ['A','B','C','D','E','F','G','H']
#teams = ['A','B','C','D','E','F','G','H','I']
teams = ['A','B','C','D','E','F','G','H','I','J','K','L','M']

df_teams = pd.DataFrame(teams,columns=['team_number'])

def createOneMatchPerTeam(df_team_list, starting_match_index):
    df_teams = df_team_list.copy()
    df_teams['rand_match'] = [ random.randint(1,len(teams))  for k in df_teams.index]
    df_teams.sort_values(by=['rand_match'], inplace=True)
    df_teams['rand_match_rank'] = df_teams['rand_match'].rank(ascending=False,method='first') 
    df_teams['MatchNum'] = [ math.floor((df_teams.iloc[k]['rand_match_rank'] - 1) / 4.0)  for k in df_teams.index]

    match_num_list = df_teams.MatchNum.unique()
    match_num_list.sort()
    df_matches = None
    cur_match_index = 0
    for match_num in match_num_list:
        df_match_teams = df_teams.query("MatchNum == " + str(match_num))
        if len(df_match_teams) == 4:
            df_match_teams['rand_team'] = [ random.randint(0,len(df_match_teams)-1)  for k in df_match_teams.index]
            df_match_teams.sort_values(by=['rand_team'], inplace=True)
            df_match_teams['rand_team_rank'] = df_match_teams['rand_team'].rank(ascending=False,method='first') 
            df_match_teams['rand_team_rank'] = df_match_teams['rand_team_rank'].astype(int)
            #df_match_teams['TeamNum'] = [ math.floor((df_match_teams.iloc[k]['rand_team_rank'] - 1) / 2.0)  for k in df_match_teams.index]
            d_match = dict()
            d_match['match_num'] = starting_match_index + cur_match_index
            cur_match_index = cur_match_index + 1
            d_match['red1'] = df_match_teams.query("rand_team_rank==1").iloc[0]['team_number']
            d_match['red2'] = df_match_teams.query("rand_team_rank==2").iloc[0]['team_number']
            d_match['blue1'] = df_match_teams.query("rand_team_rank==3").iloc[0]['team_number']
            d_match['blue2'] = df_match_teams.query("rand_team_rank==4").iloc[0]['team_number']
            if df_matches is None:
                df_matches = pd.DataFrame.from_dict([d_match])
            else:
                df_matches = df_matches.append(pd.DataFrame.from_dict([d_match]))
        else:
            print("Need to fill in teams for match " + str(match_num))
    df_matches['match_num'] = df_matches['match_num'].astype(int)

    return df_matches

def createRandomSchedule(df_team_list, min_num_matches):
    df_team = df_team_list
    num_teams = df_team.shape[0]
    num_teams_per_batch = math.floor(num_teams / 4) * 4
    df_team_match_count = df_team[['team_number']]
    df_team_match_count['match_count'] = 0
    loop_count = 0
    df_matches = None
    while df_team_match_count.match_count.min() < min_num_matches:
        if 'rand' in df_team_match_count.columns:
            df_team_match_count.drop(columns=['rand','match_count_plus_rand'])
        df_team_match_count['rand'] = [ random.random()  for k in df_team_match_count.index]
        df_team_match_count['match_count_plus_rand'] = df_team_match_count['match_count'] + df_team_match_count['rand']
        df_team_match_count.sort_values(by=['match_count','match_count_plus_rand'],ascending=[True,True], inplace=True)
        df_team_match_count['rand_match_count_rank'] = df_team_match_count['match_count_plus_rand'].rank(ascending=True,method='first')
        df_team_match_count['rand_match_count_rank'] = df_team_match_count['rand_match_count_rank'].astype(int)
        df_team_nums_this_batch = df_team_match_count.query("rand_match_count_rank <= " + str(num_teams_per_batch))
        df_teams_batch = df_team_nums_this_batch[['team_number']].join(df_team.set_index('team_number'),on='team_number')
        df_teams_batch.reset_index(inplace=True)
        
        if df_matches is None:
            df_matches = createOneMatchPerTeam(df_teams_batch, 1)
        else:
            df_matches = df_matches.append(createOneMatchPerTeam(df_teams_batch, (num_teams_per_batch / 4) * loop_count + 1))

        loop_count = loop_count + 1
        df_team_match_count.loc[df_team_match_count['team_number'].isin(df_team_nums_this_batch['team_number'].to_list()), 'match_count'] += 1
    return df_matches

print(createRandomSchedule(df_teams,10))

Nothing especially fancy here - nothing to prevent back-to-back matches, prevent duplicative pairings, etc.

3 Likes