Last active
September 20, 2024 21:41
-
-
Save varenc/63e771584ece19d9b0692f84a2b910d8 to your computer and use it in GitHub Desktop.
Varen siblings + partners gift exchange pairings for Christmas 2024
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
import pandas as pd | |
from tabulate import tabulate | |
SIBLINGS = ["McKenzie", "Reagan", "Danni", "Alex", "Thomas", "Ben", "Shane", "Eric", "Chris"] | |
PARTNERS = ["Emilie", "Julia", "Courtney", "Kirstyn", "Stefanie", "Rilka"] | |
sibling_partner_mapping = { | |
"Alex": "Emilie", | |
"Thomas": "Julia", | |
"Ben": "Courtney", | |
"Shane": "Kirstyn", | |
"Eric": "Stefanie", | |
"Chris": "Rilka" | |
} | |
assert all(s in SIBLINGS for s in sibling_partner_mapping.keys()), "Partner-havers must be siblings" | |
assert all(p in PARTNERS for p in sibling_partner_mapping.values()), "Partners must be in the partners list" | |
def generate_advanced_pairings(siblings, partners, sibling_partner_mapping, gifts_per_sibling, sibling_to_sibling_gifts): | |
""" | |
- Each sibling gives `gifts_per_sibling` gifts | |
- Partners aren't assigned to give gifts | |
- Each sibling receives `sibling_to_sibling_gifts` gifts from other siblings | |
- Each partner receives the remaining gifts, distributed as evenly as possible | |
- Siblings don't give gifts to themselves | |
- Siblings don't give gifts to their own partner | |
- Siblings don't receive gifts from a sibling they're also giving a gift to (non-reciprocal) | |
""" | |
pairings = {s: [] for s in siblings} | |
total_gifts = len(siblings) * gifts_per_sibling | |
sibling_gifts = len(siblings) * sibling_to_sibling_gifts | |
partner_gifts = total_gifts - sibling_gifts | |
# shuffle partners to ensure randomness | |
random.shuffle(partners) | |
partner_gift_counts = {p: partner_gifts // len(partners) for p in partners} | |
extra_gifts = partner_gifts % len(partners) | |
for i in range(extra_gifts): | |
partner_gift_counts[partners[i]] += 1 | |
print(f"{partner_gift_counts=}") | |
# generate sibling to sibling gifts ensuring non-reciprocity | |
for giver in siblings: | |
available = set(siblings) - {giver, sibling_partner_mapping.get(giver, '')} | |
loop_count = 0 | |
while len(pairings[giver]) < sibling_to_sibling_gifts: | |
assert loop_count < 1000, f"Looped too many times on {giver=!r}! Perhaps not solvable?" | |
recipient = random.choice(list(available)) | |
if giver not in pairings[recipient]: | |
pairings[giver].append(recipient) | |
available.remove(recipient) | |
loop_count += 1 | |
# assign partners ensuring not to give to own partner | |
for giver in siblings: | |
potential_partners = [p for p in partners if p != sibling_partner_mapping.get(giver, '') and partner_gift_counts[p] > 0] | |
while potential_partners and len(pairings[giver]) < gifts_per_sibling: | |
selected_partner = random.choice(potential_partners) | |
pairings[giver].append(selected_partner) | |
partner_gift_counts[selected_partner] -= 1 | |
potential_partners = [p for p in partners if p != sibling_partner_mapping.get(giver, '') and partner_gift_counts[p] > 0] | |
return pairings | |
def main(): | |
gifts_per_sibling = 4 | |
sibling_to_sibling_gifts = 3 | |
pairings = generate_advanced_pairings(SIBLINGS, PARTNERS, sibling_partner_mapping, gifts_per_sibling, sibling_to_sibling_gifts) | |
pairings_df = pd.DataFrame.from_dict(pairings, orient='index') | |
pairing_len=len(list(pairings.items())[0][1]) | |
print(f"{pairing_len=} ,{list(pairings.items())[0][1]=}") | |
print(tabulate(pairings_df, headers=[f"Sib Gift {x}" if x <=sibling_to_sibling_gifts else f"Partner Gift {x}" for x in range(1,pairing_len+1)], tablefmt='psql')) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment