pythonlistalgorithmround-robinsports-league-scheduling-problem

List manipulation for a Conditional Round-Robin Rugby Draw


One of the Rugby Coaches at my school have asked me to code a conditional rugby match draw for the upcoming games with the task laid out something like this: Given a list of teams from 1 - 12 split into 3 groups ([Group1 = 1, 2, 3, 4], [Group2 = 5, 6, 7, 8,], [Group3 = 9, 10, 11, 12]) generate and print an 11 round-robin matchup with the conditions that:

I have attempted multiple times but inevitably become stuck, below are my 2 attempts:

Attempt 1:

import operator
import functools
import random



###First Generation (Flawed unclean round robin)
def fixtures(teams):
    if len(teams) % 2:
        teams.append('Day off')  # if team number is odd - use 'day off' as fake team     

    rotation = list(teams)       # copy the list
    random.shuffle(rotation)

    fixtures = []
    for i in range(0, len(teams)-1):
        fixtures.append(rotation)
        rotation = [rotation[0]] + [rotation[-1]] + rotation[1:-1]

    return fixtures

def main():
    # demo code
    teams = ["Team1","Team2","Team3","Team4","Team5","Team6","Team7","Team8","Team9","Team10","Team11","Team12"]
    groupA = ["Team1","Team2","Team3","Team4"]
    groupB = ["Team5","Team6","Team7","Team8"]
    groupC = ["Team9","Team10","Team11","Team12"]

    # for one match each - use this block only
    matches = fixtures(teams)

    print("flawed matches:")
    RoundCounter = 0

    homeTeams = []
    awayTeams = []

    for f in matches:
        #print(f)
        homeTeams = f[::2]
        awayTeams = f[1::2]
        print("Home Teams:{}".format(homeTeams))
        print("Away Teams:{}".format(awayTeams))
        HomeTeamGroupA = set(homeTeams).intersection(groupA)
        HomeTeamGroupC = set(homeTeams).intersection(groupC)
        AwayTeamGroupA = set(awayTeams).intersection(groupA)
        AwayTeamGroupC = set(awayTeams).intersection(groupC)

        VSCounter = 0

        for p, o in zip(homeTeams, awayTeams):
            if p in HomeTeamGroupA:
                if o in AwayTeamGroupC:
                    AvsCPosition = awayTeams.index(o)
                    VSCounter += 1
                    RoundCleanUp(homeTeams, awayTeams, AvsCPosition, VSCounter) #if this is returned begin cleaning the round
                else: print("GroupA is versing either Group B or GroupA") #if this is returned it is a team 1-4 but is vs either group b or group a
            elif p in HomeTeamGroupC:
                if o in AwayTeamGroupA:
                    AvsCPosition = awayTeams.index(o)
                    VSCounter += 1
                    RoundCleanUp(homeTeams, awayTeams, AvsCPosition, VSCounter) #if this is returned begin cleaning the round
                else:
                    print("GroupC is versing either Group B or GroupC")  #if this is returned it is a team 9-12 but is vs either group b or group c
            else:
                pass

def RoundCleanUp(HTeam, ATeam, AvsCPos, VSCounter):
    ##gets Value of List at position
    HTeamVal = HTeam[AvsCPos]
    ATeamVal = ATeam[AvsCPos]
main()

Attempt 2:

import operator
import functools
import random


def make_round(rotation, num_teams, fixtures):
    for i in range(num_teams - 1):
        rotation = list(range(1, num_teams + 1))
        # clip to 0 .. num_teams - 2 # if i == 0, no rotation is needed (and using -0 as list index will cause problems)
        i %= (num_teams - 1)
        if i:
            rotation = rotation[:1] + rotation[-i:] + rotation[1:-i]
        half = num_teams // 2
        fixtures.append(list(rotation[:half]))
        fixtures.append(list(rotation[half:][::-1]))
    return fixtures


def make_schedule(teams):
    """Produces RoundRobin"""
    # number of teams must be even
    TeamLength = len(teams)
    if TeamLength % 2:
        TeamLength += 1  # add a dummy team for padding

    # build first round-robin
    rotation = list(teams)
    Fixture = []
    schedule = make_round(rotation, TeamLength, Fixture)

    return schedule


def homeAwayRotation(matches):
    for homeTeams, awayTeams in zip(matches[0::2], matches[1::2]):
        print("Home Rotation: {}".format(homeTeams))
        print("Away Rotation: {}".format(awayTeams))
        validation(homeTeams, awayTeams)


def validation(homeTeams, awayTeams):
    groupA = [1, 2, 3, 4]
    groupC = [9, 10, 11, 12]


    for x, y in zip(homeTeams, awayTeams):
        if x in groupA:
            if y in groupC:
                AvsCPosition = awayTeams.index(y)
                cleanDirtyData(homeTeams, awayTeams, AvsCPosition)
            else:
                    # if this is returned it is a team 1-4 but is vs either group b or group a
                print("Group A vsing either itself or GroupB\n")
        elif x in groupC:
            if y in groupA:
                AvsCPosition = awayTeams.index(y)
                cleanDirtyData(homeTeams, awayTeams, AvsCPosition)
            else:
                # if this is returned it is a team 9-12 but is vs either group b or group c
                print("Group C vsing either itself or GroupB\n")
        else:
            # if this is returned it is a team in group B
            print("This is team B\n")


def cleanDirtyData(homeTeams, awayTeams, AvsCPosition):
    HTeamVal = homeTeams[AvsCPosition]
    ATeamVal = awayTeams[AvsCPosition]
    Dirtlist = []
    Dirtlist.append(HTeamVal)
    Dirtlist.append(ATeamVal)
def main():
    # demo code
    teams = ["Team1", "Team2", "Team3", "Team4", "Team5", "Team6",
             "Team7", "Team8", "Team9", "Team10", "Team11", "Team12"]

    # for one match each - use this block only
    matches = make_schedule(teams)

    print("flawed matches:")

    homeAwayRotation(matches)


main()

My expected results would be printing each round showing which team is versing which and each team having a history a bit like this:

Any pointers or improvements I could possibly do would be greatly appreciated as I have been stuck on the final hurdle for the last 2 weeks


Solution

  • If I have understood the problem correct, then all you need is some combining of teams with every member in different groups.

    I put some code together that should solve your problem:

    def vs(team, group):
        matchups = map(lambda opponent: (team,opponent), group)
        matchups = filter(lambda tup: tup[0] != tup[1], matchups)
        return list(matchups)
    
    def matches(teams):
        group_size = len(teams) // 3
    
        # Make the groups, basically just splitting the team list in three parts
        groups = [teams[:group_size], teams[group_size:2*group_size], teams[2*group_size:]]
        matchups = []
        for index, team in enumerate(teams):
            group_index = index // group_size
            current_matchup = []
    
            # Check if we're working with group 1 or 3
            if group_index == 0 or group_index == 2:
    
                # Flip the order of a tuple
                def flip(x):
                    return (x[1], x[0])
    
                own_group = vs(team, groups[group_index])
    
                # Add matches against everyone in the group
                current_matchup.extend(own_group)
    
                # Add matches agains everyone in the group again, but now the current team is 'away'
                current_matchup.extend(list(map(flip, own_group)))
    
                # Add matches against everyone in group 2
                current_matchup.extend(vs(team, groups[1]))
    
                # Lastly, add the bye 
                current_matchup.append((team, "bye"))
            else:
                # Just all matches against all other teams, once.
                current_matchup.extend(vs(team, teams))
            matchups.append(current_matchup)
        return matchups
    
    # This can be anything. Numbers, 'Team 1' or even 'The wondrous flying squirrels of death'
    teams = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    # Make matches
    matchups = matches(teams)
    
    
    # Just pretty print
    for i in range(len(matchups)):
        matches = '\n\t'.join(map(lambda m: f'{str(m[0]).rjust(10)} vs {str(m[1]).ljust(10)}', matchups[i]))
        print(f"Team '{teams[i]}' matches:\n\t{matches}")