一、缘起
下午放学了,豆豆回到家,看到妹妹在玩《记忆翻牌》小游戏,游戏规则很简单,只要连续翻开两张同样的牌,就能消掉它们。看妹妹玩得津津有味的样子,豆豆灵机一动。我能不能使用Python编写一个简单的小游戏,来实现类似的记忆力考验效果呢?
豆豆的想法是,我可以在控制台(也就是终端)输出一个字符串,然后停上很短的时间后,马上把它擦掉。然后要求游戏者输入刚才显示的字符串。这样的话就能判断游戏者记忆的是不是正确的内容,从而进行计分了。
不过,要实现这个看似简单的小游戏,还需要一些小技巧才行。比如,我们使用Python的print
函数输出内容之后,光标就换行到下一行了,而且,怎么才能把刚才输出的内容“擦掉”呢?豆豆决定先来做一些试验。
二、准备
2.1 输出“擦除”
首先要解决的是“擦除” ‘print’输出的内容问题。豆豆想,如果我们让print
函数输出后不换行,然后暂停一秒钟后,再让print
函数输出一串以’\r’开头的空格,就能把原来输出的内容“覆盖”了。因为’\r’就是“回车”,它会让光标回到这一行的开头处的。于是豆豆试着输入以下的代码:
import time
print(12345,end='')
time.sleep(1)
print('\r ')
奇怪的是,豆豆发现第2行代码输出的“12345”根本没显示,而是一秒之后直接输出!这是怎么回事呢?经过查询资料,豆豆才知道,我们使用print
函数输出的内容并不是直接显示在屏幕上,而是会被存储到一个“缓冲区”,而这个缓冲区中出现了换行符时,系统才把它们显示到屏幕上(不同的操作系统机制略有不同),所以默认情况下print
函数会自动在输出内容后加上换行符。但由于豆豆调用print
函数时加上了end=''
参数,也就是不让它加默认的换行,所以要输出的内容就不会马上显示了。要解决这个问题,必须引用sys
库中的标准输出流stdout
对象,调用sys.stdout.flush()
,它的作用就是将缓冲区的内容马上输出到屏幕上!
于是豆豆将代码修改如下:
import time
import sys
print(12345,end='')
sys.stdout.flush()
time.sleep(1)
print('\r ')
保存、运行,果然是预期的效果。“12345”先显示出来,1秒之后就消失了。这个关键的问题解决了。
2.2 程序计时问题
接下来,豆豆考虑一共显示10道记忆力考验的题目,程序结束时提示游戏者一共答对几道题目,使用了多长时间。如何实现计时呢?通过查阅资料,豆豆了解到有许多方法可以计算程序运行时间,比如time
模块、datetime
模块、timeit
模块都有相关的函数可以实现。不过既然引用了time
模块,用它还是更简单一些,而且我们也不必要过度关心精确性,只要在程序开始时记录一下时间,在结束时再记录一下时间,用结束时间减去开始时间就可以了。
import time
time_start = time.time() # 记录开始时间
time.sleep(2)
time_end = time.time() # 记录结束时间
time_sum = time_end - time_start # 计算的时间差为程序的执行时间,单位为秒
print(time_sum)
运行发现,得到一个很长的浮点数,我们使用时保留一位小数就可以了。
有了这两段代码,豆豆觉得,实现预期的程序应该没太大问题了。
三、实现
豆豆首先实现了一个简单的版本,基于前面分析的代码,先输出一个四位数(1000~9999),暂停1秒钟后再擦除,并提示游戏者输入自己记忆的数字,判断输入的对错进行计分,再使用一个for
循环,把这个过程重复10次,最后输出分数和使用的时间:
import time
import random
import sys
start = time.time()
success = 0
for i in range(10):
num = random.randint(1000,9999)
print(num,end='')
sys.stdout.flush()
time.sleep(0.5)
print('\r ')
i = input('请输入数字:')
if int(i) == num:
success +=1
print('回答正确')
else:
print('回答错误')
end = time.time()
print('一共答对%d题,用时%.2f秒'%(success,end-start))
运行程序,豆豆发现这还是有一定挑战性的。不过游戏似乎还是太简单了点。能不能让游戏者自己设定难度呢?现在我们记忆的是一个四位数字,可以让游戏者指定一个4-10之间的数字,来决定生成数字的位数,这样难度就可以调整了。当然,这时我们得根据输入的数字计算随机数范围,而不能直接用1000~9999了,豆豆观察发现,可以使用 10的位数减一次方,也就是 10**(位数-1)
计算出下限,比如4位数字,那就是10**3=1000
,但是最大的四位数怎么算呢?我们可以用最小的五位数减一啊!豆豆不禁为自己的灵感骄傲起来。只要使用10**位数-1
不就好了?
相应地,输出的数字位数增加了,擦除的时候也需要动态指定输出的空格数量(也就是\r
后面的空格数量)。另外,擦除之前等待的时间长短也要根据数字位数计算,数字短,等待的时间短,否则等待时间长一些。豆豆觉得,4个字符是0.5秒,那么一个字符就大概按0.15秒计算,乘以字符数就行了。于是,他对程序进行了升级:
# 使用Python进行记忆力训练
import random
import time
import sys
while 1:
level = int(input('请输入一个要挑战的等级4-10之间的整数:'))
# 确保level变量在4-10范围内才退出循环
if level>=4 and level<=10:
break
level_min = 10**(level-1) # 计算最小的n位数
level_max = 10**level-1 # 计算最大的n位数
print("来训练一下你的记忆力吧!")
start = time.time()
success = 0
for i in range(10):
num = random.randint(level_min,level_max)
print(num,end='')
sys.stdout.flush()
time.sleep(0.15*level)
print('\r' + ' '*level)
i = input('请输入数字:')
if int(i) == num:
success +=1
print('回答正确')
else:
print('回答错误')
end = time.time()
print('一共答对%d题,用时%.2f秒'%(success,end-start))
大功告成了!
豆豆试玩了一下,发现自己最多到第8关就很难通过了……看来还是要好好训练啊,豆豆想。
四、总结
通过本游戏的开发,豆豆发现自己至少掌握了指定位数的随机数生成、控制台内容“擦除”以及程序运行时间计算的知识。果然,输出和应用才是最好的学习方式。
请你参考《记忆力考验》程序,编写一个《寻找幸运数字》游戏,规则如下:
1、游戏每轮生成一组10个数字,这9个数字是把0-9一共10个数字中随机抽掉一个数字,要求玩家找出并输入被抽掉的数字,必须输入正确才能进入下一轮;比如 0 1 2 4 5 6 7 8 9,需要输入缺少的数字3才能进入下一轮,输入错误会提示重新输入;
2、游戏一共进行10轮,统计使用的秒数并提示评价:
- 使用秒数大于等于25 :反应太慢啦,再去练练吧;
- 使用秒数大于等于20小于25:你的反应有点慢哦
- 使用秒数大于等于17小于20:你的反应速度还行,加油!
- 使用秒数小于17秒:你的反应速度太棒了!
看你写有点难