查看原文
其他

用Python实现“猜数字”游戏

2017-10-11 异步图书 异步图书

       点击标题下「异步图书」可快速关注          

在本文中,我们准备编写一个叫做“猜数字”的游戏。计算机想到一个1到20之间的随机数,让你来猜它是几。计算机会告诉你每次猜的数太大还是太小。如果你能够在6次之内猜到正确的数字,就赢了。

这是一个进行编程练习的很好的游戏,因为在这个小程序中,用到了随机数字、循环和用户输入。我们已经介绍过如何把值转换成不同的数据类型,以及为什么需要这么做。因为这个程序是一个游戏,所以我们会把其用户称为玩家。

主要内容:

  • import语句;

  • 模块;

  • randint()函数;

  • for语句;

  • 语句块;

  • str()函数、int()函数和float()函数;

  • 布尔类型;

  • 比较操作符;

  • =和==的区别;

  • if语句;

  • break语句。

3.1 “猜数字”的运行示例

如下是玩家运行程序时的样子。玩家输入的文本用粗体表示。

Hello!
What is your name?
AlbertWell, Albert, I am thinking of a number between 1 and 20.
Take a guess.10
Your guess is too high.
Take a guess.
2
Your guess is too low.Take a guess.
4Good job, Albert! You guessed my number in 3 guesses!

3.2 “猜数字”程序的源代码

通过点击File►New Window打开一个新的文件编辑器窗口。在出现的空白窗口中,输入源代码,并且把它保存为guess.py。然后按下F5键来运行程序。当在文件编辑器中输入代码时,要留意一些代码行前面的空格。有些行有4个或者8个缩进空格。

如果输入这些代码之后出现错误,请使用在http//www.nostarch.com/inventwithpython #diff的在线diff工具,对比你输入的代码和本书代码。

guess.py      1. # This is a Guess the Number game.              2. import random              3.              4. guessesTaken = 0              5.              6. print('Hello! What is your name?')              7. myName = input()              8.              9. number = random.randint(1, 20)             10. print('Well, ' + myName + ', I am thinking of a number between 1 and 20.')             11.             12. for i in range(6):             13.     print('Take a guess.') # Four spaces in front of "print"             14.     guess = input()             15.     guess = int(guess)             16.             17.     if guess < number:             18.         print('Your guess is too low.') # Eight spaces in front of "print"             19.             20.     if guess > number:             21.         print('Your guess is too high.')             22.             23.     if guess == number:             24.         break             25.             26. if guess == number:             27.     guessesTaken = str(guessesTaken)             28.     print('Good job, ' + myName + '! You guessed my number in ' +                       guessesTaken + ' guesses!')             29.             30. if guess != number:             31.     number = str(number)             32.     print('Nope. The number I was thinking of was ' + number + '.')

3.3 导入random模块

我们来看一下这个程序的前两行代码:

1. # This is a Guess the Number game.2. import random

第1行是一条注释,我们在第2章中介绍过注释。记住,Python会忽略掉#字符后边的所有内容。注释只是提醒我们程序要做什么。

第2行是一条import语句。记住,语句是执行某些动作的指令,而不像表达式那样会计算为一个值。我们已经见过语句了,例如把一个值存储到一个变量中的赋值语句。

Python包含许多内建的函数,有些函数存在于叫做模块的单独的程序中。可以使用一条import语句把模块导入到你的程序中,这样就可以使用这些函数了。

第2行导入了名为random的模块,以便程序可以调用random.randint()函数。这个函数将产生一个随机数字,供用户进行猜测。

既然已经导入了random模块,我们需要设置一些变量来存储程序稍后将要用到的值。

第4行创建了一个名为guessesTaken的新的变量:

4. guessesTaken = 0

我们将把玩家猜过的次数保存到这个变量中。因为此时玩家还没有做过任何猜测,所以这里保存的是整数0。

6. print('Hello! What is your name?')7. myName = input()

第6行和第7行与我们在第2章的Hello World程序中见到的代码行一样。程序员经常复用其他程序中的代码,以减少自己的工作量。

第6行是对print()函数的一次调用。请记住,函数就像是程序中的一个小程序。当程序调用一个函数时,它会运行这个小程序。print()函数中的代码把传递给它的字符串参数显示到屏幕上。

第7行要求用户输入姓名,并且将输入值存储到myName变量中(记住,这个字符串可能并不是玩家的真实姓名。它可能只是玩家输入的任意字符串。计算机则只会无条件地执行指令)。

3.4 用random.randint()函数生成随机数

既然已经设置好了其他的变量,我们可以使用random模块的函数来设置计算机的神秘数字了:

9. number = random.randint(1, 20)

第9行调用了一个名为randint()的新函数,并且把返回值存储到了变量number中。记住,函数调用可以是表达式的一部分,因为函数调用也会求得一个值。

randint()函数是由random模块提供的,所以在它前边要加上random.(别漏掉那个点),这用来告诉Python,randint()是random模块中的函数。

randint()函数会返回一个随机的整数,该整数在接收到的两个整数参数之间(也包括这两个整数)。第9行把1和20传入到函数名称后边的圆括号中,两个数之间用逗号隔开。把randint()返回的随机整数存储到名为number的变量中,这就是玩家试图猜测的神秘数字。

稍等片刻,回到交互式shell,并且输入import random,以导入random模块。然后输入random.randint(1, 20),以查看这个函数调用的结果。例如,在交互式shell中输入下面的语句。它会返回1到20之间的一个整数。再输一次这行代码,函数调用会返回一个不同的整数。randint()函数每次返回一个随机的整数,就像每次掷色子都会得到一个随机数字一样。当调用randint()函数时,所得到的结果可能是不同的(毕竟它是随机的)。

>>> import random>>> random.randint(1, 20)12>>> random.randint(1, 20)18>>> random.randint(1, 20)3>>> random.randint(1, 20)18>>> random.randint(1, 20)7

也可以通过修改参数,来尝试不同范围的数字。例如,输入random.randint(1, 4),只会得到1到4之间的整数(包含1和4)。或者尝试random.randint(1000, 2000),来获取1000到2000之间的整数。

在交互式shell中输入如下这行代码,看看会得到什么数字:

>>> random.randint(1, 4)3>>> random.randint(1000, 2000)1294

对游戏代码稍作修改,就可以使得游戏的行为有所不同。在我们的初始代码中,使用了1到20之间的一个整数:

9. number = random.randint(1, 20)10. print('Well, ' + myName + ', I am thinking of a number between 1 and 20.')

将其修改为如下所示:

9. number = random.randint(1, 100)10. print('Well, ' + myName + ', I am thinking of a number between 1 and 100.')

现在计算机将会考虑1到100之间的一个整数,而不是1到20之间的一个整数。第9行的改动将会改变随机数字的范围,但是要记住还要修改第10行,以便让游戏告诉玩家新的数字范围而不是旧的范围。

当你想要为游戏增加随机性时,使用randint()函数。在许多游戏中,都会用到随机性(想想看,有那么多的桌上游戏要使用色子)。

3.5 欢迎玩家

在计算机给number分配了一个随机数之后,它和玩家打招呼:

10. print('Well, ' + myName + ', I am thinking of a number between 1 and 20.')

第10行的print()函数根据姓名来欢迎玩家,并且告诉他们计算机所考虑的数字范围。

乍一看,好像不止1个字符串参数,但是仔细看看这一行。加号把3个字符串连接成1个字符串。最终,只有1个字符串作为参数传递给了print()函数。如果再看一下,会看到引号中的逗号以及各个部分的字符串。

3.6 流程控制语句

在前面各章中,程序执行都是从程序中最上方的指令开始的,直接向下移动,依次执行每一条指令。但是,使用for、if、else和break语句,我们可以根据条件来执行循环或者跳过指令。这些语句就是流程控制语句(flow control statement),因为它们改变了程序执行过程中的流程。

3.6.1 使用循环来重复代码

第12行是一条for语句,它表示了一个for循环的开始:

12. for i in range(6):

循环可以一遍遍地重复执行代码。第12行会将这段代码重复6次。这条for语句以关键字for打头,后面跟着一个新的变量名、in关键字、对range()函数的一次调用,该函数指定了循环应该执行的次数,此外还有一个冒号。让我们来回顾一下一些循环的概念,以便你能够使用循环。

3.6.2 组织语句块

可以把许多代码行组织到一个语句块中。语句块中的每一行代码都拥有和语句块的第1行代码相同的、最小数量的缩进。可以通过查看代码行前面的空格数,来判断语句块的起始和结束。这就是代码行的缩进(indentation)。

Python程序员通常使用增加4个空格的缩进方式,来开始一个语句块。后续的也缩进4个空格的任何代码行,都是这个语句块的一部分。当有一行代码和该语句块开始之前的缩进相同,那么,这个语句块就结束了。语句块可以嵌套在其他已有的语句块之中。在图3-1所示的代码中,我们将语句块标记出来并进行编号。

图3-1 语句块和它们的缩进,语句块中的点表示空格

在图3-1中,第12行没有缩进,它不在任何语句块之中。第13行有4个空格的缩进。既然这行的缩进大于前一行的缩进,那么就开始了一个新的语句块。在这一行之后,拥有相同的缩进或更多的缩进的任何代码行,都被认为是语句块❶的一部分。如果Python遇到了比该语句块的第一行的缩进更少的一行,那么,这个语句块就结束了。空行可以忽略掉。

第18行有8个空格的缩进,这开始了一个新的语句块❷。这个语句块位于另一个语句块❶之中。但是第20行只有4个空格的缩进。因为缩进减少了,我们知道第18行的语句块❷结束了。并且由于第20行和第13行具有相同的缩进,我们知道这是在同一个语句块❶之中。

第21行再次将缩进增加到了8个空格,所以又开始了一个新的语句块,即语句块❸。在第23行,我们退出了语句块❸,并且在第24行,进入了一个语句块中的最后一个语句块,也就是语句块❹。在第24行,语句块❶和❹都结束了。

3.6.3 for循环语句

for语句表示一个循环的开始。循环可以重复执行相同的代码。当执行到一条for语句时,它会进入for语句之后的语句块。在运行完这个语句块中的所有代码之后,执行会回到该语句块的顶部,并再次运行所有的代码。

在交互式shell中输入如下的代码:

>>> for i in range(3):    
print('Hello! i is set to', i)
Hello! i is set to 0
Hello! i is set to 1
Hello! i is set to 2

注意,在你输入了for i in range(3):并按下回车键之后,交互式shell并不会显示另一个>>>提示符,因为它期待你输入一个代码块。在最后一条指令之后,再次按下回车键,告诉交互式shell已经输入了一个代码块(只有在交互式shell中工作的时候,这才适用。当在文件编辑器中编写.py文件的时候,不需要插入一个空行)。

让我们来看一下guess.py的第12行的for循环:

12. for i in range(6):
13. print('Take a guess.') # Four spaces in front of "print"
14.guess = input()
15.guess = int(guess)
16.17.if guess < number:
18. print('Your guess is too low.') # Eight spaces in front of "print"
19.20.if guess > number:
21. print('Your guess is too high.')
22.23.if guess == number:
24. break25.26. if guess == number:

在猜数字游戏中,for语句块从第12行的for语句开始,并且这个for语句块之后的第1行是第26行。在for语句中,在条件之后总是有一个冒号(:)。以一个冒号结束的语句,期待在下一行开始一个新的语句块。图3-2说明了这一点。

图3-2 for循环的执行流程 

图3-2展示了执行流程。执行将会在第13行进入一个for语句块,并且继续向下执行,一旦程序到达了for语句块的末尾,执行会回到第13行for语句块的开始处,而不是继续向下执行下一行代码。由于for语句中的range(6)函数调用,程序执行会这么做6次。通过循环的每一次执行,叫做一次迭代(iteration)。

可以把for语句理解为:“将如下的语句块中的代码执行一定的次数”。

3.7 玩家的猜测

第13行和第14行要求玩家去猜这个神秘的数字是几,并且让他们输入自己的猜测。

13.print('Take a guess.') # Four spaces in front of "print"
14.guess = input()

输入的这个数字会存储到一个名为guess的变量中。

3.8 使用int()函数、float()函数、str()函数和bool()函数来转换值

第15行调用了一个名为int()的新函数。

15. guess = int(guess)

int()函数接受一个参数,并且返回该参数的整数形式。

在交互式shell中输入如下语句,看看int()函数是如何工作的:

>>> int('42')42

int('42')调用将会返回整数值42。

>>> 3 + int('2')5

3 + int('2')这一行给出了一个表达式,使用int()函数的返回值作为该表达式一部分。其结果是整数值5:

尽管可以传递一个字符串给int()函数,但是不能传递任意的字符串。传递'forty-two'给int()函数将会导致一个错误。

>>> int('forty-two')Traceback (most recent call last):  File "<pyshell#5>", line 1, in <module>    int('forty-two')ValueError: invalid literal for int() with base 10: 'forty-two'

传递给int()的字符串,必须是由数字组成的。在猜数字游戏中,我们使用input()函数来获取玩家的数字。

请记住,input()函数总是返回玩家所输入的文本的一个字符串。如果玩家输入的是5,input()函数将返回字符串'5',而不是整数5。Python不能使用比较操作符<和>来比较一个字符串和一个整数值:

>>> 4 < '5'Traceback (most recent call last):  File "<pyshell#0>", line 1, in <module>    4 < '5'TypeError: unorderable types: int() < str()

因此,我们需要将字符串转换为一个整数:

14.    guess = input()15.    guess = int(guess)

在第14行中,guess变量最初存储的是玩家输入的字符串值。第15行使用int()返回的整数值覆盖了guess中的字符串值。这就使得程序后面的代码能够比较guess是大于、小于或者等于number变量中的神秘数字。

类似的,float()和 str()函数分别返回和所传递的参数对应的浮点数和字符串的版本。尝试在交互式shell中输入如下内容:

>>> float('42')42.0>>> float(42)42.0

当把字符串'42'或整数42传递给float()的时候,就会返回浮点数42.0。

>>> str(42)'42'>>> str(42.0)'42.0'

当把整数42传递给str()的时候,它就会返回字符串'42'。但是,当把浮点数42.0传递给str()的时候,它就会返回字符串'42.0'。

使用int()、float()、str()和bool()函数,可以接受一种数据类型的值,而返回另一种数据类型的值。

3.9 布尔数据类型

Python中的每一个值,都属于一种数据类型。目前为止介绍的数据类型有整数、浮点数、字符串和布尔类型。布尔数据类型只有两个值:True或者False。这两个值的首字母必须大写,值的剩余部分必须小写。

例如,尝试把布尔值存储到变量中:

>>> spam = True>>> eggs = False

在这个示例中,我们将spam设置为True,把egg设置为False。记住要将首字母大写。

我们将用布尔值和比较操作符来组成条件。稍后,我们会先介绍比较操作符,然后介绍条件。

3.9.1 比较操作符

比较操作符比较两个值,并且会得到一个True或者False的布尔值。所有比较操作符如表3-1所示。

表3-1 比较操作符

操作符

操作符名称

<

小于

>

大于

<=

小于等于

>=

大于等于

==

等于

!=

不等于

我们已经介绍过数学操作符+、−、*和/。和其他操作符一样,比较操作符把值组合成诸如guessesTaken < 6这样的表达式。

猜数字程序的第17行使用了小于比较操作符。

17.     if guess < number:

稍后我们将更加详细地介绍if语句;现在,让我们来看看跟在if关键字后面的表达式(也就是guess < number部分)。这个表达式包含了两个值(即变量guess和变量number中的值),并且用一个操作符(即小于号 <)将它们连接了起来。

3.9.2 用条件检查True或False

条件(condition)是用比较操作符(如<或>)把两个值组合起来的一个表达式,并且条件的结果是一个布尔值。条件只是结果为True或False的表达式的另一种叫法而已。使用条件的一个位置,就是在if语句中。

例如,条件guessesTaken < 6表示“存储在guessesTaken中的值是否小于数字6?”。如果是,那么该条件结果为True。如果不是,该条件结果为False。

假设guess保存了整数10,而number保存了整数16。由于10小于16,这个条件计算为布尔值True。计算过程如下所示:

3.9.3 体验布尔值、比较操作符和条件

在交互式shell中,输入如下表达式来查看它们的布尔值结果:

>>> 0 < 6True>>> 6 < 0False

因为数字0小于数字6,所以条件0 < 6会返回布尔值True。但是因为6不小于0,所以条件6 < 0的结果是False。

请注意,10 < 10的结果为False,因为数字10并不小于数字10,它们一样大。

>>> 10 < 10False

如果Alice和Bob一样高,你不能说Alice比Bob高或者Alice比Bob矮,这两种说法都不对。

现在尝试在交互式shell中输入如下表达式:

>>> 10 == 10True>>> 10 == 11False>>> 11 == 10False>>> 10 != 10False

在这个示例中,10等于10,因此10 == 10计算为True。但是10不等于11,因此10 == 11为False。即便将两个值的顺序交换,11还是不等于10,因此11 == 10也为False。最后,10等于10,因此10 != 10为False。

我们也可以使用比较操作符来计算字符串表达式:

>>> 'Hello' == 'Hello'True>>> 'Goodbye' != 'Hello'True>>> 'Hello' == 'HELLO'False

'Hello'等于'Hello',因此'Hello' == 'Hello'为True。'Goodbye'不等于'Hello',因此'Goodbye' != 'Hello'也为True。注意,最后一行代码计算为False。在Python中,大写字母和小写字母是不同的,因此'Hello'不等于'HELLO'。

字符串值和整数值不会彼此相等。例如,在交互式shell中输入如下内容:

>>> 42 == 'Hello'False>>> 42 != '42'True

在第1个示例中,42是一个整数,而'Hello'是一个字符串,因此,这两个值并不相等,该表达式计算为False。在第2个示例中,字符串'42'仍然不是一个整数,因此,表达式“整数42不等于字符串'42'”计算为True。

3.9.4 =和==的区别

不要把赋值操作符(=)和“等于”比较操作符(==)搞混淆了。等号(=)用于赋值语句,用来把值存储到变量中;而双等号(==)用于表达式,用来判断两个值是否相等。当你想要使用其中某一个操作符时,很容易会错误地使用了另一个操作符。

只要记住,“等于”比较操作符(==)有两个字符,就像“不等于”比较操作符(!=)也有两个字符一样。

3.10 if语句

第17行是一条if语句。

17.     if guess < number:18.         print('Your guess is too low.') # Eight spaces in front of "print"

如果if语句的条件计算为True,if语句块后面的代码块将会运行。如果该条件为False,那么执行将会跳过if语句块中的代码。使用if语句,可以让程序只运行我们想要让它运行的某些代码。

第17行判断玩家的猜测是否小于计算机的神秘数字。如果是的话,那么执行会移入到第18行的if语句块,并且打印出一条消息来告诉玩家这一点。第20行判断玩家的猜测是否大于计算机的神秘数字。

20.     if guess > number:21.          print('Your guess is too high.')

如果这个条件为True,那么print()函数调用会告诉玩家,他们猜测的数字太大了。

3.11 用break语句提早离开循环

第23行的if语句判断guess是否等于神秘数字。如果是相等的,程序运行第24行的break语句。

23.      if guess == number:24.          break

break语句告诉执行要立即跳出for语句块,跳到for语句块结束之后的第一行。break语句只会出现在循环中,如一个for语句块中。

3.12 判断玩家是否赢了

第26行没有缩进,这表示for语句块已经结束了。

