20223210 2022-2023-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 2232
姓名: 黄湛
学号:20223210
实验教师:王志强
实验日期:2023年5月20日
必修/选修: 公选课
(一)实验内容
python的综合应用
利用tkinter编写扫雷小游戏
(二)实验过程及结果
扫雷小游戏 懂得都懂
1.初始化设置
import random
from tkinter import *
import tkinter as tk
from tkinter.messagebox import *
from functools import partial
game = Tk()
game.title("扫雷")
nx = [-1,-1,-1,0,0,1,1,1]
ny = [-1,0,1,-1,1,-1,0,1]
color = ["White","Blue","Green","Red","Brown","Gold","Pink","Purple","Black"] #不同数字的颜色
game_map = [] #隐藏的地图 ‘X’表示雷
state_map = [] #地图的状态 0未被点击 1已点击 2被标记 3被质疑 4雷被引爆
game_size = "" #地图的尺寸 (窗口大小用
size = 25 #格子大小
length = 0 #长
widthh = 0 #宽
tot = 1e9 #雷的个数
2.主界面
def home():
clear()
length = widthh = 0
tot = 1e9
game.geometry("300x280") # 窗口大小 字母x
words = tk.Label(game, text = "欢迎来到扫雷游戏!\n请选择您的游戏难度", font="五号")
words.place(y=10, relx=0.2)
b = tk.Button(game, text= "初级 9x9", font="五号", command=partial(create, 9, 9, 10))
b.place(y=60, relx=0.3)
b = tk.Button(game, text= "中级 16x16", font="五号", command=partial(create, 16, 16, 40))
b.place(y=100, relx=0.3)
b = tk.Button(game, text= "高级 16x30", font="五号", command=partial(create, 30, 16, 99))
b.place(y=140, relx=0.3)
global v1,v2,v3 #自定义地图
v1 = tk.StringVar()
e = tk.Entry(game, textvariable = v1, font="五号", bg = "White", justify = tk.CENTER)
e.place(y=220, height = 30, width=55,relx = 0.2)
v2 = tk.StringVar()
e = tk.Entry(game, textvariable = v2, font="五号", bg="White", justify = tk.CENTER)
e.place(y=220, height = 30, width=55,relx = 0.4)
v3 = tk.StringVar()
e = tk.Entry(game, textvariable = v3, font="五号", bg="White", justify = tk.CENTER)
e.place(y=220, height = 30, width=55,relx = 0.6)
b = tk.Button(game, text = " 自定义 ",font = "五号", command=DIY)
b.place(y=180, relx = 0.3)
3.自定义地图的合法性判断和弹窗
def DIY():
a = v1.get()
b = v2.get()
c = v3.get()
if a.isdigit() == 0 or b.isdigit() == 0 or c.isdigit() == 0:
showinfo(parent=game, title="请重试", message="抱歉,输入数据不合法 (>﹏<)")
return
a = int(a)
b = int(b)
c = int(c)
if a <= 0 or b <= 0 or a > 24 or b > 50:
showinfo(parent=game, title="请重试", message="抱歉,地图大小不合法 (>﹏<)")
return
elif c < 0 or c > a * b:
showinfo(parent=game, title="请重试", message="抱歉,地雷数目不合法 (>﹏<)")
return
create(a, b, c)
4.菜单栏设置
Home键返回主界面
Help键显示游戏规则
def showhelp():
with open('扫雷游戏Help.txt', encoding='utf-8') as file:
words = file.read()
showinfo(parent=game, title="扫雷游戏", message=words)
def showmenu():
m = tk.Menu(game)
m.add_command(label="Home", command=partial(home))
m.add_command(label="Help", command=partial(showhelp))
game.config(menu=m)
5.游戏地图创建以及绘制
def clear(): #用 winfo_children() 获取窗口中的所有子部件
for item in game.winfo_children():
if item.winfo_class() != "Menu":
item.destroy()
def draw():
clear()
button = Button(game, text = "≥Ö‿Ö≤", bg = "Gainsboro", command = partial(create, length, widthh, tot))
button.place(y = 3, relx = 0.4, width = 80)
if judge() == 1:
success()
return
game_size = str((length + 1)* size) + "x" + str((widthh + 2)* size)
for i in range(length):
for j in range(widthh):
button = tk.Button(game, bg = "Gainsboro")
if state_map[i][j] == 0:
pass
#button["text"] = game_map[i][j]
elif state_map[i][j] == 2:
button["fg"] = "Red"
button["text"] = "!"
elif state_map[i][j] == 3:
button["fg"] = "DarkBlue"
button["text"] = "?"
else:
button["bg"] = "White"
button["fg"] = color[game_map[i][j]]
if game_map[i][j] != 0:
button["text"] = str(game_map[i][j])
button.bind("<Button-1>", partial(left_work, a = i, b = j))
button.bind("<Button-3>", partial(right_work, a = i, b = j))
button.place(x = (i + 0.5) * size , y = (j + 1.5) * size, height = size, width = size)
game.geometry(game_size)
def create(a, b, c):
global game_map, state_map,length,widthh,tot
length = a
widthh = b
tot = c
data = random.sample(range(0, length * widthh - 1), tot)
# random.sample(range(1, n), k) 表示从1~n的范围内随机生成k个不重复的数 结果以列表返回
game_map = [[0] * widthh for i in range(length)]
state_map = [[0] * widthh for i in range(length)]
for val in data:
game_map[int(val / widthh)][val % widthh] = 'X' #雷的位置为'X'
for i in range(length):
for j in range(widthh):
if game_map[i][j] != 'X':
for k in range(8):
if i + nx[k] < 0 or i + nx[k] >= length:
pass
elif j + ny[k] < 0 or j + ny[k] >= widthh:
pass
elif game_map[i + nx[k]][j + ny[k]] == 'X':
game_map[i][j] = game_map[i][j] + 1
draw()
6.判断游戏胜利条件
地雷全部被正确标记 or 只剩含雷的方块没有被点击
def judge():
cnt1 = 0 #被成功标记的雷的个数
cnt2 = 0 #被错误标记的雷的个数
cnt3 = 0 #没被点开的格子的个数
for i in range(length):
for j in range(widthh):
if state_map[i][j] == 1:
pass
cnt3 = cnt3 + 1
if state_map[i][j] == 2:
if game_map[i][j] == 'X':
cnt1 = cnt1 + 1
else:
cnt2 = cnt2 + 1
if cnt3 == tot:
return 1
if cnt2 > 0:
return 0
if cnt1 == tot :
return 1
return 0
7.鼠标右/左击设置
左击点开格子
右击标记/质疑/取消质疑格子
def spread(a, b):
global state_map
if state_map[a][b] == 1:
return
state_map[a][b] = 1
if game_map[a][b] == 0:
for k in range(8):
if a + nx[k] < 0 or a + nx[k] >= length:
pass
elif b + ny[k] < 0 or b + ny[k] >= widthh:
pass
else:
spread(a + nx[k], b + ny[k])
def left_work(event, a, b):
global state_map
if state_map[a][b] == 1:
return
elif game_map[a][b] == 'X':
state_map[a][b] = 4
failure()
else:
spread(a, b)
draw()
def right_work(event, a, b):
global state_map
if state_map[a][b] == 1:
return
elif state_map[a][b] == 0:
state_map[a][b] = 2
elif state_map[a][b] == 2:
state_map[a][b] = 3
elif state_map[a][b] == 3:
state_map[a][b] = 0
draw()
8.游戏胜利
def success():
button = Button(game, text = "(ღ♡‿♡ღ)", bg = "Gainsboro", command = partial(create, length, widthh, tot))
button.place(y = 0, rely = 0.4, width = 80)
for i in range(length):
for j in range(widthh):
button = tk.Button(game)
if state_map[i][j] == 1:
button["bg"] = "White"
if game_map[i][j] != 0:
button["fg"] = color[game_map[i][j]]
button["text"] = game_map[i][j]
elif game_map[i][j] == "X":
button["bg"] = "LawnGreen"
button.place(x = (i + 0.5) * size , y = (j + 1.5) * size, height = size, width = size)
showinfo(parent=game, title="游戏结束", message="恭喜你,游戏胜利 (ღ♡‿♡ღ)")
9.游戏失败
def failure():
button = Button(game, text = "(>﹏<)", bg = "Gainsboro", command = partial(create, length, widthh, tot))
button.place(y = 3, relx = 0.4, width = 80)
for i in range(length):
for j in range(widthh):
button = tk.Button(game)
if state_map[i][j] == 4:
button["bg"] = "Red"
elif state_map[i][j] == 1:
button["bg"] = "White"
if game_map[i][j] != 0:
button["fg"] = color[game_map[i][j]]
button["text"] = game_map[i][j]
elif game_map[i][j] != "X":
button["bg"] = "Gainsboro"
if state_map[i][j] == 2: #错误标记
button["fg"] = "Red"
button["text"] = "X"
else:
if state_map[i][j] != 2:
button["bg"] = "MidnightBlue"
else:
button["bg"] = "LawnGreen"
button.place(x = (i + 0.5) * size , y = (j + 1.5) * size, height = size, width = size)
showinfo(parent=game, title="游戏结束", message="很遗憾,游戏失败 (>﹏<)")
10.源代码
import random
from tkinter import *
import tkinter as tk
from tkinter.messagebox import *
from functools import partial
game = Tk()
game.title("扫雷")
nx = [-1,-1,-1,0,0,1,1,1]
ny = [-1,0,1,-1,1,-1,0,1]
color = ["White","Blue","Green","Red","Brown","Gold","Pink","Purple","Black"] #不同数字的颜色
game_map = [] #隐藏的地图 ‘X’表示雷
state_map = [] #地图的状态 0未被点击 1已点击 2被标记 3被质疑 4雷被引爆
game_size = "" #地图的尺寸 (窗口大小用
size = 25 #格子大小
length = 0 #长
widthh = 0 #宽
tot = 1e9 #雷的个数
def judge():
cnt1 = 0 #被成功标记的雷的个数
cnt2 = 0 #被错误标记的雷的个数
cnt3 = 0 #没被点开的格子的个数
for i in range(length):
for j in range(widthh):
if state_map[i][j] == 1:
pass
cnt3 = cnt3 + 1
if state_map[i][j] == 2:
if game_map[i][j] == 'X':
cnt1 = cnt1 + 1
else:
cnt2 = cnt2 + 1
if cnt3 == tot:
return 1
if cnt2 > 0:
return 0
if cnt1 == tot :
return 1
return 0
def success():
button = Button(game, text = "(ღ♡‿♡ღ)", bg = "Gainsboro", command = partial(create, length, widthh, tot))
button.place(y = 0, rely = 0.4, width = 80)
for i in range(length):
for j in range(widthh):
button = tk.Button(game)
if state_map[i][j] == 1:
button["bg"] = "White"
if game_map[i][j] != 0:
button["fg"] = color[game_map[i][j]]
button["text"] = game_map[i][j]
elif game_map[i][j] == "X":
button["bg"] = "LawnGreen"
button.place(x = (i + 0.5) * size , y = (j + 1.5) * size, height = size, width = size)
showinfo(parent=game, title="游戏结束", message="恭喜你,游戏胜利 (ღ♡‿♡ღ)")
def failure():
button = Button(game, text = "(>﹏<)", bg = "Gainsboro", command = partial(create, length, widthh, tot))
button.place(y = 3, relx = 0.4, width = 80)
for i in range(length):
for j in range(widthh):
button = tk.Button(game)
if state_map[i][j] == 4:
button["bg"] = "Red"
elif state_map[i][j] == 1:
button["bg"] = "White"
if game_map[i][j] != 0:
button["fg"] = color[game_map[i][j]]
button["text"] = game_map[i][j]
elif game_map[i][j] != "X":
button["bg"] = "Gainsboro"
if state_map[i][j] == 2: #错误标记
button["fg"] = "Red"
button["text"] = "X"
else:
if state_map[i][j] != 2:
button["bg"] = "MidnightBlue"
else:
button["bg"] = "LawnGreen"
button.place(x = (i + 0.5) * size , y = (j + 1.5) * size, height = size, width = size)
showinfo(parent=game, title="游戏结束", message="很遗憾,游戏失败 (>﹏<)")
def spread(a, b):
global state_map
if state_map[a][b] == 1:
return
state_map[a][b] = 1
if game_map[a][b] == 0:
for k in range(8):
if a + nx[k] < 0 or a + nx[k] >= length:
pass
elif b + ny[k] < 0 or b + ny[k] >= widthh:
pass
else:
spread(a + nx[k], b + ny[k])
def left_work(event, a, b):
global state_map
if state_map[a][b] == 1:
return
elif game_map[a][b] == 'X':
state_map[a][b] = 4
failure()
else:
spread(a, b)
draw()
def right_work(event, a, b):
global state_map
if state_map[a][b] == 1:
return
elif state_map[a][b] == 0:
state_map[a][b] = 2
elif state_map[a][b] == 2:
state_map[a][b] = 3
elif state_map[a][b] == 3:
state_map[a][b] = 0
draw()
def clear(): #用 winfo_children() 获取窗口中的所有子部件
for item in game.winfo_children():
if item.winfo_class() != "Menu":
item.destroy()
def draw():
clear()
button = Button(game, text = "≥Ö‿Ö≤", bg = "Gainsboro", command = partial(create, length, widthh, tot))
button.place(y = 3, relx = 0.4, width = 80)
if judge() == 1:
success()
return
game_size = str((length + 1)* size) + "x" + str((widthh + 2)* size)
for i in range(length):
for j in range(widthh):
button = tk.Button(game, bg = "Gainsboro")
if state_map[i][j] == 0:
pass
#button["text"] = game_map[i][j]
elif state_map[i][j] == 2:
button["fg"] = "Red"
button["text"] = "!"
elif state_map[i][j] == 3:
button["fg"] = "DarkBlue"
button["text"] = "?"
else:
button["bg"] = "White"
button["fg"] = color[game_map[i][j]]
if game_map[i][j] != 0:
button["text"] = str(game_map[i][j])
button.bind("<Button-1>", partial(left_work, a = i, b = j))
button.bind("<Button-3>", partial(right_work, a = i, b = j))
button.place(x = (i + 0.5) * size , y = (j + 1.5) * size, height = size, width = size)
game.geometry(game_size)
def create(a, b, c):
global game_map, state_map,length,widthh,tot
length = a
widthh = b
tot = c
data = random.sample(range(0, length * widthh - 1), tot)
# random.sample(range(1, n), k) 表示从1~n的范围内随机生成k个不重复的数 结果以列表返回
game_map = [[0] * widthh for i in range(length)]
state_map = [[0] * widthh for i in range(length)]
for val in data:
game_map[int(val / widthh)][val % widthh] = 'X' #雷的位置为'X'
for i in range(length):
for j in range(widthh):
if game_map[i][j] != 'X':
for k in range(8):
if i + nx[k] < 0 or i + nx[k] >= length:
pass
elif j + ny[k] < 0 or j + ny[k] >= widthh:
pass
elif game_map[i + nx[k]][j + ny[k]] == 'X':
game_map[i][j] = game_map[i][j] + 1
draw()
def DIY():
a = v1.get()
b = v2.get()
c = v3.get()
if a.isdigit() == 0 or b.isdigit() == 0 or c.isdigit() == 0:
showinfo(parent=game, title="请重试", message="抱歉,输入数据不合法 (>﹏<)")
return
a = int(a)
b = int(b)
c = int(c)
if a <= 0 or b <= 0 or a > 24 or b > 50:
showinfo(parent=game, title="请重试", message="抱歉,地图大小不合法 (>﹏<)")
return
elif c < 0 or c > a * b:
showinfo(parent=game, title="请重试", message="抱歉,地雷数目不合法 (>﹏<)")
return
create(a, b, c)
def home():
clear()
length = widthh = 0
tot = 1e9
game.geometry("300x280") # 窗口大小 字母x
words = tk.Label(game, text = "欢迎来到扫雷游戏!\n请选择您的游戏难度", font="五号")
words.place(y=10, relx=0.2)
b = tk.Button(game, text= "初级 9x9", font="五号", command=partial(create, 9, 9, 10))
b.place(y=60, relx=0.3)
b = tk.Button(game, text= "中级 16x16", font="五号", command=partial(create, 16, 16, 40))
b.place(y=100, relx=0.3)
b = tk.Button(game, text= "高级 16x30", font="五号", command=partial(create, 30, 16, 99))
b.place(y=140, relx=0.3)
global v1,v2,v3 #自定义地图
v1 = tk.StringVar()
e = tk.Entry(game, textvariable = v1, font="五号", bg = "White", justify = tk.CENTER)
e.place(y=220, height = 30, width=55,relx = 0.2)
v2 = tk.StringVar()
e = tk.Entry(game, textvariable = v2, font="五号", bg="White", justify = tk.CENTER)
e.place(y=220, height = 30, width=55,relx = 0.4)
v3 = tk.StringVar()
e = tk.Entry(game, textvariable = v3, font="五号", bg="White", justify = tk.CENTER)
e.place(y=220, height = 30, width=55,relx = 0.6)
b = tk.Button(game, text = " 自定义 ",font = "五号", command=DIY)
b.place(y=180, relx = 0.3)
def showhelp():
with open('扫雷游戏Help.txt', encoding='utf-8') as file:
words = file.read()
showinfo(parent=game, title="扫雷游戏", message=words)
def showmenu():
m = tk.Menu(game)
m.add_command(label="Home", command=partial(home))
m.add_command(label="Help", command=partial(showhelp))
game.config(menu=m)
showmenu()
home()
game.mainloop()
11.运行视频
扫雷演示
12.代码提交到gitee
(三) 实验过程中遇到的问题以及感想体会
-
问题1:最后一行/列button的大小不对
-
问题1解决方案:将height/width的设置移动到place语句里
咱也不懂为什么啊
-
问题2:传递参数时
明明传了2个参数,却报错说传了3个
-
问题2解决方案:将partial函数里隐藏参数event置为默认值
CHATGPTNB!!!
-
问题3:无法成功导入txt文件里的字
-
问题3解决方案:
将with open('扫雷游戏Help.txt', 'r') as file:
修改为with open('扫雷游戏Help.txt', encoding='utf-8') as file:
-
编写调试的过程中,还有很多细细碎碎的小问题记不清了zzz
-
感想体会
我个人对GUI界面编程更多的兴趣,通过这次实验,对tkinter模块的应用变得更加熟练,也有了更深入的体会。
虽然在扫雷地图的更新方面还有些不足(指更新地图的美丽转场小动画延迟),但在今后的日子里,我也将继续学习提高,将编程更多更好地应用到日常生活,这是编程解决实际问题的魅力所在。
(四)全课总结
这学期非常高兴能选到王志强老师的python课 (据说往年都三秒没呀,感谢稀碎的教务系统卡我进去)
雀食如学长学姐们大力推荐的那样,python课程非常地生动有趣,也让我真正学到了不少本领。从python的基本语法,到其许多强大的库功能,以及socket、爬虫等知识。(虽然还有点懵懵)
不仅仅是理论知识的讲解,在课上老师允许我们使用电脑、并鼓励大家进行跟随实践,也时常联系到其他方面的知识,让我体会到python与其他编程语言的对比差异,深入掌握理论知识在实际项目中的应用,收益颇丰。
除此之外,python课程难度的设置也十分合理,有层次地由易到难、由浅到深,让我在每次实验过程中都能有全新的收获。
希望在之后的教学中,能有更加系统的socket编程/爬虫讲解 (弱弱教教呜呜) ,因为我对这方面的知识十分陌生,但又非常的感兴趣。 (同时,也希望加强考核难度,狠狠的拷打他们!!误)
最后,再次感谢老师的指导,祝老师的python课程能越办越好!(撒花.jpg)