CentraleSupélec LMF, UMR CNRS 9021
Département informatique Laboratoire Méthodes Formelles
Bât Breguet, 3 rue Joliot-Curie Bât 650 Ada Lovelace, Université Paris Sud
91190 Gif-sur-Yvette, France Rue Noetzlin, 91190 Gif-sur-Yvette, France
TD n°12 SIP 2019

Éléments de corrigé

Fichier de test test_hang.py

import unittest

from hangman import print_check
from hangman import check_guess
from hangman import get_guessed_word
from hangman import check_guessed_word
from hangman import calc_score

class TestHangman (unittest.TestCase) :
  def test_check_word(self):
    self.assertTrue(print_check('python', 'yponth'))
    self.assertTrue(print_check('', ''))
    self.assertTrue(print_check('', 'c'))
    self.assertFalse(print_check('c', ''))
    self.assertTrue(print_check('python', 'yponths'))
    self.assertFalse(print_check('pythons', 'yponth'))

  def test_update_turns(self):
    self.assertEqual(check_guess('p', 'python', 9), (True, 9))
    self.assertEqual(check_guess('a', 'python', 9), (False, 8))
    self.assertEqual(check_guess('', '', 9), (True, 9))
    self.assertEqual(check_guess('', 'c', 9), (True, 9))
    self.assertEqual(check_guess('a', '', 9), (False, 8))

  def test_get_guessed_word(self):
    self.assertEqual(get_guessed_word('', ''),'')
    self.assertEqual(get_guessed_word('', 'c'),'')
    self.assertEqual(get_guessed_word('centralesupelec', ''),'***************')
    self.assertEqual(get_guessed_word('centralesupelec', 'c'),'c*************c')
    self.assertEqual(get_guessed_word('centralesupelec', 'cs'),'c*******s*****c')
    self.assertEqual(get_guessed_word('centralesupelec', 'centralsup'),'centralesupelec')
    self.assertEqual(get_guessed_word('centralesupelec', 'supcentral'),'centralesupelec')

  def test_check_guessed_word(self):
    self.assertTrue(check_guessed_word('', ''))
    self.assertTrue(check_guessed_word('', 'c'))
    self.assertFalse(check_guessed_word('centralesupelec', ''))
    self.assertFalse(check_guessed_word('centralesupelec', 'cs'))
    self.assertTrue(check_guessed_word('centralesupelec', 'centralsup'))
    self.assertTrue(check_guessed_word('centralesupelec', 'supcentral'))

  def test_calc_score(self):
      self.assertEqual(calc_score('', 0), 0)
      self.assertEqual(calc_score('guess', 4), 9)
      self.assertEqual(calc_score('guess', 8), 5)

if __name__ == '__main__':
  unittest.main()

Fichier hangman.py

import random

def raw_version() :
  AA = ("python", "information", "systems", "programming", "centralesupelec")
  a = random.choice(AA)
  b = ''
  c = 10
  while c > 0:
    d = 0
    for e in a:
      if e in b:
        print(e)
      else:
        print("*")
        d += 1
    if d == 0:
      print("You won")
      break
    f = input("guess a character:")
    b += f
    if f not in a:
      c -= 1
      print("Wrong, you have ", + c, "more guesses")
      if c == 0:
        print("You Lost, the word was: " + a)

def clean_version() :
  words = ("python", "information", "systems", "programming", "centralesupelec")
  word = random.choice(words)
  guessed = ''
  rem_turns = 10
  while rem_turns > 0:
    nb_missing = 0
    for letter in word:
      if letter in guessed:
        print(letter)
      else:
        print("*")
        nb_missing += 1
    if nb_missing == 0:
      print("You won")
      break
    guess = input("guess a character:")
    if guess not in word:
      rem_turns -= 1
      if rem_turns == 0:
        print("Wrong guess, you lose, the word was: " + word)
      else:
        print("Wrong, you have ", + rem_turns, "more guesses")
    else :
      guessed += guess

# Structured version
def print_check(word, guessed) :
  wins = True
  for letter in word:
    if letter in guessed:
      print(letter)
    else:
      print("*")
      wins = False
  return wins

def check_guess(guess, word, turns) :
  good_guess = True
  if guess not in word :
    turns -= 1
    good_guess = False
    if turns == 0:
      print("Wrong guess, you lose, the word was: " + word)
    else:
      print("Wrong, you have ", + turns, "more guesses")
  return (good_guess, turns)

def simple_game(word, turns) :
  guessed = ''
  rem_turns = turns
  while rem_turns > 0:
    if print_check(word, guessed) :
      print("You win!")
      break
    guess = input("guess a character:")
    (good_guess, rem_turns) = check_guess(guess, word, rem_turns)
    if good_guess:
      guessed += guess

# simple_game(random.choice(("python", "information", "systems", "programming", "centralesupelec")), 9)

# Full version
# Ex 5
def get_guessed_word(secret_word, letters_guessed):
  masked_word = ""
  for l in secret_word :
    if l in letters_guessed :
      masked_word += l
    else:
      masked_word += '*'
  return masked_word

# Ex 6
def check_guessed_word(secret_word, letters_guessed) :
  return "*" not in get_guessed_word(secret_word, letters_guessed)

# Ex 7
def word_game(secret_word, max_guesses) :
  letters_guessed = ''
  available_letters = [chr(i) for i in range(ord('a'), ord('z') + 1)]
  num_guesses = 0
  while not check_guessed_word(secret_word, letters_guessed) \
    and num_guesses < max_guesses :
    print(max_guesses - num_guesses, " guesses left")
    print("available letters: ", available_letters)
#    guess = input("Your guess: ")
    guess = validate_input(input("Your guess: "))
    num_guesses += 1
    if guess in available_letters :
      available_letters.remove(guess)
      if guess in secret_word :
        letters_guessed += guess
        print("Good: ", get_guessed_word(secret_word, letters_guessed))
      else:
        print("Sorry: ", get_guessed_word(secret_word, letters_guessed))
    else:
      print("Be careful, just just wasted a guess on an already tested letter!")

  if check_guessed_word(secret_word, letters_guessed) :
    return num_guesses
  else:
    return -1

# Ex 8
def calc_max_guesses(secret_word) :
  # Allow two guesses for each distinct letter
  return len(set(secret_word)) * 2

scrabble_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
}

def calc_score(secret_word, num_guesses, letter_values = scrabble_values) :
  score = 0
  for l in set(secret_word):
    score += letter_values[l]
  score += calc_max_guesses(secret_word) - num_guesses
  return score


def real_word_game(secret_word) :
  n = word_game(secret_word, calc_max_guesses(secret_word))
  if n < 0 :
    print("You lose.")
  else:
    print("You win with score: ", calc_score(secret_word, n))

def validate_input(l) :
  if l.isalpha() :
    return l
  else:
    return validate_input(input("# Invalid input, please type only letters: "))

# Ex 10
def guess_whole_word(secret_word):
  guess = validate_input(input("guess a word: "))
  if guess == secret_word :
    return 0 # As if guessed in no turn
  else:
    return -1

def word_game_w(secret_word, max_guesses) :
  letters_guessed = ''
  available_letters = [chr(i) for i in range(ord('a'), ord('z') + 1)]
  num_guesses = 0
  while not check_guessed_word(secret_word, letters_guessed) \
    and num_guesses < max_guesses :
    print(max_guesses - num_guesses, " guesses left")
    print("available letters: ", available_letters)
    guess = input("Your guess, or * for guessing the whole word: ")
    if guess == "*":
      return guess_whole_word(secret_word)
    guess = validate_input(guess)
    num_guesses += 1
    if guess in available_letters :
      available_letters.remove(guess)
      if guess in secret_word :
        letters_guessed += guess
        print("Good: ", get_guessed_word(secret_word, letters_guessed))
      else:
        print("Sorry: ", get_guessed_word(secret_word, letters_guessed))
    else:
      print("Be careful, just just wasted a guess on an already tested letter!")

  if check_guessed_word(secret_word, letters_guessed) :
    return num_guesses
  else:
    return -1

def real_word_game_w(secret_word) :
  n = word_game_w(secret_word, calc_max_guesses(secret_word))
  if n < 0 :
    print("You lose.")
  else:
    print("You win with score: ", calc_score(secret_word, n))

# Ex 11
def random_word_game(secret_word, max_guesses) :
  letters_guessed = ''
  available_letters = [chr(i) for i in range(ord('a'), ord('z') + 1)]
  num_guesses = 0
  while not check_guessed_word(secret_word, letters_guessed) \
    and num_guesses < max_guesses :
    print(max_guesses - num_guesses, " guesses left")
    print("available letters: ", available_letters)
    guess = random.choice(available_letters)
    num_guesses += 1
    available_letters.remove(guess)
    if guess in secret_word :
      letters_guessed += guess
      print(guess, " is good: ", get_guessed_word(secret_word, letters_guessed))
    else:
      print(guess, " is not in the word: ", get_guessed_word(secret_word, letters_guessed))

  if check_guessed_word(secret_word, letters_guessed) :
    return num_guesses
  else:
    return -1

# Ex 12
def match(word, guess_pattern, letters_tried):
  """
  Check if a word is a possible solution according to the guess pattern
  and the letters that have already been tried.
  :param word: the word to test
  :param guess_pattern: the guess pattern, which is the secret word with '*' in place of not yet guessed letters
  :param letters_tried: the letters that have already been tried to guess the secret word
  :return: True if the word can be the secret word, else False
  """
  if len(word) != len(guess_pattern) :
    # If the word does not have the right length, we can reject it
    return False
  for i in range(len(guess_pattern)) :
    # Check if the word matches the current guess (with '*' in place of not yet guessed characters)
    if guess_pattern[i] != "*" and guess_pattern[i] != word[i] :
      return False
    # Ignore words that contains already tried letters which are not in the pattern
    if not word[i] in guess_pattern and word[i] in letters_tried :
      return False
  return True

def probabilities(guess_pattern, letters_tried, dic):
  # Filter out words that cannot be the secret one
  possible_words = [word for word in dic if match(word, guess_pattern, letters_tried)]
  print("# possibilities for ", guess_pattern, ": ", possible_words)
  # Count the number of occurrences of a character
  frequencies = {}
  for word in possible_words :
    for c in word :
      if c in letters_tried :
        # Ignore already characters that have already been tried
        continue
      if c in frequencies :
        frequencies[c] += 1
      else:
        frequencies[c] = 1
  print("# freq for ", guess_pattern, ": ", frequencies)
  # Get the total number of distinct characters
  total_chars = sum(frequencies.values())
  # Fill the table of probabilities
  prob = {}
  for c in frequencies.keys() :
    prob[c] = frequencies[c] / total_chars
  return prob

def ai_word_game(secret_word):
  with open("dic.txt", "r") as dict_file :
    words = dict_file.read().lower().splitlines()
    dic = [word.replace("-", "").replace("-", "") for word in words]
  letters_tried = ''
  num_guesses = 0
  max_guesses = calc_max_guesses(secret_word)

  while num_guesses < max_guesses \
    and not check_guessed_word(secret_word, letters_tried) :
    probs = probabilities(get_guessed_word(secret_word, letters_tried), letters_tried, dic)
    if len(probs) == 0 :
      # No more choices (word is not in dictionnary)
      break
    guess = max(probs, key=probs.get)
    num_guesses += 1
    letters_tried += guess
    if guess in secret_word :
      print(guess, " is good: ", get_guessed_word(secret_word, letters_tried))
    else:
      print(guess, " is not in the word: ", get_guessed_word(secret_word, letters_tried))

  if check_guessed_word(secret_word, letters_tried) :
    print("AI wins with score: ", calc_score(secret_word, num_guesses))
  else:
    print("AI loses.")