Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # 6.0001 Problem Set 3
- #
- # The 6.0001 Word Game
- # Created by: Kevin Luu <luuk> and Jenna Wiens <jwiens>
- #
- # Name : <your name>
- # Collaborators : <your collaborators>
- # Time spent : <total time>
- import math
- import random
- import cProfile
- VOWELS = ['a', 'e', 'i', 'o', 'u']
- CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
- HAND_SIZE = 7
- SCRABBLE_LETTER_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, '*': 0
- }
- # -----------------------------------
- # Helper code
- # (you don't need to understand this helper code)
- WORDLIST_FILENAME = "words.txt"
- def load_words():
- """
- Returns a list of valid words. Words are strings of lowercase letters.
- Depending on the size of the word list, this function may
- take a while to finish.
- """
- print("Loading word list from file...")
- # inFile: file
- inFile = open(WORDLIST_FILENAME, 'r')
- # wordlist: list of strings
- wordlist = []
- for line in inFile:
- wordlist.append(line.strip().lower())
- print(f"{len(wordlist)} words loaded.")
- return wordlist
- def get_frequency_dict(sequence):
- """
- Returns a dictionary where the keys are elements of the sequence
- and the values are integer counts, for the number of times that
- an element is repeated in the sequence.
- sequence: string or list
- return: dictionary
- """
- if sequence in get_frequency_dict.cache:
- return get_frequency_dict.cache[sequence]
- # freqs: dictionary (element_type -> int)
- freq = {}
- for x in sequence:
- freq[x] = freq.get(x, 0) + 1
- get_frequency_dict.cache[sequence] = freq
- return freq
- get_frequency_dict.cache = {}
- # (end of helper code)
- # -----------------------------------
- #
- # Problem #1: Scoring a word
- #
- def get_word_score(word, n):
- """
- Returns the score for a word. Assumes the word is a
- valid word.
- You may assume that the input word is always either a string of letters,
- or the empty string "". You may not assume that the string will only contain
- lowercase letters, so you will have to handle uppercase and mixed case strings
- appropriately.
- The score for a word is the product of two components:
- The first component is the sum of the points for letters in the word.
- The second component is the larger of:
- 1, or
- 7*wordlen - 3*(n-wordlen), where wordlen is the length of the word
- and n is the hand length when the word was played
- Letters are scored as in Scrabble; A is worth 1, B is
- worth 3, C is worth 3, D is worth 2, E is worth 1, and so on.
- word: string
- n: int >= 0
- returns: int >= 0
- """
- score_a = 0
- for c in word.lower():
- score_a += SCRABBLE_LETTER_VALUES[c]
- score_b = max(1, 7 * len(word) - 3 * (n - len(word)))
- return score_a * score_b
- #
- # Make sure you understand how this function works and what it does!
- #
- def display_hand(hand):
- """
- Displays the letters currently in the hand.
- For example:
- display_hand({'a':1, 'x':2, 'l':3, 'e':1})
- Should print out something like:
- a x x l l l e
- The order of the letters is unimportant.
- hand: dictionary (string -> int)
- """
- for letter in hand.keys():
- for j in range(hand[letter]):
- print(letter, end=' ') # print all on the same line
- print() # print an empty line
- #
- # Make sure you understand how this function works and what it does!
- # You will need to modify this for Problem #4.
- #
- def deal_hand(n):
- """
- Returns a random hand containing n lowercase letters.
- ceil(n/3) letters in the hand should be VOWELS (note,
- ceil(n/3) means the smallest integer not less than n/3).
- Hands are represented as dictionaries. The keys are
- letters and the values are the number of times the
- particular letter is repeated in that hand.
- n: int >= 0
- returns: dictionary (string -> int)
- """
- hand = {}
- num_vowels = int(math.ceil(n / 3)) - 1
- for i in range(num_vowels):
- x = random.choice(VOWELS)
- hand[x] = hand.get(x, 0) + 1
- for i in range(num_vowels, n):
- x = random.choice(CONSONANTS)
- hand[x] = hand.get(x, 0) + 1
- hand['*'] = 1
- return hand
- #
- # Problem #2: Update a hand by removing letters
- #
- def update_hand(hand, word):
- """
- Does NOT assume that hand contains every letter in word at least as
- many times as the letter appears in word. Letters in word that don't
- appear in hand should be ignored. Letters that appear in word more times
- than in hand should never result in a negative count; instead, set the
- count in the returned hand to 0 (or remove the letter from the
- dictionary, depending on how your code is structured).
- Updates the hand: uses up the letters in the given word
- and returns the new hand, without those letters in it.
- Has no side effects: does not modify hand.
- word: string
- hand: dictionary (string -> int)
- returns: dictionary (string -> int)
- """
- new_hand = hand.copy()
- for c in word.lower():
- if c in new_hand:
- new_hand[c] = max(0, new_hand[c] - 1)
- return new_hand
- #
- # Problem #3: Test word validity
- #
- def is_valid_word(word, hand, handlen=None):
- """ Checks that word is composable of letters/wildcards in hand"""
- # optimization
- if handlen == None:
- handlen = calculate_handlen(hand)
- if len(word) > handlen:
- return False
- word_freq = get_frequency_dict(word)
- wildcards = hand.get('*', 0)
- for c, count in word_freq.items():
- if hand.get(c, -1) < count:
- if c in VOWELS:
- wildcards -= 1
- if wildcards < 0:
- return False
- return False
- return True
- #
- # Problem #5: Playing a hand
- #
- def calculate_handlen(hand):
- """
- Returns the length (number of letters) in the current hand.
- hand: dictionary (string-> int)
- returns: integer
- """
- return sum(hand.values())
- def play_hand(hand, word_list):
- tree = {}
- handlen = calculate_handlen(hand)
- permutate_hand(hand, word_list, tree)
- best_seq, best_score = get_best_seq(tree, handlen)
- print("="*40)
- print("These are the words the computer figured out:")
- for word in best_seq:
- print(f"{word} scored {get_word_score(word, handlen)} points!")
- print(f"Total score for this hand was: {best_score}")
- print("="*40)
- return best_score
- def permutate_hand(hand, word_list, tree):
- handlen = calculate_handlen(hand)
- for word in word_list:
- if is_valid_word(word, hand, handlen):
- word_hand = update_hand(hand, word)
- tree[word] = {}
- permutate_hand(word_hand, word_list, tree[word])
- def get_best_seq(tree, handlen):
- best_seq = []
- best_score = -1
- for key, subtree in tree.items():
- seq = [key]
- score = get_word_score(key, handlen)
- if subtree:
- subseq, subscore = get_best_seq(subtree, handlen)
- seq += subseq
- score += subscore
- if score > best_score:
- best_seq = seq
- best_score = score
- return best_seq, best_score
- #
- # Problem #6: Playing a game
- #
- #
- # procedure you will use to substitute a letter in a hand
- #
- def substitute_hand(hand, letter):
- """
- Allow the user to replace all copies of one letter in the hand (chosen by user)
- with a new letter chosen from the VOWELS and CONSONANTS at random. The new letter
- should be different from user's choice, and should not be any of the letters
- already in the hand.
- If user provide a letter not in the hand, the hand should be the same.
- Has no side effects: does not mutate hand.
- For example:
- substitute_hand({'h':1, 'e':1, 'l':2, 'o':1}, 'l')
- might return:
- {'h':1, 'e':1, 'o':1, 'x':2} -> if the new letter is 'x'
- The new letter should not be 'h', 'e', 'l', or 'o' since those letters were
- already in the hand.
- hand: dictionary (string -> int)
- letter: string
- returns: dictionary (string -> int)
- """
- local_hand = hand.copy()
- if letter not in local_hand:
- return local_hand
- possible_letters = list(set(SCRABBLE_LETTER_VALUES.keys()) - set(hand.keys()))
- count = local_hand[letter]
- del local_hand[letter]
- local_hand[random.choice(possible_letters)] = count
- return local_hand
- def ask_int(prompt):
- while True:
- value = input(prompt)
- try:
- return int(value)
- except:
- print("Enter a number, idiot!")
- def ask_letter(prompt):
- while True:
- value = input(prompt)
- if value in SCRABBLE_LETTER_VALUES.keys():
- return value
- print('Not a scrabble letter, bakkkaaaa')
- def ask_yesno(prompt):
- while True:
- value = input(prompt)
- if value.lower() == 'yes':
- return True
- if value.lower() == 'no':
- return False
- print('Answer yes/no, aho')
- def play_game(word_list):
- """
- Allow the user to play a series of hands
- * Asks the user to input a total number of hands
- * Accumulates the score for each hand into a total score for the
- entire series
- * For each hand, before playing, ask the user if they want to substitute
- one letter for another. If the user inputs 'yes', prompt them for their
- desired letter. This can only be done once during the game. Once the
- substitue option is used, the user should not be asked if they want to
- substitute letters in the future.
- * For each hand, ask the user if they would like to replay the hand.
- If the user inputs 'yes', they will replay the hand and keep
- the better of the two scores for that hand. This can only be done once
- during the game. Once the replay option is used, the user should not
- be asked if they want to replay future hands. Replaying the hand does
- not count as one of the total number of hands the user initially
- wanted to play.
- * Note: if you replay a hand, you do not get the option to substitute
- a letter - you must play whatever hand you just had.
- * Returns the total score for the series of hands
- word_list: list of lowercase strings
- """
- hands = ask_int("How many games do you want the computer to play for you?")
- letters_per_hand = ask_int("How many letters should the computer play per hand?")
- score = 0
- can_substitute = True
- for i in range(hands):
- hand = deal_hand(letters_per_hand)
- print(f"This is the hand the computer {'wants' if can_substitute else 'will'} play: ")
- display_hand(hand)
- if can_substitute:
- substitute = ask_yesno('Would you like to trick the computer by substituting a character?')
- if substitute:
- can_substitute = False
- letter = ask_letter('Nice trick! Which letter?')
- hand = substitute_hand(hand, letter)
- print("Here is your tricky new hand, the computer will be stumped!")
- display_hand(hand)
- score += play_hand(hand, word_list)
- print(f"Nice, the computer has scored a total of {score} points!")
- #
- # Build data structures used for entire session and play game
- # Do not remove the "if __name__ == '__main__':" line - this code is executed
- # when the program is run directly, instead of through an import statement
- #
- if __name__ == '__main__':
- word_list = load_words()
- cProfile.run('play_game(word_list)')
- #play_game(word_list)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement