Advertisement
gruntfutuk

hangman_again.py

Feb 26th, 2023 (edited)
508
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.62 KB | None | 0 0
  1. from string import ascii_lowercase  # shame to restrict to ascii, your choice
  2. from io import StringIO  # to use a string like a file
  3. from random import choice  # to randomly pick a word
  4. from pathlib import Path  # to better handle files on your computer
  5. import logging  # for outputting programmer/tester info
  6. import argparse  # to take arguments from command line, to enable debugging
  7.  
  8.  
  9. HANGMAN_ASCII_ART = """Welcome to the
  10.  _    _                                        
  11. | |  | |                                        
  12. | |__| | __ _ _ __   __ _ _ __ ___   __ _ _ __        __ _  __ _ _ __ ___   ___
  13. |  __  |/ _` | '_ \ / _` | '_ ` _ \ / _` | '_ \     / _` |/ _` | '_ ` _ \ / __|
  14. | |  | | (_| | | | | (_| | | | | | | (_| | | | |    | ( | | (_| | | | | | | (__
  15. |_|  |_|\__,_|_| |_|\__, |_| |_| |_|\__,_|_| |_|     \__, |\__,_|_| |_| |_|____|
  16.                      __/ |                            __/ |
  17.                     |___/                            |___/"""
  18.  
  19. MAX_TRIES = 'You Have 6 Tries'
  20.  
  21. HANGMAN_PHOTOS = {
  22.  
  23.     1: """
  24. x-------x
  25.    """,
  26.  
  27.     2: """
  28.    x-------x
  29.    |
  30.    |
  31.    |
  32.    |
  33.    |
  34. x-------x
  35.    """,
  36.  
  37.     3: """
  38.    x-------x
  39.    |       |
  40.    |       0
  41.    |
  42.    |
  43.    |
  44. x-------x
  45.    """,
  46.  
  47.     4: """
  48.    x-------x
  49.    |       |
  50.    |       0
  51.    |       |
  52.    |
  53.    |
  54. x-------x
  55.    """,
  56.  
  57.     5: r"""
  58.    x-------x
  59.    |       |
  60.    |       0
  61.    |      /|\    
  62.    |
  63.    |
  64. x-------x
  65.    """,
  66.  
  67.     6: r"""
  68.    x-------x
  69.    |       |
  70.    |       0
  71.    |      /|\    
  72.    |      /
  73.    |
  74. x-------x
  75.    """,
  76.  
  77.     7: r"""
  78.    x-------x
  79.    |       |
  80.    |       0
  81.    |      /|\    
  82.    |      / \
  83.    |
  84. x-------x
  85.    """
  86. }
  87.  
  88. # using StringIO buffer instead of external file, use can choose to name a file
  89. BUFFER = """Alpha
  90. Beta
  91. Charlie
  92. Delta"""
  93.  
  94. def print_hangman(fails: int) -> None:
  95.     """ prints state of gallows based on number of failed guesses """
  96.     if fails == 0:
  97.         return
  98.     if fails in HANGMAN_PHOTOS:
  99.         print(HANGMAN_PHOTOS[fails])
  100.         print(f"\n{fails} bad guess{'' if fails == 1 else 'es'}\n")
  101.     else:
  102.         raise ValueError(f"Invalid number of tries, {fails}, for HANGMAN_PHOTOS")
  103.  
  104.  
  105. def get_word(file_path: Path | str) -> str:
  106.     """ pick and return random word from internal list or user supplied file """
  107.  
  108.     # added a lot here to original to give you some ideas on handling files and dealing with exceptions
  109.     # needs more testing / experimentation
  110.     # notice we decide whether to use open or StringIO function
  111.     if isinstance(file_path, Path):  # check we have a Path object
  112.         if not (file_path.exists() and file_path.is_file()):  # are we looking at a file?
  113.             logging.error(f'{file_path=} exists or is_file test failed', exc_info=True)
  114.             raise IOError(f'File, {file_path} not available')
  115.         opener = open  # so we can use open function
  116.     elif isinstance(file_path, str) and file_path:  # otherwise assume we have multine string
  117.         opener = StringIO  # so we can use StringIO function
  118.     else:  # not expecting anything other than a Path or a string
  119.         logging.error(f'{file_path=} not a Path or str test failed', exc_info=True)
  120.         raise IOError(f'File/buffer, {file_path} not available')
  121.  
  122.     logging.debug(f'trying to read {file_path=}')
  123.     try:  # let's see if this works
  124.         with opener(file_path) as file:
  125.             words = file.read().split()
  126.     except IOError:  # oops (probably some other exceptions need catching here)
  127.         logging.error(f'{file_path=} caused an error when reading', exc_info=True)
  128.         raise IOError(f'Unable to read file, {file_path}')
  129.  
  130.     word = choice(words).lower()  # pick a word at random, force it to be lowercase
  131.     logging.debug(f'{word=}')  # handy to know what was picked when testing
  132.  
  133.     return word
  134.  
  135. def guess_word(file_path: Path | str) -> str | None:
  136.     """Prompts the player to guess the secret word at the specified index in the file."""
  137.     # actually, this runction is a lot simpler now as we ask for file_path in main
  138.     try:
  139.         secret_word = get_word(file_path)
  140.     except IOError as errmsg:
  141.         print(errmsg)
  142.         return None
  143.     except OSError as errmsg:
  144.         print(errmsg)
  145.         return None
  146.     return secret_word
  147.  
  148.  
  149. def check_valid_input(letter_guessed: list[str], old_letters_guessed: list[str]) -> bool:
  150.     if len(letter_guessed) != 1:
  151.         return False
  152.     if letter_guessed not in ascii_lowercase:
  153.         return False
  154.     if letter_guessed in old_letters_guessed:  # already tried that one, maybe a little unfair
  155.         return False
  156.     return True
  157.  
  158.  
  159. def try_update_letter_guessed(letter_guessed: list[str], old_letters_guessed: list[str]) -> bool:
  160.     if check_valid_input(letter_guessed, old_letters_guessed):
  161.         old_letters_guessed.append(letter_guessed)
  162.         status = True
  163.         logging.debug(f'Added guess {letter_guessed} to guesses so far')
  164.     else:
  165.         print(f"{letter_guessed} is not a good choice")
  166.         status = False
  167.     print("Guesses so far:", ", ".join(sorted(old_letters_guessed)))
  168.     return status
  169.  
  170.  
  171. def show_hidden_word(secret_word: list[str], old_letters_guessed: list[str]) -> str:
  172.     return '\nPattern: ' + ''.join(ltr if ltr in old_letters_guessed else '_' for ltr in secret_word)
  173.  
  174. def check_win(secret_word: list[str], old_letters_guessed: list[str]) -> bool:
  175.     return set(secret_word).issubset(old_letters_guessed)
  176.  
  177.  
  178. def main() -> None:
  179.  
  180.     print(HANGMAN_ASCII_ART)
  181.     print(MAX_TRIES)
  182.     print()
  183.  
  184.     num_of_tries = 0
  185.     old_letters_guessed = []
  186.     num_of_fails = 0
  187.  
  188.     user_file_path = input('File to use: (return for built-in words) ')
  189.     if user_file_path:
  190.        file_path = Path(user_file_path)
  191.     else:
  192.         file_path = BUFFER  # internal multi-line string of words
  193.  
  194.     secret_word = guess_word(file_path)
  195.     if secret_word is None:
  196.         print('Was not able to pick a secret word')
  197.         return  # could pass an error code here
  198.  
  199.     guessed = False
  200.     while num_of_fails <= 6 and not guessed:
  201.         num_of_tries += 1
  202.         print_hangman(num_of_fails)
  203.         print(show_hidden_word(secret_word, old_letters_guessed))
  204.         letter_guessed = input(f"Guess #{num_of_tries:2}: please guess a letter (or whole word): ").strip().lower()
  205.         if letter_guessed == secret_word:  # have they jumped ahead and guessed whole word?
  206.             guessed = True
  207.         else:
  208.             valid = try_update_letter_guessed(letter_guessed, old_letters_guessed)
  209.             if not valid or letter_guessed not in secret_word:
  210.                 num_of_fails += 1
  211.             else:
  212.                 guessed = check_win(secret_word, old_letters_guessed)
  213.  
  214.     print_hangman(num_of_fails)
  215.     print(show_hidden_word(secret_word, old_letters_guessed))
  216.     if guessed:
  217.         print(f"Congratulations! You guessed the word with {num_of_tries} guesses.")
  218.     else:
  219.         print("Sorry, you ran out of tries. The word was:", secret_word)
  220.  
  221. def set_up_logging():
  222.  
  223.     # setting up argparse to use command line arguments to check debugging status
  224.     parser = argparse.ArgumentParser()
  225.     parser.add_argument(
  226.         '-d', '--debug',
  227.         help="Print lots of debugging statements",
  228.         action="store_const", dest="loglevel", const=logging.DEBUG,
  229.         default=logging.WARNING,
  230.     )
  231.     parser.add_argument(
  232.         '-v', '--verbose',
  233.         help="Be verbose",
  234.         action="store_const", dest="loglevel", const=logging.INFO,
  235.     )
  236.     args = parser.parse_args()
  237.  
  238.     logging.basicConfig(level=args.loglevel)
  239.  
  240.  
  241. if __name__ == '__main__':
  242.     set_up_logging()
  243.     main()
  244.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement