#!/usr/bin/python
#############
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#############
#############
# Christmas Gift Exchange Name Matcher
# Copyright (c)2010, Adam Monsen
#
# Rules:
# 1. give to a different person than last year
# 2. partners can't be nuclear family members
# 3. can't choose yourself
# 4. participants give one gift only
# 5. participants receive one gift only
#############
participants = [ 'Jim', 'Suzy', 'D.J.', 'Grandma', 'Grandpa', 'Amika', 'Ruby', 'Jose']
last_years_pairs = [
('Jim', 'Amika'),
('Amika', 'D.J.'),
('Ruby', 'Jim'),
('Grandma', 'Suzy'),
('D.J.', 'Grandpa'),
('Grandpa', 'Ruby'),
('Jose', 'Grandma'),
('Suzy', 'Jose'),
]
families = [
('Grandma', 'Grandpa'),
('Suzy', 'Jim', 'D.J.'),
('Ruby', 'Jose'),
]
import random
import sys
debug_on = False
debug_fine_on = False
from copy import copy
def debug(msg):
if debug_on: print msg
def debug_fine(msg):
if debug_fine_on: print msg
def possible_givers(participants, chosen):
# rule 4 - participants give one gift only
possible_givers = copy(participants)
for giver in map(lambda x: x[0], chosen):
possible_givers.remove(giver)
return possible_givers
def possible_recipients(giver, participants, chosen, last_years_pairs, families):
"""Exclude all rule-breaking recipients and return whatever's left, if
anything."""
possible_recipients = copy(participants)
# rule 3 - can't choose yourself
possible_recipients.remove(giver)
# rule 1 - different pairing than last year
for tmp_giver, recipient in last_years_pairs:
if tmp_giver == giver and (giver, recipient) in last_years_pairs \
and recipient in possible_recipients:
debug_fine("%s gave to %s last year, excluding" % (giver, recipient))
possible_recipients.remove(recipient)
# rule 2 - give outside nuclear family
for family in families:
if giver in family:
for recipient in family:
if recipient in possible_recipients:
debug_fine("%s and %s are family, excluding" % (giver, recipient))
possible_recipients.remove(recipient)
# rule 5 - participants receive one gift only
for pair in chosen:
if pair[1] in possible_recipients:
debug_fine("%s is already paired, excluding" % pair[1])
possible_recipients.remove(pair[1])
return possible_recipients
if __name__ == '__main__':
chosen = []
while len(chosen) != len(participants):
givers = possible_givers(participants, chosen)
# if we omit randomization here, our "abort/retry" (below) may fail,
# although I'm not really sure why
giver = random.choice(givers)
debug_fine("GIVER: %s" % giver)
recipients = possible_recipients(giver, participants, \
chosen, last_years_pairs, families)
# brute force abort/retry
if len(recipients) == 0:
debug("No recipients, trying a different giver...")
if len(givers) <= 1:
debug("No other givers to try! Starting over...")
chosen = []
if len(givers) == 2:
debug("Stalemate! Two givers left but they can't give to eachother. Starting over...")
chosen = []
continue
recipient = random.choice(recipients)
debug_fine("RECIPIENT: %s" % recipient)
chosen.append( (giver, recipient) )
debug("PAIRS ARE NOW: %s" % chosen)
chosen.sort()
for giver, recipient in chosen:
print "%s gives to %s" % (giver, recipient)