Python、スコア2638 2675 2676 2689 2699 2717
結果:
OXYPHENBUTAZONE for 615
MICROEARTHQUAKE for 525
FLAVOURDYNAMICS for 435
ADJUSTABILITIES for 375
PREINTERVIEWING for 360
WATERFLOODINGS for 308
EAGLE?OOD? for 100
Left-over word: E
2717
コード:
import time
from multiprocessing import Pool
start_tiles = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJKLLLLMMNNNNNNOOOOOOOOPPQRRRRRRSSSSTTTTTTUUUUVVWWXYYZ??"
start_tiles = {l: start_tiles.count(l) for l in set(start_tiles)}
values = {"A": 1,"B": 3,"C": 3,"D": 2,"E": 1,"F": 4,"G": 2,"H": 4,"I": 1,"J": 8,"K": 5,"L": 1,"M": 3,"N": 1,"O": 1,"P": 3,"Q": 10,"R": 1,"S": 1,"T": 1,"U": 1,"V": 4,"W": 4,"X": 8,"Y": 4,"Z": 10,"?": 0}
with open("sowpods.txt") as f:
full_dictionary = list(l.strip() for l in f if l.strip())
def num_wilds_needed(word, tiles):
return sum(max(0, word.count(l) - tiles[l]) for l in word)
def word_is_possible(word, tiles):
# never replace 1st letter with wild, for simplicity
if tiles[word[0]] <= 0:
return False
return num_wilds_needed(word, tiles) <= tiles['?']
def word_score(word):
return sum(values[c] for c in word) * len(word)
def final_score(words, tiles_left, print_leftover=False):
left_over_word = ""
for tile, counts in tiles_left.iteritems():
left_over_word += tile * counts
if print_leftover:
print "Left-over word: %s" % (left_over_word,)
return sum(word_score(word) for word in words) - word_score(left_over_word)
def filter_dictionary(dictionary, tiles_left, start_letters):
return [word for word in dictionary
if word[0] in start_letters and word_is_possible(word, tiles_left)]
def pick_word(next_word, start_letters, tiles_left, dictionary):
if not word_is_possible(next_word, tiles_left):
raise ValueError("Using word that is impossible: %s" % (next_word,))
next_letters = set(start_letters)
next_letters.remove(next_word[0])
next_tiles = dict(tiles_left)
for c in next_word:
next_tiles[c] -= 1
next_dictionary = filter_dictionary(dictionary, next_tiles, next_letters)
return next_letters, next_tiles, next_dictionary
class FakeResult:
def __init__(self, value):
self.value = value
def get(self, timeout=None):
return self.value
class FakePool:
def apply_async(self, f, args):
res = f(*args)
return FakeResult(res)
def proc_next_word(next_word,
start_letters, tiles_left, filtered_sorted_dictionary,
depth, picks, prefix):
score = word_score(next_word)
next_letters, next_tiles, next_dictionary = pick_word(
next_word, start_letters, tiles_left, filtered_sorted_dictionary)
if len(prefix) / 2 < 5:
print "%sDepth %d: ?, %s for %d, %d possible words left" % (
prefix, len(prefix) / 2, next_word, score, len(filtered_sorted_dictionary))
next_words, next_score = search(FakePool(), next_letters, next_tiles, next_dictionary,
depth-1, picks, prefix + " ")
if len(prefix) / 2 < 5:
print "%sDepth %d: %d, %s for %d" % (
prefix, len(prefix) / 2, score + next_score, next_word, score)
return [next_word] + next_words, score + next_score
def wildify(word, tiles_left):
# replace missing letters with wilds
while True:
for c in word:
if tiles_left[c] < word.count(c):
word = word[0] + word[1:].replace(c, '?', word.count(c) - tiles_left[c])
break
else:
break
return word
def search(pool, start_letters, tiles_left, filtered_sorted_dictionary, depth, picks, prefix=""):
if not filtered_sorted_dictionary:
# no words left - penalize for tiles left
return [], final_score([], tiles_left)
if depth == 0:
raise ValueError("Hit depth 0")
if tiles_left['?'] > 0:
# proc top few and re-calculate score based on wildcarding
best_word_candidates = [wildify(w, tiles_left) for w in filtered_sorted_dictionary[:10000]]
best_word_candidates.sort(key=word_score, reverse=True)
else:
# no wildification needed
best_word_candidates = filtered_sorted_dictionary
best_words = best_word_candidates[:picks]
if depth == 1:
# only look at 1 word since depth 0 will do nothing
best_words = [best_words[0]]
results = [pool.apply_async(proc_next_word, (next_word,
start_letters, tiles_left, filtered_sorted_dictionary,
depth, picks, prefix))
for next_word in best_words]
results = [result.get() for result in results]
return max(results, key=lambda (words, result): result)
if __name__ == "__main__":
start_letters = set("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
tiles_left = dict(start_tiles)
print "Preparing word list..."
dictionary = filter_dictionary(full_dictionary, tiles_left, start_letters)
dictionary.sort(key=word_score, reverse=True)
print "Starting search..."
pool = Pool(8)
words, _ = search(pool, start_letters, tiles_left, dictionary, 666, 5)
for word in words:
for c in word:
if tiles_left[c] <= 0:
raise ValueError("Invalid word list")
tiles_left[c] -= 1
print
print "\n".join(("%s for %s" % (word, word_score(word)) for word in words))
print final_score(words, tiles_left, True)
説明:
ツリー全体を検索する深さ優先検索picks
。各段階で最高の単語を選択します。
最初にスコアで単語リスト全体を一度ソートします。各単語を選択した後、次の反復のために、現在は使用できなくなったすべての単語をフィルターで除外し、順序を保持するため、各ステップでリストを並べ替える必要はありません。ワイルドカードに対処するために、ワイルドカードが必要になる可能性がある場合、上位10000の候補を選択し、必要に応じて欠落している文字をワイルドカードに置き換え、新しい(低い)スコアに基づいて再ソートします。
この出力は、のためであるpicks=5
と取った8m01s
私の8コアマシン上で実行します。