1. Introdução
Esta é uma maneira de abordar esse problema sistematicamente: se você tem um algoritmo que joga bem o enforcado, pode considerar a dificuldade de cada palavra como o número de suposições erradas que seu programa aceitaria se adivinhasse essa palavra.
2. Além da estratégia da forca
Há uma ideia que está implícita em algumas das outras respostas e comentários, de que a estratégia ideal para o solucionador seria basear suas decisões na frequência das letras em inglês ou na frequência das palavras em algum corpus. Essa é uma ideia sedutora, mas não está bem certa. O solucionador se sai melhor se modelar com precisão a distribuição de palavras escolhidas pelo setter , e um setter humano pode muito bem estar escolhendo palavras com base em sua raridade ou evitar letras usadas com frequência. Por exemplo, embora E
é a letra mais frequentemente utilizados em Inglês, se o setter escolhe sempre a partir das palavras JUGFUL
, RHYTHM
, SYZYGY
, e ZYTHUM
, em seguida, um solucionador perfeito não começar por adivinhar E
!
A melhor abordagem para modelar o setter depende do contexto, mas acho que algum tipo de inferência indutiva bayesiana funcionaria bem em um contexto onde o solucionador joga muitos jogos contra o mesmo setter ou contra um grupo de setters semelhantes.
3. Um algoritmo da forca
Aqui, delinearei um solucionador que é muito bom (mas longe de ser perfeito). Ele modela o setter escolhendo palavras uniformemente de um dicionário fixo. É um algoritmo guloso : a cada estágio ele adivinha a letra que minimiza o número de erros, ou seja, palavras que não contêm o palpite. Por exemplo, se nenhuma suposição foi feita até agora, e as palavras possíveis são DEED
, DEAD
e DARE
, então:
- se você adivinhar
D
ou E
, não há erros;
- se você adivinhar
A
, há um miss ( DEED
);
- se você adivinhar
R
, há dois erros ( DEED
e DEAD
);
- se você adivinhar qualquer outra letra, há três erros.
Portanto, D
ou E
é um bom palpite nesta situação.
(Obrigado ao Coronel Panic nos comentários por apontar que as suposições corretas são gratuitas no carrasco - esqueci-me totalmente disso na minha primeira tentativa!)
4. Implementação
Esta é uma implementação desse algoritmo em Python:
from collections import defaultdict
from string import ascii_lowercase
def partition(guess, words):
"""Apply the single letter 'guess' to the sequence 'words' and return
a dictionary mapping the pattern of occurrences of 'guess' in a
word to the list of words with that pattern.
>>> words = 'deed even eyes mews peep star'.split()
>>> sorted(list(partition('e', words).items()))
[(0, ['star']), (2, ['mews']), (5, ['even', 'eyes']), (6, ['deed', 'peep'])]
"""
result = defaultdict(list)
for word in words:
key = sum(1 << i for i, letter in enumerate(word) if letter == guess)
result[key].append(word)
return result
def guess_cost(guess, words):
"""Return the cost of a guess, namely the number of words that don't
contain the guess.
>>> words = 'deed even eyes mews peep star'.split()
>>> guess_cost('e', words)
1
>>> guess_cost('s', words)
3
"""
return sum(guess not in word for word in words)
def word_guesses(words, wrong = 0, letters = ''):
"""Given the collection 'words' that match all letters guessed so far,
generate tuples (wrong, nguesses, word, guesses) where
'word' is the word that was guessed;
'guesses' is the sequence of letters guessed;
'wrong' is the number of these guesses that were wrong;
'nguesses' is len(guesses).
>>> words = 'deed even eyes heel mere peep star'.split()
>>> from pprint import pprint
>>> pprint(sorted(word_guesses(words)))
[(0, 1, 'mere', 'e'),
(0, 2, 'deed', 'ed'),
(0, 2, 'even', 'en'),
(1, 1, 'star', 'e'),
(1, 2, 'eyes', 'en'),
(1, 3, 'heel', 'edh'),
(2, 3, 'peep', 'edh')]
"""
if len(words) == 1:
yield wrong, len(letters), words[0], letters
return
best_guess = min((g for g in ascii_lowercase if g not in letters),
key = lambda g:guess_cost(g, words))
best_partition = partition(best_guess, words)
letters += best_guess
for pattern, words in best_partition.items():
for guess in word_guesses(words, wrong + (pattern == 0), letters):
yield guess
5. Resultados de exemplo
Usando essa estratégia, é possível avaliar a dificuldade de adivinhar cada palavra de uma coleção. Aqui, considero as palavras de seis letras no dicionário do meu sistema:
>>> words = [w.strip() for w in open('/usr/share/dict/words') if w.lower() == w]
>>> six_letter_words = set(w for w in words if len(w) == 6)
>>> len(six_letter_words)
15066
>>> results = sorted(word_guesses(six_letter_words))
As palavras mais fáceis de adivinhar neste dicionário (junto com a sequência de adivinhações necessárias para que o solucionador as adivinhe) são as seguintes:
>>> from pprint import pprint
>>> pprint(results[:10])
[(0, 1, 'eelery', 'e'),
(0, 2, 'coneen', 'en'),
(0, 2, 'earlet', 'er'),
(0, 2, 'earner', 'er'),
(0, 2, 'edgrew', 'er'),
(0, 2, 'eerily', 'el'),
(0, 2, 'egence', 'eg'),
(0, 2, 'eleven', 'el'),
(0, 2, 'enaena', 'en'),
(0, 2, 'ennead', 'en')]
e as palavras mais difíceis são estas:
>>> pprint(results[-10:])
[(12, 16, 'buzzer', 'eraoiutlnsmdbcfg'),
(12, 16, 'cuffer', 'eraoiutlnsmdbpgc'),
(12, 16, 'jugger', 'eraoiutlnsmdbpgh'),
(12, 16, 'pugger', 'eraoiutlnsmdbpcf'),
(12, 16, 'suddle', 'eaioulbrdcfghmnp'),
(12, 16, 'yucker', 'eraoiutlnsmdbpgc'),
(12, 16, 'zipper', 'eraoinltsdgcbpjk'),
(12, 17, 'tuzzle', 'eaioulbrdcgszmnpt'),
(13, 16, 'wuzzer', 'eraoiutlnsmdbpgc'),
(13, 17, 'wuzzle', 'eaioulbrdcgszmnpt')]
A razão de serem difíceis é porque, depois de adivinhar -UZZLE
, você ainda tem sete possibilidades restantes:
>>> ' '.join(sorted(w for w in six_letter_words if w.endswith('uzzle')))
'buzzle guzzle muzzle nuzzle puzzle tuzzle wuzzle'
6. Escolha da lista de palavras
É claro que, ao preparar listas de palavras para seus filhos, você não começaria com o dicionário do sistema do seu computador, mas sim com uma lista de palavras que acha que eles provavelmente conhecerão. Por exemplo, você pode dar uma olhada nas listas do Wikcionário das palavras mais usadas em vários corpora em inglês.
Por exemplo, entre as 1.700 palavras de seis letras nas 10.000 palavras mais comuns no Project Gutenberg em 2006 , as dez mais difíceis são estas:
[(6, 10, 'losing', 'eaoignvwch'),
(6, 10, 'monkey', 'erdstaoync'),
(6, 10, 'pulled', 'erdaioupfh'),
(6, 10, 'slaves', 'erdsacthkl'),
(6, 10, 'supper', 'eriaoubsfm'),
(6, 11, 'hunter', 'eriaoubshng'),
(6, 11, 'nought', 'eaoiustghbf'),
(6, 11, 'wounds', 'eaoiusdnhpr'),
(6, 11, 'wright', 'eaoithglrbf'),
(7, 10, 'soames', 'erdsacthkl')]
(Soames Forsyte é um personagem da Saga Forsyte de John Galsworthy ; a lista de palavras foi convertida para minúsculas, então não foi possível remover nomes próprios rapidamente.)