26. if guess == number:

执行会离开for语句块,要么由于它已经循环了6次(当玩家用尽了猜测的机会的时候),要么由于执行了第24行的break语句(当玩家猜对了数字的时候)。第26行判断玩家是否猜对了。如果猜对了,执行进入第27行的if语句块。

27.      guessesTaken = str(guessesTaken)28.      print('Good job, ' + myName + '! You guessed my number in ' +           guessesTaken + ' guesses!')

只有第26行的if语句中的条件为True的时候(也就是玩家猜对了计算机的数字),才会执行第27行和第28行。

第27行调用str()函数,该函数会返回guessesTaken的字符串形式。第28行把字符串连接到一起,告诉玩家他们赢得了游戏以及他们猜了多少次。只有字符串值才可以和其他的字符串连接。这就是为什么第27行必须把guessesTaken转换为字符串形式。否则的话,试图把一个字符串和一个整数连接在一起,将会导致Python显示一个错误。

3.13 判断玩家是否输了

如果玩家用尽了猜测次数,执行将会跳到如下的这行代码:

30. if guess != number:

第30行使用“不等于”比较操作符!=来判断玩家最后猜测的数字是否不等于神秘数字。如果这个条件结果为True,执行会移入到第31行的if语句块中。

第31和第32行在if语句块中,只有第30行的条件为True的时候,才会执行这两行。

31.     number = str(number)32.     print('Nope. The number I was thinking of was ' + number + '.')

在这个语句块中,程序告诉玩家他们没有猜中的神秘数字是什么。这需要把字符串连接起来,但是number中存储的是一个整数值。第31行会用字符串形式覆盖number,以便能够在第32行把它与字符串'Nope. The number I was thinking of was '连接起来。

此时,执行已经到了代码的末尾,程序结束了。恭喜!你已经编写了自己的第一款真正的游戏!

你可以通过修改玩家猜测的次数来让程序变得更难。要让玩家只能猜4次,把第12行的代码:

12. for i in range(4):

通过设置条件guessesTaken < 4,就可以确保循环中的代码只运行4次,而不是6次。这会使游戏变得更难。要让游戏简单一些,把条件设置为guessesTaken < 8或guessesTaken < 10。这会导致循环运行的次数更多一些,从而接受玩家更多次的猜测。

3.14 小结

编程就是为程序编写代码的行为,也就是说,创建计算机可以执行的程序。

当你看到某人使用计算机程序时(例如,玩你的“猜数字”游戏),你所看到的只是屏幕上出现的一些文本。根据程序的指令以及玩家用键盘输入的文本(程序的输入),程序决定了到底在屏幕上显示什么样的文本(程序输出)。程序只是基于用户输入而执行的动作的一个指令集。

只有几个不同种类的指令。

  • 表达式是由操作符连接的值。表达式的最终结果是一个单独的值,例如2 + 2的结果是4,或者'Hello' + ' ' + 'World'的结果是'Hello World'。当表达式跟在if和while关键字后面的时候,我们也可以把它们称为条件。

  • 赋值语句把值存储到变量中,以便在随后的程序中能够使用这个值。

  • if、while和break语句都是流程控制语句,可以导致执行跳过指令、循环地执行指令或者跳出循环。函数调用也可以通过跳转到函数中的指令来改变执行的 流程。

  • print()函数和input()函数。这两个函数分别在屏幕上显示文本和从键盘上接收到的文本。这叫做I/O(输入/输出),因为I/O负责程序的输入和输出。

就这些了,只有这4种指令。当然,关于这4种类型的指令,还有许多的细节。在本书后面的各章中,我们将学习新的数据类型和操作符、新的流程控制语句以及许多其他的Python函数;还有不同类型的I/O,如用鼠标输入、输出声音和图形,而不是只输出文本。

本文摘自《Python游戏编程快速上手(第4版)》

延伸推荐:

10月| 异步新书人气王票选

Python3.5的新书,这一本不可错过!


点击阅读原文,购买《Python游戏编程快速上手(第4版)》

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存