import itertools
def calc_word_value(reversed_word, symbol_map):
result = 0
for letter, factor in reversed_word:
result += symbol_map[letter] * factor
return result
def print_result(words, answer, variant):
t_words = []
for word in words:
t_words.append("".join(str(variant[l]) for l in word))
t_answer = "".join(str(variant[l]) for l in answer)
print("{:15s} {} = {}".format("INPUT:", " + ".join(words), answer))
print("{:15s} {} = {}".format("SOLUTION:", " + ".join(t_words), t_answer))
print("")
def solver(puzzle, full_search=False):
words_line, answer = puzzle.split('=')
words = [word.strip() for word in words_line.split('+')]
answer = answer.strip()
all_words = words + [answer]
# 'abcd' --> [('d', 1), ('c', 10), ('b', 100), ('a', 1000)]
rev_words = []
for word in words:
rev_word = [(letter, 10**num) for num, letter in enumerate(word[::-1])]
rev_words.append(rev_word)
rev_answer = [(letter, 10**num) for num, letter in enumerate(answer[::-1])]
letters = sorted(set("".join(all_words)))
LEN = len(letters)
for digits_selection in itertools.combinations(range(10), len(letters)):
for digits_permutation in itertools.permutations(digits_selection):
# make dict: letter --> digit (int)
variant = dict(zip(letters, digits_permutation))
is_allowed = True
for word in all_words:
if variant[word[0]] == 0:
is_allowed = False
if is_allowed:
words_sum = 0
for rev_word in rev_words:
words_sum += calc_word_value(rev_word, variant)
answer_sum = calc_word_value(rev_answer, variant)
if words_sum == answer_sum:
print_result(words, answer, variant)
if not full_search:
return
full_search = False
solver("send + more = money", full_search=full_search)
solver("ZEROES + ONES = BINARY", full_search=full_search)
solver("COUPLE + COUPLE = QUARTET", full_search=full_search)
solver("DO + YOU + FEEL = LUCKY", full_search=full_search)
solver("ELEVEN + NINE + FIVE + FIVE = THIRTY", full_search=full_search)
aW1wb3J0IGl0ZXJ0b29scwoKCmRlZiBjYWxjX3dvcmRfdmFsdWUocmV2ZXJzZWRfd29yZCwgc3ltYm9sX21hcCk6CiAgICByZXN1bHQgPSAwCiAgICBmb3IgbGV0dGVyLCBmYWN0b3IgaW4gcmV2ZXJzZWRfd29yZDoKICAgICAgICByZXN1bHQgKz0gc3ltYm9sX21hcFtsZXR0ZXJdICogZmFjdG9yCiAgICByZXR1cm4gcmVzdWx0CgpkZWYgcHJpbnRfcmVzdWx0KHdvcmRzLCBhbnN3ZXIsIHZhcmlhbnQpOgogICAgdF93b3JkcyA9IFtdCiAgICBmb3Igd29yZCBpbiB3b3JkczoKICAgICAgICB0X3dvcmRzLmFwcGVuZCgiIi5qb2luKHN0cih2YXJpYW50W2xdKSBmb3IgbCBpbiB3b3JkKSkKICAgIAogICAgdF9hbnN3ZXIgPSAiIi5qb2luKHN0cih2YXJpYW50W2xdKSBmb3IgbCBpbiBhbnN3ZXIpCgogICAgcHJpbnQoIns6MTVzfSB7fSA9IHt9Ii5mb3JtYXQoIklOUFVUOiIsICIgKyAiLmpvaW4od29yZHMpLCBhbnN3ZXIpKQogICAgcHJpbnQoIns6MTVzfSB7fSA9IHt9Ii5mb3JtYXQoIlNPTFVUSU9OOiIsICIgKyAiLmpvaW4odF93b3JkcyksIHRfYW5zd2VyKSkKICAgIHByaW50KCIiKQoKCmRlZiBzb2x2ZXIocHV6emxlLCBmdWxsX3NlYXJjaD1GYWxzZSk6CiAgICB3b3Jkc19saW5lLCBhbnN3ZXIgPSBwdXp6bGUuc3BsaXQoJz0nKQogICAgd29yZHMgPSBbd29yZC5zdHJpcCgpIGZvciB3b3JkIGluIHdvcmRzX2xpbmUuc3BsaXQoJysnKV0KICAgIGFuc3dlciA9IGFuc3dlci5zdHJpcCgpCiAgICBhbGxfd29yZHMgPSB3b3JkcyArIFthbnN3ZXJdCgogICAgIyAnYWJjZCcgLS0+IFsoJ2QnLCAxKSwgKCdjJywgMTApLCAoJ2InLCAxMDApLCAoJ2EnLCAxMDAwKV0KICAgIHJldl93b3JkcyA9IFtdCiAgICBmb3Igd29yZCBpbiB3b3JkczoKICAgICAgICByZXZfd29yZCA9IFsobGV0dGVyLCAxMCoqbnVtKSBmb3IgbnVtLCBsZXR0ZXIgaW4gZW51bWVyYXRlKHdvcmRbOjotMV0pXQogICAgICAgIHJldl93b3Jkcy5hcHBlbmQocmV2X3dvcmQpCiAgICByZXZfYW5zd2VyID0gWyhsZXR0ZXIsIDEwKipudW0pIGZvciBudW0sIGxldHRlciBpbiBlbnVtZXJhdGUoYW5zd2VyWzo6LTFdKV0KCiAgICBsZXR0ZXJzID0gc29ydGVkKHNldCgiIi5qb2luKGFsbF93b3JkcykpKQogICAgTEVOID0gbGVuKGxldHRlcnMpCgogICAgZm9yIGRpZ2l0c19zZWxlY3Rpb24gaW4gaXRlcnRvb2xzLmNvbWJpbmF0aW9ucyhyYW5nZSgxMCksIGxlbihsZXR0ZXJzKSk6CiAgICAgICAgZm9yIGRpZ2l0c19wZXJtdXRhdGlvbiBpbiBpdGVydG9vbHMucGVybXV0YXRpb25zKGRpZ2l0c19zZWxlY3Rpb24pOgogICAgICAgICAgICAjIG1ha2UgZGljdDogbGV0dGVyIC0tPiBkaWdpdCAoaW50KQogICAgICAgICAgICB2YXJpYW50ID0gZGljdCh6aXAobGV0dGVycywgZGlnaXRzX3Blcm11dGF0aW9uKSkKICAgICAgICAgICAgaXNfYWxsb3dlZCA9IFRydWUKICAgICAgICAgICAgZm9yIHdvcmQgaW4gYWxsX3dvcmRzOgogICAgICAgICAgICAgICAgaWYgdmFyaWFudFt3b3JkWzBdXSA9PSAwOgogICAgICAgICAgICAgICAgICAgIGlzX2FsbG93ZWQgPSBGYWxzZQoKICAgICAgICAgICAgaWYgaXNfYWxsb3dlZDoKICAgICAgICAgICAgICAgIHdvcmRzX3N1bSA9IDAKICAgICAgICAgICAgICAgIGZvciByZXZfd29yZCBpbiByZXZfd29yZHM6CiAgICAgICAgICAgICAgICAgICAgd29yZHNfc3VtICs9IGNhbGNfd29yZF92YWx1ZShyZXZfd29yZCwgdmFyaWFudCkKCiAgICAgICAgICAgICAgICBhbnN3ZXJfc3VtID0gY2FsY193b3JkX3ZhbHVlKHJldl9hbnN3ZXIsIHZhcmlhbnQpCiAgICAgICAgICAgICAgICBpZiB3b3Jkc19zdW0gPT0gYW5zd2VyX3N1bToKICAgICAgICAgICAgICAgICAgICBwcmludF9yZXN1bHQod29yZHMsIGFuc3dlciwgdmFyaWFudCkKICAgICAgICAgICAgICAgICAgICBpZiBub3QgZnVsbF9zZWFyY2g6CiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybgoKCmZ1bGxfc2VhcmNoID0gRmFsc2UKCnNvbHZlcigic2VuZCArIG1vcmUgPSBtb25leSIsIGZ1bGxfc2VhcmNoPWZ1bGxfc2VhcmNoKQpzb2x2ZXIoIlpFUk9FUyArIE9ORVMgPSBCSU5BUlkiLCBmdWxsX3NlYXJjaD1mdWxsX3NlYXJjaCkKc29sdmVyKCJDT1VQTEUgKyBDT1VQTEUgPSBRVUFSVEVUIiwgZnVsbF9zZWFyY2g9ZnVsbF9zZWFyY2gpCnNvbHZlcigiRE8gKyBZT1UgKyBGRUVMID0gTFVDS1kiLCBmdWxsX3NlYXJjaD1mdWxsX3NlYXJjaCkKc29sdmVyKCJFTEVWRU4gKyBOSU5FICsgRklWRSArIEZJVkUgPSBUSElSVFkiLCBmdWxsX3NlYXJjaD1mdWxsX3NlYXJjaCk=