Games are fun, especially board games. But what if we could out-source all the valuable time we waste playing board games and just have computers do it for us?

Case Study 0:

Codenames is a fun and popular game that has a very low barrier to entry, making it the perfect target to automate out of our leisure time.


Get your teammates to guess all of your words as quickly as possible by using related words to connect them. Try not to give clues that could relate the opponent words or the assassin.


Like most problems in life this can be solved with word vectors. Let's take the pre-trained Wikipedia vector corpus used in previous posts and try to figure out the optimal clue given a set of words to relate.

This boils down to using model.most_similar, seen here in the gensim documentation. The words you want to relate with a clue are passed as positive samples, and if you wish you may pass the assassin or other words you do not want to relate as negative samples.

Using only positive examples get us this:

The above example solicits a clue for "turkey" and "bottle". The first suggestion is "bottles" which, for some reason, scored high in similarity to our codewords. Let's remove words that are too similar to our codewords:


Much better. In fact, a baster is most definitely a turkey bottle. Let's try another example to see if our negative inputs change things correctly:


Vehicle in fact would be a good clue for three vehicles. But what if the only two words left to guess for your team are "amublance" and "tank", the assassin word is "car", and your opponents are only one word away from winning?


Capitalization issues aside, not bad. I think I may have found my new Codenames strategy. That should free up a lot of my time.

Future Work

Precalculate all possible word combinations in Codenames. Create another program that also uses the same corpus of words to make guesses based on this program's clues. Sit back and relax.


model = KeyedVectors.load_word2vec_format('../ind/wiki-news-300d-1M-subword.vec')

ap = argparse.ArgumentParser()
ap.add_argument("-c", "--codes", required=True,
	help="words you want to give a clue for")
ap.add_argument("-a", "--assassin", required=False,
	help="the assassin word you don't want them to guess")
args = vars(ap.parse_args())

codewords = args['codes'].split()
assassin = [args['assassin']] if args['assassin'] else []

clues = model.most_similar(positive=codewords, negative=assassin, topn=20)

def illegal(clue):
	return [ code for code in codewords if (clue[0] in code or code in clue[0]) ]

print("I think the following would be good clues for this set of words:")
filtered = [ clue for clue in clues if not illegal(clue) ]
for c in filtered: