Program Arcade Games
With Python And Pygame

Chapter 4: 随机数,循环和猜数字游戏

在我们开始学习图形相关内容前,最后一步是学会如何循环执行一段代码。 大多数游戏都会“循环”。 它们会将某段相同的代码不断重复执行。 例如,下面这个猜数字游戏就会根据用户的每次猜测不断循环。

Video: The for loop
Hi! I'm thinking of a random number between 1 and 100.
--- Attempt 1
Guess what number I am thinking of: 50
Too high.
--- Attempt 2
Guess what number I am thinking of: 25
Too high.
--- Attempt 3
Guess what number I am thinking of: 17
Too high.
--- Attempt 4
Guess what number I am thinking of: 9
Too low.
--- Attempt 5
Guess what number I am thinking of: 14
Too high.
--- Attempt 6
Guess what number I am thinking of: 12
Too high.
--- Attempt 7
Guess what number I am thinking of: 10
Too low.
Aw, you ran out of tries. The number was 11.

等一下,这跟图形和视频游戏有何相关? 关系很大。 游戏显示的每一其实都是在执行循环。 你也许对游戏显示的每秒帧数(FPS)这个统计数据很熟悉。FPS表示了计算机每秒更新屏幕显示的次数。 速率要高,游戏越流畅。(尽管超过60FPS就会高于大多数显示器本身的更新频率,但是没有太大意义。) 图例4.1展示了游戏Eve Online和运行它时电脑能够显示的帧数的图形变化。

fig.fps
Figure 4.1: FPS在视频游戏中

在游戏中的循环的工作原理就和图例中的流程图一样4.2. 尽管现在的游戏无比复杂,但是循环的内部其实和我们第一站写的计算器是一样的。 获得用户输入,执行计算,输出结果。在视频游戏里,我们总在努力以每秒60次的速率重复这些过程。

fig.game_loop
Figure 4.2: Game loop

循环之中还可以嵌套循环。一个真正的“循环的循环'。 来看下面这个“Draw Everything”的图例4.2. 这组代码通过循环把每一个游戏中需要的物件画出来。 这个循环是在画出游戏每一帧的循环之内的,如图所示4.3.

fig.draw_everything
Figure 4.3: Draw everything loop

在Python中有两种主要的循环, for循环和while寻呼。循如果你想重复一个已知的次数,可使用for循环。如果你想循环直到发生了什么事件 (比如用户按下了退出按钮)那就使用while循环。

例如,for循环可以用来打印所有学生的成绩,如果计算机知道学生人数的话。 while循环可以用于查看用户什么时候点击了鼠标按键,因为计算机无法得知需要等多久。

4.1 For循环

下面这个for循环的例子运行了五次print语句。 它也可以轻松地运行100或者1000000次,只需要把5改成需要循环的次数。 请注意在写法上for语句和if语句的相似性。 两者都以冒号结束,并使用缩进来标记需要被影响的语句。

for i in range(5):
    print("I will not chew gum in class.")
Output:
I will not chew gum in class.
I will not chew gum in class.
I will not chew gum in class.
I will not chew gum in class.
I will not chew gum in class.

第一行中的i是以个记录和追踪程序熏话了多少次的变量。 它是一个新的变量并且可以被任何合法的变量名来命名。程序员通常使用i来做循环变量名, 因为i是increment(增长)的缩写。 这个变量能够帮助我们追踪循环何时结束。

range函数控制了循环的次数。在这个例子中,循环了5次。

下面这个例子会打印“Please,”五次,和“Can I go to the mall?”仅一次。 “Can I go to the mall?”没有缩进所以它不属于for循环,也就不会打印直至 for循环完成。

for i in range(5):
    print("Please,")
print("Can I go to the mall?")
Output:
Please,
Please,
Please,
Please,
Please,
Can I go to the mall?

下面这段代码沿用了先前的例子并缩进了第三行。这个改动会使得程序打印五遍“Please,”和“Can I go to the mall?” 因为“Can I go to the mall?”现在缩进了,变成了for循环的一部分,所以会和“Please,”一起打印五遍出来。

for i in range(5):
    print("Please,")
    print("Can I go to the mall?")
Output:
Please,
Can I go to the mall?
Please,
Can I go to the mall?
Please,
Can I go to the mall?
Please,
Can I go to the mall?
Please,
Can I go to the mall?

下面的代码会打印出0到9。请注意循环是从0开始而且不包括10。 很容易假设range(10)会包含10,但事实上循环结束在它之前。

for i in range(10):
    print(i)
Output:
0
1
2
3
4
5
6
7
8
9

程序并非一定要使用变量名i, 也可以是其他的命名。 比如程序员可能会使用lineNumber,如果她正在处理一个文本文件。

如果一个程序员想要从1循环到10, 有几种方法可以做到。第一种方法是给range函数传递两个数字而不是一个。 第一个数字是起始值,第二个是比结尾值大1的数。

的确需要花一些练习时间去习惯for包含起始值但不包含结尾值的概念。 下面这个例子使用了(1,11)的范围,数字1到10会被打印出来。起始值1包括在内,而末尾值11并没有。

for i in range(1, 11):
    print(i)
Output:
1
2
3
4
5
6
7
8
9
10

另一种方法去打印1到10是依然使用range(10) 并使用变量i来从0循环到9。 只不过每一次在打印的时候加上1,效果是一样的。两种办法都可行。

# Print the numbers 1 to 10.
for i in range(10):
    print(i + 1)

4.1.1 用比1大的数来计数

如果一个程序需要以2来计数或者其他的数量,方法很简单。也有两种途径。 最简单的办法是使用第三个数来告诉range函数它是以2来增加的。第二种办法是依然以1来增加,但是变量本身乘以2。 下面这段代码列出了这两种方法。

#两种方法去打印从2到10的偶数
for i in range(2,12,2):
    print(i)

for i in range(5):
    print((i + 1) * 2)
Output:
2
4
6
8
10
2
4
6
8
10

我们也可以通过给range函数一个负的步长来倒者数到0。 在下面这段代码中,是从10开始,一直倒数但不含0,每次增加的是-1。 使用这样的循环最难的地方是有时候会不小心把起始值和末尾值调换了位置。 这样的程序应该以大的数开始。而通常for循环都是从range函数里最小的值开始计数的。 的

for i in range(10, 0, -1):
    print(i)
Output:
10
9
8
7
6
5
4
3
2
1

如果一个程序需要以一个比较复杂样式的计数方法,我们可以把记数都列出来。(一个详细的讨论会在第七章,这里只是给你们预览下,有所了解)

for i in [2,6,4,2,4,6,7,4]:
    print(i)

This prints:

2
6
4
2
4
6
7
4

4.1.2 嵌套循环

试着去猜测下端代码会打印出什么。然后输入代码来瞧瞧你是否正确。

# 这打印的结果是什么是 为什么?
for i in range(3):
    print("a")
for j in range(3):
	print("b")
Video: Nesting for loops

下段代码和上面的几乎一样。 第二个for循环也被缩进了一步,所以它现在是被嵌套在第一个循环里面的。 这个改动使得程序的运行变化相当大。试着运行下看看。

# 这打印的结果是什么? 为什么?
for i in range(3):
    print("a")
    for j in range(3):
        print("b")

print("Done")

我不会告诉你代码是在做什么,去让计算机告诉你。

Video: for loop animation

4.1.3 累积总计

一个配合循环使用的常规操作是获得累积总计。 在这本书中“累积总计”的代码样式被运用地很多。 累积一个总的得分,一个人账户上所有的交易,用累积总计求平均数,等等。 你或许应该给这段代码做上书签,以便将来对此回来查阅。 下面的代码中,用户依次输入了五个数字,程序求出了它的累积总计。

total = 0
for i in range(5):
    new_number = int(input("Enter a number: " ))
    total += new_number
print("The total is: ", total)
Video: Using a loop to keep a running total

请注意第一行创建了一个变量total, 并给它设了个初始值0。 经常很容易忘记需要创建一个初始值变量,并设为0。没有它电脑不知道第四行在做什么。 它不知道如何把new_number加到 total上,因为total还没有值。

一种常见的错误是使用i加到total上而不用new_number。 请记住,我们正在对用户输入的数做累加,而不是现在循环计数的累加。

说到循环计数,我们可以用它来做些数学运算。例如:

$s=\sum\limits_{n=1}^{100}n$

如果你不熟悉这种方程,其实它只是一种华丽的写法来描述:

$s=1+2+3+4+5 \ldots 98+99+100$

下面的代码把1到100的数加了起来。它展示了循环中累积在循环之中的位置。 它也用到了另一个变量sum来追踪记录累积总和。

#和是多少?
sum = 0
for i in range(1, 101):
    sum = sum + i
print(sum)

这里是另一种变形。它向用户取了五个数,并求出其中有多少次输入的是0。

total = 0
for i in range(5):
    new_number = int(input( "Enter a number: "))
    if new_number == 0:
        total += 1
print("You entered a total of", total, "zeros")

程序员如果理解了嵌套的for循环和累积总和,那么就应该能预测到下面各段代码的输出结果。

# a的值是什么?
a = 0
for i in range(10):
    a = a + 1
print(a)

# a的值是什么?
a = 0
for i in range(10):
    a = a + 1
for j in range(10):
    a = a + 1
print(a)

#a的值是什么?
a = 0
for i in range(10):
    a = a + 1
    for j in range(10):
        a = a + 1
print(a)

不要太快跳过这个部分。请试一试预测上面代码的结果。然后把它复制到Python程序中来运行下看看你是否正确。 如果不对,要想办法明白为什么。

4.2 for循环举例

这个例子涵盖了常用for循环的用例和它们具体是如何工作的。

# Sample Python/Pygame Programs
# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# Print 'Hi' 10 times
for i in range(10):
    print("Hi")

# Print 'Hello' 5 times and 'There' once
for i in range(5):
    print("Hello")
print("There")

# Print 'Hello' 'There' 5 times
for i in range(5):
    print("Hello")
    print("There")

# Print the numbers 0 to 9
for i in range(10):
    print(i)

# Two ways to print the numbers 1 to 10
for i in range(1, 11):
    print(i)

for i in range(10):
    print(i + 1)

# Two ways to print the even numbers 2 to 10
for i in range(2, 12, 2):
    print(i)

for i in range(5):
    print((i + 1) * 2)

# Count down from 10 down to 1 (not zero)
for i in range(10, 0, -1):
    print(i)

# Print numbers out of a list
for i in [2, 6, 4, 2, 4, 6, 7, 4]:
    print(i)

# What does this print? Why?
for i in range(3):
    print("a")
    for j in range(3):
        print("b")

# What is the value of a?
a = 0
for i in range(10):
    a = a + 1
print(a)

# What is the value of a?
a = 0
for i in range(10):
    a = a + 1
for j in range(10):
    a = a + 1
print(a)

# What is the value of a?
a = 0
for i in range(10):
    a = a + 1
    for j in range(10):
        a = a + 1
print(a)

# What is the value of sum?
sum = 0
for i in range(1, 101):
    sum = sum + i

4.3 While循环

Video: The while loop

for循环常用在程序知道循环的次数的时候; while循环常用在程序需要循环至某个特殊条件发生的时候;

奇特的是, while循环可以适用于任何for循环使用的场景。 可以理解成循环至某个增加的变量到达了一个阈值。那既然while循环无所不能,还需要for循环做什么? 因为for循环更简单而且更容易写。 一个for循环看起来是这样的:

for i in range(10):
    print(i)

while循环完成起来是这样的:

i = 0
while i < 10:
    print(i)
    i = i + 1

while寻呼的第一行设置了一个“哨兵”变量用于对已经执行过的循环次数进行记录。 这在for循环中是自动完成的所以可以省去一行代码。 第二行起是真正的while循环。while循环的格式也很if语句很相思。 如果条件成立,循环中的代码就会重复执行。 第四行添加了变量的递增。在for循环中这是自动发生的,同样省去了一行代码。 所以从代码角度,for循环要比while循环更简洁且易于阅读。 另一方面while寻呼可以完成各种任务。

一种常见的错误是把for循环和while循环混淆起来。下面这段代码展示了一个 程序员不能分清楚for循环还是while循环。

while range(10):
    print(i)
不用把range函数和while循环一起使用!

range函数只在for循环中才能工作。 不要把它用在while循环中!

4.3.1 使用递增运算符

递增运算符常常用于while循环之中。 下面的代码

i = i + 1

可以简化成:

i += 1

while循环中看起来是这样的:

i = 0
while i < 10:
    print(i)
    i += 1

这对减法和乘法也同样适用。例如:

i *= 2

和下面是相同的:

i = i * 2

看看你是否能分析出下面的代码会打印出什么:

i = 1
while i <= 2 ** 32:
    print(i)
    i *= 2

4.3.2 循环直至用户想退出

