Le but de ce défi est d'écrire un programme capable de deviner un mot dans le plus petit nombre possible de tentatives. Il est basé sur le concept de l'émission Lingo TV ( http://en.wikipedia.org/wiki/Lingo_(US_game_show) ).
Règles
Étant donné une longueur de mot passée comme premier argument sur sa ligne de commande, le programme du joueur dispose de cinq tentatives pour deviner le mot en écrivant une supposition sur sa sortie standard suivie d'un seul \n
caractère.
Après avoir fait une supposition, le programme reçoit une chaîne sur son entrée standard, également suivie d'un seul \n
caractère.
La chaîne a la même longueur que le mot à deviner et se compose d'une séquence des caractères suivants:
X
: ce qui signifie que la lettre donnée n'est pas présente dans le mot à deviner?
: ce qui signifie que la lettre donnée est présente dans le mot à deviner, mais à un autre endroitO
: ce qui signifie que la lettre à cet endroit a été correctement devinée
Par exemple, si le mot à deviner est dents
, et le programme envoie le mot dozes
, il recevra OXX?O
parce que d
et s
sont corrects, e
est mal placé et o
et z
n'est pas présent.
Attention, si une lettre est présente plus de fois dans la tentative de devinette que dans le mot à deviner, elle ne sera pas marquée comme ?
et O
plus de fois que le nombre d'occurrences de la lettre dans le mot à deviner. Par exemple, si le mot à deviner est cozies
et que le programme envoie tosses
, il recevra XOXXOO
car il n'y en a qu'un s
à localiser.
Les mots sont choisis dans une liste de mots anglais. Si le mot envoyé par le programme n'est pas un mot valide de la bonne longueur, la tentative est considérée comme un échec automatique et seuls X
sont renvoyés.
Le programme du lecteur doit supposer qu'un fichier nommé wordlist.txt
et contenant un mot par ligne est présent dans le répertoire de travail actuel et peut être lu si nécessaire.
Les suppositions ne doivent être composées que de caractères alphabétiques en minuscules ( [a-z]
).
Aucune autre opération réseau ou fichier n'est autorisée pour le programme.
Le jeu se termine lorsqu'une chaîne composée uniquement de O
est retournée ou après que le programme a effectué 5 tentatives et n'a pas pu deviner le mot.
Notation
Le score d'un match est donné par la formule donnée:
score = 100 * (6 - number_of_attempts)
Donc, si le mot est correctement deviné au premier essai, 500 points sont accordés. Le dernier essai vaut 100 points.
Ne pas deviner le mot n'accorde aucun point.
La fosse
Les programmes des joueurs seront évalués en essayant de leur faire deviner 100 mots aléatoires pour chaque longueur de mot comprise entre 4 et 13 caractères inclusivement.
La sélection aléatoire des mots sera effectuée à l'avance de sorte que toutes les entrées devront deviner les mêmes mots.
Le programme gagnant et la réponse acceptée seront ceux qui atteindront le score le plus élevé.
Les programmes seront exécutés sur une machine virtuelle Ubuntu, en utilisant le code à https://github.com/noirotm/lingo . Les implémentations dans n'importe quel langage sont acceptées tant que des instructions raisonnables pour les compiler et / ou les exécuter sont fournies.
Je fournis quelques implémentations de test en ruby dans le référentiel git, n'hésitez pas à vous en inspirer.
Cette question sera périodiquement mise à jour avec des classements pour les réponses publiées afin que les challengers puissent améliorer leurs entrées.
L'évaluation finale officielle aura lieu le 1er juillet .
Mise à jour
Les entrées peuvent désormais supposer la présence de wordlistN.txt
fichiers pour accélérer la lecture de la liste de mots pour la longueur de mot actuelle pour N entre 4 et 13 inclusivement.
Par exemple, il existe un wordlist4.txt
fichier contenant les quatre mots et wordlist10.txt
contenant les dix mots, etc.
Résultats du premier tour
À la date du 01/07/2014, trois entrées ont été soumises, avec les résultats suivants:
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)
Toutes les entrées ont fonctionné de manière cohérente, avec un gagnant clair, étant l'entrée C ++ de @ edc65.
Tous les candidats sont assez impressionnants. Jusqu'à présent, je n'ai même pas pu battre @ chinese-perl-goth.
Si plus d'entrées sont soumises, une autre évaluation aura lieu. Les entrées actuelles peuvent également être améliorées si vous sentez que vous pouvez faire mieux.
la source
Réponses:
C ++ 267700 points
Un portage à partir d'un ancien moteur MasterMind.
Différences avec MasterMind:
L'idée de base est de choisir le mot qui minimise l'espace de la solution. L'algorithme est vraiment lent pour la première supposition (je veux dire «jours»), mais la meilleure première supposition dépend uniquement du mot len, il est donc codé en dur dans la source. Les autres suppositions se font en quelques secondes.
Le code
(Compiler avec g ++ -O3)
Mes scores
Évaluation avec jargon, 100 tours:
Total 265'100
Scores auto-évalués
Voici mes points moyens, marqués sur toute la liste de mots. Pas complètement fiable car certains détails de l'algorithme ont changé pendant les tests.
D'après ces chiffres, mon score moyen devrait être proche de 257'800
PIT SCORE
Enfin, j'ai installé Ruby, j'ai maintenant un score «officiel»:
la source
Java, 249700 points (bat Chinese Perl Goth dans mon test)
Liste de classement mise à jour:
Voici l' ancienne liste de classement utilisant
pit.rb
:Par rapport à @chineseperlgoth, je perds en mots plus courts (<6 caractères) mais je gagne en mots plus longs (> = 6 caractères).L'idée est similaire à @chineseperlgoth, c'est juste que mon idée principale est de trouver la supposition (peut être n'importe quel mot de la même longueur, pas nécessairement l'une des possibilités restantes) qui donne le plus d'informations pour la prochaine supposition.
Actuellement, je joue toujours avec la formule, mais pour le tableau de bord ci-dessus, je choisis le mot qui donnera le minimum pour:La dernière version utilise des scores différents pour trouver la meilleure estimation suivante, qui maximise le nombre de "possibilités uniques" après la supposition actuelle. Pour ce faire, essayez tous les mots de la liste de mots élagués (pour gagner du temps) sur tous les candidats possibles et voyez quelle hypothèse est la plus susceptible de produire une "possibilité unique" (c'est-à-dire qu'après cette supposition, il n'y aura qu'une seule réponse possible) pour le prochaine supposition.
Ainsi, par exemple, cette course:
Des trois premières suppositions, nous avons déjà obtenu "* oo * s" avec un "n" quelque part et nous devons encore trouver une autre lettre. Maintenant, la beauté de cet algorithme est qu'au lieu de deviner des mots similaires à cette forme, il devine plutôt le mot qui n'a aucun rapport avec les suppositions précédentes, essayant de donner plus de lettres, révélant, espérons-le, la lettre manquante. Dans ce cas, il arrive également d'obtenir correctement la position du "b" manquant et se termine par la bonne estimation finale "boons".
Voici le code:
la source
Perl
Il y a encore de la place pour des améliorations mais au moins ça bat les joueurs inclus dans l'exemple :)
Suppose un accès en écriture au répertoire actuel pour la mise en cache des listes de mots (pour le faire fonctionner un peu plus rapidement); va créer des
wordlist.lenN.stor
fichiers en utilisantStorable
. S'il s'agit d'un problème, supprimezread_cached_wordlist
et modifiez le code à utiliser uniquementread_wordlist
.Explication
Tout d'abord, je construis un histogramme des fréquences des lettres dans tous les mots de la liste de mots actuelle (
build_histogram
). Ensuite, je dois choisir ma prochaine supposition - ce qui est fait parfind_best_word
. L'algorithme de notation ajoute simplement les valeurs de l'histogramme ensemble, en ignorant les lettres déjà vues. Cela me donne un mot contenant les lettres les plus fréquentes dans la liste de mots. S'il y a plus d'un mot avec un score donné, j'en choisis un au hasard. Après avoir trouvé un mot, je l'envoie au moteur de jeu, lis la réponse puis essaie de faire quelque chose d'utile :)Je maintiens un ensemble de conditions, c'est-à-dire des lettres qui peuvent apparaître à une position donnée dans un mot. Au début, cela est simple
(['a'..'z'] x $len)
, mais il est mis à jour en fonction des conseils donnés en réponse (voirupdate_conds
). Je construis ensuite une expression régulière à partir de ces conditions et je filtre la liste de mots à travers elle.Au cours des tests, j'ai découvert que le filtrage susmentionné ne gère pas
?
trop bien, d'où le deuxième filtre (filter_wordlist_by_reply
). Cela profite du fait qu'une lettre marquée comme?
apparaît dans le mot à une position différente et filtre la liste de mots en conséquence.Ces étapes sont répétées pour chaque itération de la boucle principale, à moins que la solution ne soit trouvée (ou qu'il ne soit plus possible de lire depuis stdin, ce qui signifie un échec).
Code
la source