O objetivo desse desafio é escrever um programa capaz de adivinhar uma palavra no menor número possível de tentativas. É baseado no conceito do programa de TV Lingo ( http://en.wikipedia.org/wiki/Lingo_(US_game_show) ).
Regras
Dado o tamanho de uma palavra passada como o primeiro argumento em sua linha de comando, o programa do jogador realiza cinco tentativas de adivinhar a palavra escrevendo uma suposição na saída padrão seguida por um único \n
caractere.
Depois de adivinhar, o programa recebe uma string em sua entrada padrão, também seguida por um único \n
caractere.
A cadeia tem o mesmo comprimento que a palavra a adivinhar e é composta por uma sequência dos seguintes caracteres:
X
: o que significa que a letra fornecida não está presente na palavra para adivinhar?
: o que significa que a letra fornecida está presente na palavra para adivinhar, mas em outro localO
: o que significa que a letra neste local foi adivinhada corretamente
Por exemplo, se a palavra a adivinhar for dents
, e o programa a enviar dozes
, ela receberá OXX?O
porque d
e s
está correta, e
está fora de lugar o
e z
não está presente.
Tenha cuidado para que, se uma carta estiver presente mais vezes na tentativa de adivinhação do que na palavra para adivinhar, ela não será marcada como ?
e O
mais vezes que o número de ocorrências da letra na palavra para adivinhar. Por exemplo, se a palavra a adivinhar for cozies
e o programa enviar tosses
, ela será recebida XOXXOO
porque existe apenas uma s
para localizar.
As palavras são escolhidas de uma lista de palavras em inglês. Se a palavra enviada pelo programa não for uma palavra válida do tamanho correto, a tentativa será considerada uma falha automática e somente X
serão retornadas.
O programa player deve assumir que um arquivo nomeado wordlist.txt
e contendo uma palavra por linha está presente no diretório de trabalho atual e pode ser lido conforme necessário.
As suposições devem ser compostas apenas por caracteres minúsculos alfabéticos ( [a-z]
).
Nenhuma outra operação de rede ou arquivo é permitida para o programa.
O jogo termina quando uma sequência composta apenas O
é retornada ou depois que o programa fez 5 tentativas e não conseguiu adivinhar a palavra.
Pontuação
A pontuação de um jogo é dada pela fórmula dada:
score = 100 * (6 - number_of_attempts)
Portanto, se a palavra for adivinhada corretamente na primeira tentativa, são dados 500 pontos. A última tentativa vale 100 pontos.
Não adivinhar a palavra concede zero pontos.
O pit
Os programas dos jogadores serão avaliados tentando fazê-los adivinhar 100 palavras aleatórias para cada tamanho de palavra entre 4 e 13 caracteres, inclusive.
A seleção aleatória de palavras será feita com antecedência, para que todas as entradas tenham que adivinhar as mesmas palavras.
O programa vencedor e a resposta aceita serão os que alcançarem a pontuação mais alta.
Os programas serão executados em uma máquina virtual Ubuntu, usando o código em https://github.com/noirotm/lingo . As implementações em qualquer idioma são aceitas desde que sejam fornecidas instruções razoáveis para compilar e / ou executá-las.
Estou fornecendo algumas implementações de teste em ruby no repositório git, fique à vontade para se inspirar nelas.
Esta pergunta será atualizada periodicamente com as classificações das respostas publicadas, para que os desafiantes possam melhorar suas entradas.
A avaliação final oficial será realizada no dia 1º de julho .
Atualizar
Agora, as entradas podem assumir a presença de wordlistN.txt
arquivos para acelerar a leitura da lista de palavras para o tamanho atual das palavras para N entre 4 e 13, inclusive.
Por exemplo, há um wordlist4.txt
arquivo contendo todas as quatro palavras da letra e wordlist10.txt
todas as dez palavras da letra, e assim por diante.
Resultados da primeira volta
Na data de 01/07/2014 - três entradas foram enviadas, com os seguintes resultados:
4 5 6 7 8 9 10 11 12 13 Total
./chinese-perl-goth.pl 8100 12400 15700 19100 22100 25800 27900 30600 31300 33600 226600
java Lingo 10600 14600 19500 22200 25500 28100 29000 31600 32700 33500 247300
./edc65 10900 15800 22300 24300 27200 29600 31300 33900 33400 33900 262600
** Rankings **
1: ./edc65 (262600)
2: java Lingo (247300)
3: ./chinese-perl-goth.pl (226600)
Todas as entradas tiveram desempenho consistente, com um vencedor claro, sendo a entrada em C ++ da @ edc65.
Todos os participantes são incríveis. Até agora, até agora não consegui derrotar @ chinese-perl-goth.
Se mais entradas forem enviadas, outra avaliação será realizada. As entradas atuais também podem ser aprimoradas se você sentir que pode fazer melhor.
fonte
Respostas:
Pontos C ++ 267700
Uma portabilidade de um mecanismo antigo do MasterMind.
Diferenças do MasterMind:
A idéia básica é escolher a palavra que minimiza o espaço da solução. O algoritmo é realmente lento para o primeiro palpite (quero dizer 'dias'), mas o melhor primeiro palpite depende apenas da palavra len, por isso é codificado na fonte. As outras suposições são feitas em questão de segundos.
O código
(Compile com g ++ -O3)
Minhas pontuações
Avaliação com jargão, 100 rodadas:
Total 265'100
Pontuações auto-avaliadas
Aqui estão os meus pontos médios, pontuados em toda a lista de palavras. Não é totalmente confiável porque alguns detalhes do algoritmo foram alterados durante os testes.
De acordo com esses números, minha pontuação média deve estar perto de 257'800
PONTUAÇÃO DO POÇO
Por fim, instalei o Ruby, então agora tenho uma pontuação 'oficial':
fonte
Java, 249700 pontos (supera o chinês Perl Goth no meu teste)
Lista de classificação atualizada:
Aqui está o antigo ranking usando
pit.rb
:Comparado a @chineseperlgoth, perco em palavras mais curtas (<6 caracteres), mas ganho em palavras mais longas (> = 6 caracteres).A ideia é semelhante a @chineseperlgoth, mas a minha principal idéia é encontrar o palpite (pode ser qualquer palavra do mesmo tamanho, não necessariamente uma das possibilidades restantes) que fornece mais informações para o próximo palpite.
Atualmente, ainda estou jogando com a fórmula, mas para o placar acima, escolhi a palavra que produzirá o mínimo para:A versão mais recente usa pontuação diferente para encontrar a próxima melhor estimativa, o que maximiza o número de "possibilidade única" após a estimativa atual. Isso é feito tentando todas as palavras da lista de palavras removidas (para economizar tempo) em todos os possíveis candidatos e ver qual palpite é mais provável de produzir "possibilidade única" (ou seja, após esse palpite, haverá apenas uma resposta possível) para o próximo palpite.
Então, por exemplo, esta corrida:
Desde os três primeiros palpites, já temos "* oo * s" com um "n" em algum lugar e ainda precisamos descobrir mais uma letra. Agora, a beleza desse algoritmo é que, em vez de adivinhar palavras semelhantes a essa forma, ele adivinha a palavra que não tem nenhuma relação com suposições anteriores, tentando dar mais letras, revelando a letra que falta. Nesse caso, ele também obtém a posição do "b" ausente corretamente e conclui com o palpite final correto "boons".
Aqui está o código:
fonte
Perl
Ainda há espaço para melhorias, mas pelo menos ele supera os exemplos de jogadores incluídos :)
Assume acesso de gravação ao diretório atual para armazenar em cache listas de palavras (para torná-lo um pouco mais rápido); criará
wordlist.lenN.stor
arquivos usandoStorable
. Se este for um problema, removaread_cached_wordlist
e altere o código para usar apenasread_wordlist
.Explicação
Primeiro, construo um histograma de frequências de letras em todas as palavras da lista de palavras atual (
build_histogram
). Então eu preciso escolher o meu próximo palpite - o que é feito porfind_best_word
. O algoritmo de pontuação está apenas adicionando os valores do histograma, pulando as letras já vistas. Isso me dá uma palavra que contém as letras mais frequentes na lista de palavras. Se houver mais de uma palavra com uma determinada pontuação, eu escolho uma aleatoriamente. Tendo encontrado uma palavra, eu a envio ao mecanismo do jogo, leia a resposta e tente fazer algo útil com ela :)Eu mantenho um conjunto de condições, ou seja, letras que podem ocorrer em uma determinada posição em uma palavra. No início, isso é simples
(['a'..'z'] x $len)
, mas é atualizado com base nas dicas fornecidas na resposta (consulteupdate_conds
). Eu construo um regex a partir dessas condições e filtre a lista de palavras através dele.Durante os testes, descobri que a filtragem acima mencionada não lida
?
muito bem com s, daí o segundo filtro (filter_wordlist_by_reply
). Isso tira proveito do fato de que uma letra marcada como?
ocorre na palavra em uma posição diferente e filtra a lista de palavras de acordo.Essas etapas são repetidas para cada iteração do loop principal, a menos que a solução seja encontrada (ou não é mais possível ler do stdin, o que significa uma falha).
Código
fonte