一个很常见的操作是循环至用户提出了退出的请求:

quit = "n"
while quit == "n":
    quit = input("Do you want to quit? ")

有许多种办法让循环结束。使用一个布尔型变量来触发事件是其中方法之一。这有一个例子:

done = False
while not done:
    quit = input("Do you want to quit? ")
    if quit == "y":
        done = True

    attack = input("Does your elf attack the dragon? ")
    if attack == "y":
        print("Bad choice, you died.")
        done = True

但这并不完美,因为当用户想要退出的时候,代码还是会问她是否想攻击巨龙。你应该如何来解决?

这里有个例子使用了while循环,直到某个值非常接近于0:

value = 0
increment = 0.5
while value < 0.999:
    value += increment
    increment *= 0.5
    print(value)

4.3.3 使用while寻呼时的常见错误

程序员想从10开始倒数。 下面的代码出了什么问题,应该如何解决?

i = 10
while i == 0:
    print(i)
    i -= 1

下面的代码试图数到10的过程中哪里出了错? 实际运行是会发生什么? 应该如何解决?

i = 1
while i < 10:
    print(i)

4.4 while循环的举例

虾米的程序涵盖了我们讨论过的while循环的多种用法。

# Sample Python/Pygame Programs
# Simpson College Computer Science
# http://programarcadegames.com/
# http://simpson.edu/computer-science/

# A while loop can be used anywhere a for loop is used:
i = 0
while i < 10:
    print(i)
    i = i + 1

# This is the same as:
for i in range(10):
    print(i)

# It is possible to short hand the code:
# i = i + 1
# With the following:
# i += 1
# This can be done with subtraction, and multiplication as well.
i = 0
while i < 10:
    print(i)
    i += 1

# What would this print?
i = 1
while i <= 2**32:
    print(i)
    i *= 2

# A very common operation is to loop until the user performs
# a request to quit
quit = "n"
while quit == "n":
    quit = input("Do you want to quit? ")

# There may be several ways for a loop to quit. Using a boolean
# to trigger the event is a way of handling that.
done = False
while not done:
    quit = input("Do you want to quit? ")
    if quit == "y":
        done = True

    attack = input("Does your elf attach the dragon? ")
    if attack == "y":
        print("Bad choice, you died.")
        done = True

value = 0
increment = 0.5
while value < 0.999:
    value += increment
    increment *= 0.5
    print(value)

# -- Common problems with while loops --

# The programmer wants to count down from 10
# What is wrong and how to fix it?
i = 10
while i == 0:
    print(i)
    i -= 1

# What is wrong with this loop that tries
# to count to 10? What will happen when it is run?
i = 1
while i < 10:
    print(i)

4.5 随机数

随机数在计算机科学中十分受到重用,尤其是涉及到游戏和模拟的程序。

4.5.1 randrange函数

视频:随机数

默认情况下,Python不知道如何产生随机数。所以非常有必要的让Python能够导入一个可以产生随机数的核心库。 为了使用随机数,第一步就是在在程序顶端使用一个 import 语句:

import random

和pygame一样, 非常重要的是,不要创建一个和被导入的内容名字相同的文件。 创建一个random.py的文件会造成Python开始导入这个新创建的文件而不是能产生随机数的系统中的核心库文件。新

在这之后,随机数可以经由randrange函数产生。 例如,下面的代码会生成一个在0到49之间的随机数。默认条件下,会包含下限0。

my_number = random.randrange(50)

下面的代码会产生100到200之间的随机数。要注意的就是range函数的第二个参数定义的上限值并不包含在内。 因此如果你想产生包括200在内的随机数,设置成201。

my_list = ["rock", "paper", "scissors"]
random_index = random.randrange(3)
print(my_list[random_index])

4.5.2 random函数

先前的例子都是产生整数。如果一个需要一个浮点数,程序员可以使用random函数。

下面的代码产生了一个0到1之间的随机数,例如0.4355991106620656.

my_number = random.random()

使用一些几本的数学技巧,这个数可以被调整。例如下面的代码中产生了一个10到15之间的浮点数:

my_number = random.random() * 5 + 10

4.6 复习

4.6.1 选择题小测验

Click here for a multiple-choice quiz.

4.6.2 练习表

Click here for the chapter worksheet.

4.6.3 实验

Click here for the chapter lab.


You are not logged in. Log in here and track your progress.