This is an interesting one, because I didn't get in on an interview. Rather my roommate asked me to do this, so - real world consequences!
My roommate wanted to set up a balanced set of Settlers of Catan matches. If you don't know about Catan, all that matters is that its a board game that can be played with 3 or 4 people per game. Here's the breakdown of what he asked:
I was able to determine that this should be possible, and even come up with a solution. Unfortunately, I had to brute force it, though I was able to include some optimizations.
My question is, is it there a deterministic algorithm that could solve this problem? What about the general case?
In case you're interested, here's the code that I used. It's a greedy approach that tries to pack the pairs with the least matches played against each other first into the 4-player match, then the 3-player match. I use some RNG weights to break ties, otherwise the outcome will end up unbalanced (I've included the no RNG code as comments, if you want to comment that out and see for yourself). A successful run will yield a set of rounds where every player does play 4 4-player matches and 3 3-player matches, and faces off against each other 3 times.
from itertools import combinations
import heapq
import random
random.seed(1)
def simulate():
player_ids = [1, 2, 3, 4, 5, 6, 7]
combos = combinations(player_ids, 2)
q = []
new_q = []
for combo in combos:
heapq.heappush(q, (0, random.random(), combo))
# heapq.heappush(q, (0, combo))
for game_round in range(7):
print(f'round {game_round + 1}')
player_4_set = set()
player_3_set = set()
while q:
count, _, combo = heapq.heappop(q)
# count, combo = heapq.heappop(q)
combo_set = set(combo)
union_4 = player_4_set.union(combo_set)
if len(union_4) <= 4:
player_4_set = union_4
heapq.heappush(new_q, (count + 1, random.random(), combo))
# heapq.heappush(new_q, (count + 1, combo))
continue
union_3 = player_3_set.union(combo_set)
inter = player_4_set.intersection(combo_set)
if len(union_3) <= 3 and len(inter) == 0:
player_3_set = union_3
heapq.heappush(new_q, (count + 1, random.random(), combo))
# heapq.heappush(new_q, (count + 1, combo))
continue
heapq.heappush(new_q, (count, random.random(), combo))
# heapq.heappush(new_q, (count, combo))
print(player_4_set)
print(player_3_set)
# print(new_q)
q = new_q
new_q = []
result = [item[0] for item in q]
return result
counter = 1
while True:
result = simulate()
print(result)
if min(result) == max(result):
print(f'result reached in {counter} iterations')
break
counter += 1