Secret Santa


version 0.1: gist
version 0.2: adds support for taboo pairings
version 0.3: python3 version, reproduced below.

Future directions: This is no blind multi-party computation. But it could be?

Further documentation: Wikipedia


#!/usr/bin/python3 import random import base64 """Secret Santa generator 0.3, trusted 1-party version. Ever been dissatisfied with random santa assignment because the group would break up into smaller circles? This script prevents this. Usage: Edit seed and names, run the script. Each participant will have the obfuscated name of the presentee printed next to their name, run it through the base64 decoder of your choice to reveal it. There is no encryption, that means nothing is preventing you from spoilering yourself. Don't do it though. Each combination of seed and names will give you exactly the same results each time you run the script, so test with a throwaway seed before generating the real list.""" # seed = 1 # testing purposes seed = 20170218 # live seed, a date is good fit here # participants names = [ 'Donald', 'Daisy', 'Tick', 'Trick', 'Track' ] # people who shouldn't get each other because they're partners or enemies or whatever tabooCombinations = [ ('Donald', 'Daisy'), ('Daisy', 'Donald') ] """generate random string from alphabet""" def randomstring(l): chars = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' return ''.join([random.choice(chars) for _ in range(l)]) """pad string with random data""" def obfuscate(s, l): offset = random.randint(0, l - len(s)) padded = randomstring(offset) + s + randomstring(l - len(s) - offset) return base64.b64encode(padded.encode('utf-8')).decode('utf-8') """check present list for permissibility, not optimized for speed""" def taboo(ls): for t in ls: if t in tabooCombinations: return True return False print('seed: %s' % seed) generator = random.seed(seed) # reproducibility shuffles = 0 while True: random.shuffle(names, generator) presents = list(zip(names, names[1:] + names[:1])) # copy, right rotate copy, zip if not taboo(presents): break # only finish if we find a good ordering shuffles += 1 random.shuffle(presents, generator) # output list shouldn't betray present order print("necessary reshuffles: %s" % shuffles) print("") #print('\n'.join(['%s: %s' % (pair[0], pair[1]) for pair in presents])) print('\n'.join(['%s: %s' % (pair[0], obfuscate(pair[1], max(map(len,names)) + 5)) for pair in presents]))