Advertisement
Guest User

Hangman

a guest
Jan 9th, 2020
2,311
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.66 KB | None | 0 0
  1. from random import choice
  2. from bs4 import BeautifulSoup
  3. from string import ascii_letters
  4. import requests, sys, webbrowser
  5.  
  6.  
  7. class Hangman:
  8.  
  9.     def __init__(self, *words):
  10.         """A text-based game of the all-time classic, Hangman. Can you save the man from the gallows?
  11.  
  12.        :param words:
  13.            Word(s) to be used for the game. String(s) can be passed directly as parameter(s) or in a list or
  14.            in a tuple. A word is chosen by random and assigned to 'self.word', which is then used for the
  15.            player to guess in a game of a hangman.
  16.        """
  17.         while isinstance(words, list) or isinstance(words, tuple):
  18.             words = choice(words)
  19.         if not isinstance(words, str):
  20.             raise TypeError(f"Expected 'str' type. Got {type(words)} instead.")
  21.         self.word = words.lower()
  22.         self.guesses = set(' ')
  23.         self.attempts = 8
  24.         self.center = num if (calc := len(self.word) * 2 - 1) < (num := 15 + len(self.word)) else calc
  25.  
  26.     def play(self):
  27.         """Initiate a game of hangman.
  28.        """
  29.         self.draw_hangman()
  30.         while not set(self.word).issubset(self.guesses):
  31.             if (guess := self.get_letter()) in set(self.word):
  32.                 self.success_msg(guess)
  33.                 continue
  34.             if self.failed_msg(guess):
  35.                 return
  36.         print(f"{'YOU WIN':^{self.center}}\n{'You guessed ' + self.word.upper():^{self.center}}\n")
  37.  
  38.     def get_letter(self):
  39.         """Return a single letter from the player's input. Also checks if the input is unique from previous inputs.
  40.        """
  41.         while True:
  42.             if (guess := input('Enter a letter: ').lower()).startswith('/'):
  43.                 self.commands(guess[1:])
  44.                 return ' '
  45.             if len(guess) > 1 or not guess.isalpha():
  46.                 print("Please enter one letter.")
  47.                 continue
  48.             if guess in self.guesses:
  49.                 print(f"You already entered '{guess}'. Please enter another letter.")
  50.                 continue
  51.             return guess
  52.  
  53.     def commands(self, command):
  54.         """User commands that can alter the games state.
  55.  
  56.        :reset: Resets attempts to default value(8).
  57.        :inc  : Increment attempts by 1.
  58.        :dec  : Decrement attempts by 1.
  59.        :free : Allows the player to enter any amount letters without decrementing attempts.
  60.        :show : Reveals the answer.
  61.        :solve: Automatically win the game.
  62.        :quit : Quit the game.
  63.        """
  64.         cmds = {
  65.             'reset': lambda: (setattr(self, 'attempts', 8), print(f"Attempts reset back to {self.attempts}.")),
  66.             'inc'  : lambda: (setattr(self, 'attempts', self.attempts + 1), print('Attempts incremented by 1.')),
  67.             'dec'  : lambda: (setattr(self, 'attempts', self.attempts - 1), print('Attempts decremented by 1.')),
  68.             'free' : lambda: self.guesses.update(input('Enter any letter(s): ')),
  69.             'show' : lambda: print(self.word),
  70.             'solve': lambda: self.guesses.update(self.word),
  71.             'quit' : lambda: sys.exit(0)
  72.         }
  73.  
  74.         try:
  75.              cmds[command]()
  76.         except KeyError:
  77.             print('Unknown command.')
  78.  
  79.     def success_msg(self, guess):
  80.         """Display a success message along with the current progress of the game. Also updates set for correct guesses.
  81.        """
  82.         self.guesses.update(guess)
  83.         print(f"\n{'SUCCESS!':^{self.center}}")
  84.         self.draw_hangman()
  85.  
  86.     def failed_msg(self, guess):
  87.         """Display a failed message along with the current progress of the game. Also updates set for incorrect guesses
  88.        and subtracts one from attempts
  89.  
  90.        :returns:
  91.            Returns True if the player still has attempts. Otherwise, returns False.
  92.        """
  93.         self.attempts -= 1
  94.         self.guesses.update(guess)
  95.         print(f"\n{'FAILED!':^{self.center}}")
  96.         self.draw_hangman()
  97.         if self.attempts < 1:
  98.             print(f"{'GAME OVER':^{self.center}}\n{'The answer was ' + self.word.upper():^{self.center}}\n")
  99.             self.attempts = 8
  100.             return True
  101.         return False
  102.  
  103.     def draw_hangman(self):
  104.         """Displays the number of attempts remaining and draws the hangman based on the attempts the player has left.
  105.        Also shows the player's progress on what letters they've guessed correctly.
  106.        """
  107.         print(f"{str(self.attempts) + ' Attempts left':^{self.center}}")
  108.  
  109.         # Displays hangman board when these strings are joined with a new line separator. i.e. '\n'.join(_hangman)
  110.         _hangman = ['  _____ ', '  |   | ', '      | ', '      | ', '      | ', '     _|_']
  111.  
  112.         # Modifies the hangman board based on player attempts.
  113.         if self.attempts < 8: _hangman[2] = f"{_hangman[2][:2]}O{_hangman[2][3:]}"   # Draw head
  114.         if self.attempts < 7: _hangman[3] = f"{_hangman[3][:1]}/{_hangman[3][2:]}"   # Draw left arm
  115.         if self.attempts < 6: _hangman[3] = f"{_hangman[3][:2]}|{_hangman[3][3:]}"   # Draw torso
  116.         if self.attempts < 5: _hangman[3] = f"{_hangman[3][:3]}\\{_hangman[3][4:]}"  # Draw right arm
  117.         if self.attempts < 4: _hangman[4] = f"{_hangman[4][:0]}_{_hangman[4][1:]}"   # Draw left foot
  118.         if self.attempts < 3: _hangman[4] = f"{_hangman[4][:1]}/{_hangman[4][2:]}"   # Draw left leg
  119.         if self.attempts < 2: _hangman[4] = f"{_hangman[4][:2]}'\\{_hangman[4][4:]}" # Draw right leg
  120.         if self.attempts < 1: _hangman[4] = f"{_hangman[4][:4]}_{_hangman[4][5:]}"   # Draw right foot
  121.  
  122.         print(*[f"{section:^{self.center}}" for section in _hangman], sep='\n')
  123.         print(f"{' '.join([x.upper() if x in self.guesses else '_' for x in self.word]):^{self.center}}", '\n')
  124.  
  125.  
  126. class Comment:
  127.  
  128.     def __init__(self):
  129.         """Module used for grabbing random comments from pornhub's comment section.
  130.        """
  131.         self.url = 'https://www.pornhub.com/'
  132.         data = requests.get(self.url)
  133.         self.soup = BeautifulSoup(data.text, 'lxml')
  134.  
  135.     def get_rand_video(self):
  136.         """Returns url of a random video from the hot section
  137.        """
  138.         hot_section = self.soup.find('ul', id='hotVideosSection')
  139.         return self.url[:-1] + choice([section.div.div.a['href'] for section in hot_section.find_all(
  140.             'li', class_='js-pop videoblock videoBox')])
  141.  
  142.     def get_rand_comment(self, url=None, comment_len=(1, 50)):
  143.         """Returns a random comment from a random video and removes any non-alphabetical characters.
  144.  
  145.        :param url:
  146.            Link to a video source on pornhub.
  147.        :param comment_len:
  148.            A set of numbers to determine the length of a comment from minimum length to max length.
  149.        :return:
  150.            random comment parsed from the comment section of a video.
  151.        """
  152.         _min, _max = comment_len
  153.         if _min > _max:
  154.             raise ValueError('Minimum length cannot exceed Max length for the parameter, comment_len.')
  155.  
  156.         while True:
  157.             if url is None:
  158.                 url = self.get_rand_video()
  159.             data = requests.get(url)
  160.             soup = BeautifulSoup(data.text, 'lxml')
  161.             filtered_comments = [com for comment in soup.find_all('div', class_='commentMessage')
  162.                         if (com:=filter_comment(comment.span.text, comment_len)) is not None]
  163.  
  164.             if not filtered_comments:
  165.                 print(f"Could not find a comment within the range of {_min} and {_max} for this video.\n If this prompt "
  166.                       f"consists, consider widening the range for the '{Comment.get_rand_comment.__code__.co_varnames[2]}' "
  167.                       f"parameter in the module, '{Comment.__name__}'")
  168.                 if ask_player('Would you like to find another video? '):
  169.                     continue
  170.                 return
  171.             return choice(filtered_comments)
  172.  
  173.  
  174. def filter_comment(comment='', comment_len=()):
  175.     """Strips a comment of all it's dignity.
  176.  
  177.    :param comment:
  178.        A comment to be filtered from all non-alphabetical characters
  179.    :param comment_len:
  180.        A range from a set of numbers in a tuple or a list, which determines the length of the comment after
  181.        it has been filtered.
  182.    :return:
  183.        Return the filtered comment if it's within the given range in comment_len. Otherwise, return None.
  184.    """
  185.     _min, _max = comment_len
  186.     filtered_comment = ''.join([char for char in comment if char in ascii_letters + ' ']).strip()
  187.     if _min < len(filtered_comment) < _max:
  188.         return filtered_comment
  189.  
  190.  
  191. def ask_player(prompt):
  192.     """Ask the player if he/she would like to play.
  193.  
  194.    :param prompt:
  195.        The message to be prompted to the user for questioning.
  196.    :returns: Boolean
  197.    """
  198.     while True:
  199.         if (play := input(f"{prompt} [Y/N]: ").lower()) in ['yes', 'y', '1']:
  200.             return True
  201.         if play in ['no', 'n', '2']:
  202.             return False
  203.         print('Invalid input. Please try again.')
  204.  
  205.  
  206. def main(prompt_vid=False):
  207.     """Loops a game of a hangman.
  208.  
  209.    :param prompt_vid:
  210.        If True, ask the player to open the related video to the default web browser after a game.
  211.    :return: None
  212.    """
  213.     getter = Comment()
  214.     while ask_player("Would you like to play Hangman?"):
  215.         print('Fetching Comment...')
  216.         random_video = getter.get_rand_video()
  217.         rand_comment = getter.get_rand_comment(random_video, (5, 50))
  218.         Hangman(rand_comment).play()
  219.         if prompt_vid:
  220.             if ask_player('Would you like to open the video in your web browser?'):
  221.                 webbrowser.open(random_video)
  222.     sys.exit(0)
  223.  
  224.  
  225. if __name__ == '__main__':
  226.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement