MIT 6.00.1x 计算机科学和Python编程导论 Set 4

Word Scores 为单词算分值

感谢 glhezjnucn 童鞋的给力翻译!!
The first step is to implement some code that allows us to calculate the score for a single word. The function getWordScore should accept as input a string of lowercase letters (a word) and return the integer score for that word, using the game’s scoring rules.
代码设计的第一步是让我们为一个单词计算它的得分。函数getWordScore 接收一个小写字母串 (一个单词),然后按照游戏的积分规则返回它的得分值 。

Hints

  • You may assume that the input word is always either a string of lowercase letters, or the empty string “”. 你可以假设输入总是小写字母串或者空串 “”.

  • You will want to use the SCRABBLE_LETTER_VALUES dictionary defined at the top of ps4a.py. You should not change its value. 你可能要用到SCRABBLE_LETTER_VALUES词典常量,文件ps4a.py开始部分中定义的. 不可以改变它的值.

  • Do not assume that there are always 7 letters in a hand! The parameter n is the number of letters required for a bonus score (the maximum number of letters in the hand). Our goal is to keep the code modular - if you want to try playing your word game with n=10 or n=4, you will be able to do it by simply changing the value of HAND_SIZE! 不能假定每手总是7张牌! 参数 n 用于判断是否给奖励分(手上牌的最大张数). 我们的目标是模块化设计- 你可以玩n=10 或 n=4的游戏, 只须更改 HAND_SIZE的值来达到这个目的!

  • Testing: If this function is implemented properly, and you run test_ps4a.py, you should see that the test_getWordScore() tests pass. Also test your implementation of getWordScore, using some reasonable English words. 测试: 如果函数被正确的编写,你运行test_ps4a.py, 你将看到test_getWordScore() 检测通过. 用你的函数getWordScore检测一些合理单词的分值.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def getWordScore(word, n):
"""
Returns the score for a word. Assumes the word is a valid word.

The score for a word is the sum of the points for letters in the
word, multiplied by the length of the word, PLUS 50 points if all n
letters are used on the first turn.

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 (see SCRABBLE_LETTER_VALUES)

word: string (lowercase letters)
n: integer (HAND_SIZE; i.e., hand size required for additional points)
returns: int = 0
"""
letter_score,extra_score = 0,0
for letter in word:
letter_score +=SCRABBLE_LETTER_VALUES[letter]
if len(word) == n:
extra_score = 50
return letter_score * len(word) + extra_score


Dealing With Hands 一手牌的处理过程

**Please read this problem entirely!!**请完整阅读这个问题!! The majority of this problem consists of learning how to read code, which is an incredibly useful and important skill. At the end, you will implement a short function. Be sure to take your time on this problem - it may seem easy, but reading someone else’s code can be challenging and this is an important exercise.这个问题的一个重要目的是学习如何去阅读代码,这是相当有用且重要的技能。在结尾,你将写一个短小的函数。在这个问题上你该确保花点时间-它看起来简单,但读别人的代码又是具有挑战性的,这是一次很重要的练习。

Representing hands 表示一手牌

A hand is the set of letters held by a player during the game. The player is initially dealt a set of random letters. For example, the player could start out with the following hand: a, q, l, m, u, i, l. In our program, a hand will be represented as a dictionary: the keys are (lowercase) letters and the values are the number of times the particular letter is repeated in that hand. For example, the above hand would be represented as:
一手牌是指游戏过程中玩家手上的字母集合。玩家最初被随机分配到一手牌。例如玩家手中开始时的牌: a, q, l, m, u, i, l. 在我们的程序中,一手牌,表示为一个词典变量: 小写字母作为键值,对应的值是字母出现的次数,上面的这手字母牌表示为:
hand = {'a':1, 'q':1, 'l':2, 'm':1, 'u':1, 'i':1}
Notice how the repeated letter ‘l’ is represented. Remember that with a dictionary, the usual way to access a value is hand[‘a’], where ‘a’ is the key we want to find. However, this only works if the key is in the dictionary; otherwise, we get a KeyError. To avoid this, we can use the call hand.get(‘a’,0). This is the “safe” way to access a value if we are not sure the key is in the dictionary. d.get(key,default) returns the value for key if key is in the dictionary d, else default. If default is not given, it returns None, so that this method never raises a KeyError. For example:
注意重复字母l的表示.记住从词典变量中得到一个值通常的方法是hand[‘a’], 其中’a’ 是我们要找的键值.不过,这个方式只有当键值在词典中的时候才可以,否则会导致KeyError错误.避免这个,可以用hand.get(‘a’,0). 这种形式比较安全些,如果我们不太确定键值是否在词典中. d.get(key,default)将返回对应值(键值存在),或者返回缺省值(这里是0),如果不给缺省值,那返回None,因此这个方法不会引发KeyError. 例如:

1
2
3
4
5
6
>>> hand['e']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'e'
>>> hand.get('e', 0)
0

Converting words into dictionary representation

One useful function we’ve defined for you is getFrequencyDict, defined near the top of ps4a.py. When given a string of letters as an input, it returns a dictionary where the keys are letters and the values are the number of times that letter is represented in the input string. For example:
我们已经为你设计了一个有用的函数getFrequencyDict,给它一个字母串,它返回这个字母串所表示的一个词典(即一手牌)

1
2
>>> getFrequencyDict("hello")
{'h': 1, 'e': 1, 'l': 2, 'o': 1}

As you can see, this is the same kind of dictionary we use to represent hands. 这就是一手牌的表示形式。

Displaying a hand 显示一手牌

Given a hand represented as a dictionary, we want to display it in a user-friendly way. We have provided the implementation for this in the displayHand function. Take a few minutes right now to read through this function carefully and understand what it does and how it works.
以词典形式给定一手牌之后,我们想将牌以用户-友好的形式显示出来,我们为你提供了函数displayHand. 现在就花几分钟去仔细阅读这个函数,看看它是怎么工作的。

Generating a random hand 随机生成一手牌

The hand a player is dealt is a set of letters chosen at random. We provide you with the implementation of a function that generates this random hand, dealHand. The function takes as input a positive integer n, and returns a new object, a hand containing n lowercase letters. Again, take a few minutes (right now!) to read through this function carefully and understand what it does and how it works.
玩家发到的牌是随机选取的字母集合,我们给你设计了实现这个目的的函数dealHand. 这个函数以一个正整数n为输入参数,生成一个含n个小写字母的一手牌。同样,现在(对就现在!)就花几分钟去仔细阅读这个函数,看看它是怎么工作的。

Removing letters from a hand (you implement this) 从一手牌中移去字母

The player starts with a hand, a set of letters. As the player spells out words, letters from this set are used up. For example, the player could start out with the following hand: a, q, l, m, u, i, l. The player could choose to spell the word quail . This would leave the following letters in the player’s hand: l, m. Your task is to implement the function updateHand, which takes in two inputs - a hand and a word (string). updateHand uses letters from the hand to spell the word, and then returns a copy of the hand, containing only the letters remaining. For example:
玩家从一手牌:一组字母集合开始,当玩家组合出一个单词,字母集中的字母就被消耗掉。就算玩家从这手牌开始: a, q, l, m, u, i, l. 他可能组合一个单词quail . 那么玩家手上就剩余l, m.现在你的任务是实现函数updateHand(更新手上的牌), 接收两个输入参数 – 一手牌, 一个单词 (字母串). updateHand 使用手上的牌组装单词,然后返回剩余的牌,(玩家新的一手牌)例如

1
2
3
4
5
6
7
8
>>> hand = {'a':1, 'q':1, 'l':2, 'm':1, 'u':1, 'i':1}
>>> displayHand(hand) # Implemented for you已经为你设计好的
a q l l m u i
>>> hand = updateHand(hand, 'quail') # You implement this function!你实现的函数!
>>> hand
{'a':0, 'q':0, 'l':1, 'm':1, 'u':0, 'i':0}
>>> displayHand(hand)
l m

Implement the updateHand function. Make sure this function has no side effects: i.e., it must not mutate the hand passed in. Before pasting your function definition here, be sure you’ve passed the appropriate tests in test_ps4a.py.
实现这个updateHand 函数. 确保这个函数不产生副作用: i.e., 不能改变传入的那手牌.在将代码复制到提交区之前,确信已通过test_ps4a.py的测试.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def updateHand(hand, word):
"""
Assumes that 'hand' has all the letters in word.
In other words, this assumes that however many times
a letter appears in 'word', 'hand' has at least as
many of that letter in it.

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)
"""
# TO DO ... <-- Remove this comment when you code this function
hand_copy = hand.copy()
for letter in word:
hand_copy[letter] -= 1
return hand_copy

Valid Words

At this point, we have written code to generate a random hand and display that hand to the user. We can also ask the user for a word (Python’s raw_input) and score the word (using your getWordScore). However, at this point we have not written any code to verify that a word given by a player obeys the rules of the game. A valid word is in the word list; and it is composed entirely of letters from the current hand. Implement the isValidWord function.
到此,我们已经写出代码来生成随机的一手字母牌,并将牌显示给玩家看。我们可以请用户(玩家)输入一个单词(用raw_input),对单词计算得分(用函数getWordScore)。然而,至此我们并没有写代码来检查用户输入的单词是否遵循游戏规则。有效的单词必须是单词列表中的词,而且它必须完全由手上的字母牌构成. 实现这样的函数isValidWord.
Testing: Make sure the test_isValidWord tests pass. In addition, you will want to test your implementation by calling it multiple times on the same hand - what should the correct behavior be? Additionally, the empty string (‘’) is not a valid word - if you code this function correctly, you shouldn’t need an additional check for this condition.
测试: 确保test_isValidWord 的测试要通过. 而且,你可能对同一手牌测试多次来检查你的函数设计-需要怎样的正确行为呢? 另外, 空的字符串不是有效单词-如果你的代码正确编写的话,你是不需要对这个进行额外检查的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def isValidWord(word, hand, wordList):
"""
Returns True if word is in the wordList and is entirely
composed of letters in the hand. Otherwise, returns False.

Does not mutate hand or wordList.

word: string
hand: dictionary (string - int)
wordList: list of lowercase strings
"""
# TO DO ... <-- Remove this comment when you code this function
hand_copy =hand.copy()
if word in wordList:
for letter in word:
hand_copy[letter] = hand_copy.get(letter,0) - 1
for fres in hand_copy.values():
if fres < 0:
return False
else:
pass
return True
else:
return False


Hand Length 计算手上字母牌的长度

We are now ready to begin writing the code that interacts with the player. We’ll be implementing the playHand function. This function allows the user to play out a single hand. First, though, you’ll need to implement the helper calculateHandlen function, which can be done in under five lines of code.
现在我们已经可以开始编写与玩家交互的代码了。我们将实现函数playHand, 该函数允许玩家打一手牌(就玩一次)。当然首先,你需要实现一个辅助函数 calculateHandlen, (译者注:计算出手中牌的长度,重复字母按重复次数计算,您可以设想为手上有多少张牌),该函数可以用少于5行的代码来实现(译者注:如果您实在想挑战一下,那就写一行代码)。

1
2
3
4
5
6
7
8
9
10
11
12
def calculateHandlen(hand):
"""
Returns the length (number of letters) in the current hand.

hand: dictionary (string int)
returns: integer
"""
# TO DO... <-- Remove this comment when you code this function
sum_len = 0
for length in hand.values():
sum_len += length
return sum_len

Playing a Hand 玩一手牌(玩一次)的设计

In ps4a.py, note that in the function playHand, there is a bunch of pseudocode. This pseudocode is provided to help guide you in writing your function. Check out the Why Pseudocode? resource to learn more about the What and Why of Pseudocode before you start coding your solution.
在文件ps4a.py中, 注意在函数playHand,有一大堆的伪代码. 这些伪代码指导你代码编写。要是不了解啥是伪代码,为什么用伪代码,去搜索学习一下吧.
Note: Do not assume that there will always be 7 letters in a hand! The parameter n represents the size of the hand.注意:不要假设一手牌始终是7张牌,参数n代表一手牌的长度.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def playHand(hand, wordList, n):
"""
Allows the user to play the given hand, as follows:

* The hand is displayed.
* The user may input a word or a single period (the string ".")
to indicate they're done playing
* Invalid words are rejected, and a message is displayed asking
the user to choose another word until they enter a valid word or "."
* When a valid word is entered, it uses up letters from the hand.
* After every valid word: the score for that word is displayed,
the remaining letters in the hand are displayed, and the user
is asked to input another word.
* The sum of the word scores is displayed when the hand finishes.
* The hand finishes when there are no more unused letters or the user
inputs a "."

hand: dictionary (string - int)
wordList: list of lowercase strings
n: integer (HAND_SIZE; i.e., hand size required for additional points)

"""
# BEGIN PSEUDOCODE (download ps4a.py to see)
hand_copy = hand.copy()
total_score = 0
while calculateHandlen(hand_copy) != 0:
print 'Current Hand: ',
displayHand(hand_copy)
word = raw_input('Enter word, or a "." to indicate that you are finished: ')
if word =='.':
print 'Goodbye! Total score: %s points.' % total_score
break
else:
if isValidWord(word, hand_copy, wordList):
word_score = getWordScore(word, n)
total_score += word_score
print '"%s" earned %s points. Total: %s points' %(word,word_score,total_score)
hand_copy = updateHand(hand_copy, word)
print
else:
print 'Invalid word, please try again.'
print
if calculateHandlen(hand_copy) == 0:
print 'Run out of letters. Total score: %s points.' % total_score

Playing a Game 完成最后设计

A game consists of playing multiple hands. We need to implement one final function to complete our word-game program. Write the code that implements the playGame function. You should remove the code that is currently uncommented in the playGame body. Read through the specification and make sure you understand what this function accomplishes. For the game, you should use the HAND_SIZE constant to determine the number of cards in a hand.
完整的游戏是可以让玩家反复打多手牌,为此我们需要最后完成一个函数,以便完成我们的单词游戏(译注:到此Part A完工)。为函数playGame 编写代码。你需要将playGame函数体中目前没有注释的代码先移去(译注:ps4a.py会自动调用playGame,有一句print是要去掉的)。通读那里的说明,理解函数应该做什么。在游戏中,你需要用HAND_SIZE常量来决定一手牌的初始长度(译注:即不可以限定是文中作为举例提到的7).
Testing: Try out this implementation as if you were playing the game. Try out different values for HAND_SIZE with your program, and be sure that you can play the wordgame with different hand sizes by modifying only the variable HAND_SIZE.
测试: 就像你真的在玩这个游戏一样检测你的设计,并尝试不同的HAND_SIZE,确保你的游戏只需通过改变HAND_SIZE就可以玩不同长度一手牌的单词游戏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def playGame(wordList):
"""
Allow the user to play an arbitrary number of hands.

1) Asks the user to input 'n' or 'r' or 'e'.
* If the user inputs 'n', let the user play a new (random) hand.
* If the user inputs 'r', let the user play the last hand again.
* If the user inputs 'e', exit the game.
* If the user inputs anything else, tell them their input was invalid.

2) When done playing the hand, repeat from step 1
"""
# TO DO ... <-- Remove this comment when you code this function
n = HAND_SIZE
command ,can_replay = None,None
while command != 'e':
command = raw_input('Enter n to deal a new hand, r to replay the last hand, or e to end game: ')
if command == 'e':
break
elif command == 'n':
last_hand = dealHand(n)
playHand(last_hand, wordList, n)
can_replay = True
print
elif command =='r':
if can_replay:
playHand(last_hand, wordList, n)
print
else:
print 'You have not played a hand yet. Please play a new hand first!'
print
else:
print 'Invalid command.'

Computer Chooses a Word 计算机选单词

Part B is dependent on your functions from ps4a.py, so be sure to complete ps4a.py before working on ps4b.py Part B 建立在你在ps4a.py中设计好的函数,因此确保在开始做ps4b.py之前你已经完成ps4a.py
Now that you have completed your word game code, you decide that you would like to enable your computer (SkyNet) to play the game (your hidden agenda is to prove once and for all that computers are inferior to human intellect!) In Part B you will make a modification to the playHand function from part A that will enable this to happen. The idea is that you will be able to compare how you as a user succeed in the game compared to the computer’s performance.
到此你已经完成了单词游戏的编码,你可能会想到希望你的计算机(Skynet)来玩这个游戏(你的潜在动机可能想一次来证明计算机总是低于人类的智能!) 在Part B,你将对Part A的playHand函数做出修改,以便实现这样的想法。想法是,你可以比较你作为用户玩家如何在游戏中胜过计算机玩家的表现。
It is your responsibility to create the function compChooseWord(hand, wordList, n). Pseudocode is provided in the file ps4b.py.
你负责创建函数compChooseWord(hand, wordList, n). ps4b.py中提供了伪代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def compChooseWord(hand, wordList, n):
"""
Given a hand and a wordList, find the word that gives
the maximum value score, and return it.

This word should be calculated by considering all the words
in the wordList.

If no words in the wordList can be made from the hand, return None.

hand: dictionary (string -> int)
wordList: list (string)
returns: string or None
"""
# BEGIN PSEUDOCODE (available within ps4b.py)
max_score,best_word = 0,None
for word in wordList:
if isValidWord(word, hand, wordList):
if getWordScore(word,n) max_score:
max_score = getWordScore(word,n)
best_word = word
return best_word

Computer Plays a Hand 计算机玩一手牌

Now that we have the ability to let the computer choose a word, we need to set up a function to allow the computer to play a hand - in a manner very similar to Part A’s playHand function (get the hint?).
前面我们已经能让计算机来选一个单词了(译注:在本地机器测试时,遍历8万多单词选出最高个分),现在我们需要设计一个函数让计算机玩一手牌,就像Part A的playHand.
Implement the compPlayHand function. This function should allow the computer to play a given hand, using the procedure you just wrote in the previous part. This should be very similar to the earlier version in which a user selected the word, although deciding when it is done playing a particular hand will be different.
编写函数compPlayHand .这个函数能让计算机来玩给定的一手牌,这与前面的设计应该很类似,那里是用户玩家给出单词,当然,应对特殊的一手牌的时候可能有区别。
Be sure to test your function on some randomly generated hands using dealHand.用dealHand随机产生一手牌来测试你的函数实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def compPlayHand(hand, wordList, n):
"""
Allows the computer to play the given hand, following the same procedure
as playHand, except instead of the user choosing a word, the computer
chooses it.

1) The hand is displayed.
2) The computer chooses a word.
3) After every valid word: the word and the score for that word is
displayed, the remaining letters in the hand are displayed, and the
computer chooses another word.
4) The sum of the word scores is displayed when the hand finishes.
5) The hand finishes when the computer has exhausted its possible
choices (i.e. compChooseWord returns None).

hand: dictionary (string -> int)
wordList: list (string)
n: integer (HAND_SIZE; i.e., hand size required for additional points)
"""
# TO DO ..
hand_copy = hand.copy()
total_score = 0
while calculateHandlen(hand_copy) != 0:
print 'Current Hand: ',
displayHand(hand_copy)
word = compChooseWord(hand_copy, wordList, n)
if word == None:
print 'Total score: %s points.' % total_score
break
else:
word_score = getWordScore(word, n)
total_score += word_score
print '"%s" earned %s points. Total: %s points' %(word,word_score,total_score)
hand_copy = updateHand(hand_copy, word)
print
if calculateHandlen(hand_copy) == 0:
print 'Total score: %s points.' % total_score
def compChooseWord(hand, wordList, n):
"""
Given a hand and a wordList, find the word that gives
the maximum value score, and return it.

This word should be calculated by considering all the words
in the wordList.

If no words in the wordList can be made from the hand, return None.

hand: dictionary (string - int)
wordList: list (string)
n: integer (HAND_SIZE; i.e., hand size required for additional points)

returns: string or None
"""
max_score,best_word = 0,None
for word in wordList:
if isValidWord(word, hand, wordList):
if getWordScore(word,n) > max_score:
max_score = getWordScore(word,n)
best_word = word
return best_word

