PageRenderTime 51ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/ps10.py

https://bitbucket.org/c4nn1b4l/mit-introduction-to-computer-science-and-programming-python-3
Python | 395 lines | 208 code | 26 blank | 161 comment | 33 complexity | e03dfe2f912ca50b3a0ab50e4c4b924f MD5 | raw file
  1. # Backend code for PS10
  2. import random
  3. import string
  4. # Global Constants
  5. VOWELS = 'aeiou'
  6. CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
  7. HAND_SIZE = 30
  8. SCRABBLE_LETTER_VALUES = {
  9. 'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, 'f': 4, 'g': 2, 'h': 4, 'i': 1,
  10. 'j': 8, 'k': 5, 'l': 1, 'm': 3, 'n': 1, 'o': 1, 'p': 3, 'q': 10, 'r': 1,
  11. 's': 1, 't': 1, 'u': 1, 'v': 4, 'w': 4, 'x': 8, 'y': 4, 'z': 10
  12. }
  13. HUMAN_SOLO = 0
  14. HUMAN_VS_HUMAN = 1
  15. HUMAN_VS_COMP = 2
  16. WORDLIST_FILENAME = "words.txt"
  17. def getFrequencyDict(sequence):
  18. """
  19. Given a sequence of letters, convert the sequence to a dictionary of
  20. letters -> frequencies. Used by containsLetters().
  21. returns: dictionary of letters -> frequencies
  22. """
  23. freq = {}
  24. for x in sequence:
  25. freq[x] = freq.get(x,0) + 1
  26. return freq
  27. def getWordScore(word):
  28. """
  29. Computes the score of a word (no bingo bonus is added).
  30. word: The word to score (a string).
  31. returns: score of the word.
  32. """
  33. score = 0
  34. for ch in word:
  35. score += SCRABBLE_LETTER_VALUES[ch]
  36. if len(word) == HAND_SIZE:
  37. score += 50
  38. return score
  39. #
  40. # Problem 2: Representing a Hand
  41. #
  42. class Hand(object):
  43. def __init__(self, handSize, initialHandDict = None):
  44. """
  45. Initialize a hand.
  46. handSize: The size of the hand
  47. postcondition: initializes a hand with random set of initial letters.
  48. """
  49. num_vowels = handSize / 3
  50. if initialHandDict is None:
  51. initialHandDict = {}
  52. for i in range(num_vowels):
  53. x = VOWELS[random.randrange(0,len(VOWELS))]
  54. initialHandDict[x] = initialHandDict.get(x, 0) + 1
  55. for i in range(num_vowels, handSize):
  56. x = CONSONANTS[random.randrange(0,len(CONSONANTS))]
  57. initialHandDict[x] = initialHandDict.get(x, 0) + 1
  58. #elif handSize != len(initialHandDict):
  59. # raise ValueError('hand size is not equal with the length of initialHandDict')
  60. self.initialSize = handSize
  61. self.handDict = initialHandDict
  62. def update(self, word):
  63. """
  64. Remove letters in word from this hand.
  65. word: The word (a string) to remove from the hand
  66. postcondition: Letters in word are removed from this hand
  67. """
  68. for char in word:
  69. if self.handDict.get(char,0) == 0:
  70. return None
  71. elif self.handDict.get(char, 0) > 1:
  72. self.handDict[char] -= 1
  73. else:
  74. del self.handDict[char]
  75. return self.handDict
  76. def containsLetters(self, letters):
  77. """
  78. Test if this hand contains the characters required to make the input
  79. string (letters)
  80. returns: True if the hand contains the characters to make up letters,
  81. False otherwise
  82. """
  83. copyHand = self.handDict.copy()
  84. isIt = True
  85. for char in letters:
  86. if char in copyHand.keys() and copyHand[char] > 0:
  87. copyHand[char] -= 1
  88. continue
  89. else:
  90. isIt = False
  91. break
  92. return isIt
  93. def isEmpty(self):
  94. """
  95. Test if there are any more letters left in this hand.
  96. returns: True if there are no letters remaining, False otherwise.
  97. """
  98. isitempty = True
  99. for char in self.handDict.keys():
  100. if self.handDict[char] >= 1:
  101. isitempty = False
  102. break
  103. else:
  104. continue
  105. return isitempty
  106. def __eq__(self, other):
  107. """
  108. Equality test, for testing purposes
  109. returns: True if this Hand contains the same number of each letter as
  110. the other Hand, False otherwise
  111. """
  112. equV = True
  113. for char in self.handDict.keys():
  114. if char in other.handDict.keys() and self.handDict[char] == other.handDict[char]:
  115. continue
  116. else:
  117. equV = False
  118. break
  119. return equV
  120. def __str__(self):
  121. """
  122. Represent this hand as a string
  123. returns: a string representation of this hand
  124. """
  125. string = ''
  126. for letter in self.handDict.keys():
  127. for j in range(self.handDict[letter]):
  128. string = string + letter + ' '
  129. return string
  130. #
  131. # Problem 3: Representing a Player
  132. #
  133. class Player(object):
  134. """
  135. General class describing a player.
  136. Stores the player's ID number, hand, and score.
  137. """
  138. def __init__(self, idNum, hand):
  139. """
  140. Initialize a player instance.
  141. idNum: integer: 1 for player 1, 2 for player 2. Used in informational
  142. displays in the GUI.
  143. hand: An object of type Hand.
  144. postcondition: This player object is initialized
  145. """
  146. self.points = 0.
  147. self.idNum = idNum
  148. self.hand = hand
  149. def getHand(self):
  150. """
  151. Return this player's hand.
  152. returns: the Hand object associated with this player.
  153. """
  154. return self.hand
  155. def addPoints(self, points):
  156. """
  157. Add points to this player's total score.
  158. points: the number of points to add to this player's score
  159. postcondition: this player's total score is increased by points
  160. """
  161. self.points += points
  162. def getPoints(self):
  163. """
  164. Return this player's total score.
  165. returns: A float specifying this player's score
  166. """
  167. return self.points
  168. def getIdNum(self):
  169. """
  170. Return this player's ID number (either 1 for player 1 or
  171. 2 for player 2).
  172. returns: An integer specifying this player's ID number.
  173. """
  174. return self.idNum
  175. def __cmp__(self, other):
  176. """
  177. Compare players by their scores.
  178. returns: 1 if this player's score is greater than other player's score,
  179. -1 if this player's score is less than other player's score, and 0 if
  180. they're equal.
  181. """
  182. if self.points > other.points:
  183. return 1
  184. elif self.points < other.points:
  185. return -1
  186. else:
  187. return 0
  188. def __str__(self):
  189. """
  190. Represent this player as a string
  191. returns: a string representation of this player
  192. """
  193. return 'Player %d\n\nScore: %.2f\n' % \
  194. (self.getIdNum(), self.getPoints())
  195. #
  196. # Problem 4: Representing a Computer Player
  197. #
  198. class ComputerPlayer(Player):
  199. """
  200. A computer player class.
  201. Does everything a Player does, but can also pick a word using the
  202. PickBestWord method.
  203. """
  204. def pickBestWord(self, wordlist):
  205. """
  206. Pick the best word available to the computer player.
  207. returns: The best word (a string), given the computer player's hand and
  208. the wordlist
  209. """
  210. answers = {}
  211. for word in wordlist.getList():
  212. wogo = True
  213. if self.hand.containsLetters(word):
  214. answers.update({word : getWordScore(word)})
  215. else:
  216. pass
  217. if len(answers) != 0:
  218. max_point = max(answers.values())
  219. for word in answers.keys():
  220. if answers[word] == max_point:
  221. return word
  222. def playHand(self, callback, wordlist):
  223. """
  224. Play a hand completely by passing chosen words to the callback
  225. function.
  226. """
  227. while callback(self.pickBestWord(wordlist)): pass
  228. class HumanPlayer(Player):
  229. """
  230. A Human player class.
  231. No methods are needed because everything is taken care of by the GUI.
  232. """
  233. class Wordlist(object):
  234. """
  235. A word list.
  236. """
  237. def __init__(self):
  238. """
  239. Initializes a Wordlist object.
  240. postcondition: words are read in from a file (WORDLIST_FILENAME, a
  241. global constant) and stored as a list.
  242. """
  243. inputFile = open(WORDLIST_FILENAME)
  244. try:
  245. self.wordlist = []
  246. for line in inputFile:
  247. self.wordlist.append(line.strip().lower())
  248. finally:
  249. inputFile.close()
  250. def containsWord(self, word):
  251. """
  252. Test whether this wordlist includes word
  253. word: The word to check (a string)
  254. returns: True if word is in this Wordlist, False if word is not in
  255. Wordlist
  256. """
  257. return word in self.wordlist
  258. def getList(self):
  259. return self.wordlist
  260. class EndHand(Exception): pass
  261. class Game(object):
  262. """
  263. Stores the state needed to play a round of the word game.
  264. """
  265. def __init__(self, mode, wordlist):
  266. """
  267. Initializes a game.
  268. mode: Can be one of three constant values - HUMAN_SOLO, HUMAN_VS_COMP,
  269. and HUMAN_VS_HUMAN
  270. postcondition: Initializes the players nd their hands.
  271. """
  272. hand = Hand(HAND_SIZE)
  273. hand2 = Hand(HAND_SIZE, hand.handDict.copy())
  274. if mode == HUMAN_SOLO:
  275. self.players = [HumanPlayer(1, hand)]
  276. elif mode == HUMAN_VS_COMP:
  277. self.players = [HumanPlayer(1, hand),
  278. ComputerPlayer(2, hand2)]
  279. elif mode == HUMAN_VS_HUMAN:
  280. self.players = [HumanPlayer(1, hand),
  281. HumanPlayer(2, hand2)]
  282. self.playerIndex = 0
  283. self.wordlist = wordlist
  284. def getCurrentPlayer(self):
  285. """
  286. Gets the Player object corresponding to the active player.
  287. returns: The active Player object.
  288. """
  289. return self.players[self.playerIndex]
  290. def nextPlayer(self):
  291. """
  292. Changes the game state so that the next player is the active player.
  293. postcondition: playerIndex is incremented
  294. """
  295. if self.playerIndex + 1 < len(self.players):
  296. self.playerIndex = self.playerIndex + 1
  297. return True
  298. else:
  299. return False
  300. def gameOver(self):
  301. """
  302. Determines if the game is over
  303. returns: True if the playerIndex >= the number of players, False
  304. otherwise
  305. """
  306. return self.playerIndex >= len(self.players)
  307. def tryWord(self, word):
  308. if word == '.':
  309. raise EndHand()
  310. player = self.getCurrentPlayer()
  311. hand = player.getHand()
  312. if self.wordlist.containsWord(word) and hand.containsLetters(word):
  313. points = getWordScore(word)
  314. player.addPoints(points)
  315. hand.update(word)
  316. if hand.isEmpty():
  317. raise EndHand()
  318. return points
  319. else:
  320. return None
  321. def getWinner(self):
  322. return max(self.players)
  323. def getNumPlayers(self):
  324. return len(self.players)
  325. def isTie(self):
  326. return len(self.players) > 1 and \
  327. self.players[0].getPoints() == self.players[1].getPoints()
  328. def __str__(self):
  329. """
  330. Convert this game object to a string
  331. returns: the concatenation of the string representation of the players
  332. """
  333. string = ''
  334. for player in self.players:
  335. string = string + str(player)
  336. return string