import random from collections import defaultdict def schedule_round(teams, sheets, history, seed=None, max_tries=500): """ teams: lista med lag (t.ex. ["A","B","C","D"]) sheets: lista med banor (t.ex. ["Sheet1","Sheet2"]) history: set med frozenset({lag1, lag2}) som redan mötts seed: valfri seed för reproducerbarhet max_tries: hur många försök att hitta parningar utan upprepning """ if seed is not None: rnd = random.Random(seed) else: rnd = random teams_local = teams[:] rnd.shuffle(teams_local) # Om udda antal lag -> en "bye" bye = None if len(teams_local) % 2 == 1: bye = teams_local.pop() # sist lottade får vila # Försök hitta parningar utan att lag mötts tidigare for _ in range(max_tries): rnd.shuffle(teams_local) pairs = [(teams_local[i], teams_local[i+1]) for i in range(0, len(teams_local), 2)] if all(frozenset(p) not in history for p in pairs): break else: # lyckades inte undvika alla upprepningar – kör bara den senaste uppsättningen par pairs = [(teams_local[i], teams_local[i+1]) for i in range(0, len(teams_local), 2)] # Slumpa banfördelning need = min(len(pairs), len(sheets)) chosen_sheets = sheets[:] rnd.shuffle(chosen_sheets) chosen_sheets = chosen_sheets[:need] # Bygg schema och uppdatera historik round_games = [] for (t1, t2), sh in zip(pairs, chosen_sheets): round_games.append({"home": t1, "away": t2, "sheet": sh}) history.add(frozenset((t1, t2))) # Om fler matcher än banor: resterande matcher får inte plats denna omgång overflow = pairs[need:] return round_games, overflow, bye def schedule_tournament(teams, sheets, num_rounds, seed=None): history = set() rnd = random.Random(seed) teams = teams[:] all_rounds = [] waiting_matches = [] # ifall det blev fler matcher än banor en omgång for r in range(1, num_rounds+1): # Om tidigare overflow, spela dem först i denna omgång carry = waiting_matches[:] waiting_matches = [] # Skapa nya par för de lag som inte är upptagna i carry busy = set(x for pair in carry for x in pair) free_teams = [t for t in teams if t not in busy] # Lottning för fria lag games, overflow, bye = schedule_round(free_teams, sheets, history, seed=rnd.randint(0, 10**9)) # Blanda in carry-matcher på lediga banor om möjligt rnd.shuffle(carry) # Lediga banor kvar efter games used_sheets = len(games) free_sheet_count = max(0, len(sheets) - used_sheets) round_games = games[:] for pair in carry[:free_sheet_count]: sh = f"Sheet(extra-{len(round_games)+1})" # eller återanvänd lediga verkliga sheets om du följer resurser strikt round_games.append({"home": pair[0], "away": pair[1], "sheet": sh}) history.add(frozenset(pair)) # Ev. kvarvarande carry-matcher + nya overflow flyttas fram waiting_matches = carry[free_sheet_count:] + overflow all_rounds.append({"round": r, "games": round_games, "bye": bye}) return all_rounds, waiting_matches # Exempel teams = ["Lag A", "Lag B", "Lag C", "Lag D", "Lag E", "Lag F"] sheets = ["Bana 1", "Bana 2", "Bana 3"] schema, kvar = schedule_tournament(teams, sheets, num_rounds=3, seed=42) for r in schema: print(f"Omgång {r['round']}:") for g in r["games"]: print(f" {g['home']} vs {g['away']} – {g['sheet']}") if r["bye"]: print(f" Vila: {r['bye']}") print() if kvar: print("Matcher som inte hann spelas och måste schemaläggas senare:", kvar)