You and Your Computer 与计算机一起玩

(呵呵:干到这一步了,哪怕是甲骨文,翻译也是多余的,讲白了,改写playGame函数,可以让计算机玩,也可以让用户来玩,比如说用户先对一手牌玩一次,再让计算机重玩同一手牌,PK一下,用户是否干得出色)
问题集分11个页面,1-2是介绍性的文字材料,题目从page3到page11,其中page3-page8为Part A共设计6个函数:它们分别是getWordScore, updateHand, isValidWord, calculateHandlen, playHand和playGame,与文件ps4a.py对应,page9-page11为Part B共设计3个函数,它们分别是:compChooseWord, compPlayHand, playGame,与文件ps4b.py对应。
Now that your computer can choose a word, you need to give the computer the option to play. Write the code that re-implements the playGame function. You will modify the function to behave as described below in the function’s comments. As before, you should use the HAND_SIZE constant to determine the number of cards in a hand. Be sure to try out different values for HAND_SIZE with your program.
遵循函数体的描述(翻译在下面),修改playGame函数。你需要为计算机增加一个选项(也就是程序运行的时候,u-用户玩,c-计算机玩,看输出样例显得更重要)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def playGame(wordList):
"""
Allow the user to play an arbitrary number of hands.

1) Asks the user to input 'n' or 'r' or 'e'.
* If the user inputs 'e', immediately exit the game.
* If the user inputs anything that's not 'n', 'r', or 'e', keep asking them again.

2) Asks the user to input a 'u' or a 'c'.
* If the user inputs anything that's not 'c' or 'u', keep asking them again.

3) Switch functionality based on the above choices:
* If the user inputted 'n', play a new (random) hand.
* Else, if the user inputted 'r', play the last hand again.
* If the user inputted 'u', let the user play the game
with the selected hand, using playHand.
* If the user inputted 'c', let the computer play the
game with the selected hand, using compPlayHand.

4) After the computer or user has played the hand, repeat from step 1

wordList: list (string)
"""
# TO DO...
n = HAND_SIZE
command ,can_replay = None,None
while command != 'e':
command = raw_input('Enter n to deal a new hand, r to replay the last hand, or e to end game: ')
if command == 'e':
break
elif command == 'n':
print
while True:
game_mode = raw_input('Enter u to have yourself play, c to have the computer play: ')
if game_mode == 'u':
print
last_hand = dealHand(n)
playHand(last_hand, wordList, n)
can_replay = True
print
break
elif game_mode == 'c':
print
last_hand = dealHand(n)
compPlayHand(last_hand, wordList, n)
can_replay = True
print
break
else:
print 'Invalid command.'
print
elif command =='r':
print
if can_replay:
while True:
game_mode = raw_input('Enter u to have yourself play, c to have the computer play: ')
if game_mode =='u':
print
playHand(last_hand, wordList, n)
print
break
elif game_mode == 'c':
print
compPlayHand(last_hand, wordList, n)
print
break
else:
print 'Invalid command.'
print
else:
print 'You have not played a hand yet. Please play a new hand first!'
print
else:
print 'Invalid command.'
print