IA pour le jeu du pendu
This commit is contained in:
parent
ce73c3b5a3
commit
70da7ee736
5 changed files with 390 additions and 7 deletions
7
README
7
README
|
|
@ -5,12 +5,13 @@ Apprendre à programmer, sans se faire chier
|
||||||
Jeux de base
|
Jeux de base
|
||||||
------------
|
------------
|
||||||
* Devine le numéro - CODÉ
|
* Devine le numéro - CODÉ
|
||||||
* Pense à un numéro - CODÉ
|
* Pense à un numéro - CODÉ
|
||||||
* Donjon dont vous êtes le héro
|
* Simulateur de Mounty Hall - CODÉ
|
||||||
* Jeu du pendu - CODÉ
|
* Jeu du pendu - CODÉ
|
||||||
|
* IA du pendu - CODÉ
|
||||||
|
* Donjon dont vous êtes le héro
|
||||||
* Mastermind
|
* Mastermind
|
||||||
* Ordonner par permutations
|
* Ordonner par permutations
|
||||||
* Simulateur de Mounty Hall - CODÉ
|
|
||||||
|
|
||||||
Jeux de tableaux
|
Jeux de tableaux
|
||||||
----------------
|
----------------
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ def play( secret_word ):
|
||||||
""" Boucle de jeu principale, renvoie vrai si le joueur a gagné """
|
""" Boucle de jeu principale, renvoie vrai si le joueur a gagné """
|
||||||
partial_word = "_" * len(secret_word)
|
partial_word = "_" * len(secret_word)
|
||||||
fails = 0
|
fails = 0
|
||||||
used_letter = ""
|
used_letters = ""
|
||||||
while fails < len( BOARDS_PIC ):
|
while fails < len( BOARDS_PIC ):
|
||||||
display( BOARDS_PIC, partial_word, fails )
|
display( BOARDS_PIC, partial_word, fails )
|
||||||
|
|
||||||
|
|
@ -117,12 +117,12 @@ def play( secret_word ):
|
||||||
if letter == secret_word:
|
if letter == secret_word:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if letter in used_letter:
|
if letter in used_letters:
|
||||||
print("Vous avez déjà essayé les lettres suivantes :",used_letter)
|
print("Vous avez déjà essayé les lettres suivantes :",used_letters)
|
||||||
# Aller directement à l'itération suivante, sans compter les ratés.
|
# Aller directement à l'itération suivante, sans compter les ratés.
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
used_letter += letter
|
used_letters += letter
|
||||||
|
|
||||||
if letter in secret_word:
|
if letter in secret_word:
|
||||||
partial_word = process( letter, partial_word, secret_word )
|
partial_word = process( letter, partial_word, secret_word )
|
||||||
|
|
|
||||||
144
src/pendu_ordi_0.py
Normal file
144
src/pendu_ordi_0.py
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
import pendu_humain as pendu
|
||||||
|
|
||||||
|
def is_compatible( word, partial_word ):
|
||||||
|
""" Teste si un mot complet est compatible avec un mot partiel.
|
||||||
|
Par exemple :
|
||||||
|
>>> from pendu_ordi import *
|
||||||
|
>>> is_compatible("hirondelle","_a____e__e")
|
||||||
|
False
|
||||||
|
>>> is_compatible("hirondelle","______e__e")
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
if len(word) != len(partial_word):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for i in range(len(word)):
|
||||||
|
if partial_word[i] == "_":
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif word[i] != partial_word[i]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def compatible_words( words, partial_word ):
|
||||||
|
""" Filtre une liste de mots en ne gardant que les mots compatibles avec un mot partiel """
|
||||||
|
compatibles = []
|
||||||
|
for word in words:
|
||||||
|
if is_compatible( word, partial_word ):
|
||||||
|
compatibles.append( word )
|
||||||
|
|
||||||
|
return compatibles
|
||||||
|
|
||||||
|
|
||||||
|
def letters_in( words ):
|
||||||
|
letters = set()
|
||||||
|
for word in words:
|
||||||
|
for letter in word:
|
||||||
|
letters.add(letter)
|
||||||
|
return letters
|
||||||
|
|
||||||
|
|
||||||
|
def guess_letter( available_letters ):
|
||||||
|
# Enlève et retourne une lettre
|
||||||
|
return available_letters.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def guess_letter_random( available_letters ):
|
||||||
|
# Converti le set en list...
|
||||||
|
letters = list( available_letters )
|
||||||
|
# ... afin de pouvoir y tirer un élément au hasard.
|
||||||
|
letter = random.choice( letters )
|
||||||
|
# Répercuter le changement sur les lettres disponibles.
|
||||||
|
available_letters.remove( letter )
|
||||||
|
return letter
|
||||||
|
|
||||||
|
|
||||||
|
def guess_letter_random_vowels( available_letters ):
|
||||||
|
# Les accents comptent comme des lettres différentes !
|
||||||
|
vowels = set( ['a','e','i','o','u','y','é','è','à','ù','ë','ê','â','ï'] )
|
||||||
|
|
||||||
|
# Lettres présentes à la fois dans les voyelles ET les lettres disponibles
|
||||||
|
available_vowels = available_letters & vowels
|
||||||
|
|
||||||
|
# S'il y a au moins une voyelle dans les lettres disponibles
|
||||||
|
if len( available_vowels ) > 0:
|
||||||
|
# Les tirer en priorité
|
||||||
|
available_letters = available_vowels
|
||||||
|
|
||||||
|
letters = list( available_letters )
|
||||||
|
letter = random.choice( letters )
|
||||||
|
available_letters.remove( letter )
|
||||||
|
return letter
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
|
||||||
|
print("Entrez le nombre de lettres de votre mot")
|
||||||
|
word_size = int( input() )
|
||||||
|
|
||||||
|
used_letters = set()
|
||||||
|
partial_word = "_" * word_size
|
||||||
|
fails = 0
|
||||||
|
|
||||||
|
words = pendu.filter_wordsize( pendu.download_dic( "http://nojhan.net/aapssfc/data/french_dictionary.utf8" ), word_size )
|
||||||
|
|
||||||
|
while fails < len( pendu.BOARDS_PIC ):
|
||||||
|
pendu.display( pendu.BOARDS_PIC, partial_word, fails )
|
||||||
|
|
||||||
|
words = compatible_words( words, partial_word )
|
||||||
|
print(len(words),"mots compatibles")
|
||||||
|
|
||||||
|
# Construit la liste des lettres existantes dans les mots restants.
|
||||||
|
remaining_letters = letters_in( words )
|
||||||
|
|
||||||
|
# Ne garde que les lettres n'ayant pas déjà été utilisées
|
||||||
|
remaining_letters = remaining_letters - used_letters
|
||||||
|
print(len(remaining_letters),"lettres restantes")
|
||||||
|
|
||||||
|
letter = guess_letter_random_vowels( remaining_letters )
|
||||||
|
used_letters.add( letter )
|
||||||
|
print("Je pense à la lettre : «",letter,"», est-elle présente dans le mot ? [o/n]")
|
||||||
|
answer = input()
|
||||||
|
answer.lower()
|
||||||
|
|
||||||
|
if answer == "n":
|
||||||
|
fails += 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
is_correct = False
|
||||||
|
while not is_correct:
|
||||||
|
print("Entrez le nouveau mot partiel :")
|
||||||
|
new_word = input()
|
||||||
|
new_word = new_word.lower()
|
||||||
|
|
||||||
|
print("«",new_word,"», est-ce correct ? [o/n]")
|
||||||
|
answer = input()
|
||||||
|
|
||||||
|
if len(new_word) != len(partial_word):
|
||||||
|
print("Le nombre de lettres ne correspond pas !")
|
||||||
|
is_correct = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if answer.lower() == "o":
|
||||||
|
is_correct = True
|
||||||
|
|
||||||
|
# Ici, on aurait put écrire :
|
||||||
|
# continue
|
||||||
|
#else:
|
||||||
|
# break
|
||||||
|
# mais cela aurait été inutile !
|
||||||
|
|
||||||
|
partial_word = new_word
|
||||||
|
|
||||||
|
if "_" not in partial_word:
|
||||||
|
break
|
||||||
|
|
||||||
|
if fails >= len( pendu.BOARDS_PIC ):
|
||||||
|
print("J'ai perdu :-(")
|
||||||
|
else:
|
||||||
|
print("J'ai gagné :-)")
|
||||||
164
src/pendu_ordi_1.py
Normal file
164
src/pendu_ordi_1.py
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
|
||||||
|
import random
|
||||||
|
import operator
|
||||||
|
|
||||||
|
import pendu_humain as pendu
|
||||||
|
|
||||||
|
def is_compatible( word, partial_word ):
|
||||||
|
""" Teste si un mot complet est compatible avec un mot partiel.
|
||||||
|
Par exemple :
|
||||||
|
>>> from pendu_ordi import *
|
||||||
|
>>> is_compatible("hirondelle","_a____e__e")
|
||||||
|
False
|
||||||
|
>>> is_compatible("hirondelle","______e__e")
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
if len(word) != len(partial_word):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for i in range(len(word)):
|
||||||
|
if partial_word[i] == "_":
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif word[i] != partial_word[i]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def compatible_words( words, partial_word, used_letters ):
|
||||||
|
""" Filtre une liste de mots en ne gardant que les mots compatibles avec un mot partiel """
|
||||||
|
compatibles = []
|
||||||
|
for word in words:
|
||||||
|
# Ensemble des lettres utilisées dans le mot
|
||||||
|
word_letters = set(word)
|
||||||
|
# Les lettres disponibles sont celles qui ont été utilisées MOINS celles déjà devinées
|
||||||
|
false_letters = used_letters - set(partial_word.replace("_",""))
|
||||||
|
|
||||||
|
# Si le mot est compatible ET qu'aucune des lettres utilisées n'y est
|
||||||
|
if is_compatible( word, partial_word ) and len( false_letters & word_letters ) == 0:
|
||||||
|
compatibles.append( word )
|
||||||
|
|
||||||
|
return compatibles
|
||||||
|
|
||||||
|
|
||||||
|
def guess_letter_frequency( words, used_letters, verbose = True ):
|
||||||
|
""" Choisi une lettre en fonction de sa fréquence dans la liste des mots compatibles disponibles """
|
||||||
|
freqs = {}
|
||||||
|
for word in words:
|
||||||
|
for letter in word:
|
||||||
|
if letter not in used_letters:
|
||||||
|
# Si la lettre n'a pas déjà été rencontrée.
|
||||||
|
if letter not in freqs:
|
||||||
|
# Créer la clef correspondante dans le dictionnaire.
|
||||||
|
freqs[letter] = 1
|
||||||
|
else:
|
||||||
|
# Incrémenter le compteur d'occurence.
|
||||||
|
freqs[letter] += 1
|
||||||
|
|
||||||
|
best_letter = best_letter_0( freqs )
|
||||||
|
if verbose:
|
||||||
|
print("Lettre la plus probable : «",best_letter,"» avec",freqs[best_letter],"occurences")
|
||||||
|
return best_letter
|
||||||
|
|
||||||
|
|
||||||
|
def best_letter_0( freqs ):
|
||||||
|
# Première clef dans le dictionnaire
|
||||||
|
best_letter = list(freqs.keys())[0]
|
||||||
|
for letter in freqs:
|
||||||
|
if freqs[letter] > freqs[best_letter]:
|
||||||
|
best_letter = letter
|
||||||
|
return best_letter
|
||||||
|
|
||||||
|
|
||||||
|
def best_letter_1( freqs ):
|
||||||
|
# On pourrait aussi trier les clefs du dictionnaire selon la valeur des éléments associés
|
||||||
|
sorted_letters = sorted( freqs.items(), key=operator.itemgetter(1) )
|
||||||
|
# Une fois triés par ordre croissant, la lettre la plus probable est en dernière
|
||||||
|
best_letter = sorted_letters[-1][0]
|
||||||
|
return best_letter
|
||||||
|
|
||||||
|
|
||||||
|
def ask_correct():
|
||||||
|
print("Est-ce correct ? [o/n]")
|
||||||
|
answer = input()
|
||||||
|
answer = answer.lower()
|
||||||
|
|
||||||
|
if answer == 'o':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def ask_partial_word( partial_word ):
|
||||||
|
is_correct = False
|
||||||
|
while not is_correct:
|
||||||
|
print("Entrez le nouveau mot partiel :")
|
||||||
|
new_word = input()
|
||||||
|
new_word = new_word.lower()
|
||||||
|
|
||||||
|
print("«",new_word,"»")
|
||||||
|
is_correct = ask_correct()
|
||||||
|
|
||||||
|
if len(new_word) != len(partial_word):
|
||||||
|
print("Le nombre de lettres ne correspond pas !")
|
||||||
|
is_correct = False
|
||||||
|
|
||||||
|
return new_word
|
||||||
|
|
||||||
|
|
||||||
|
def play( partial_word, words ):
|
||||||
|
used_letters = set()
|
||||||
|
fails = 0
|
||||||
|
|
||||||
|
while fails < len( pendu.BOARDS_PIC ):
|
||||||
|
pendu.display( pendu.BOARDS_PIC, partial_word, fails )
|
||||||
|
|
||||||
|
words = compatible_words( words, partial_word, used_letters )
|
||||||
|
print(len(words),"mots compatibles")
|
||||||
|
|
||||||
|
# S'il ne reste qu'un mot à tester,
|
||||||
|
if len( words ) == 1:
|
||||||
|
# on le propose directement.
|
||||||
|
print("Je pense au mot «",words[0],"»")
|
||||||
|
return ask_correct()
|
||||||
|
|
||||||
|
elif len(words) == 0:
|
||||||
|
print("Je ne connais pas ce mot.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
letter = guess_letter_frequency( words, used_letters )
|
||||||
|
used_letters.add( letter )
|
||||||
|
print("Je pense à la lettre : «",letter,"»")
|
||||||
|
|
||||||
|
if ask_correct():
|
||||||
|
partial_word = ask_partial_word( partial_word )
|
||||||
|
else:
|
||||||
|
fails += 1
|
||||||
|
|
||||||
|
# Si c'est la dernière chance mais qu'il reste trop de mots à tester
|
||||||
|
if fails == len(pendu.BOARDS_PIC) and len(words) > 1:
|
||||||
|
# on tente au hasard
|
||||||
|
print("Je pense au mot «",random.choice(words),"»")
|
||||||
|
return ask_correct()
|
||||||
|
|
||||||
|
if "_" not in partial_word:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
|
||||||
|
print("Entrez votre mot secret")
|
||||||
|
word_size = len( input() )
|
||||||
|
partial_word = "_" * word_size
|
||||||
|
|
||||||
|
words = pendu.filter_wordsize( pendu.download_dic( "http://nojhan.net/aapssfc/data/french_dictionary.utf8" ), word_size )
|
||||||
|
|
||||||
|
won = play( partial_word, words )
|
||||||
|
|
||||||
|
if won:
|
||||||
|
print("J'ai gagné :-)")
|
||||||
|
else:
|
||||||
|
print("J'ai perdu :-(")
|
||||||
74
src/pendu_ordi_1_test.py
Normal file
74
src/pendu_ordi_1_test.py
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
import pendu_humain as pendu
|
||||||
|
import pendu_ordi_1 as AI
|
||||||
|
|
||||||
|
def auto_play( secret_word, words ):
|
||||||
|
used_letters = set()
|
||||||
|
fails = 0
|
||||||
|
partial_word = "_" * len(secret_word)
|
||||||
|
|
||||||
|
while fails < len( pendu.BOARDS_PIC ):
|
||||||
|
words = AI.compatible_words( words, partial_word, used_letters )
|
||||||
|
|
||||||
|
if len( words ) == 1:
|
||||||
|
return secret_word == words[0]
|
||||||
|
|
||||||
|
elif len(words) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
letter = AI.guess_letter_frequency( words, used_letters, False )
|
||||||
|
used_letters.add( letter )
|
||||||
|
|
||||||
|
if letter in secret_word:
|
||||||
|
partial_word = pendu.process( letter, partial_word, secret_word )
|
||||||
|
else:
|
||||||
|
fails += 1
|
||||||
|
|
||||||
|
if fails == len(pendu.BOARDS_PIC) and len(words) > 1:
|
||||||
|
return secret_word == random.choice(words)
|
||||||
|
|
||||||
|
if "_" not in partial_word:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
|
||||||
|
games = 100
|
||||||
|
|
||||||
|
all_words = pendu.download_dic( "http://nojhan.net/aapssfc/data/french_dictionary.utf8" )
|
||||||
|
|
||||||
|
all_played = 0
|
||||||
|
all_won = 0
|
||||||
|
lost_on = []
|
||||||
|
|
||||||
|
# Essais des mots de tailles variables
|
||||||
|
for word_size in range(1,20):
|
||||||
|
words = pendu.filter_wordsize( all_words, word_size )
|
||||||
|
print(len(words),"mots de taille",word_size,"...")
|
||||||
|
|
||||||
|
# Joue sur quelques mots tirés au hasard
|
||||||
|
played = 0
|
||||||
|
won = 0
|
||||||
|
for r in range(games):
|
||||||
|
secret_word = random.choice( words )
|
||||||
|
if auto_play( secret_word, words ):
|
||||||
|
won += 1
|
||||||
|
else:
|
||||||
|
lost_on.append( secret_word )
|
||||||
|
played += 1
|
||||||
|
|
||||||
|
print("\tvictoires :",won,"/",played)
|
||||||
|
all_won += won
|
||||||
|
all_played += played
|
||||||
|
|
||||||
|
print("Probabilité de gagner :",all_won/float(all_played))
|
||||||
|
if len(lost_on) > 0:
|
||||||
|
print("Perte sur les mots :\n",", ".join(lost_on))
|
||||||
|
|
||||||
|
# Exercice : essayer tous les mots de chaques tailles
|
||||||
|
# et estimer ainsi la probabilité réelle de gagner
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue