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]))
[up]