3

What approach would be best for trying to distribute the opponents equally in a Switch Doubles Round Robin Tournament, i.e. where you switch partners each round.

For example in a 8 player tournament you would play 7 rounds and play against each player 3 or 4 times and with every player once. When using the "rotate right" approach the playing with part is correct but the opponents are not distributed equally.

RBarryYoung
  • 55,398
  • 14
  • 96
  • 137
  • This is a general question (and likely to be closed as it is asking for advice). I would start at the Python itertools library and perhaps look at `combinations('ABCD', 2)` – rajah9 Nov 21 '21 at 14:17
  • 2
    Like [this](https://stackoverflow.com/a/69384206/2144669)? – David Eisenstat Nov 21 '21 at 14:19
  • @DavidEisenstat Yes that would be a solution for `n = 8` but I'm looking for a solutions for `n > 3; n % 2 = 0` – Axel Nilsson Nov 21 '21 at 14:40
  • It generalizes to powers of two >= 4, but ok. – David Eisenstat Nov 21 '21 at 14:43
  • @DavidEisenstat Yes but the jump between 32 players and 64 is way too large unfortunately for my problem. – Axel Nilsson Nov 21 '21 at 14:52
  • 2
    This kind of problem is general covered by the tables for movements in Duplicate Bridge tournaments. Specifically in your case, by the movements for [individuals tournaments](https://en.wikipedia.org/wiki/Duplicate_bridge_movements#Movements_for_individual_games). – RBarryYoung Nov 21 '21 at 18:19
  • @RBarryYoung Thank you! I found some examples by looking at bridge tournaments, and I will keep on looking to see if I can find a general solution! – Axel Nilsson Nov 22 '21 at 20:29
  • @AxelNilsson These kinds of movements a very complex and as far as I know, there is no known general solution. Even general-category solutions like the Shomate movement (n=Prime*4) or Finite field solutions (n=powers of two) are few and far between. Yours is easier because you are not requiring full movements, but even so, it is a very hard problem. – RBarryYoung Nov 23 '21 at 16:07

1 Answers1

1

Several years ago, I was asked by a friend to provide a solution for a golf tournament round robin. Your problem is similar to that one. Here's how I would go about solving the problem:

  1. Make a list of the combinations of players taken two at a time.
  2. For each round make a of the combinations list and:
    a. select on pair from your round_combinations and:
    i. delete the combination from your player combinations
    ii. remove all combinations involving the players in your selection from the round_combinations.
    iii. select a second pair and remove the pair from the player combinations and remove the pairs from the round combinations lists.
    b. repeat until the round_combination list is empty
  3. Repeat step 2 for each round or until the player_combinations list is empty.

Here is the code used to implement this approach:

# Given
players = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']

from itertools import combinations
from copy import deepcopy

def filterByParties(ptys:list, pairs: list([tuple])) -> list([tuple]):
    # return pairs where neither player is in ptys list
    return [x for x in pairs if ptys.count(x[0]) == 0 and ptys.count(x[1]) == 0]
#  Alternative implementation without using list comprehension
#    rslt = []
#    ptr = 0
#    while ptr < len(pairs):
#        pr = pairs[ptr]
#        if ptys.count(pr[0]) == 0 and  ptys.count(pr[1]) == 0:
#            rslt.append(pr)
#        ptr += 1
#    return rslt

def dropPair(pr:list, pairings: list([tuple])) -> list([tuple]):
    # return list of parings minus pr
    return [x for x in pairings if  pr[0] != x[0] or pr[1] != x[1]]
#  Alternative implementation without using list comprehension
#    ptr = -1
#    for i, pair in enumerate(pairings):
#        if pr[0] == pair[0] and pr[1] == pair[1]:
#            ptr = i
#            break
#    if ptr >= 0:
#        pairings.pop(ptr)
#    return pairings         

def doublesTournament(players: list([str])):
    # Given list of players, produce a listing for a doubles tennis tournament
    # Where the tournament is split into rounds of play in which all players
    # play matches with the different participants
    tournament_pairings = list(combinations(players, 2))
    tournament_rounds = len(players) -1
    matches_per_round = len(players)//4
    tournament_schedule = []
    for rnd in range(tournament_rounds):
        rnd_play_list = []
        # Make true copy of parinings for assigning match play
        match_pairings = deepcopy(tournament_pairings)
        while match_pairings:
            team_one = match_pairings.pop()
            match_pairings = filterByParties(team_one, match_pairings)
            tournament_pairings = dropPair(team_one, tournament_pairings)
            team_two = match_pairings.pop()
            match_pairings = filterByParties(team_two, match_pairings)
            tournament_pairings = dropPair(team_two, tournament_pairings) 
            rnd_play_list.append((team_one, team_two))
        tournament_schedule.append(rnd_play_list)
    for r, round_play in enumerate(tournament_schedule):
        print(f'Round {r+1}')
        for m, match_play in enumerate(round_play):
            print(f'\tMatch {m+1}:  {match_play[0]} vs {match_play[1]}')  

doublesTournament(players) 

Yields:

Round 1
    Match 1:  ('G', 'H') vs ('E', 'F')
    Match 2:  ('C', 'D') vs ('A', 'B')
Round 2
    Match 1:  ('F', 'H') vs ('E', 'G')
    Match 2:  ('B', 'D') vs ('A', 'C')
Round 3
    Match 1:  ('F', 'G') vs ('E', 'H')
    Match 2:  ('B', 'C') vs ('A', 'D')
Round 4
    Match 1:  ('D', 'H') vs ('C', 'G')
    Match 2:  ('B', 'F') vs ('A', 'E')
Round 5
    Match 1:  ('D', 'G') vs ('C', 'H')
    Match 2:  ('B', 'E') vs ('A', 'F')
Round 6
    Match 1:  ('D', 'F') vs ('C', 'E')
    Match 2:  ('B', 'H') vs ('A', 'G')
Round 7
    Match 1:  ('D', 'E') vs ('C', 'F')
    Match 2:  ('B', 'G') vs ('A', 'H')
itprorh66
  • 3,110
  • 4
  • 9
  • 21
  • Hey! I just tried your solution and it's similar to what I have at the moment. With 16 players it gives `Round 1 Match 1: ('O', 'P') vs ('M', 'N')` `Round 2 Match 1: ('N', 'P') vs ('M', 'O')` Round 1 Match 1 and Round 2 Match 1 are the same players just in a different order which unfortunately won't work. Thanks for the suggestion though! – Axel Nilsson Nov 25 '21 at 09:44
  • That can't happen if implemented as presented. For 16 players, I get for Round 1 Match 1 = ('O', 'P') vs ('M', 'N'), Match 2 = ('K', 'L') vs ('I', 'J') , and for Round 2 Match 1 = ('N', 'P') vs ('M', 'O'), and Match 2 = ('J', 'L') vs ('I', 'K'), which are different partner mixes. – itprorh66 Nov 25 '21 at 13:24
  • We got the same result if you look closely. OPMN and NPMO are the same players, just in a different configuration. Are they different teams? Yes. Are they playing against any of the other 12 players? Unfortunately no. – Axel Nilsson Nov 25 '21 at 14:05