1. # 6.00 Problem Set 3A Solutions
  2. #
  3. # The 6.00 Word Game
  4. # Created by: Kevin Luu <luuk> and Jenna Wiens <jwiens>
  5. #
  6. #
  7.  
  8. import random
  9. import string
  10. import sys
  11.  
  12. VOWELS = 'aeiou'
  13. CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
  14. HAND_SIZE = 7
  15.  
  16. SCRABBLE_LETTER_VALUES = {
  17.     '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
  18. }
  19.  
  20. # -----------------------------------
  21. # Helper code
  22. # (you don't need to understand this helper code)
  23.  
  24. WORDLIST_FILENAME = "words.txt"
  25.  
  26. def load_words():
  27.     """
  28.    Returns a list of valid words. Words are strings of lowercase letters.
  29.    
  30.    Depending on the size of the word list, this function may
  31.    take a while to finish.
  32.    """
  33.     print "Loading word list from file..."
  34.     # inFile: file
  35.     inFile = open(WORDLIST_FILENAME, 'r', 0)
  36.     # wordlist: list of strings
  37.     wordlist = []
  38.     for line in inFile:
  39.         wordlist.append(line.strip().lower())
  40.     print "  ", len(wordlist), "words loaded."
  41.     return wordlist
  42.    
  43. word_list = load_words()
  44.  
  45. def get_frequency_dict(sequence):
  46.     """
  47.    Returns a dictionary where the keys are elements of the sequence
  48.    and the values are integer counts, for the number of times that
  49.    an element is repeated in the sequence.
  50.  
  51.    sequence: string or list
  52.    return: dictionary
  53.    """
  54.     # freqs: dictionary (element_type -> int)
  55.     freq = {}
  56.     for x in sequence:
  57.         freq[x] = freq.get(x,0) + 1
  58.     return freq
  59.    
  60.  
  61. # (end of helper code)
  62. # -----------------------------------
  63.  
  64. #
  65. # Problem #1: Scoring a word
  66. #
  67. def get_word_score(word, n):
  68.     '''Returns the score for a word. Assumes the word is a
  69.    valid word.'''
  70.     sum = 0
  71.     #if is_valid_word is true
  72.     for e in word:
  73.         sum += SCRABBLE_LETTER_VALUES[e]
  74.     score = len(word)*sum
  75.     if len(word) == n:
  76.         score += 50
  77.     return score
  78.     # TO DO...
  79. #
  80. # Make sure you understand how this function works and what it does!
  81. #
  82. def display_hand(hand):
  83.     """
  84.    Displays the letters currently in the hand.
  85.  
  86.    For example:
  87.       display_hand({'a':1, 'x':2, 'l':3, 'e':1})
  88.    Should print out something like:
  89.       a x x l l l e
  90.    The order of the letters is unimportant.
  91.  
  92.    hand: dictionary (string -> int)
  93.    """
  94.     for letter in hand.keys():
  95.         for j in range(hand[letter]):
  96.              print letter,              # print all on the same line
  97.     print                               # print an empty line
  98.  
  99. #
  100. # Make sure you understand how this function works and what it does!
  101. #
  102. def deal_hand(n):
  103.     """
  104.    Returns a random hand containing n lowercase letters.
  105.    At least n/3 the letters in the hand should be VOWELS.
  106.  
  107.    Hands are represented as dictionaries. The keys are
  108.    letters and the values are the number of times the
  109.    particular letter is repeated in that hand.
  110.  
  111.    n: int >= 0
  112.    returns: dictionary (string -> int)
  113.    """
  114.     hand={}
  115.     num_vowels = n / 3
  116.    
  117.     for i in range(num_vowels):
  118.         x = VOWELS[random.randrange(0,len(VOWELS))]
  119.         hand[x] = hand.get(x, 0) + 1
  120.        
  121.     for i in range(num_vowels, n):    
  122.         x = CONSONANTS[random.randrange(0,len(CONSONANTS))]
  123.         hand[x] = hand.get(x, 0) + 1
  124.        
  125.     return hand
  126.  
  127. #
  128. # Problem #2: Update a hand by removing letters
  129. #
  130. def update_hand(hand, word):
  131.     """
  132.    Assumes that 'hand' has all the letters in word.
  133.     In other words, this assumes that however many times
  134.     a letter appears in 'word', 'hand' has at least as
  135.     many of that letter in it.
  136.  
  137.    Updates the hand: uses up the letters in the given word
  138.    and returns the new hand, without those letters in it.
  139.  
  140.    Has no side effects: does not modify hand.
  141.  
  142.    word: string
  143.    hand: dictionary (string -> int)    
  144.    returns: dictionary (string -> int)
  145.    """
  146.     for letter in word:
  147.         hand[letter] -= 1
  148.     for letter in hand.keys():
  149.         if hand[letter] == 0:
  150.             hand.pop(letter, 0)
  151.     return hand
  152.  
  153. #
  154. # Problem #3: Test word validity
  155. #
  156. def is_valid_word(word, hand, word_list):
  157.     """
  158.    Returns True if word is in the word_list and is entirely
  159.    composed of letters in the hand. Otherwise, returns False.
  160.    Does not mutate hand or word_list.
  161.    
  162.    word: string
  163.    hand: dictionary (string -> int)
  164.    word_list: list of lowercase strings
  165.    """
  166.     hand1 = hand
  167.     if word in word_list:
  168.         for letter in word:
  169.             if letter in hand1 and hand1[letter] > 0:
  170.                 hand1[letter] -= 1
  171.             else:
  172.                 return False
  173.         return True
  174.     else:
  175.         return False
  176.  
  177. def calculate_handlen(hand):
  178.     handlen = 0
  179.     for v in hand.values():
  180.         handlen += v
  181.     return handlen
  182.  
  183. #
  184. # Problem #4: Playing a hand
  185. #
  186. def play_hand(hand, word_list, n):
  187.     '''Allows the user to play a single, randomnly generated hand'''
  188.     total = 0
  189.     while True:
  190.         display_hand(hand)
  191.         word = raw_input('Enter a word compiled from the characters above or a period to end the hand:')
  192.         if word == '.':
  193.             break
  194.         else:
  195.             if is_valid_word(word, hand, word_list) == False:
  196.                 print 'Invalid word, please enter another word.'
  197.             else:
  198.                 score = get_word_score(word, n)
  199.                 total += score
  200.                 print '"'+word+'"','earned',score,'points. Total:',total,'points.'
  201.                 update_hand(hand, word)
  202.     print "Total score:",total
  203.    
  204. #play_hand(word_list, 7)
  205. #
  206. # Problem #5: Playing a game
  207. # Make sure you understand how this code works!
  208. #
  209. def play_game(hand, word_list, n):
  210.     hand1 = hand.copy()
  211.     while True:
  212.         play_hand(hand, word_list, n)
  213.         print 'Input "n" to play a new hand, "r" to play the last hand again, "e" to exit the game.'
  214.         choice = raw_input('')
  215.         if choice != 'n' and choice != 'r' and choice != 'e':
  216.             print 'Entry invalid, try again.'
  217.         elif choice == 'n':
  218.             play_game(deal_hand(n), word_list, n)
  219.         elif choice == 'r':
  220.             play_game(hand1, word_list, n)
  221.         else:
  222.             sys.exit()
  223.  
  224. #play_game(deal_hand(7), word_list, 7)
  225. #
  226. # Build data structures used for entire session and play game
  227. #
  228. if __name__ == '__main__':
  229.     word_list = load_words()
  230.     play_game(word_list)