Skip to content

Instantly share code, notes, and snippets.

@migurski
Created May 17, 2020 18:33
Show Gist options
  • Save migurski/912f1556a7c3f3db85ab839b53441b77 to your computer and use it in GitHub Desktop.
Save migurski/912f1556a7c3f3db85ab839b53441b77 to your computer and use it in GitHub Desktop.
Generate Codenames board in Google Sheets
#!/usr/bin/env python3
import json
import random
import copy
import argparse
import oauth2client.service_account
import apiclient.discovery
parser = argparse.ArgumentParser()
parser.add_argument('players')
parser.add_argument('judges')
parser.add_argument('--clear', action='store_const', const=True)
parser.add_argument('--redfirst', action='store_const', const=False, dest='bluefirst')
parser.add_argument('--bluefirst', action='store_const', const=True, dest='bluefirst')
args = parser.parse_args()
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
WORDS = "Africa", "Agent", "Air", "Alien", "Alps", "Amazon", "Ambulance", "America", "Angel", "Antarctica", "Apple", "Arm", "Atlantis", "Australia", "Aztec", "Back", "Ball", "Band", "Bank", "Bar", "Bark", "Bat", "Battery", "Beach", "Bear", "Beat", "Bed", "Beijing", "Bell", "Belt", "Berlin", "Bermuda", "Berry", "Bill", "Block", "Board", "Bolt", "Bomb", "Bond", "Boom", "Boot", "Bottle", "Bow", "Box", "Bridge", "Brush", "Buck", "Buffalo", "Bug", "Bugle", "Button", "Calf", "Canada", "Cap", "Capital", "Car", "Card", "Carrot", "Casino", "Cast", "Cat", "Cell", "Centaur", "Center", "Chair", "Change", "Charge", "Check", "Chest", "Chick", "China", "Chocolate", "Church", "Circle", "Cliff", "Cloak", "Club", "Code", "Cold", "Comic", "Compound", "Concert", "Conductor", "Contract", "Cook", "Copper", "Cotton", "Court", "Cover", "Crane", "Crash", "Cricket", "Cross", "Crown", "Cycle", "Czech", "Dance", "Date", "Day", "Death", "Deck", "Degree", "Diamond", "Dice", "Dinosaur", "Disease", "Doctor", "Dog", "Draft", "Dragon", "Dress", "Drill", "Drop", "Duck", "Dwarf", "Eagle", "Egypt", "Embassy", "Engine", "England", "Europe", "Eye", "Face", "Fair", "Fall", "Fan", "Fence", "Field", "Fighter", "Figure", "File", "Film", "Fire", "Fish", "Flute", "Fly", "Foot", "Force", "Forest", "Fork", "France", "Game", "Gas", "Genius", "Germany", "Ghost", "Giant", "Glass", "Glove", "Gold", "Grace", "Grass", "Greece", "Green", "Ground", "Ham", "Hand", "Hawk", "Head", "Heart", "Helicopter", "Himalayas", "Hole", "Hollywood", "Honey", "Hood", "Hook", "Horn", "Horse", "Horseshoe", "Hospital", "Hotel", "Ice", "Ice Cream", "India", "Iron", "Ivory", "Jack", "Jam", "Jet", "Jupiter", "Kangaroo", "Ketchup", "Key", "Kid", "King", "Kiwi", "Knife", "Knight", "Lab", "Lap", "Laser", "Lawyer", "Lead", "Lemon", "Leprechaun", "Life", "Light", "Limousine", "Line", "Link", "Lion", "Litter", "Loch Ness", "Lock", "Log", "London", "Luck", "Mail", "Mammoth", "Maple", "Marble", "March", "Mass", "Match", "Mercury", "Mexico", "Microscope", "Millionaire", "Mine", "Mint", "Missile", "Model", "Mole", "Moon", "Moscow", "Mount", "Mouse", "Mouth", "Mug", "Nail", "Needle", "Net", "New York", "Night", "Ninja", "Note", "Novel", "Nurse", "Nut", "Octopus", "Oil", "Olive", "Olympus", "Opera", "Orange", "Organ", "Palm", "Pan", "Pants", "Paper", "Parachute", "Park", "Part", "Pass", "Paste", "Penguin", "Phoenix", "Piano", "Pie", "Pilot", "Pin", "Pipe", "Pirate", "Pistol", "Pit", "Pitch", "Plane", "Plastic", "Plate", "Platypus", "Play", "Plot", "Point", "Poison", "Pole", "Police", "Pool", "Port", "Post", "Pound", "Press", "Princess", "Pumpkin", "Pupil", "Pyramid", "Queen", "Rabbit", "Racket", "Ray", "Revolution", "Ring", "Robin", "Robot", "Rock", "Rome", "Root", "Rose", "Roulette", "Round", "Row", "Ruler", "Satellite", "Saturn", "Scale", "School", "Scientist", "Scorpion", "Screen", "Scuba Diver", "Seal", "Server", "Shadow", "Shakespeare", "Shark", "Ship", "Shoe", "Shop", "Shot", "Sink", "Skyscraper", "Slip", "Slug", "Smuggler", "Snow", "Snowman", "Sock", "Soldier", "Soul", "Sound", "Space", "Spell", "Spider", "Spike", "Spine", "Spot", "Spring", "Spy", "Square", "Stadium", "Staff", "Star", "State", "Stick", "Stock", "Straw", "Stream", "Strike", "String", "Sub", "Suit", "Superhero", "Swing", "Switch", "Table", "Tablet", "Tag", "Tail", "Tap", "Teacher", "Telescope", "Temple", "Theater", "Thief", "Thumb", "Tick", "Tie", "Time", "Tokyo", "Tooth", "Torch", "Tower", "Track", "Train", "Triangle", "Trip", "Trunk", "Tube", "Turkey", "Undertaker", "Unicorn", "Vacuum", "Van", "Vet", "Wake", "Wall", "War", "Washer", "Washington", "Watch", "Water", "Wave", "Web", "Well", "Whale", "Whip", "Wind", "Witch", "Worm", "Yard"
FIELDS = ','.join([
"userEnteredValue",
"userEnteredFormat.backgroundColor",
"userEnteredFormat.textFormat.foregroundColor",
"userEnteredFormat.textFormat.fontSize",
])
def make_service(cred_data):
''' Create a Google service account instance from credentials object.
'''
creds = oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_dict(cred_data, SCOPES)
return apiclient.discovery.build('sheets', 'v4', credentials=creds)
with open('code-words-277404-828ee38406e7.json') as file:
cred_data = json.load(file)
service = make_service(cred_data)
def combined(format, text_format):
result = copy.deepcopy(format)
result['textFormat'].update(**text_format)
return result
def celldata(string, format):
return {
"userEnteredValue": {'stringValue': string},
"userEnteredFormat": format
}
white = {"red": 1, "green": 1, "blue": 1, "alpha": 1}
black = {"red": 0, "green": 0, "blue": 0, "alpha": 1}
red = {"red": 0xD0/0xFF, "green": 0x02/0xFF, "blue": 0x1B/0xFF, "alpha": 1}
blue = {"red": 0x4A/0xFF, "green": 0x90/0xFF, "blue": 0xE2/0xFF, "alpha": 1}
gray = {"red": 0xD8/0xFF, "green": 0xD8/0xFF, "blue": 0xD8/0xFF, "alpha": 1}
text_word = {"fontSize": 14}
text_emoji = {"fontSize": 36}
colors_nothing = {"backgroundColor": white, "textFormat": {"foregroundColor": black}}
colors_assassin = {"backgroundColor": black, "textFormat": {"foregroundColor": white}}
colors_redspy = {"backgroundColor": red, "textFormat": {"foregroundColor": white}}
colors_bluespy = {"backgroundColor": blue, "textFormat": {"foregroundColor": white}}
colors_bystander = {"backgroundColor": gray, "textFormat": {"foregroundColor": black}}
if args.clear:
formats = [colors_bystander] * 25
words = [''] * 25
else:
formats = [colors_bluespy if args.bluefirst else colors_redspy]
formats += [colors_redspy] * 8 + [colors_bluespy] * 8
formats += [colors_assassin] + [colors_bystander] * 7
random.shuffle(formats)
words = random.choices(WORDS, k=25)
resp1 = service.spreadsheets().get(spreadsheetId=args.players).execute()
players_sheet = resp1['sheets'][0]['properties']['sheetId']
resp2 = service.spreadsheets().get(spreadsheetId=args.judges).execute()
judges_sheet = resp2['sheets'][0]['properties']['sheetId']
body1 = {
'requests': [
{
'updateCells': {
'rows':
[
{
'values': [
celldata(words[index], combined(colors_nothing, text_word))
for index in range(offset, len(words), 5)
]
}
for offset in range(5)
],
"fields": FIELDS,
"range": {
"sheetId": players_sheet,
"startRowIndex": 0,
"endRowIndex": 5,
"startColumnIndex": 0,
"endColumnIndex": 5
}
}
},
]
}
if args.bluefirst:
first_color, second_color = colors_bluespy, colors_redspy
else:
first_color, second_color = colors_redspy, colors_bluespy
body2 = {
'requests': [
{
'updateCells': {
'rows':
[
{
'values': [
celldata(words[index], combined(formats[index], text_word))
for index in range(offset, len(words), 5)
]
}
for offset in range(5)
],
"fields": FIELDS,
"range": {
"sheetId": judges_sheet,
"startRowIndex": 0,
"endRowIndex": 5,
"startColumnIndex": 0,
"endColumnIndex": 5
}
}
},
{
'updateCells': {
'rows':
[
#{
# 'values': [
# celldata('', combined(colors_nothing, text_word))
# for i in range(5)
# ]
#},
{
'values': [
celldata("\ud83d\udd75\ud83c\udfff", combined(first_color, text_emoji)),
celldata("\ud83d\udd75\ud83c\udffb\u200d\u2640\ufe0f", combined(second_color, text_emoji)),
celldata("\ud83e\udd26\ud83c\udffc\u200d\u2642\ufe0f", combined(colors_bystander, text_emoji)),
celldata("\ud83e\udd26\ud83c\udffe\u200d\u2640\ufe0f", combined(colors_bystander, text_emoji)),
celldata("\u2620\ufe0f", combined(colors_assassin, text_emoji)),
]
},
],
"fields": FIELDS,
"range": {
"sheetId": judges_sheet,
"startRowIndex": 6,
"endRowIndex": 7,
"startColumnIndex": 0,
"endColumnIndex": 5
}
}
},
]
}
print(service.spreadsheets().batchUpdate(spreadsheetId=args.players, body=body1).execute())
print(service.spreadsheets().batchUpdate(spreadsheetId=args.judges, body=body2).execute())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment