程序说明
本项目我们将用Python语言设计一个计算机程序来模拟“猫咪藏在哪个房间”游戏,该程序中,计算机代替你朋友的角色,而你的角色不变。你将通过一个输入框和几个按钮来操作你的程序,在该项目中,计算机的回应将显示在专用的输出控制台,先不要关心画布的反应。其实,构造每个项目的初始版本时,把一些有用的信息输出到控制台是一种行之有效的开发策略,也就是说,先集中精力实现正确的程序逻辑,然后设法在画布上让信息以某种优美的形式展示出来,因为调试图形输出的逻辑错误有时候极具挑战。程序的文件名称为GuessRoomNumber.py,程序除了要用到前一个项目“杠子老虎鸡虫”已经熟悉的Python语言功能之外,还要使用SimpleGUITk来创建窗口、画布、输入框、按钮等。
通过此项目可以练习如下能力
- 了解二分查找(折半查找)的秘密
- 理解计算机程序的运行是如何受事件驱动的
- 增强逻辑思维能力
- 培养解决问题的能力
项目编写步骤
- 确定一组存储游戏状态的全局变量。显而易见,你需要一个全局变量来保存程序产生的秘密房间号码,建议该变量的名称为secret_number。为了扩展基础版,你还需要其它一些全局变量。
- 弄清楚如何在给定的low(下限)至high(上限)范围内产生一个随机秘密数字,及秘密房间号。在谈及范围时,我们遵循Python的标准,即包含下限,排除上限,数学上表示为[low, high)。例如[0, 3)意味着该范围包含0、1、2三个数字。我们建议在开始编码时将范围设置为[0,25),即25个房间。
- 搞清楚如何利用simpleguitk模块包创建一个文本输入框。你将使用该文本输入框来获取游戏玩家输入的猜测数字。
- 编写事件处理函数guess_room_number(input),参数input保存着玩家输入的猜测值,将该值与秘密房间号进行比较并输出恰当的回应。注意,input为字符串类型,因此在比较之前,你必须将其转化为整型数字。如果玩家猜中了房间号,除了输出恰当的信息之外,请补充输出“请点击房间按钮重新开始游戏”,之后玩家再猜,均输出“请点击房间按钮重新开始游戏”。
- 至此,玩家可以无限制的猜测下去,直到猜对为止,猜中后如果希望继续玩一把,必须关闭程序,然后重新运行程序。
- 编写new_game()函数,将产生秘密房间号的代码移到该函数中,也就是说,调用new_game()函数将会计算秘密房间号并将其赋值给一个全局变量,该函数也应当输出一些恰当的信息提示玩家。完成该函数后,你就可以在启动窗口的代码之前调用new_game()。
- 第1个增强功能:使用simpleguitk模块包添加重新启动游戏的按钮,这样你就无需在玩家猜中后先关闭程序,然后重新运行程序。你必须添加三个按钮:“25个房间”、“36个房间”、“100个房间”,从而允许玩家选择秘密房间号的范围。玩家在游戏运行的任何时刻点击任何一个按钮将重新开始一轮游戏并输出恰当的信息。每个按钮的事件处理函数要为秘密房间号的上限范围(应当是一个全局变量)设置对应的值,然后调用new_game()在新的范围内重新设置秘密房间号,最后设置标志新一轮游戏已经开始的全局变量为True。
- 在玩该游戏时,可能你已经注意到,如果猫咪藏在25个房间中的某一个,即猜测的范围为[0,25),首先猜12是一个好注意,如果计算机的回应为“太小了”,秘密房间号一定在[13,25)范围内,然后最好猜19,以此类推。这种每次缩小一半猜测范围的技术就是计算机科学领域中著名的二分查找算法。
- 第2个增强功能:为游戏程序添加限制玩家猜测次数的功能,每次猜测后,你的程序应当补充输出剩余的猜测机会,一旦玩家用完了规定的猜测机会,玩家失败,游戏程序输出相应的提示信息,玩家如果想继续游戏,必须点击房间按钮。
- 如果玩家按照二分查找策略来猜测,在[low, high)范围内,要保证玩家在n次之内猜中,n必须为满足2 ** n >= high - low + 1的最小整数。因此,对于范围[0,25),n为5,[0,36),n为6,[0,100),n为7,我们可以在三个按钮的事件处理函数中为表示剩余猜测机会的全局变量赋予相应的值(5、6、7),当然我们也可以利用math.log and math.ceil函数通过low和high计算n。
- 游戏程序初次启动时,猜测的范围应当为[0,25),一轮游戏结束后(玩家猜中后或玩家用完了猜测机会),应提示玩家点击房间按钮开始新一轮游戏,如果玩家此时没有点击按钮继续进行猜测(直接按回车,或输入新的数字再按回车),应当提示玩家点击房间按钮开始新一轮游戏。只要玩家点击房间按钮,当前一轮游戏无条件结束,在按钮载明的范围立即开始新一轮游戏。
以下为项目代码
#代码注释齐全
import simpleguitk as gui # 简单用户图形界面接口
import random # 用random.randrange(range_high)随机生成猫咪要藏的房间号
import math # 用math.log(x, base)计算猜中所需的最少次数
# 初始化全局变量
secret_number = None # 每个回合计算机随机产生的秘密数字
range_high = 25 # 房间号的上限
new_game_started = True # 新一轮游戏是否开始
remaining_guesses = 0 # 一个回合剩余的猜测次数
canvas_height = 500 # 画布高度,单位为像素
canvas_width = 500 # 画布宽度,单位为像素
room_list = [] # 所有房间的状态
cat_image = gui.load_image("http://202.201.225.74/video/PythonResoure/ProjectResource/images/project2/cat.png") # 猫咪图片
def new_game():
"""启动或重新启动游戏的辅助函数"""
# 添加你自己的代码后删除pass
global secret_number, new_game_started
if new_game_started == True: # 判断是否是开始新游戏
new_game_started = False # 标记新游戏未开始
range25() # 设置房间号范围为[0, 25)
else:
secret_number = random.randrange(range_high) # 生成新的随机答案
# 初始化所有房间
room_list.clear()
for i in range(range_high):
room_list.append(False)
def range25():
"""创建25个房间"""
# 设置房间号的范围为[0,25)并重新开始游戏
global range_high, remaining_guesses
print("------------range25()------------------")
print('新的一轮游戏开始')
print('您应当在0和24之间猜一个数字')
print('您拥有5次机会')
range_high = 25 # 设置房间范围为[0,25)
remaining_guesses = int(math.log(25, 2)) + 1 # 计算剩余的猜测次数
new_game() # 开始新游戏
def range36():
"""创建36个房间"""
# 设置房间号的范围为[0,36)并重新开始游戏
global range_high, remaining_guesses
print("------------range36()------------------")
print('新的一轮游戏开始')
print('您应当在0和36之间猜一个数字')
print('您拥有6次机会')
range_high = 36 # 设置房间范围为[0,36)
remaining_guesses = int(math.log(36, 2)) + 1 # 计算剩余的猜测次数
new_game() # 开始新游戏
def range100():
"""创建100个房间"""
# 设置房间号的范围为[0,100)并重新开始游戏
global range_high, remaining_guesses
print("------------range100()------------------")
print('新的一轮游戏开始')
print('您应当在0和100之间猜一个数字')
print('您拥有7次机会')
range_high = 100 # 设置房间范围为[0,100)
remaining_guesses = int(math.log(100, 2)) + 1 # 计算剩余的猜测次数
new_game() # 开始新游戏
def guess_room_number(user_input):
"""文本输入框控件的处理函数,也是游戏的主要逻辑实现的函数"""
global remaining_guesses, secret_number
user_input = int(user_input) # 将用户输入转换为整数
if remaining_guesses == 0: # 检查是否已经没有剩余猜测次数
print("机会已用完,请点击房间号重新开始")
else:
if user_input == secret_number: # 判断用户猜测是否正确
print("正确,请点击房间号重新开始")
elif user_input > secret_number:
print("--太大了")
remaining_guesses -= 1 # 剩余次数减1
print('您还有' + str(remaining_guesses) + '机会')
elif user_input < secret_number:
print("--太小了")
remaining_guesses -= 1 # 剩余次数减1
print('您还有' + str(remaining_guesses) + '机会')
update_room(int(user_input)) # 更新房间状态,请不要修改这行代码
frame = gui.create_frame("猫咪藏在哪个房间", canvas_width, canvas_height) # 创建窗口
# 注册控件事件
frame.add_button("25个房间", range25, 100)
frame.add_button("36个房间", range36, 100)
frame.add_button("100个房间", range100, 100)
frame.add_input("猜测房间", guess_room_number, 100)
# 调用new_game初始化一些全局变量并向终端输出新一轮游戏开始的相关信息
new_game()
def draw(canvas):
"""画布显示刷新事件的处理函数"""
root = int(math.sqrt(range_high))
length = canvas_width // root
for row in range(root):
for col in range(root):
index = root * row + col
text_width = frame.get_canvas_textwidth(str(index), 12, 'sans-serif')
top_left = [length * col, length * row]
top_right = [length * (col + 1), length * row]
bottom_right = [length * (col + 1), length * (row + 1)]
bottom_left = [length * col, length * (row + 1)]
if not room_list[index]:
canvas.draw_polygon([top_left, top_right, bottom_right, bottom_left], 1, 'Red', 'Green')
canvas.draw_text(str(index), (top_left[0] + (length - text_width) / 2, top_left[1] + length - 2),
12,
'White', 'sans-serif')
else:
canvas.draw_polygon([top_left, top_right, bottom_right, bottom_left], 1, 'Red', 'Blue')
canvas.draw_text(str(index), (top_left[0] + (length - text_width) / 2, top_left[1] + length - 2),
12,
'White', 'sans-serif')
if index != secret_number:
no_cat = frame.get_canvas_textwidth('没有猫', 12, 'sans-serif')
canvas.draw_text('没有猫', (top_left[0] + (length - no_cat) / 2, top_left[1] + length / 2), 12,
'White', 'sans-serif')
else:
canvas.draw_image(cat_image, [256, 256], [512, 512],
[top_left[0] + length // 2, top_left[1] + length // 2], [length, length])
def update_room(guess):
"""更新房间的状态"""
if guess == secret_number:
for i in range(range_high):
room_list[i] = True
elif guess < secret_number:
if remaining_guesses == 0:
for i in range(range_high):
room_list[i] = True
else:
for i in range(0, guess + 1):
room_list[i] = True
else:
if remaining_guesses == 0:
for i in range(range_high):
room_list[i] = True
else:
for i in range(guess, range_high):
room_list[i] = True
if __name__ == '__main__':
frame.set_draw_handler(draw) # 注册显示刷新处理事件,每秒调用draw函数60次
frame.start() # 启动窗口框架
第三方库的下载
不说废话,直接来个最有效的下载方式,通过pip命令下载
打开Terminal,然后输入命令:pip install 第三方库+ 源地址
pip install simpleguitk -i https://pyp.tuna.tsinghua.edu.cn/simple
这里用了清华源