zxy0307 commited on
Commit
23d80ff
·
verified ·
1 Parent(s): b256b78

Upload 132 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ 06_PyQt5控件.ipynb filter=lfs diff=lfs merge=lfs -text
37
+ 06_PyQt6-PySide6.ipynb filter=lfs diff=lfs merge=lfs -text
01_tkinter基础知识.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
02_tkinter实战:记事本软件开发.ipynb ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "583a0ea6",
6
+ "metadata": {},
7
+ "source": [
8
+ "## 开发记事本软件"
9
+ ]
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": 1,
14
+ "id": "d5ea2318",
15
+ "metadata": {
16
+ "ExecuteTime": {
17
+ "end_time": "2023-10-05T06:41:22.135176Z",
18
+ "start_time": "2023-10-05T06:40:53.213945Z"
19
+ }
20
+ },
21
+ "outputs": [
22
+ {
23
+ "name": "stderr",
24
+ "output_type": "stream",
25
+ "text": [
26
+ "2023-10-05 14:40:56.137 python[83674:5585746] +[CATransaction synchronize] called within transaction\n"
27
+ ]
28
+ }
29
+ ],
30
+ "source": [
31
+ "\"\"\"开发记事本软件\"\"\"\n",
32
+ "\n",
33
+ "from tkinter.filedialog import *\n",
34
+ "from tkinter.colorchooser import *\n",
35
+ "from tkinter import *\n",
36
+ "\n",
37
+ "class Application(Frame):\n",
38
+ "\n",
39
+ " def __init__(self, master=None):\n",
40
+ " super().__init__(master)\n",
41
+ " self.master = master\n",
42
+ "\n",
43
+ " # textpad 表示 Text文本框对象\n",
44
+ " self.textpad = None\n",
45
+ "\n",
46
+ " # 布局app\n",
47
+ " self.pack()\n",
48
+ "\n",
49
+ " # 为app 添加组件\n",
50
+ " self.createWidget()\n",
51
+ "\n",
52
+ " def createWidget(self):\n",
53
+ " # 创建主菜单栏\n",
54
+ " menubar = Menu(root)\n",
55
+ "\n",
56
+ " # 创建子菜单\n",
57
+ " menuFile = Menu(menubar)\n",
58
+ " menuEdit = Menu(menubar)\n",
59
+ " menuHelp = Menu(menubar)\n",
60
+ "\n",
61
+ " # 将子菜单加入到主菜单栏\n",
62
+ " menubar.add_cascade(label=\"文件(F)\", menu=menuFile)\n",
63
+ " menubar.add_cascade(label=\"编辑(E)\", menu=menuEdit)\n",
64
+ " menubar.add_cascade(label=\"帮助(H)\", menu=menuHelp)\n",
65
+ "\n",
66
+ " # 添加菜单项\n",
67
+ " menuFile.add_command(\n",
68
+ " label=\"新建\", accelerator=\"ctrl+n\", command=self.newfile)\n",
69
+ " menuFile.add_command(\n",
70
+ " label=\"打开\", accelerator=\"ctrl+o\", command=self.openfile)\n",
71
+ " menuFile.add_command(\n",
72
+ " label=\"保存\", accelerator=\"ctrl+s\", command=self.savefile)\n",
73
+ " menuFile.add_separator() # 添加分割线\n",
74
+ " menuFile.add_command(\n",
75
+ " label=\"退出\", accelerator=\"ctrl+q\", command=self.exit)\n",
76
+ "\n",
77
+ " # 将主菜单栏 添加到根窗口\n",
78
+ " root[\"menu\"] = menubar\n",
79
+ "\n",
80
+ " # 增加快捷键的处理\n",
81
+ " root.bind(\"<Control-n>\", lambda event: self.newfile())\n",
82
+ " root.bind(\"<Control-o>\", lambda event: self.openfile())\n",
83
+ " root.bind(\"<Control-s>\", lambda event: self.savefile())\n",
84
+ " root.bind(\"<Control-q>\", lambda event: self.exit())\n",
85
+ "\n",
86
+ " # 创建 Text 多行文本框\n",
87
+ " self.textpad = Text(root, width=50, height=30)\n",
88
+ " self.textpad.pack() # 布局到 app中\n",
89
+ "\n",
90
+ " # 创建上下菜单,并添加菜单项\n",
91
+ " self.contextMenu = Menu(root)\n",
92
+ " self.contextMenu.add_command(label=\"背景颜色\", command=self.openAskColor)\n",
93
+ "\n",
94
+ " # 为右键绑定事件\n",
95
+ " root.bind(\"<Button-2>\", self.createContextMenu)\n",
96
+ "\n",
97
+ " def newfile(self):\n",
98
+ " \"\"\"新建文件\"\"\"\n",
99
+ "\n",
100
+ " # 把 text控件中所有的内容清空\n",
101
+ " self.textpad.delete(\"1.0\", \"end\")\n",
102
+ "\n",
103
+ " # 打开 创建文件对话框\n",
104
+ " self.filename = asksaveasfilename(title=\"另存为\",\n",
105
+ " initialfile=\"未命名.txt\", # 文件默认名\n",
106
+ " filetypes=[(\"文本文档\", \"*.txt\")],\n",
107
+ " defaultextension=\".txt\") # 默认文件类型\n",
108
+ " # 保存 新建的文件\n",
109
+ " self.savefile()\n",
110
+ "\n",
111
+ " def openfile(self):\n",
112
+ " \"\"\"打开文件\"\"\"\n",
113
+ "\n",
114
+ " # 把text控件中所有的内容清空\n",
115
+ " self.textpad.delete(\"1.0\", \"end\")\n",
116
+ "\n",
117
+ " # 创建文件对话框\n",
118
+ " with askopenfile(title=\"打开文本文件\") as f:\n",
119
+ "\n",
120
+ " # 读取文件内容\n",
121
+ " self.textpad.insert(INSERT, f.read())\n",
122
+ " self.filename = f.name\n",
123
+ "\n",
124
+ " def savefile(self):\n",
125
+ " \"\"\"将文本框的信息 保存到文件中\"\"\"\n",
126
+ " with open(self.filename, \"w\") as f:\n",
127
+ " c = self.textpad.get(1.0, END)\n",
128
+ " f.write(c)\n",
129
+ "\n",
130
+ " def exit(self):\n",
131
+ " root.quit()\n",
132
+ "\n",
133
+ " def openAskColor(self):\n",
134
+ " # 打开颜色选择框\n",
135
+ " s1 = askcolor(color=\"red\", title=\"选择背景色\")\n",
136
+ "\n",
137
+ " # 修改文本框的背景色\n",
138
+ " self.textpad.config(bg=s1[1])\n",
139
+ "\n",
140
+ " def createContextMenu(self, event):\n",
141
+ " # 菜单在鼠标右键单击的坐标处显示\n",
142
+ " self.contextMenu.post(event.x_root, event.y_root)\n",
143
+ "\n",
144
+ "\n",
145
+ "if __name__ == '__main__':\n",
146
+ " root = Tk()\n",
147
+ " root.geometry(\"450x300+200+300\")\n",
148
+ " root.title(\"简易记事本\")\n",
149
+ " app = Application(master=root)\n",
150
+ " root.mainloop()"
151
+ ]
152
+ },
153
+ {
154
+ "cell_type": "markdown",
155
+ "id": "992ea581",
156
+ "metadata": {},
157
+ "source": [
158
+ "## 将 python 程序打包成 exe 文件\n",
159
+ "\n",
160
+ "\n",
161
+ "我们可以使用 pyinstaller 模块实现将 python 项目打包成 exe 文件。\n",
162
+ "\n",
163
+ "操作步骤如下: \n",
164
+ "\n",
165
+ "1. 安装 pyinstaller 模块\n",
166
+ " - 在 pycharm 中操作:file-->setting-->Project:xxx -->Project interpretor,再点 击+即可。\n",
167
+ "\n",
168
+ "\n",
169
+ "2. 在 pycharm 的 Terminal 终端输入如下命令: pyinstaller -F xxxx.py\n",
170
+ " - 【注】相关参数如下:\n",
171
+ " - --icon=图标路径 (pyinstaller -F --icon=my.ico XXXX.py)\n",
172
+ " - -F 打包成一个 exe 文件\n",
173
+ " - -w 使用窗口,无控制台\n",
174
+ " - -c 使用控制台,无窗口\n",
175
+ " - -D 创建一个目录,里面包含 exe 以及其他一些依赖性文件\n",
176
+ "\n",
177
+ "\n",
178
+ "3. 在项目的 dist 目录下可以看到生成了 exe 文件,直接在windows 系统中使用即可。\n",
179
+ "\n",
180
+ "\n",
181
+ "【注】exe 文件本质是将 python 解释器和程序打包到了一起,这样我们执行程序时就不用管 windows 系统是不是有 python 解释器。\n",
182
+ "\n",
183
+ "【注】pyinstaller 目前只支持 python3.6。如果你是 python3.7 会执行失败。"
184
+ ]
185
+ },
186
+ {
187
+ "cell_type": "code",
188
+ "execution_count": null,
189
+ "id": "6e64e519",
190
+ "metadata": {},
191
+ "outputs": [],
192
+ "source": []
193
+ }
194
+ ],
195
+ "metadata": {
196
+ "kernelspec": {
197
+ "display_name": "Python 3 (ipykernel)",
198
+ "language": "python",
199
+ "name": "python3"
200
+ },
201
+ "language_info": {
202
+ "codemirror_mode": {
203
+ "name": "ipython",
204
+ "version": 3
205
+ },
206
+ "file_extension": ".py",
207
+ "mimetype": "text/x-python",
208
+ "name": "python",
209
+ "nbconvert_exporter": "python",
210
+ "pygments_lexer": "ipython3",
211
+ "version": "3.9.12"
212
+ },
213
+ "toc": {
214
+ "base_numbering": 1,
215
+ "nav_menu": {},
216
+ "number_sections": true,
217
+ "sideBar": true,
218
+ "skip_h1_title": false,
219
+ "title_cell": "Table of Contents",
220
+ "title_sidebar": "Contents",
221
+ "toc_cell": false,
222
+ "toc_position": {
223
+ "height": "calc(100% - 180px)",
224
+ "left": "10px",
225
+ "top": "150px",
226
+ "width": "248.1875px"
227
+ },
228
+ "toc_section_display": true,
229
+ "toc_window_display": true
230
+ }
231
+ },
232
+ "nbformat": 4,
233
+ "nbformat_minor": 5
234
+ }
03_tkinter实战:开发画图软件.ipynb ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 5,
6
+ "id": "13cd1000",
7
+ "metadata": {
8
+ "ExecuteTime": {
9
+ "end_time": "2023-10-05T07:16:15.649473Z",
10
+ "start_time": "2023-10-05T07:16:12.271320Z"
11
+ }
12
+ },
13
+ "outputs": [],
14
+ "source": [
15
+ "\"\"\"开发画图软件的菜单\"\"\"\n",
16
+ "\n",
17
+ "from tkinter.filedialog import *\n",
18
+ "from tkinter.colorchooser import *\n",
19
+ "from tkinter import *\n",
20
+ "\n",
21
+ "# 根窗口的宽度和高度\n",
22
+ "win_width = 900\n",
23
+ "win_height = 450\n",
24
+ "\n",
25
+ "\n",
26
+ "class Application(Frame):\n",
27
+ "\n",
28
+ " def __init__(self, master=None, # 设置父组件\n",
29
+ " bgcolor=\"#000000\"): # 设置背景色\n",
30
+ " super().__init__(master)\n",
31
+ "\n",
32
+ " self.master = master # app的父组件\n",
33
+ " self.bgcolor = bgcolor # 背景色\n",
34
+ " self.fgcolor = \"#ff0000\" # 前景色\n",
35
+ "\n",
36
+ " # 记录鼠标坐标\n",
37
+ " self.x = 0\n",
38
+ " self.y = 0\n",
39
+ "\n",
40
+ " # 是否开始画画\n",
41
+ " self.startDrawFlag = False\n",
42
+ "\n",
43
+ " # 记录最后绘制图形\n",
44
+ " self.lastDraw = 0\n",
45
+ "\n",
46
+ " # 绘图区canvas对象\n",
47
+ " self.drawpad = None\n",
48
+ "\n",
49
+ " # 部署app\n",
50
+ " self.pack()\n",
51
+ "\n",
52
+ " # 为app添加组件\n",
53
+ " self.createWidget()\n",
54
+ "\n",
55
+ " def createWidget(self):\n",
56
+ " # 绘图区:创建画布\n",
57
+ " self.drawpad = Canvas(root, width=win_width,\n",
58
+ " height=win_height*0.8, bg=self.bgcolor)\n",
59
+ " self.drawpad.pack()\n",
60
+ "\n",
61
+ " # 创建操作按钮\n",
62
+ " # 开始按钮\n",
63
+ " btn_start = Button(root, text=\"开始\", name=\"start\")\n",
64
+ " btn_start.pack(side=\"left\", padx=\"10\")\n",
65
+ "\n",
66
+ " # 画笔按钮\n",
67
+ " btn_pen = Button(root, text=\"画笔\", name=\"pen\")\n",
68
+ " btn_pen.pack(side=\"left\", padx=\"10\")\n",
69
+ "\n",
70
+ " # 画矩形按钮\n",
71
+ " btn_rect = Button(root, text=\"画矩形\", name=\"rect\")\n",
72
+ " btn_rect.pack(side=\"left\", padx=\"10\")\n",
73
+ "\n",
74
+ " # 清屏按钮\n",
75
+ " btn_clear = Button(root, text=\"清屏\", name=\"clear\")\n",
76
+ " btn_clear.pack(side=\"left\", padx=\"10\")\n",
77
+ "\n",
78
+ " # 橡皮擦按钮\n",
79
+ " btn_eraser = Button(root, text=\"橡皮擦\", name=\"eraser\")\n",
80
+ " btn_eraser.pack(side=\"left\", padx=\"10\")\n",
81
+ "\n",
82
+ " # 画直线按钮\n",
83
+ " btn_line = Button(root, text=\"直线\", name=\"line\")\n",
84
+ " btn_line.pack(side=\"left\", padx=\"10\")\n",
85
+ "\n",
86
+ " # 画箭头按钮\n",
87
+ " btn_lineArrow = Button(root, text=\"直线(箭头)\", name=\"lineArrow\")\n",
88
+ " btn_lineArrow.pack(side=\"left\", padx=\"10\")\n",
89
+ "\n",
90
+ " # 颜色按钮\n",
91
+ " btn_color = Button(root, text=\"颜色\", name=\"chooseColor\")\n",
92
+ " btn_color.pack(side=\"left\", padx=\"10\")\n",
93
+ "\n",
94
+ " # 给所有按钮 绑定鼠标点击事件\n",
95
+ " btn_pen.bind_class(\"Button\", \"<1>\", self.myEventManager)\n",
96
+ "\n",
97
+ " # 为右键绑定事件 -- 加载上下文菜单\n",
98
+ " root.bind(\"<Button-2>\", self.createContextMenu)\n",
99
+ "\n",
100
+ " # 画布绑定 鼠标拖动事件,默认使用画笔画画\n",
101
+ " self.drawpad.bind(\"<B1-Motion>\", self.myDrawPen)\n",
102
+ " self.drawpad.bind(\"<ButtonRelease-1>\", self.stopDraw)\n",
103
+ "\n",
104
+ " # 增加修改颜色的 快捷键\n",
105
+ " root.bind(\"<KeyPress-r>\", self.kuaijiejian)\n",
106
+ " root.bind(\"<KeyPress-g>\", self.kuaijiejian)\n",
107
+ " root.bind(\"<KeyPress-y>\", self.kuaijiejian)\n",
108
+ "\n",
109
+ " def myEventManager(self, event):\n",
110
+ "\n",
111
+ " # 获取组件的名字\n",
112
+ " name = event.widget.winfo_name()\n",
113
+ "\n",
114
+ " # 一松开鼠标,结束上一阶段绘画\n",
115
+ " self.drawpad.bind(\"<ButtonRelease-1>\", self.stopDraw)\n",
116
+ "\n",
117
+ " # 根据 按下按钮的不同,执行不同的函数\n",
118
+ " if name == \"rect\":\n",
119
+ " self.drawpad.bind(\"<B1-Motion>\", self.myDrawRect)\n",
120
+ "\n",
121
+ " elif name == \"clear\":\n",
122
+ " self.drawpad.delete(\"all\")\n",
123
+ "\n",
124
+ " elif name == \"eraser\":\n",
125
+ " self.drawpad.bind(\"<B1-Motion>\", self.myEraser)\n",
126
+ "\n",
127
+ " elif name == \"line\":\n",
128
+ " self.drawpad.bind(\"<B1-Motion>\", self.myLine)\n",
129
+ "\n",
130
+ " elif name == \"lineArrow\":\n",
131
+ " self.drawpad.bind(\"<B1-Motion>\", self.myLineArrow)\n",
132
+ "\n",
133
+ " elif name == \"chooseColor\":\n",
134
+ " # 创建颜色选择框\n",
135
+ " c = askcolor(color=self.fgcolor, title=\"选择画笔颜色\")\n",
136
+ " self.fgcolor = c[1]\n",
137
+ "\n",
138
+ " else: # 画笔\n",
139
+ " self.drawpad.bind(\"<B1-Motion>\", self.myDrawPen)\n",
140
+ "\n",
141
+ " def createContextMenu(self, event):\n",
142
+ " # 菜单在鼠标右键单击的坐标处显示\n",
143
+ " self.contextMenu.post(event.x_root, event.y_root)\n",
144
+ "\n",
145
+ " def myDrawPen(self, event):\n",
146
+ " \"\"\"画笔\"\"\"\n",
147
+ " \n",
148
+ " # 记录鼠标按下时的坐标\n",
149
+ " if not self.startDrawFlag:\n",
150
+ " self.startDrawFlag = True\n",
151
+ " self.x = event.x\n",
152
+ " self.y = event.y\n",
153
+ " \n",
154
+ " # 不停地绘制小矩形\n",
155
+ " self.lastDraw = self.drawpad.create_line(\n",
156
+ " self.x, self.y, event.x, event.y, fill=self.fgcolor)\n",
157
+ " self.x = event.x\n",
158
+ " self.y = event.y\n",
159
+ "\n",
160
+ " def stopDraw(self, event):\n",
161
+ " \"\"\"结束上一阶段的绘画,开启下一阶段的绘画\"\"\"\n",
162
+ " self.startDrawFlag = False\n",
163
+ " self.lastDraw = 0\n",
164
+ "\n",
165
+ " def myDrawRect(self, event):\n",
166
+ " \"\"\"绘制矩形\"\"\"\n",
167
+ "\n",
168
+ " # 鼠标拖动时,会不停地绘制矩形,我们只希望保留最后一个矩形\n",
169
+ " self.drawpad.delete(self.lastDraw)\n",
170
+ "\n",
171
+ " # 鼠标刚按下,记录此时的坐标\n",
172
+ " if not self.startDrawFlag:\n",
173
+ " self.startDrawFlag = True\n",
174
+ " self.x = event.x\n",
175
+ " self.y = event.y\n",
176
+ "\n",
177
+ " # 绘制矩形\n",
178
+ " self.lastDraw = self.drawpad.create_rectangle(\n",
179
+ " self.x, self.y, event.x, event.y, outline=self.fgcolor)\n",
180
+ "\n",
181
+ " def myEraser(self, event):\n",
182
+ " \"\"\"橡皮擦是一个小矩形,矩形内进行背景色填充\"\"\"\n",
183
+ " if not self.startDrawFlag:\n",
184
+ " self.startDrawFlag = True\n",
185
+ "\n",
186
+ " self.drawpad.create_rectangle(event.x - 4, event.y - 4, event.x + 4, event.y + 4,\n",
187
+ " outline=self.bgcolor, fill=self.bgcolor)\n",
188
+ "\n",
189
+ " def myLine(self, event):\n",
190
+ " \"\"\"绘制直线\"\"\"\n",
191
+ " \n",
192
+ " # 鼠标拖动时,会不停地绘制直线,我们只希望保留最后一个\n",
193
+ " self.drawpad.delete(self.lastDraw)\n",
194
+ "\n",
195
+ " # 按下鼠标时,记录鼠标位置\n",
196
+ " if not self.startDrawFlag:\n",
197
+ " self.startDrawFlag = True\n",
198
+ " self.x = event.x\n",
199
+ " self.y = event.y\n",
200
+ "\n",
201
+ " # 绘制直线\n",
202
+ " self.lastDraw = self.drawpad.create_line(self.x, self.y, event.x, event.y,\n",
203
+ " fill=self.fgcolor)\n",
204
+ "\n",
205
+ " def myLineArrow(self, event):\n",
206
+ " \"\"\"绘制箭头\"\"\"\n",
207
+ " \n",
208
+ " # 鼠标拖动时,会不停地绘制箭头,我们只希望保留最后一个\n",
209
+ " self.drawpad.delete(self.lastDraw)\n",
210
+ "\n",
211
+ " # 按下鼠标时,记录鼠标位置\n",
212
+ " if not self.startDrawFlag:\n",
213
+ " self.startDrawFlag = True\n",
214
+ " self.x = event.x\n",
215
+ " self.y = event.y\n",
216
+ "\n",
217
+ " # 绘制箭头\n",
218
+ " self.lastDraw = self.drawpad.create_line(self.x, self.y, event.x, event.y,\n",
219
+ " fill=self.fgcolor, arrow=LAST)\n",
220
+ "\n",
221
+ " # 处理快捷键\n",
222
+ " def kuaijiejian(self, event):\n",
223
+ " if event.char == \"r\":\n",
224
+ " self.fgcolor = \"#ff0000\"\n",
225
+ " elif event.char == \"g\":\n",
226
+ " self.fgcolor = \"#00ff00\"\n",
227
+ " elif event.char == \"y\":\n",
228
+ " self.fgcolor = \"#ffff00\"\n",
229
+ "\n",
230
+ "\n",
231
+ "if __name__ == '__main__':\n",
232
+ " \n",
233
+ " # 设置根窗口\n",
234
+ " root = Tk()\n",
235
+ " root.geometry(str(win_width)+\"x\"+str(win_height)+\"+400+400\")\n",
236
+ " root.title(\"画图软件\")\n",
237
+ " \n",
238
+ " app = Application(master=root)\n",
239
+ " root.mainloop()"
240
+ ]
241
+ }
242
+ ],
243
+ "metadata": {
244
+ "kernelspec": {
245
+ "display_name": "Python 3 (ipykernel)",
246
+ "language": "python",
247
+ "name": "python3"
248
+ },
249
+ "language_info": {
250
+ "codemirror_mode": {
251
+ "name": "ipython",
252
+ "version": 3
253
+ },
254
+ "file_extension": ".py",
255
+ "mimetype": "text/x-python",
256
+ "name": "python",
257
+ "nbconvert_exporter": "python",
258
+ "pygments_lexer": "ipython3",
259
+ "version": "3.9.12"
260
+ },
261
+ "toc": {
262
+ "base_numbering": 1,
263
+ "nav_menu": {},
264
+ "number_sections": true,
265
+ "sideBar": true,
266
+ "skip_h1_title": false,
267
+ "title_cell": "Table of Contents",
268
+ "title_sidebar": "Contents",
269
+ "toc_cell": false,
270
+ "toc_position": {
271
+ "height": "calc(100% - 180px)",
272
+ "left": "10px",
273
+ "top": "150px",
274
+ "width": "248.1875px"
275
+ },
276
+ "toc_section_display": true,
277
+ "toc_window_display": true
278
+ }
279
+ },
280
+ "nbformat": 4,
281
+ "nbformat_minor": 5
282
+ }
04_Pygame基础知识.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
05_Pygame实战:坦克大战游戏.ipynb ADDED
@@ -0,0 +1,1124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "ecf4b439",
6
+ "metadata": {},
7
+ "source": [
8
+ "## 导包"
9
+ ]
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": 1,
14
+ "id": "9ebb63d1",
15
+ "metadata": {
16
+ "ExecuteTime": {
17
+ "end_time": "2023-10-05T17:25:00.137183Z",
18
+ "start_time": "2023-10-05T17:25:00.017474Z"
19
+ }
20
+ },
21
+ "outputs": [
22
+ {
23
+ "name": "stdout",
24
+ "output_type": "stream",
25
+ "text": [
26
+ "pygame 2.1.0 (SDL 2.0.16, Python 3.9.12)\n",
27
+ "Hello from the pygame community. https://www.pygame.org/contribute.html\n"
28
+ ]
29
+ }
30
+ ],
31
+ "source": [
32
+ "import pygame\n",
33
+ "import time\n",
34
+ "import random\n",
35
+ "from pygame.sprite import Sprite"
36
+ ]
37
+ },
38
+ {
39
+ "cell_type": "markdown",
40
+ "id": "9f539676",
41
+ "metadata": {},
42
+ "source": [
43
+ "## 定义全局变量"
44
+ ]
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "execution_count": 2,
49
+ "id": "488fc023",
50
+ "metadata": {
51
+ "ExecuteTime": {
52
+ "end_time": "2023-10-05T17:25:00.174212Z",
53
+ "start_time": "2023-10-05T17:25:00.146175Z"
54
+ }
55
+ },
56
+ "outputs": [],
57
+ "source": [
58
+ "SCREEN_WIDTH = 800\n",
59
+ "SCREEN_HEIGHT = 500\n",
60
+ "BG_COLOR = pygame.Color(0, 0, 0)\n",
61
+ "TEXT_COLOR = pygame.Color(255, 0, 0)"
62
+ ]
63
+ },
64
+ {
65
+ "cell_type": "markdown",
66
+ "id": "eb87a38f",
67
+ "metadata": {},
68
+ "source": [
69
+ "## 基类"
70
+ ]
71
+ },
72
+ {
73
+ "cell_type": "code",
74
+ "execution_count": 3,
75
+ "id": "5ce76157",
76
+ "metadata": {
77
+ "ExecuteTime": {
78
+ "end_time": "2023-10-05T17:25:00.199049Z",
79
+ "start_time": "2023-10-05T17:25:00.192539Z"
80
+ }
81
+ },
82
+ "outputs": [],
83
+ "source": [
84
+ "# 定义一个基类\n",
85
+ "class BaseItem(Sprite):\n",
86
+ " def __init__(self, color, width, height):\n",
87
+ " pygame.sprite.Sprite.__init__(self)"
88
+ ]
89
+ },
90
+ {
91
+ "cell_type": "markdown",
92
+ "id": "e6d9cfc3",
93
+ "metadata": {},
94
+ "source": [
95
+ "## 坦克类"
96
+ ]
97
+ },
98
+ {
99
+ "cell_type": "code",
100
+ "execution_count": 4,
101
+ "id": "15a2dd18",
102
+ "metadata": {
103
+ "ExecuteTime": {
104
+ "end_time": "2023-10-05T17:25:00.225216Z",
105
+ "start_time": "2023-10-05T17:25:00.207309Z"
106
+ }
107
+ },
108
+ "outputs": [],
109
+ "source": [
110
+ "class Tank(BaseItem):\n",
111
+ " def __init__(self, left, top):\n",
112
+ " \"\"\"初始化方法:添加距离左边left 距离上边top\"\"\"\n",
113
+ " \n",
114
+ " # 创建字典 -- 朝不同的方向走,坦克也不尽相同\n",
115
+ " self.images = {\n",
116
+ " 'U': pygame.image.load('img/p1tankU.gif'),\n",
117
+ " 'D': pygame.image.load('img/p1tankD.gif'),\n",
118
+ " 'L': pygame.image.load('img/p1tankL.gif'),\n",
119
+ " 'R': pygame.image.load('img/p1tankR.gif'),\n",
120
+ " }\n",
121
+ " \n",
122
+ " # 默认方向\n",
123
+ " self.direction = 'L'\n",
124
+ " \n",
125
+ " # 根据当前方向,获取图片\n",
126
+ " self.image = self.images[self.direction]\n",
127
+ " \n",
128
+ " # 根据图片,获取坦克所在区域\n",
129
+ " self.rect = self.image.get_rect()\n",
130
+ " \n",
131
+ " # 调整坦克所在区域\n",
132
+ " self.rect.left = left\n",
133
+ " self.rect.top = top\n",
134
+ " \n",
135
+ " # 设置速度 \n",
136
+ " self.speed = 5\n",
137
+ " \n",
138
+ " # 坦克移动的开关\n",
139
+ " self.stop = True\n",
140
+ " \n",
141
+ " # 是否活着\n",
142
+ " self.live = True\n",
143
+ " \n",
144
+ " # 记录坦克上一时刻的坐标\n",
145
+ " self.oldLeft = self.rect.left\n",
146
+ " self.oldTop = self.rect.top\n",
147
+ "\n",
148
+ "\n",
149
+ " def move(self):\n",
150
+ " \"\"\"移动函数\"\"\"\n",
151
+ " \n",
152
+ " # 记录坦克上一时刻的坐标\n",
153
+ " self.oldLeft = self.rect.left\n",
154
+ " self.oldTop = self.rect.top\n",
155
+ " \n",
156
+ " # 判断坦克的方向,根据一定的速度,进行移动\n",
157
+ " if self.direction == 'L':\n",
158
+ " if self.rect.left > 0:\n",
159
+ " self.rect.left -= self.speed\n",
160
+ " \n",
161
+ " elif self.direction == 'U':\n",
162
+ " if self.rect.top > 0:\n",
163
+ " self.rect.top -= self.speed\n",
164
+ " \n",
165
+ " elif self.direction == 'D':\n",
166
+ " if self.rect.top+self.rect.height < SCREEN_HEIGHT:\n",
167
+ " self.rect.top += self.speed\n",
168
+ " \n",
169
+ " elif self.direction == 'R':\n",
170
+ " if self.rect.left+self.rect.height < SCREEN_WIDTH:\n",
171
+ " self.rect.left += self.speed\n",
172
+ "\n",
173
+ " \n",
174
+ " def shot(self):\n",
175
+ " \"\"\"射击函数\"\"\"\n",
176
+ " return Bullet(self) # 返回子弹对象\n",
177
+ "\n",
178
+ " def stay(self):\n",
179
+ " \"\"\"暂停\"\"\"\n",
180
+ " self.rect.left = self.oldLeft\n",
181
+ " self.rect.top = self.oldTop\n",
182
+ " \n",
183
+ "\n",
184
+ " def hitWall(self):\n",
185
+ " \"\"\"撞到墙体\"\"\"\n",
186
+ " for wall in MainGame.wallList:\n",
187
+ " if pygame.sprite.collide_rect(self, wall):\n",
188
+ " self.stay()\n",
189
+ " \n",
190
+ "\n",
191
+ " def displayTank(self):\n",
192
+ " \"\"\"展示坦克\"\"\"\n",
193
+ " # 获取展示的对象\n",
194
+ " self.image = self.images[self.direction]\n",
195
+ " \n",
196
+ " # 调用blit方法展示\n",
197
+ " MainGame.window.blit(self.image, self.rect)\n",
198
+ "\n"
199
+ ]
200
+ },
201
+ {
202
+ "cell_type": "markdown",
203
+ "id": "873e8cdc",
204
+ "metadata": {},
205
+ "source": [
206
+ "## 我方坦克类"
207
+ ]
208
+ },
209
+ {
210
+ "cell_type": "code",
211
+ "execution_count": 5,
212
+ "id": "e1b9c207",
213
+ "metadata": {
214
+ "ExecuteTime": {
215
+ "end_time": "2023-10-05T17:25:00.244806Z",
216
+ "start_time": "2023-10-05T17:25:00.227216Z"
217
+ }
218
+ },
219
+ "outputs": [],
220
+ "source": [
221
+ "# 我方坦克\n",
222
+ "class MyTank(Tank):\n",
223
+ " def __init__(self, left, top):\n",
224
+ " super(MyTank, self).__init__(left, top)\n",
225
+ "\n",
226
+ "\n",
227
+ " def myTank_hit_enemyTank(self):\n",
228
+ " \"\"\"撞到敌方坦克\"\"\"\n",
229
+ " # 循环遍历敌方坦克列表\n",
230
+ " for enemyTank in MainGame.enemyTankList:\n",
231
+ " if pygame.sprite.collide_rect(self, enemyTank):\n",
232
+ " # 暂停\n",
233
+ " self.stay()"
234
+ ]
235
+ },
236
+ {
237
+ "cell_type": "markdown",
238
+ "id": "4a2e4ee2",
239
+ "metadata": {},
240
+ "source": [
241
+ "## 敌方坦克类"
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "code",
246
+ "execution_count": 6,
247
+ "id": "bbc6b556",
248
+ "metadata": {
249
+ "ExecuteTime": {
250
+ "end_time": "2023-10-05T17:25:00.273074Z",
251
+ "start_time": "2023-10-05T17:25:00.246530Z"
252
+ }
253
+ },
254
+ "outputs": [],
255
+ "source": [
256
+ "# 敌方坦克\n",
257
+ "class EnemyTank(Tank):\n",
258
+ " def __init__(self, left, top, speed):\n",
259
+ " # 调用父类的初始化方法\n",
260
+ " super(EnemyTank, self).__init__(left, top)\n",
261
+ " \n",
262
+ " # 加载敌方坦克图片集\n",
263
+ " self.images = {\n",
264
+ " 'U': pygame.image.load('img/enemy1U.gif'),\n",
265
+ " 'D': pygame.image.load('img/enemy1D.gif'),\n",
266
+ " 'L': pygame.image.load('img/enemy1L.gif'),\n",
267
+ " 'R': pygame.image.load('img/enemy1R.gif')\n",
268
+ " }\n",
269
+ " \n",
270
+ " # 随机生成敌方坦克的方向\n",
271
+ " self.direction = self.randDirection()\n",
272
+ " \n",
273
+ " # 根据方向获取图片\n",
274
+ " self.image = self.images[self.direction]\n",
275
+ " \n",
276
+ " # 获取敌方坦克区域\n",
277
+ " self.rect = self.image.get_rect()\n",
278
+ " \n",
279
+ " # 更改敌方坦克位置\n",
280
+ " self.rect.left = left\n",
281
+ " self.rect.top = top\n",
282
+ " \n",
283
+ " # 速度\n",
284
+ " self.speed = speed\n",
285
+ " \n",
286
+ " # 移动开关键\n",
287
+ " self.flag = True\n",
288
+ " \n",
289
+ " # 增加一个步数变量 step -- 每移动 step步,改变一次方向\n",
290
+ " self.step = 60\n",
291
+ "\n",
292
+ " \n",
293
+ " def enemyTank_hit_myTank(self):\n",
294
+ " \"\"\"撞到我方坦克\"\"\"\n",
295
+ " if pygame.sprite.collide_rect(self, MainGame.my_tank):\n",
296
+ " # 停止\n",
297
+ " self.stay()\n",
298
+ " \n",
299
+ " def randDirection(self):\n",
300
+ " \"\"\"随机生成敌方坦克的方向\"\"\"\n",
301
+ " num = random.randint(1, 4)\n",
302
+ " \n",
303
+ " if num == 1:\n",
304
+ " return 'U'\n",
305
+ " elif num == 2:\n",
306
+ " return 'D'\n",
307
+ " elif num == 3:\n",
308
+ " return \"L\"\n",
309
+ " elif num == 4:\n",
310
+ " return 'R'\n",
311
+ "\n",
312
+ "\n",
313
+ " def randMove(self):\n",
314
+ " \"\"\"敌方坦克随机移动\"\"\"\n",
315
+ " \n",
316
+ " if self.step <= 0:\n",
317
+ " # 修改方向\n",
318
+ " self.direction = self.randDirection()\n",
319
+ " \n",
320
+ " # 让步数复位\n",
321
+ " self.step = 60\n",
322
+ " \n",
323
+ " else:\n",
324
+ " # 移动\n",
325
+ " self.move()\n",
326
+ " \n",
327
+ " # 让步数递减\n",
328
+ " self.step -= 1\n",
329
+ "\n",
330
+ " def shot(self):\n",
331
+ " \"\"\"重写 发射函数\"\"\"\n",
332
+ " \n",
333
+ " # 10% 的概率,发射子弹\n",
334
+ " num = random.randint(1, 100)\n",
335
+ " if num < 10:\n",
336
+ " return Bullet(self)"
337
+ ]
338
+ },
339
+ {
340
+ "cell_type": "markdown",
341
+ "id": "afc41a05",
342
+ "metadata": {},
343
+ "source": [
344
+ "## 子弹类"
345
+ ]
346
+ },
347
+ {
348
+ "cell_type": "code",
349
+ "execution_count": 7,
350
+ "id": "f23b42c3",
351
+ "metadata": {
352
+ "ExecuteTime": {
353
+ "end_time": "2023-10-05T17:25:00.288620Z",
354
+ "start_time": "2023-10-05T17:25:00.274879Z"
355
+ }
356
+ },
357
+ "outputs": [],
358
+ "source": [
359
+ "class Bullet(BaseItem):\n",
360
+ " \"\"\"子弹类\"\"\"\n",
361
+ "\n",
362
+ " def __init__(self, tank):\n",
363
+ " \"\"\"初始化函数\"\"\"\n",
364
+ "\n",
365
+ " # 加载子弹图片\n",
366
+ " self.image = pygame.image.load('img/enemymissile.gif')\n",
367
+ "\n",
368
+ " # 坦克的方向决定子弹的方向\n",
369
+ " self.direction = tank.direction\n",
370
+ "\n",
371
+ " # 获取子弹区域\n",
372
+ " self.rect = self.image.get_rect()\n",
373
+ "\n",
374
+ " # 初始化子弹的位置 -- 不同的方向,初始化的位置也不同\n",
375
+ " if self.direction == 'U':\n",
376
+ " self.rect.left = tank.rect.left + tank.rect.width / 2 - self.rect.width / 2\n",
377
+ " self.rect.top = tank.rect.top - self.rect.height\n",
378
+ "\n",
379
+ " elif self.direction == 'D':\n",
380
+ " self.rect.left = tank.rect.left + tank.rect.width / 2 - self.rect.width / 2\n",
381
+ " self.rect.top = tank.rect.top + tank.rect.height\n",
382
+ "\n",
383
+ " elif self.direction == 'L':\n",
384
+ " self.rect.left = tank.rect.left - self.rect.width / 2 - self.rect.width / 2\n",
385
+ " self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2\n",
386
+ "\n",
387
+ " elif self.direction == 'R':\n",
388
+ " self.rect.left = tank.rect.left + tank.rect.width\n",
389
+ " self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2\n",
390
+ "\n",
391
+ " # 子弹的速度\n",
392
+ " self.speed = 6\n",
393
+ "\n",
394
+ " # 子弹的状态 -- 是否存在\n",
395
+ " self.live = True\n",
396
+ "\n",
397
+ " def move(self):\n",
398
+ " \"\"\"移动函数\"\"\"\n",
399
+ "\n",
400
+ " if self.direction == 'U':\n",
401
+ " if self.rect.top > 0:\n",
402
+ " self.rect.top -= self.speed\n",
403
+ " else:\n",
404
+ " # 修改子弹的状态\n",
405
+ " self.live = False\n",
406
+ "\n",
407
+ " elif self.direction == 'R':\n",
408
+ " if self.rect.left+self.rect.width < SCREEN_WIDTH:\n",
409
+ " self.rect.left += self.speed\n",
410
+ " else:\n",
411
+ " # 修改子弹的状态\n",
412
+ " self.live = False\n",
413
+ "\n",
414
+ " elif self.direction == 'D':\n",
415
+ " if self.rect.top+self.rect.height < SCREEN_HEIGHT:\n",
416
+ " self.rect.top += self.speed\n",
417
+ " else:\n",
418
+ " # 修改子弹的状态\n",
419
+ " self.live = False\n",
420
+ " \n",
421
+ " elif self.direction == 'L':\n",
422
+ " if self.rect.left > 0:\n",
423
+ " self.rect.left -= self.speed\n",
424
+ " else:\n",
425
+ " # 修改子弹的状态\n",
426
+ " self.live = False\n",
427
+ "\n",
428
+ " \n",
429
+ " def hitWall(self):\n",
430
+ " \"\"\"子弹碰到墙体\"\"\"\n",
431
+ " \n",
432
+ " # 循环遍历墙壁列表\n",
433
+ " for wall in MainGame.wallList:\n",
434
+ " \n",
435
+ " # 如果相撞\n",
436
+ " if pygame.sprite.collide_rect(self, wall):\n",
437
+ " \n",
438
+ " # 修改子弹的生存状态,让子弹消失\n",
439
+ " self.live = False\n",
440
+ " \n",
441
+ " # 墙壁的生命值减小\n",
442
+ " wall.hp -= 1\n",
443
+ " \n",
444
+ " # 墙壁被破坏严重时,则消失\n",
445
+ " if wall.hp <= 0:\n",
446
+ " \n",
447
+ " # 修改墙壁的生存状态\n",
448
+ " wall.live = False\n",
449
+ "\n",
450
+ "\n",
451
+ " def displayBullet(self):\n",
452
+ " \"\"\"展示子弹\"\"\"\n",
453
+ " # 将图片surface加载到窗口\n",
454
+ " MainGame.window.blit(self.image, self.rect)\n",
455
+ "\n",
456
+ "\n",
457
+ " def myBullet_hit_enemyTank(self):\n",
458
+ " \"\"\"我方子弹与敌方坦克的碰撞\"\"\"\n",
459
+ " \n",
460
+ " # 循环遍历敌方坦克列表\n",
461
+ " for enemyTank in MainGame.enemyTankList:\n",
462
+ " \n",
463
+ " # 如果相撞\n",
464
+ " if pygame.sprite.collide_rect(enemyTank, self):\n",
465
+ " \n",
466
+ " # 修改敌方坦克和我方子弹的状态\n",
467
+ " enemyTank.live = False\n",
468
+ " self.live = False\n",
469
+ " \n",
470
+ " # 创建爆炸对象\n",
471
+ " explode = Explode(enemyTank)\n",
472
+ " \n",
473
+ " # 将爆炸对象添加到爆炸列表中\n",
474
+ " MainGame.explodeList.append(explode)\n",
475
+ " \n",
476
+ "\n",
477
+ " def enemyBullet_hit_myTank(self):\n",
478
+ " \"\"\"敌方子弹与我方坦克的碰撞\"\"\"\n",
479
+ " if MainGame.my_tank and MainGame.my_tank.live:\n",
480
+ " if pygame.sprite.collide_rect(MainGame.my_tank, self):\n",
481
+ " \n",
482
+ " # 产生爆炸对象\n",
483
+ " explode = Explode(MainGame.my_tank)\n",
484
+ " \n",
485
+ " # 将爆炸对象添加到爆炸列表中\n",
486
+ " MainGame.explodeList.append(explode)\n",
487
+ " \n",
488
+ " # 修改敌方子弹与我方坦克的状态\n",
489
+ " self.live = False\n",
490
+ " MainGame.my_tank.live = False"
491
+ ]
492
+ },
493
+ {
494
+ "cell_type": "markdown",
495
+ "id": "11a80705",
496
+ "metadata": {},
497
+ "source": [
498
+ "## 墙体类"
499
+ ]
500
+ },
501
+ {
502
+ "cell_type": "code",
503
+ "execution_count": 8,
504
+ "id": "881b05ec",
505
+ "metadata": {
506
+ "ExecuteTime": {
507
+ "end_time": "2023-10-05T17:25:00.294896Z",
508
+ "start_time": "2023-10-05T17:25:00.290645Z"
509
+ }
510
+ },
511
+ "outputs": [],
512
+ "source": [
513
+ "class Wall():\n",
514
+ " def __init__(self, left, top):\n",
515
+ " \n",
516
+ " # 加载墙壁图片\n",
517
+ " self.image = pygame.image.load('img/steels.gif')\n",
518
+ " \n",
519
+ " # 获取墙壁的区域\n",
520
+ " self.rect = self.image.get_rect()\n",
521
+ " \n",
522
+ " # 初始化墙体位置\n",
523
+ " self.rect.left = left\n",
524
+ " self.rect.top = top\n",
525
+ " \n",
526
+ " # 标记是否存活\n",
527
+ " self.live = True\n",
528
+ " \n",
529
+ " # 设置生命值\n",
530
+ " self.hp = 3\n",
531
+ "\n",
532
+ " def displayWall(self):\n",
533
+ " \"\"\"展示墙壁\"\"\"\n",
534
+ " MainGame.window.blit(self.image, self.rect)"
535
+ ]
536
+ },
537
+ {
538
+ "cell_type": "markdown",
539
+ "id": "86515d54",
540
+ "metadata": {},
541
+ "source": [
542
+ "## 爆炸类"
543
+ ]
544
+ },
545
+ {
546
+ "cell_type": "code",
547
+ "execution_count": 9,
548
+ "id": "c60e6eaa",
549
+ "metadata": {
550
+ "ExecuteTime": {
551
+ "end_time": "2023-10-05T17:25:00.305090Z",
552
+ "start_time": "2023-10-05T17:25:00.298733Z"
553
+ }
554
+ },
555
+ "outputs": [],
556
+ "source": [
557
+ "class Explode():\n",
558
+ " def __init__(self, tank):\n",
559
+ " \"\"\"初始化函数:传入爆炸的坦克\"\"\"\n",
560
+ " \n",
561
+ " # 获取爆炸位置\n",
562
+ " self.rect = tank.rect\n",
563
+ " \n",
564
+ " # 爆炸的图片\n",
565
+ " self.images = [\n",
566
+ " pygame.image.load('img/blast0.gif'),\n",
567
+ " pygame.image.load('img/blast1.gif'),\n",
568
+ " pygame.image.load('img/blast2.gif'),\n",
569
+ " pygame.image.load('img/blast3.gif'),\n",
570
+ " pygame.image.load('img/blast4.gif'),\n",
571
+ " ]\n",
572
+ " \n",
573
+ " # 索引 -- 依次使用不同的爆炸图片\n",
574
+ " self.step = 0\n",
575
+ " \n",
576
+ " self.image = self.images[self.step]\n",
577
+ " \n",
578
+ " # 是否进行爆炸\n",
579
+ " self.live = True\n",
580
+ "\n",
581
+ "\n",
582
+ " def displayExplode(self):\n",
583
+ " \"\"\"展示爆炸\"\"\"\n",
584
+ " if self.step < len(self.images):\n",
585
+ " \n",
586
+ " # 根据索引获取爆炸图片\n",
587
+ " self.image = self.images[self.step]\n",
588
+ " self.step += 1\n",
589
+ " \n",
590
+ " # 展示爆炸图片\n",
591
+ " MainGame.window.blit(self.image, self.rect)\n",
592
+ " \n",
593
+ " else:\n",
594
+ " # 修改爆炸对象的状态\n",
595
+ " self.live = False\n",
596
+ " self.step = 0\n"
597
+ ]
598
+ },
599
+ {
600
+ "cell_type": "markdown",
601
+ "id": "4acda6df",
602
+ "metadata": {},
603
+ "source": [
604
+ "## 音乐类"
605
+ ]
606
+ },
607
+ {
608
+ "cell_type": "code",
609
+ "execution_count": 10,
610
+ "id": "5943ae85",
611
+ "metadata": {
612
+ "ExecuteTime": {
613
+ "end_time": "2023-10-05T17:25:00.310602Z",
614
+ "start_time": "2023-10-05T17:25:00.306990Z"
615
+ }
616
+ },
617
+ "outputs": [],
618
+ "source": [
619
+ "class Music():\n",
620
+ " def __init__(self, filename):\n",
621
+ " \"\"\"初始化函数\"\"\"\n",
622
+ " \n",
623
+ " # 获取音乐名称\n",
624
+ " self.filename = filename\n",
625
+ " \n",
626
+ " # 初始化背景音乐\n",
627
+ " pygame.mixer.init()\n",
628
+ " \n",
629
+ " # 加载背景音乐\n",
630
+ " pygame.mixer.music.load(self.filename)\n",
631
+ "\n",
632
+ " \n",
633
+ " def play(self):\n",
634
+ " \"\"\"播放音乐\"\"\"\n",
635
+ " pygame.mixer.music.play()\n"
636
+ ]
637
+ },
638
+ {
639
+ "cell_type": "markdown",
640
+ "id": "b0a5fb3d",
641
+ "metadata": {},
642
+ "source": [
643
+ "## 主游戏类"
644
+ ]
645
+ },
646
+ {
647
+ "cell_type": "code",
648
+ "execution_count": 11,
649
+ "id": "f524cd94",
650
+ "metadata": {
651
+ "ExecuteTime": {
652
+ "end_time": "2023-10-05T17:25:00.354619Z",
653
+ "start_time": "2023-10-05T17:25:00.312692Z"
654
+ }
655
+ },
656
+ "outputs": [],
657
+ "source": [
658
+ "class MainGame():\n",
659
+ " \"\"\"\n",
660
+ " 主游戏类:\n",
661
+ " 游戏整体运行的逻辑\n",
662
+ " \"\"\"\n",
663
+ " \n",
664
+ " # 窗口\n",
665
+ " window = None\n",
666
+ " \n",
667
+ " # 我方坦克\n",
668
+ " my_tank = None\n",
669
+ " \n",
670
+ " # 存储敌方坦克的列表\n",
671
+ " enemyTankList = []\n",
672
+ " \n",
673
+ " # 定义敌方坦克的数量\n",
674
+ " enemyTankCount = 5\n",
675
+ " \n",
676
+ " # 存储我方子弹的列表\n",
677
+ " myBulletList = []\n",
678
+ " \n",
679
+ " # 存储敌方子弹的列表\n",
680
+ " enemyBulletList = []\n",
681
+ " \n",
682
+ " # 存储爆炸效果的列表\n",
683
+ " explodeList = []\n",
684
+ " \n",
685
+ " # 存储墙壁的列表\n",
686
+ " wallList = []\n",
687
+ "\n",
688
+ " def __init__(self):\n",
689
+ " \"\"\"初始化\"\"\"\n",
690
+ " pass\n",
691
+ " \n",
692
+ "\n",
693
+ " def startGame(self):\n",
694
+ " \"\"\"开始游戏\"\"\"\n",
695
+ " \n",
696
+ " # 初始化 pygame\n",
697
+ " pygame.display.init()\n",
698
+ " \n",
699
+ " # 设置窗口的大小及显示\n",
700
+ " MainGame.window = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])\n",
701
+ " \n",
702
+ " # 初始化 我方坦克\n",
703
+ " self.createMytank()\n",
704
+ " \n",
705
+ " # 初始化敌方坦克,并将敌方坦克添加到列表中\n",
706
+ " self.createEnemyTank()\n",
707
+ " \n",
708
+ " # 初始化墙壁\n",
709
+ " self.createWall()\n",
710
+ " \n",
711
+ " # 设置窗口的标题\n",
712
+ " pygame.display.set_caption('坦克大战1.03')\n",
713
+ " \n",
714
+ " while True:\n",
715
+ " \n",
716
+ " # 让坦克移动的速度慢一点\n",
717
+ " time.sleep(0.02)\n",
718
+ " \n",
719
+ " # 给窗口设置背景色\n",
720
+ " MainGame.window.fill(BG_COLOR)\n",
721
+ " \n",
722
+ " # 获取事件\n",
723
+ " self.getEvent()\n",
724
+ " \n",
725
+ " # 绘制文字\n",
726
+ " MainGame.window.blit(self.getTextSuface(\n",
727
+ " '敌方坦克剩余数量%d' % len(MainGame.enemyTankList)), (10, 10))\n",
728
+ " \n",
729
+ " ## 调用坦克显示的方法\n",
730
+ " if MainGame.my_tank and MainGame.my_tank.live:\n",
731
+ " # 我方坦克存活\n",
732
+ " MainGame.my_tank.displayTank()\n",
733
+ " else:\n",
734
+ " # 我方坦克死亡,删除我方坦克\n",
735
+ " del MainGame.my_tank\n",
736
+ " MainGame.my_tank = None\n",
737
+ " \n",
738
+ " # 循环遍历敌方坦克列表,展示敌方坦克\n",
739
+ " self.blitEnemyTank()\n",
740
+ " \n",
741
+ " # 循环遍历显示我方坦克的子弹\n",
742
+ " self.blitMyBullet()\n",
743
+ " \n",
744
+ " # 循环遍历敌方子弹列表,展示敌方子弹\n",
745
+ " self.blitEnemyBullet()\n",
746
+ " \n",
747
+ " # 循环遍历爆炸列表,展示爆炸效果\n",
748
+ " self.blitExplode()\n",
749
+ " \n",
750
+ " # 循环遍历墙壁列表,展示墙壁\n",
751
+ " self.blitWall()\n",
752
+ " \n",
753
+ " # 调用移动方法\n",
754
+ " if MainGame.my_tank and MainGame.my_tank.live:\n",
755
+ " # 如果坦克的开关是开启,才可以移动\n",
756
+ " if not MainGame.my_tank.stop:\n",
757
+ " MainGame.my_tank.move()\n",
758
+ " \n",
759
+ " # 检测我方坦克是否与墙壁发生碰撞\n",
760
+ " MainGame.my_tank.hitWall()\n",
761
+ " \n",
762
+ " # 检测我方坦克是否与敌方坦克发生碰撞\n",
763
+ " MainGame.my_tank.myTank_hit_enemyTank()\n",
764
+ "\n",
765
+ " # 更新屏幕\n",
766
+ " pygame.display.update()\n",
767
+ " \n",
768
+ " \n",
769
+ " def blitWall(self):\n",
770
+ " \"\"\"循环遍历墙壁列表,展示墙壁\"\"\"\n",
771
+ " for wall in MainGame.wallList:\n",
772
+ " # 判断墙壁是否存活\n",
773
+ " if wall.live:\n",
774
+ " # 调用墙壁的显示方法\n",
775
+ " wall.displayWall()\n",
776
+ " else:\n",
777
+ " # 从墙壁列表移出\n",
778
+ " MainGame.wallList.remove(wall)\n",
779
+ " \n",
780
+ "\n",
781
+ " def createWall(self):\n",
782
+ " \"\"\"初始化墙壁\"\"\"\n",
783
+ " for i in range(6):\n",
784
+ " \n",
785
+ " # 创建墙壁\n",
786
+ " wall = Wall(i*130, 220)\n",
787
+ " # 将墙壁添加到列表中\n",
788
+ " MainGame.wallList.append(wall)\n",
789
+ " \n",
790
+ "\n",
791
+ " def createMytank(self):\n",
792
+ " \"\"\"创建我方坦克\"\"\"\n",
793
+ " MainGame.my_tank = MyTank(350, 300)\n",
794
+ " \n",
795
+ " # 创建 Music对象\n",
796
+ " music = Music('img/start.wav')\n",
797
+ " \n",
798
+ " # 播放音乐\n",
799
+ " music.play()\n",
800
+ " \n",
801
+ "\n",
802
+ " def createEnemyTank(self):\n",
803
+ " \"\"\"创建敌方坦克\"\"\"\n",
804
+ " top = 100\n",
805
+ " \n",
806
+ " # 循环生成敌方坦克\n",
807
+ " for i in range(MainGame.enemyTankCount):\n",
808
+ " left = random.randint(0, 600)\n",
809
+ " speed = random.randint(1, 4)\n",
810
+ " enemy = EnemyTank(left, top, speed)\n",
811
+ " MainGame.enemyTankList.append(enemy)\n",
812
+ " \n",
813
+ "\n",
814
+ " def blitExplode(self):\n",
815
+ " \"\"\"展示爆炸效果\"\"\"\n",
816
+ " \n",
817
+ " # 遍历爆炸列表\n",
818
+ " for explode in MainGame.explodeList:\n",
819
+ " if explode.live:\n",
820
+ " # 展示爆炸效果\n",
821
+ " explode.displayExplode()\n",
822
+ " else:\n",
823
+ " # 在爆炸列表中移除\n",
824
+ " MainGame.explodeList.remove(explode)\n",
825
+ " \n",
826
+ "\n",
827
+ " def blitEnemyTank(self):\n",
828
+ " \"\"\"展示敌方坦克\"\"\"\n",
829
+ " for enemyTank in MainGame.enemyTankList:\n",
830
+ " \n",
831
+ " # 如果当前敌方坦克还活着\n",
832
+ " if enemyTank.live:\n",
833
+ " \n",
834
+ " # 展示\n",
835
+ " enemyTank.displayTank()\n",
836
+ " \n",
837
+ " # 移动\n",
838
+ " enemyTank.randMove()\n",
839
+ " \n",
840
+ " # 调用检测是否与墙壁碰撞\n",
841
+ " enemyTank.hitWall()\n",
842
+ " \n",
843
+ " # 检测敌方坦克是否与我方坦克发生碰撞\n",
844
+ " if MainGame.my_tank and MainGame.my_tank.live:\n",
845
+ " enemyTank.enemyTank_hit_myTank()\n",
846
+ " \n",
847
+ " # 发射子弹\n",
848
+ " enemyBullet = enemyTank.shot()\n",
849
+ " \n",
850
+ " # 敌方子弹是否是None,如果不为None则添加到敌方子弹列表中\n",
851
+ " if enemyBullet:\n",
852
+ " # 将敌方子弹存储到敌方子弹列表中\n",
853
+ " MainGame.enemyBulletList.append(enemyBullet)\n",
854
+ " \n",
855
+ " # 如果死亡,则从敌方坦克列表中移除 \n",
856
+ " else: \n",
857
+ " MainGame.enemyTankList.remove(enemyTank)\n",
858
+ "\n",
859
+ "\n",
860
+ " def blitMyBullet(self):\n",
861
+ " \"\"\"展示我方子弹\"\"\"\n",
862
+ " \n",
863
+ " # 遍历子弹列表\n",
864
+ " for myBullet in MainGame.myBulletList:\n",
865
+ " \n",
866
+ " # 子弹如果存在,则展示\n",
867
+ " if myBullet.live:\n",
868
+ " \n",
869
+ " # 展示子弹\n",
870
+ " myBullet.displayBullet()\n",
871
+ " \n",
872
+ " # 移动子弹\n",
873
+ " myBullet.move()\n",
874
+ " \n",
875
+ " # 判断我方子弹是否击中敌方\n",
876
+ " myBullet.myBullet_hit_enemyTank()\n",
877
+ " \n",
878
+ " # 判断我方子弹是否击中墙壁\n",
879
+ " myBullet.hitWall()\n",
880
+ " \n",
881
+ " # 子弹如果不存在,则从列表中删除\n",
882
+ " else:\n",
883
+ " MainGame.myBulletList.remove(myBullet)\n",
884
+ "\n",
885
+ " def blitEnemyBullet(self):\n",
886
+ " \"\"\"展示敌方子弹\"\"\"\n",
887
+ " \n",
888
+ " # 遍历敌方子弹列表\n",
889
+ " for enemyBullet in MainGame.enemyBulletList:\n",
890
+ " \n",
891
+ " # 如果敌方子弹存在\n",
892
+ " if enemyBullet.live: \n",
893
+ " \n",
894
+ " # 展示敌方子弹\n",
895
+ " enemyBullet.displayBullet()\n",
896
+ " \n",
897
+ " # 移动子弹\n",
898
+ " enemyBullet.move()\n",
899
+ " \n",
900
+ " # 敌方子弹 是否击中 我方坦克\n",
901
+ " enemyBullet.enemyBullet_hit_myTank()\n",
902
+ " \n",
903
+ " # 敌方子弹是否击中墙壁\n",
904
+ " enemyBullet.hitWall()\n",
905
+ " \n",
906
+ " # 如果子弹不存在,则从列表中移除\n",
907
+ " else:\n",
908
+ " MainGame.enemyBulletList.remove(enemyBullet)\n",
909
+ "\n",
910
+ " \n",
911
+ "\n",
912
+ " def endGame(self):\n",
913
+ " \"\"\"结束游戏\"\"\"\n",
914
+ " \n",
915
+ " print('谢谢使用,欢迎再次使用')\n",
916
+ " pygame.quit()\n",
917
+ " exit()\n",
918
+ " \n",
919
+ "\n",
920
+ " def getTextSuface(self, text):\n",
921
+ " \"\"\"左上角添加文字\"\"\"\n",
922
+ " \n",
923
+ " # 初始化字体模块\n",
924
+ " pygame.font.init()\n",
925
+ " \n",
926
+ " # 获取字体Font对象\n",
927
+ " font = pygame.font.SysFont('ヒラキノ角コシックw8', 18)\n",
928
+ " \n",
929
+ " # 绘制文字信息\n",
930
+ " textSurface = font.render(text, True, TEXT_COLOR)\n",
931
+ " \n",
932
+ " return textSurface\n",
933
+ " \n",
934
+ "\n",
935
+ " def getEvent(self):\n",
936
+ " \"\"\"获取事件\"\"\"\n",
937
+ " \n",
938
+ " # 获取所有事件\n",
939
+ " eventList = pygame.event.get()\n",
940
+ " \n",
941
+ " # 遍历事件\n",
942
+ " for event in eventList:\n",
943
+ " \n",
944
+ " # 鼠标点击退出,则结束游戏\n",
945
+ " if event.type == pygame.QUIT:\n",
946
+ " self.endGame()\n",
947
+ " \n",
948
+ " # 如果是键盘事件\n",
949
+ " if event.type == pygame.KEYDOWN:\n",
950
+ " \n",
951
+ " # 当我方坦克死亡\n",
952
+ " if not MainGame.my_tank:\n",
953
+ " \n",
954
+ " # 如果按下 Esc键\n",
955
+ " if event.key == pygame.K_ESCAPE:\n",
956
+ " # 我方坦克重生\n",
957
+ " self.createMytank()\n",
958
+ " \n",
959
+ " # 如果我方坦克存活\n",
960
+ " if MainGame.my_tank and MainGame.my_tank.live:\n",
961
+ " \n",
962
+ " # 按左键\n",
963
+ " if event.key == pygame.K_LEFT:\n",
964
+ " # 切换方向\n",
965
+ " MainGame.my_tank.direction = 'L'\n",
966
+ " \n",
967
+ " # 修改坦克的开关状态\n",
968
+ " MainGame.my_tank.stop = False\n",
969
+ " \n",
970
+ " \n",
971
+ " # 按右键\n",
972
+ " elif event.key == pygame.K_RIGHT:\n",
973
+ " # 切换方向\n",
974
+ " MainGame.my_tank.direction = 'R'\n",
975
+ " \n",
976
+ " # 修改坦克的开关状态\n",
977
+ " MainGame.my_tank.stop = False\n",
978
+ " \n",
979
+ " \n",
980
+ " # 按上键\n",
981
+ " elif event.key == pygame.K_UP:\n",
982
+ " # 切换方向\n",
983
+ " MainGame.my_tank.direction = 'U'\n",
984
+ " \n",
985
+ " # 修改坦克的开关状态\n",
986
+ " MainGame.my_tank.stop = False\n",
987
+ " \n",
988
+ " \n",
989
+ " # 按下键\n",
990
+ " elif event.key == pygame.K_DOWN:\n",
991
+ " # 切换方向\n",
992
+ " MainGame.my_tank.direction = 'D'\n",
993
+ " \n",
994
+ " # 修改坦克的开关状态\n",
995
+ " MainGame.my_tank.stop = False\n",
996
+ " \n",
997
+ " \n",
998
+ " # 按空格\n",
999
+ " elif event.key == pygame.K_SPACE:\n",
1000
+ " \"\"\"发射子弹\"\"\"\n",
1001
+ " \n",
1002
+ " # 最多发射 3发子弹\n",
1003
+ " if len(MainGame.myBulletList) < 3:\n",
1004
+ " # 创建子弹\n",
1005
+ " myBullet = Bullet(MainGame.my_tank)\n",
1006
+ " \n",
1007
+ " # 将子弹添加进列表\n",
1008
+ " MainGame.myBulletList.append(myBullet)\n",
1009
+ " \n",
1010
+ " # 添加音效\n",
1011
+ " music = Music('img/hit.wav')\n",
1012
+ " music.play()\n",
1013
+ "\n",
1014
+ " # 松开方向键\n",
1015
+ " if event.type == pygame.KEYUP:\n",
1016
+ " \"\"\"坦克停止移动,修改坦克的开关状态\"\"\"\n",
1017
+ " \n",
1018
+ " # 判断松开的键是上、下、左、右时候才停止坦克移动\n",
1019
+ " if event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:\n",
1020
+ " if MainGame.my_tank and MainGame.my_tank.live:\n",
1021
+ " MainGame.my_tank.stop = True"
1022
+ ]
1023
+ },
1024
+ {
1025
+ "cell_type": "markdown",
1026
+ "id": "c153a418",
1027
+ "metadata": {},
1028
+ "source": [
1029
+ "## main函数"
1030
+ ]
1031
+ },
1032
+ {
1033
+ "cell_type": "code",
1034
+ "execution_count": 12,
1035
+ "id": "f9ba8d23",
1036
+ "metadata": {
1037
+ "ExecuteTime": {
1038
+ "end_time": "2023-10-05T17:25:45.036981Z",
1039
+ "start_time": "2023-10-05T17:25:00.355339Z"
1040
+ },
1041
+ "collapsed": true
1042
+ },
1043
+ "outputs": [
1044
+ {
1045
+ "name": "stderr",
1046
+ "output_type": "stream",
1047
+ "text": [
1048
+ "2023-10-06 01:25:02.476 python[85798:5756894] TSM AdjustCapsLockLEDForKeyTransitionHandling - _ISSetPhysicalKeyboardCapsLockLED Inhibit\n"
1049
+ ]
1050
+ },
1051
+ {
1052
+ "name": "stdout",
1053
+ "output_type": "stream",
1054
+ "text": [
1055
+ "谢谢使用,欢迎再次使用\n"
1056
+ ]
1057
+ },
1058
+ {
1059
+ "ename": "error",
1060
+ "evalue": "display Surface quit",
1061
+ "output_type": "error",
1062
+ "traceback": [
1063
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
1064
+ "\u001b[0;31merror\u001b[0m Traceback (most recent call last)",
1065
+ "Input \u001b[0;32mIn [12]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m----> 2\u001b[0m \u001b[43mMainGame\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstartGame\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
1066
+ "Input \u001b[0;32mIn [11]\u001b[0m, in \u001b[0;36mMainGame.startGame\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgetEvent()\n\u001b[1;32m 68\u001b[0m \u001b[38;5;66;03m# 绘制文字\u001b[39;00m\n\u001b[0;32m---> 69\u001b[0m \u001b[43mMainGame\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwindow\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mblit\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgetTextSuface\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 70\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m敌方坦克剩余数量\u001b[39;49m\u001b[38;5;132;43;01m%d\u001b[39;49;00m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m%\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mMainGame\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43menemyTankList\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 72\u001b[0m \u001b[38;5;66;03m## 调用坦克显示的方法\u001b[39;00m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m MainGame\u001b[38;5;241m.\u001b[39mmy_tank \u001b[38;5;129;01mand\u001b[39;00m MainGame\u001b[38;5;241m.\u001b[39mmy_tank\u001b[38;5;241m.\u001b[39mlive:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;66;03m# 我方坦克存活\u001b[39;00m\n",
1067
+ "\u001b[0;31merror\u001b[0m: display Surface quit"
1068
+ ]
1069
+ }
1070
+ ],
1071
+ "source": [
1072
+ "if __name__ == '__main__':\n",
1073
+ " MainGame().startGame()"
1074
+ ]
1075
+ },
1076
+ {
1077
+ "cell_type": "code",
1078
+ "execution_count": null,
1079
+ "id": "4c763c1a",
1080
+ "metadata": {},
1081
+ "outputs": [],
1082
+ "source": []
1083
+ }
1084
+ ],
1085
+ "metadata": {
1086
+ "kernelspec": {
1087
+ "display_name": "Python 3 (ipykernel)",
1088
+ "language": "python",
1089
+ "name": "python3"
1090
+ },
1091
+ "language_info": {
1092
+ "codemirror_mode": {
1093
+ "name": "ipython",
1094
+ "version": 3
1095
+ },
1096
+ "file_extension": ".py",
1097
+ "mimetype": "text/x-python",
1098
+ "name": "python",
1099
+ "nbconvert_exporter": "python",
1100
+ "pygments_lexer": "ipython3",
1101
+ "version": "3.9.12"
1102
+ },
1103
+ "toc": {
1104
+ "base_numbering": 1,
1105
+ "nav_menu": {},
1106
+ "number_sections": true,
1107
+ "sideBar": true,
1108
+ "skip_h1_title": false,
1109
+ "title_cell": "Table of Contents",
1110
+ "title_sidebar": "Contents",
1111
+ "toc_cell": false,
1112
+ "toc_position": {
1113
+ "height": "calc(100% - 180px)",
1114
+ "left": "10px",
1115
+ "top": "150px",
1116
+ "width": "248.1875px"
1117
+ },
1118
+ "toc_section_display": true,
1119
+ "toc_window_display": true
1120
+ }
1121
+ },
1122
+ "nbformat": 4,
1123
+ "nbformat_minor": 5
1124
+ }
05_Pygame实战:坦克大战游戏.py ADDED
@@ -0,0 +1,760 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pygame
2
+ import time
3
+ import random
4
+ from pygame.sprite import Sprite
5
+
6
+ SCREEN_WIDTH = 800
7
+ SCREEN_HEIGHT = 500
8
+ BG_COLOR = pygame.Color(0, 0, 0)
9
+ TEXT_COLOR = pygame.Color(255, 0, 0)
10
+
11
+ # 定义一个基类
12
+ class BaseItem(Sprite):
13
+ def __init__(self, color, width, height):
14
+ pygame.sprite.Sprite.__init__(self)
15
+
16
+
17
+ class Tank(BaseItem):
18
+ def __init__(self, left, top):
19
+ """初始化方法:添加距离左边left 距离上边top"""
20
+
21
+ # 创建字典 -- 朝不同的方向走,坦克也不尽相同
22
+ self.images = {
23
+ 'U': pygame.image.load('img/p1tankU.gif'),
24
+ 'D': pygame.image.load('img/p1tankD.gif'),
25
+ 'L': pygame.image.load('img/p1tankL.gif'),
26
+ 'R': pygame.image.load('img/p1tankR.gif'),
27
+ }
28
+
29
+ # 默认方向
30
+ self.direction = 'L'
31
+
32
+ # 根据当前方向,获取图片
33
+ self.image = self.images[self.direction]
34
+
35
+ # 根据图片,获取坦克所在区域
36
+ self.rect = self.image.get_rect()
37
+
38
+ # 调整坦克所在区域
39
+ self.rect.left = left
40
+ self.rect.top = top
41
+
42
+ # 设置速度
43
+ self.speed = 5
44
+
45
+ # 坦克移动的开关
46
+ self.stop = True
47
+
48
+ # 是否活着
49
+ self.live = True
50
+
51
+ # 记录坦克上一时刻的坐标
52
+ self.oldLeft = self.rect.left
53
+ self.oldTop = self.rect.top
54
+
55
+ def move(self):
56
+ """移动函数"""
57
+
58
+ # 记录坦克上一时刻的坐标
59
+ self.oldLeft = self.rect.left
60
+ self.oldTop = self.rect.top
61
+
62
+ # 判断坦克的方向,根据一定的速度,进行移动
63
+ if self.direction == 'L':
64
+ if self.rect.left > 0:
65
+ self.rect.left -= self.speed
66
+
67
+ elif self.direction == 'U':
68
+ if self.rect.top > 0:
69
+ self.rect.top -= self.speed
70
+
71
+ elif self.direction == 'D':
72
+ if self.rect.top + self.rect.height < SCREEN_HEIGHT:
73
+ self.rect.top += self.speed
74
+
75
+ elif self.direction == 'R':
76
+ if self.rect.left + self.rect.height < SCREEN_WIDTH:
77
+ self.rect.left += self.speed
78
+
79
+ def shot(self):
80
+ """射击函数"""
81
+ return Bullet(self) # 返回子弹对象
82
+
83
+ def stay(self):
84
+ """暂停"""
85
+ self.rect.left = self.oldLeft
86
+ self.rect.top = self.oldTop
87
+
88
+ def hitWall(self):
89
+ """撞到墙体"""
90
+ for wall in MainGame.wallList:
91
+ if pygame.sprite.collide_rect(self, wall):
92
+ self.stay()
93
+
94
+ def displayTank(self):
95
+ """展示坦克"""
96
+ # 获取展示的对象
97
+ self.image = self.images[self.direction]
98
+
99
+ # 调用blit方法展示
100
+ MainGame.window.blit(self.image, self.rect)
101
+
102
+ # 我方坦克
103
+ class MyTank(Tank):
104
+ def __init__(self, left, top):
105
+ super(MyTank, self).__init__(left, top)
106
+
107
+
108
+ def myTank_hit_enemyTank(self):
109
+ """撞到敌方坦克"""
110
+ # 循环遍历敌方坦克列表
111
+ for enemyTank in MainGame.enemyTankList:
112
+ if pygame.sprite.collide_rect(self, enemyTank):
113
+ # 暂停
114
+ self.stay()
115
+
116
+
117
+ # 敌方坦克
118
+ class EnemyTank(Tank):
119
+ def __init__(self, left, top, speed):
120
+ # 调用父类的初始化方法
121
+ super(EnemyTank, self).__init__(left, top)
122
+
123
+ # 加载敌方坦克图片集
124
+ self.images = {
125
+ 'U': pygame.image.load('img/enemy1U.gif'),
126
+ 'D': pygame.image.load('img/enemy1D.gif'),
127
+ 'L': pygame.image.load('img/enemy1L.gif'),
128
+ 'R': pygame.image.load('img/enemy1R.gif')
129
+ }
130
+
131
+ # 随机生成敌方坦克的方向
132
+ self.direction = self.randDirection()
133
+
134
+ # 根据方向获取图片
135
+ self.image = self.images[self.direction]
136
+
137
+ # 获取敌方坦克区域
138
+ self.rect = self.image.get_rect()
139
+
140
+ # 更改敌方坦克位置
141
+ self.rect.left = left
142
+ self.rect.top = top
143
+
144
+ # 速度
145
+ self.speed = speed
146
+
147
+ # 移动开关键
148
+ self.flag = True
149
+
150
+ # 增加一个步数变量 step -- 每移动 step步,改变一次方向
151
+ self.step = 60
152
+
153
+ def enemyTank_hit_myTank(self):
154
+ """撞到我方坦克"""
155
+ if pygame.sprite.collide_rect(self, MainGame.my_tank):
156
+ # 停止
157
+ self.stay()
158
+
159
+ def randDirection(self):
160
+ """随机生成敌方坦克的方向"""
161
+ num = random.randint(1, 4)
162
+
163
+ if num == 1:
164
+ return 'U'
165
+ elif num == 2:
166
+ return 'D'
167
+ elif num == 3:
168
+ return "L"
169
+ elif num == 4:
170
+ return 'R'
171
+
172
+ def randMove(self):
173
+ """敌方坦克随机移动"""
174
+
175
+ if self.step <= 0:
176
+ # 修改方向
177
+ self.direction = self.randDirection()
178
+
179
+ # 让步数复位
180
+ self.step = 60
181
+
182
+ else:
183
+ # 移动
184
+ self.move()
185
+
186
+ # 让步数递减
187
+ self.step -= 1
188
+
189
+ def shot(self):
190
+ """重写 发射函数"""
191
+
192
+ # 10% 的概率,发射子弹
193
+ num = random.randint(1, 100)
194
+ if num < 10:
195
+ return Bullet(self)
196
+
197
+
198
+ class Bullet(BaseItem):
199
+ """子弹类"""
200
+
201
+ def __init__(self, tank):
202
+ """初始化函数"""
203
+
204
+ # 加载子弹图片
205
+ self.image = pygame.image.load('img/enemymissile.gif')
206
+
207
+ # 坦克的方向决定子弹的方向
208
+ self.direction = tank.direction
209
+
210
+ # 获取子弹区域
211
+ self.rect = self.image.get_rect()
212
+
213
+ # 初始化子弹的位置 -- 不同的方向,初始化的位置也不同
214
+ if self.direction == 'U':
215
+ self.rect.left = tank.rect.left + tank.rect.width / 2 - self.rect.width / 2
216
+ self.rect.top = tank.rect.top - self.rect.height
217
+
218
+ elif self.direction == 'D':
219
+ self.rect.left = tank.rect.left + tank.rect.width / 2 - self.rect.width / 2
220
+ self.rect.top = tank.rect.top + tank.rect.height
221
+
222
+ elif self.direction == 'L':
223
+ self.rect.left = tank.rect.left - self.rect.width / 2 - self.rect.width / 2
224
+ self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2
225
+
226
+ elif self.direction == 'R':
227
+ self.rect.left = tank.rect.left + tank.rect.width
228
+ self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2
229
+
230
+ # 子弹的速度
231
+ self.speed = 6
232
+
233
+ # 子弹的状态 -- 是否存在
234
+ self.live = True
235
+
236
+ def move(self):
237
+ """移动函数"""
238
+
239
+ if self.direction == 'U':
240
+ if self.rect.top > 0:
241
+ self.rect.top -= self.speed
242
+ else:
243
+ # 修改子弹的状态
244
+ self.live = False
245
+
246
+ elif self.direction == 'R':
247
+ if self.rect.left + self.rect.width < SCREEN_WIDTH:
248
+ self.rect.left += self.speed
249
+ else:
250
+ # 修改子弹的状态
251
+ self.live = False
252
+
253
+ elif self.direction == 'D':
254
+ if self.rect.top + self.rect.height < SCREEN_HEIGHT:
255
+ self.rect.top += self.speed
256
+ else:
257
+ # 修改子弹的状态
258
+ self.live = False
259
+
260
+ elif self.direction == 'L':
261
+ if self.rect.left > 0:
262
+ self.rect.left -= self.speed
263
+ else:
264
+ # 修改子弹的状态
265
+ self.live = False
266
+
267
+ def hitWall(self):
268
+ """子弹碰到墙体"""
269
+
270
+ # 循环遍历墙壁列表
271
+ for wall in MainGame.wallList:
272
+
273
+ # 如果相撞
274
+ if pygame.sprite.collide_rect(self, wall):
275
+
276
+ # 修改子弹的生存状态,让子弹消失
277
+ self.live = False
278
+
279
+ # 墙壁的生命值减小
280
+ wall.hp -= 1
281
+
282
+ # 墙壁被破坏严重时,则消失
283
+ if wall.hp <= 0:
284
+ # 修改墙壁的生存状态
285
+ wall.live = False
286
+
287
+ def displayBullet(self):
288
+ """展示子弹"""
289
+ # 将图片surface加载到窗口
290
+ MainGame.window.blit(self.image, self.rect)
291
+
292
+ def myBullet_hit_enemyTank(self):
293
+ """我方子弹与敌方坦克的碰撞"""
294
+
295
+ # 循环遍历敌方坦克列表
296
+ for enemyTank in MainGame.enemyTankList:
297
+
298
+ # 如果相撞
299
+ if pygame.sprite.collide_rect(enemyTank, self):
300
+ # 修改敌方坦克和我方子弹的状态
301
+ enemyTank.live = False
302
+ self.live = False
303
+
304
+ # 创建爆炸对象
305
+ explode = Explode(enemyTank)
306
+
307
+ # 将爆炸对象添加到爆炸列表中
308
+ MainGame.explodeList.append(explode)
309
+
310
+ def enemyBullet_hit_myTank(self):
311
+ """敌方子弹与我方坦克的碰撞"""
312
+ if MainGame.my_tank and MainGame.my_tank.live:
313
+ if pygame.sprite.collide_rect(MainGame.my_tank, self):
314
+ # 产生爆炸对象
315
+ explode = Explode(MainGame.my_tank)
316
+
317
+ # 将爆炸对象添加到爆炸列表中
318
+ MainGame.explodeList.append(explode)
319
+
320
+ # 修改敌方子弹与我方坦克的状态
321
+ self.live = False
322
+ MainGame.my_tank.live = False
323
+
324
+
325
+ class Wall():
326
+ def __init__(self, left, top):
327
+ # 加载墙壁图片
328
+ self.image = pygame.image.load('img/steels.gif')
329
+
330
+ # 获取墙壁的区域
331
+ self.rect = self.image.get_rect()
332
+
333
+ # 初始化墙体位置
334
+ self.rect.left = left
335
+ self.rect.top = top
336
+
337
+ # 标记是否存活
338
+ self.live = True
339
+
340
+ # 设置生命值
341
+ self.hp = 3
342
+
343
+ def displayWall(self):
344
+ """展示墙壁"""
345
+ MainGame.window.blit(self.image, self.rect)
346
+
347
+
348
+ class Explode():
349
+ def __init__(self, tank):
350
+ """初始化函数:传入爆炸的坦克"""
351
+
352
+ # 获取爆炸位置
353
+ self.rect = tank.rect
354
+
355
+ # 爆炸的图片
356
+ self.images = [
357
+ pygame.image.load('img/blast0.gif'),
358
+ pygame.image.load('img/blast1.gif'),
359
+ pygame.image.load('img/blast2.gif'),
360
+ pygame.image.load('img/blast3.gif'),
361
+ pygame.image.load('img/blast4.gif'),
362
+ ]
363
+
364
+ # 索引 -- 依次使用不同的爆炸图片
365
+ self.step = 0
366
+
367
+ self.image = self.images[self.step]
368
+
369
+ # 是否进行爆炸
370
+ self.live = True
371
+
372
+ def displayExplode(self):
373
+ """展示爆炸"""
374
+ if self.step < len(self.images):
375
+
376
+ # 根据索引获取爆炸图片
377
+ self.image = self.images[self.step]
378
+ self.step += 1
379
+
380
+ # 展示爆炸图片
381
+ MainGame.window.blit(self.image, self.rect)
382
+
383
+ else:
384
+ # 修改爆炸对象的状态
385
+ self.live = False
386
+ self.step = 0
387
+
388
+
389
+ class Music():
390
+ def __init__(self, filename):
391
+ """初始化函数"""
392
+
393
+ # 获取音乐名称
394
+ self.filename = filename
395
+
396
+ # 初始化背景音乐
397
+ pygame.mixer.init()
398
+
399
+ # 加载背景音乐
400
+ pygame.mixer.music.load(self.filename)
401
+
402
+ def play(self):
403
+ """播放音乐"""
404
+ pygame.mixer.music.play()
405
+
406
+
407
+ class MainGame():
408
+ """
409
+ 主游戏类:
410
+ 游戏整体运行的逻辑
411
+ """
412
+
413
+ # 窗口
414
+ window = None
415
+
416
+ # 我方坦克
417
+ my_tank = None
418
+
419
+ # 存储敌方坦克的列表
420
+ enemyTankList = []
421
+
422
+ # 定义敌方坦克的数量
423
+ enemyTankCount = 5
424
+
425
+ # 存储我方子弹的列表
426
+ myBulletList = []
427
+
428
+ # 存储敌方子弹的列表
429
+ enemyBulletList = []
430
+
431
+ # 存储爆炸效果的列表
432
+ explodeList = []
433
+
434
+ # 存储墙壁的列表
435
+ wallList = []
436
+
437
+ def __init__(self):
438
+ """初始化"""
439
+ pass
440
+
441
+ def startGame(self):
442
+ """开始游戏"""
443
+
444
+ # 初始化 pygame
445
+ pygame.display.init()
446
+
447
+ # 设置窗口的大小及显示
448
+ MainGame.window = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
449
+
450
+ # 初始化 我方坦克
451
+ self.createMytank()
452
+
453
+ # 初始化敌方坦克,并将敌方坦克添加到列表中
454
+ self.createEnemyTank()
455
+
456
+ # 初始化墙壁
457
+ self.createWall()
458
+
459
+ # 设置窗口的标题
460
+ pygame.display.set_caption('坦克大战1.03')
461
+
462
+ while True:
463
+
464
+ # 让坦克移动的速度慢一点
465
+ time.sleep(0.02)
466
+
467
+ # 清空主窗口
468
+ MainGame.window.fill(BG_COLOR)
469
+
470
+ # 获取事件
471
+ self.getEvent()
472
+
473
+ # 绘制文字
474
+ MainGame.window.blit(self.getTextSuface(
475
+ '敌方坦克剩余数量%d' % len(MainGame.enemyTankList)), (10, 10))
476
+
477
+ ## 调用坦克显示的方法
478
+ if MainGame.my_tank and MainGame.my_tank.live:
479
+ # 我方坦克存活
480
+ MainGame.my_tank.displayTank()
481
+ else:
482
+ # 我方坦克死亡,删除我方坦克
483
+ del MainGame.my_tank
484
+ MainGame.my_tank = None
485
+
486
+ # 循环遍历敌方坦克列表,展示敌方坦克
487
+ self.blitEnemyTank()
488
+
489
+ # 循环遍历显示我方坦克的子弹
490
+ self.blitMyBullet()
491
+
492
+ # 循环遍历敌方子弹列表,展示敌方子弹
493
+ self.blitEnemyBullet()
494
+
495
+ # 循环遍历爆炸列表,展示爆炸效果
496
+ self.blitExplode()
497
+
498
+ # 循环遍历墙壁列表,展示墙壁
499
+ self.blitWall()
500
+
501
+ # 调用移动方法
502
+ if MainGame.my_tank and MainGame.my_tank.live:
503
+ # 如果坦克的开关是开启,才可以移动
504
+ if not MainGame.my_tank.stop:
505
+ MainGame.my_tank.move()
506
+
507
+ # 检测我方坦克是否与墙壁发生碰撞
508
+ MainGame.my_tank.hitWall()
509
+
510
+ # 检测我方坦克是否与敌方坦克发生碰撞
511
+ MainGame.my_tank.myTank_hit_enemyTank()
512
+
513
+ # 更新屏幕
514
+ pygame.display.update()
515
+
516
+ def blitWall(self):
517
+ """循环遍历墙壁列表,展示墙壁"""
518
+ for wall in MainGame.wallList:
519
+ # 判断墙壁是否存活
520
+ if wall.live:
521
+ # 调用墙壁的显示方法
522
+ wall.displayWall()
523
+ else:
524
+ # 从墙壁列表移出
525
+ MainGame.wallList.remove(wall)
526
+
527
+ def createWall(self):
528
+ """初始化墙壁"""
529
+ for i in range(6):
530
+ # 创建墙壁
531
+ wall = Wall(i * 130, 220)
532
+ # 将墙壁添加到列表中
533
+ MainGame.wallList.append(wall)
534
+
535
+ def createMytank(self):
536
+ """创建我方坦克"""
537
+ MainGame.my_tank = MyTank(350, 300)
538
+
539
+ # 创建 Music对象
540
+ music = Music('img/start.wav')
541
+
542
+ # 播放音乐
543
+ music.play()
544
+
545
+ def createEnemyTank(self):
546
+ """创建敌方坦克"""
547
+ top = 100
548
+
549
+ # 循环生成敌方坦克
550
+ for i in range(MainGame.enemyTankCount):
551
+ left = random.randint(0, 600)
552
+ speed = random.randint(1, 4)
553
+ enemy = EnemyTank(left, top, speed)
554
+ MainGame.enemyTankList.append(enemy)
555
+
556
+ def blitExplode(self):
557
+ """展示爆炸效果"""
558
+
559
+ # 遍历爆炸列表
560
+ for explode in MainGame.explodeList:
561
+ if explode.live:
562
+ # 展示爆炸效果
563
+ explode.displayExplode()
564
+ else:
565
+ # 在爆炸列表中移除
566
+ MainGame.explodeList.remove(explode)
567
+
568
+ def blitEnemyTank(self):
569
+ """展示敌方坦克"""
570
+ for enemyTank in MainGame.enemyTankList:
571
+
572
+ # 如果当前敌方坦克还活着
573
+ if enemyTank.live:
574
+
575
+ # 展示
576
+ enemyTank.displayTank()
577
+
578
+ # 移动
579
+ enemyTank.randMove()
580
+
581
+ # 调用检测是否与墙壁碰撞
582
+ enemyTank.hitWall()
583
+
584
+ # 检测敌方坦克是否与我方坦克发生碰撞
585
+ if MainGame.my_tank and MainGame.my_tank.live:
586
+ enemyTank.enemyTank_hit_myTank()
587
+
588
+ # 发射子弹
589
+ enemyBullet = enemyTank.shot()
590
+
591
+ # 敌方子弹是否是None,如果不为None则添加到敌方子弹列表中
592
+ if enemyBullet:
593
+ # 将敌方子弹存储到敌方子弹列表中
594
+ MainGame.enemyBulletList.append(enemyBullet)
595
+
596
+ # 如果死亡,则从敌方坦克列表中移除
597
+ else:
598
+ MainGame.enemyTankList.remove(enemyTank)
599
+
600
+ def blitMyBullet(self):
601
+ """展示我方子弹"""
602
+
603
+ # 遍历子弹列表
604
+ for myBullet in MainGame.myBulletList:
605
+
606
+ # 子弹如果存在,则展示
607
+ if myBullet.live:
608
+
609
+ # 展示子弹
610
+ myBullet.displayBullet()
611
+
612
+ # 移动子弹
613
+ myBullet.move()
614
+
615
+ # 判断我方子弹是否击中敌方
616
+ myBullet.myBullet_hit_enemyTank()
617
+
618
+ # 判断我方子弹是否击中墙壁
619
+ myBullet.hitWall()
620
+
621
+ # 子弹如果不存在,则从列表中删除
622
+ else:
623
+ MainGame.myBulletList.remove(myBullet)
624
+
625
+ def blitEnemyBullet(self):
626
+ """展示敌方子弹"""
627
+
628
+ # 遍历敌方子弹列表
629
+ for enemyBullet in MainGame.enemyBulletList:
630
+
631
+ # 如果敌方子弹存在
632
+ if enemyBullet.live:
633
+
634
+ # 展示敌方子弹
635
+ enemyBullet.displayBullet()
636
+
637
+ # 移动子弹
638
+ enemyBullet.move()
639
+
640
+ # 敌方子弹 是否击中 我方坦克
641
+ enemyBullet.enemyBullet_hit_myTank()
642
+
643
+ # 敌方子弹是否击中墙壁
644
+ enemyBullet.hitWall()
645
+
646
+ # 如果子弹不存在,则从列表中移除
647
+ else:
648
+ MainGame.enemyBulletList.remove(enemyBullet)
649
+
650
+ def endGame(self):
651
+ """结束游戏"""
652
+
653
+ print('谢谢使用,欢迎再次使用')
654
+ pygame.quit()
655
+ exit()
656
+
657
+ def getTextSuface(self, text):
658
+ """左上角添加文字"""
659
+
660
+ # 初始化字体模块
661
+ pygame.font.init()
662
+
663
+ # 获取字体Font对象
664
+ font = pygame.font.SysFont('ヒラキノ角コシックw8', 18)
665
+
666
+ # 绘制文字信息
667
+ textSurface = font.render(text, True, TEXT_COLOR)
668
+
669
+ return textSurface
670
+
671
+ def getEvent(self):
672
+ """获取事件"""
673
+
674
+ # 获取所有事件
675
+ eventList = pygame.event.get()
676
+
677
+ # 遍历事件
678
+ for event in eventList:
679
+
680
+ # 鼠标点击退出,则结束游戏
681
+ if event.type == pygame.QUIT:
682
+ self.endGame()
683
+
684
+ # 如果是键盘事件
685
+ if event.type == pygame.KEYDOWN:
686
+
687
+ # 当我方坦克死亡
688
+ if not MainGame.my_tank:
689
+
690
+ # 如果按下 Esc键
691
+ if event.key == pygame.K_ESCAPE:
692
+ # 我方坦克重生
693
+ self.createMytank()
694
+
695
+ # 如果我方坦克存活
696
+ if MainGame.my_tank and MainGame.my_tank.live:
697
+
698
+ # 按左键
699
+ if event.key == pygame.K_LEFT:
700
+ # 切换方向
701
+ MainGame.my_tank.direction = 'L'
702
+
703
+ # 修改坦克的开关状态
704
+ MainGame.my_tank.stop = False
705
+
706
+
707
+ # 按右键
708
+ elif event.key == pygame.K_RIGHT:
709
+ # 切换方向
710
+ MainGame.my_tank.direction = 'R'
711
+
712
+ # 修改坦克的开关状态
713
+ MainGame.my_tank.stop = False
714
+
715
+
716
+ # 按上键
717
+ elif event.key == pygame.K_UP:
718
+ # 切换方向
719
+ MainGame.my_tank.direction = 'U'
720
+
721
+ # 修改坦克的开关状态
722
+ MainGame.my_tank.stop = False
723
+
724
+
725
+ # 按下键
726
+ elif event.key == pygame.K_DOWN:
727
+ # 切换方向
728
+ MainGame.my_tank.direction = 'D'
729
+
730
+ # 修改坦克的开关状态
731
+ MainGame.my_tank.stop = False
732
+
733
+
734
+ # 按空格
735
+ elif event.key == pygame.K_SPACE:
736
+ """发射子弹"""
737
+
738
+ # 最多发射 3发子弹
739
+ if len(MainGame.myBulletList) < 3:
740
+ # 创建子弹
741
+ myBullet = Bullet(MainGame.my_tank)
742
+
743
+ # 将子弹添加进列表
744
+ MainGame.myBulletList.append(myBullet)
745
+
746
+ # 添加音效
747
+ music = Music('img/hit.wav')
748
+ music.play()
749
+
750
+ # 松开方向键
751
+ if event.type == pygame.KEYUP:
752
+ """坦克停止移动,修改坦克的开关状态"""
753
+
754
+ # 判断松开的键是上、下、左、右时候才停止坦克移动
755
+ if event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
756
+ if MainGame.my_tank and MainGame.my_tank.live:
757
+ MainGame.my_tank.stop = True
758
+
759
+ if __name__ == '__main__':
760
+ MainGame().startGame()
06_PyQt5控件.ipynb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b5efa7da262aeda4dc2d5f5ddfebe2ded5ac6b1bcbd10b844630ded7afdba8d0
3
+ size 11522617
06_PyQt6-PySide6.ipynb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:79420cd37515a89456d24ac84cc59a3e74e4034cb84396b8d8af9123fdc22707
3
+ size 118934552
06_Pygame实战:AI贪吃蛇.ipynb ADDED
@@ -0,0 +1,942 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "bc8cda21",
7
+ "metadata": {
8
+ "ExecuteTime": {
9
+ "end_time": "2023-10-05T14:06:32.358450Z",
10
+ "start_time": "2023-10-05T14:06:09.861617Z"
11
+ }
12
+ },
13
+ "outputs": [
14
+ {
15
+ "name": "stdout",
16
+ "output_type": "stream",
17
+ "text": [
18
+ "pygame 2.1.0 (SDL 2.0.16, Python 3.9.12)\n",
19
+ "Hello from the pygame community. https://www.pygame.org/contribute.html\n",
20
+ "安全路径吃食物\n",
21
+ "安全路径吃食物\n",
22
+ "安全路径吃食物\n",
23
+ "安全路径吃食物\n",
24
+ "安全路径吃食物\n",
25
+ "安全路径吃食物\n",
26
+ "安全路径吃食物\n",
27
+ "安全路径吃食物\n",
28
+ "安全路径吃食物\n",
29
+ "安全路径吃食物\n",
30
+ "安全路径吃食物\n",
31
+ "安全路径吃食物\n",
32
+ "安全路径吃食物\n",
33
+ "安全路径吃食物\n",
34
+ "安全路径吃食物\n",
35
+ "安全路径吃食物\n",
36
+ "安全路径吃食物\n",
37
+ "安全路径吃食物\n",
38
+ "安全路径吃食物\n",
39
+ "安全路径吃食物\n",
40
+ "安全路径吃食物\n",
41
+ "安全路径吃食物\n",
42
+ "安全路径吃食物\n",
43
+ "安全路径吃食物\n",
44
+ "安全路径吃食物\n",
45
+ "安全路径吃食物\n",
46
+ "安全路径吃食物\n",
47
+ "安全路径吃食物\n",
48
+ "安全路径吃食物\n",
49
+ "安全路径吃食物\n",
50
+ "安全路径吃食物\n",
51
+ "安全路径吃食物\n",
52
+ "安全路径吃食物\n",
53
+ "安全路径吃食物\n",
54
+ "安全路径吃食物\n",
55
+ "安全路径吃食物\n",
56
+ "安全路径吃食物\n",
57
+ "安全路径吃食物\n",
58
+ "安全路径吃食物\n",
59
+ "安全路径吃食物\n",
60
+ "安全路径吃食物\n",
61
+ "安全路径吃食物\n",
62
+ "安全路径吃食物\n",
63
+ "安全路径吃食物\n",
64
+ "安全路径吃食物\n",
65
+ "安全路径吃食物\n",
66
+ "安全路径吃食物\n",
67
+ "安全路径吃食物\n",
68
+ "安全路径吃食物\n",
69
+ "安全路径吃食物\n",
70
+ "安全路径吃食物\n",
71
+ "安全路径吃食物\n",
72
+ "安全路径吃食物\n",
73
+ "安全路径吃食物\n",
74
+ "安全路径吃食物\n",
75
+ "安全路径吃食物\n",
76
+ "安全路径吃食物\n",
77
+ "安全路径吃食物\n",
78
+ "安全路径吃食物\n",
79
+ "安全路径吃食物\n",
80
+ "安全路径吃食物\n",
81
+ "安全路径吃食物\n",
82
+ "安全路径吃食物\n",
83
+ "安全路径吃食物\n",
84
+ "安全路径吃食物\n",
85
+ "安全路径吃食物\n",
86
+ "安全路径吃食物\n",
87
+ "安全路径吃食物\n",
88
+ "安全路径吃食物\n",
89
+ "安全路径吃食物\n",
90
+ "安全路径吃食物\n",
91
+ "安全路径吃食物\n",
92
+ "安全路径吃食物\n",
93
+ "安全路径吃食物\n",
94
+ "安全路径吃食物\n",
95
+ "安全路径吃食物\n",
96
+ "安全路径吃食物\n",
97
+ "安全路径吃食物\n",
98
+ "安全路径吃食物\n",
99
+ "安全路径吃食物\n",
100
+ "安全路径吃食物\n",
101
+ "安全路径吃食物\n",
102
+ "安全路径吃食物\n",
103
+ "安全路径吃食物\n",
104
+ "安全路径吃食物\n",
105
+ "安全路径吃食物\n",
106
+ "安全路径吃食物\n",
107
+ "安全路径吃食物\n",
108
+ "安全路径吃食物\n",
109
+ "安全路径吃食物\n",
110
+ "安全路径吃食物\n",
111
+ "安全路径吃食物\n",
112
+ "安全路径吃食物\n",
113
+ "安全路径吃食物\n",
114
+ "安全路径吃食物\n",
115
+ "安全路径吃食物\n",
116
+ "安全路径吃食物\n",
117
+ "安全路径吃食物\n",
118
+ "安全路径吃食物\n",
119
+ "安全路径吃食物\n",
120
+ "安全路径吃食物\n",
121
+ "安全路径吃食物\n",
122
+ "安全路径吃食物\n",
123
+ "安全路径吃食物\n",
124
+ "安全路径吃食物\n",
125
+ "安全路径吃食物\n",
126
+ "安全路径吃食物\n",
127
+ "安全路径吃食物\n",
128
+ "安全路径吃食物\n",
129
+ "安全路径吃食物\n",
130
+ "安全路径吃食物\n",
131
+ "安全路径吃食物\n",
132
+ "安全路径吃食物\n",
133
+ "安全路径吃食物\n",
134
+ "安全路径吃食物\n",
135
+ "安全路径吃食物\n",
136
+ "安全路径吃食物\n",
137
+ "安全路径吃食物\n",
138
+ "安全路径吃食物\n",
139
+ "安全路径吃食物\n",
140
+ "安全路径吃食物\n",
141
+ "安全路径吃食物\n",
142
+ "安全路径吃食物\n",
143
+ "安全路径吃食物\n",
144
+ "安全路径吃食物\n",
145
+ "安全路径吃食物\n",
146
+ "安全路径吃食物\n",
147
+ "安全路径吃食物\n",
148
+ "安全路径吃食物\n",
149
+ "安全路径吃食物\n",
150
+ "安全路径吃食物\n",
151
+ "安全路径吃食物\n",
152
+ "安全路径吃食物\n",
153
+ "安全路径吃食物\n",
154
+ "安全路径吃食物\n",
155
+ "安全路径吃食物\n",
156
+ "安全路径吃食物\n",
157
+ "安全路径吃食物\n",
158
+ "安全路径吃食物\n",
159
+ "安全路径吃食物\n",
160
+ "安全路径吃食物\n",
161
+ "安全路径吃食物\n",
162
+ "安全路径吃食物\n",
163
+ "安全路径吃食物\n",
164
+ "安全路径吃食物\n",
165
+ "安全路径吃食物\n",
166
+ "安全路径吃食物\n",
167
+ "安全路径吃食物\n",
168
+ "安全路径吃食物\n",
169
+ "安全路径吃食物\n",
170
+ "安全路径吃食物\n",
171
+ "安全路径吃食物\n",
172
+ "安全路径吃食物\n",
173
+ "安全路径吃食物\n",
174
+ "安全路径吃食物\n",
175
+ "安全路径吃食物\n",
176
+ "安全路径吃食物\n",
177
+ "安全路径吃食物\n",
178
+ "安全路径吃食物\n",
179
+ "安全路径吃食物\n",
180
+ "安全路径吃食物\n",
181
+ "安全路径吃食物\n",
182
+ "安全路径吃食物\n",
183
+ "安全路径吃食物\n",
184
+ "安全路径吃食物\n",
185
+ "安全路径吃食物\n",
186
+ "安全路径吃食物\n",
187
+ "安全路径吃食物\n",
188
+ "安全路径吃食物\n",
189
+ "安全路径吃食物\n",
190
+ "安全路径吃食物\n",
191
+ "安全路径吃食物\n",
192
+ "安全路径吃食物\n",
193
+ "安全路径吃食物\n",
194
+ "安全路径吃食物\n",
195
+ "安全路径吃食物\n",
196
+ "安全路径吃食物\n",
197
+ "安全路径吃食物\n",
198
+ "安全路径吃食物\n",
199
+ "安全路径吃食物\n",
200
+ "安全路径吃食物\n",
201
+ "安全路径吃食物\n",
202
+ "安全路径吃食物\n",
203
+ "安全路径吃食物\n",
204
+ "安全路径吃食物\n",
205
+ "安全路径吃食物\n",
206
+ "安全路径吃食物\n",
207
+ "安全路径吃食物\n",
208
+ "安全路径吃食物\n",
209
+ "安全路径吃食物\n",
210
+ "安全路径吃食物\n",
211
+ "安全路径吃食物\n",
212
+ "安全路径吃食物\n",
213
+ "安全路径吃食物\n",
214
+ "安全路径吃食物\n",
215
+ "安全路径吃食物\n",
216
+ "安全路径吃食物\n",
217
+ "安全路径吃食物\n",
218
+ "安全路径吃食物\n",
219
+ "安全路径吃食物\n",
220
+ "安全路径吃食物\n",
221
+ "安全路径吃食物\n",
222
+ "安全路径吃食物\n",
223
+ "安全路径吃食物\n",
224
+ "安全路径吃食物\n",
225
+ "安全路径吃食物\n",
226
+ "安全路径吃食物\n",
227
+ "安全路径吃食物\n",
228
+ "安全路径吃食物\n",
229
+ "安全路径吃食物\n",
230
+ "安全路径吃食物\n",
231
+ "安全路径吃食物\n",
232
+ "安全路径吃食物\n",
233
+ "安全路径吃食物\n",
234
+ "安全路径吃食物\n",
235
+ "安全路径吃食物\n",
236
+ "安全路径吃食物\n",
237
+ "安全路径吃食物\n",
238
+ "安全路径吃食物\n",
239
+ "安全路径吃食物\n",
240
+ "安全路径吃食物\n",
241
+ "安全路径吃食物\n",
242
+ "安全路径吃食物\n",
243
+ "安全路径吃食物\n",
244
+ "安全路径吃食物\n",
245
+ "安全路径吃食物\n",
246
+ "安全路径吃食物\n",
247
+ "安全路径吃食物\n",
248
+ "安全路径吃食物\n",
249
+ "安全路径吃食物\n",
250
+ "安全路径吃食物\n",
251
+ "安全路径吃食物\n",
252
+ "安全路径吃食物\n",
253
+ "安全路径吃食物\n",
254
+ "安全路径吃食物\n",
255
+ "安全路径吃食物\n",
256
+ "安全路径吃食物\n",
257
+ "安全路径吃食物\n",
258
+ "安全路径吃食物\n",
259
+ "安全路径吃食物\n",
260
+ "安全路径吃食物\n",
261
+ "安全路径吃食物\n",
262
+ "安全路径吃食物\n",
263
+ "安全路径吃食物\n",
264
+ "安全路径吃食物\n",
265
+ "安全路径吃食物\n",
266
+ "安全路径吃食物\n",
267
+ "安全路径吃食物\n",
268
+ "安全路径吃食物\n",
269
+ "安全路径吃食物\n",
270
+ "安全路径吃食物\n",
271
+ "安全路径吃食物\n",
272
+ "安全路径吃食物\n",
273
+ "安全路径吃食物\n",
274
+ "安全路径吃食物\n",
275
+ "安全路径吃食物\n",
276
+ "安全路径吃食物\n",
277
+ "安全路径吃食物\n",
278
+ "安全路径吃食物\n",
279
+ "安全路径吃食物\n",
280
+ "安全路径吃食物\n",
281
+ "安全路径吃食物\n",
282
+ "安全路径吃食物\n",
283
+ "安全路径吃食物\n",
284
+ "安全路径吃食物\n",
285
+ "安全路径吃食物\n",
286
+ "安全路径吃食物\n",
287
+ "安全路径吃食物\n",
288
+ "安全路径吃食物\n",
289
+ "安全路径吃食物\n",
290
+ "安全路径吃食物\n",
291
+ "安全路径吃食物\n",
292
+ "安全路径吃食物\n",
293
+ "安全路径吃食物\n",
294
+ "安全路径吃食物\n",
295
+ "安全路径吃食物\n",
296
+ "安全路径吃食物\n",
297
+ "安全路径吃食物\n",
298
+ "安全路径吃食物\n",
299
+ "安全路径吃食物\n",
300
+ "安全路径吃食物\n",
301
+ "安全路径吃食物\n",
302
+ "安全路径吃食物\n",
303
+ "安全路径吃食物\n",
304
+ "安全路径吃食物\n",
305
+ "安全路径吃食物\n",
306
+ "安全路��吃食物\n",
307
+ "安全路径吃食物\n",
308
+ "安全路径吃食物\n",
309
+ "安全路径吃食物\n",
310
+ "安全路径吃食物\n",
311
+ "安全路径吃食物\n",
312
+ "安全路径吃食物\n",
313
+ "安全路径吃食物\n",
314
+ "安全路径吃食物\n",
315
+ "安全路径吃食物\n",
316
+ "安全路径吃食物\n",
317
+ "安全路径吃食物\n",
318
+ "安全路径吃食物\n",
319
+ "安全路径吃食物\n",
320
+ "安全路径吃食物\n",
321
+ "安全路径吃食物\n",
322
+ "安全路径吃食物\n",
323
+ "安全路径吃食物\n",
324
+ "安全路径吃食物\n",
325
+ "安全路径吃食物\n",
326
+ "安全路径吃食物\n",
327
+ "安全路径吃食物\n",
328
+ "安全路径吃食物\n",
329
+ "安全路径吃食物\n",
330
+ "安全路径吃食物\n",
331
+ "安全路径吃食物\n",
332
+ "安全路径吃食物\n",
333
+ "安全路径吃食物\n",
334
+ "安全路径吃食物\n",
335
+ "安全路径吃食物\n",
336
+ "安全路径吃食物\n",
337
+ "安全路径吃食物\n",
338
+ "安全路径吃食物\n",
339
+ "安全路径吃食物\n",
340
+ "安全路径吃食物\n",
341
+ "安全路径吃食物\n",
342
+ "安全路径吃食物\n",
343
+ "安全路径吃食物\n",
344
+ "安全路径吃食物\n",
345
+ "安全路径吃食物\n",
346
+ "安全路径吃食物\n",
347
+ "安全路径吃食物\n",
348
+ "安全路径吃食物\n",
349
+ "安全路径吃食物\n",
350
+ "安全路径吃食物\n",
351
+ "安全路径吃食物\n",
352
+ "安全路径吃食物\n",
353
+ "安全路径吃食物\n",
354
+ "安全路径吃食物\n",
355
+ "安全路径吃食物\n",
356
+ "安全路径吃食物\n",
357
+ "安全路径吃食物\n",
358
+ "安全路径吃食物\n",
359
+ "安全路径吃食物\n",
360
+ "安全路径吃食物\n",
361
+ "安全路径吃食物\n",
362
+ "安全路径吃食物\n",
363
+ "安全路径吃食物\n",
364
+ "安全路径吃食物\n",
365
+ "安全路径吃食物\n",
366
+ "安全路径吃食物\n",
367
+ "安全路径吃食物\n",
368
+ "安全路径吃食物\n",
369
+ "安全路径吃食物\n",
370
+ "安全路径吃食物\n",
371
+ "安全路径吃食物\n",
372
+ "安全路径吃食物\n",
373
+ "安全路径吃食物\n",
374
+ "安全路径吃食物\n",
375
+ "安全路径吃食物\n",
376
+ "安全路径吃食物\n",
377
+ "安全路径吃食物\n",
378
+ "安全路径吃食物\n",
379
+ "安全路径吃食物\n",
380
+ "安全路径吃食物\n",
381
+ "安全路径吃食物\n",
382
+ "安全路径吃食物\n",
383
+ "安全路径吃食物\n",
384
+ "安全路径吃食物\n",
385
+ "安全路径吃食物\n",
386
+ "安全路径吃食物\n",
387
+ "安全路径吃食物\n",
388
+ "安全路径吃食物\n",
389
+ "安全路径吃食物\n",
390
+ "安全路径吃食物\n",
391
+ "安全路径吃食物\n",
392
+ "安全路径吃食物\n",
393
+ "安全路径吃食物\n",
394
+ "安全路径吃食物\n",
395
+ "安全路径吃食物\n",
396
+ "安全路径吃食物\n",
397
+ "安全路径吃食物\n",
398
+ "安全路径吃食物\n",
399
+ "安全路径吃食物\n",
400
+ "安全路径吃食物\n",
401
+ "安全路径吃食物\n",
402
+ "安全路径吃食物\n",
403
+ "安全路径吃食物\n",
404
+ "安全路径吃食物\n",
405
+ "安全路径吃食物\n",
406
+ "安全路径吃食物\n",
407
+ "安全路径吃食物\n",
408
+ "安全路径吃食物\n",
409
+ "安全路径吃食物\n",
410
+ "安全路径吃食物\n",
411
+ "安全路径吃食物\n",
412
+ "安全路径吃食物\n",
413
+ "安全路径吃食物\n",
414
+ "安全路径吃食物\n",
415
+ "安全路径吃食物\n",
416
+ "安全路径吃食物\n",
417
+ "安全路径吃食物\n",
418
+ "安全路径吃食物\n",
419
+ "安全路径吃食物\n",
420
+ "安全路径吃食物\n",
421
+ "安全路径吃食物\n"
422
+ ]
423
+ },
424
+ {
425
+ "ename": "KeyboardInterrupt",
426
+ "evalue": "",
427
+ "output_type": "error",
428
+ "traceback": [
429
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
430
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
431
+ "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m<cell line: 369>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 374\u001b[0m \u001b[38;5;66;03m# 当前布局能找到吃food_path\u001b[39;00m\n\u001b[1;32m 375\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m food_flag \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 376\u001b[0m \u001b[38;5;66;03m# 虚拟蛇探路,吃到食物后如果能找到tail_path则返回1\u001b[39;00m\n\u001b[0;32m--> 377\u001b[0m virtual_tail_flag \u001b[38;5;241m=\u001b[39m \u001b[43mexploretheway\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfood_Position\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msnake_Body\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 379\u001b[0m \u001b[38;5;66;03m# 虚拟蛇吃完食物后找得到tail_path\u001b[39;00m\n\u001b[1;32m 380\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m virtual_tail_flag \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n",
432
+ "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36mexploretheway\u001b[0;34m(food_position0, snake_body0)\u001b[0m\n\u001b[1;32m 229\u001b[0m food_position \u001b[38;5;241m=\u001b[39m food_position0\u001b[38;5;241m.\u001b[39mcopy()\n\u001b[1;32m 231\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[1;32m 232\u001b[0m \u001b[38;5;66;03m# 获取觅食路径\u001b[39;00m\n\u001b[0;32m--> 233\u001b[0m path, flag1 \u001b[38;5;241m=\u001b[39m \u001b[43mBFS\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfood_position\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msnake_body\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 235\u001b[0m \u001b[38;5;66;03m# 更新蛇头\u001b[39;00m\n\u001b[1;32m 236\u001b[0m snake_body\u001b[38;5;241m.\u001b[39minsert(\u001b[38;5;241m0\u001b[39m, path[\u001b[38;5;241m1\u001b[39m])\n",
433
+ "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36mBFS\u001b[0;34m(food_position0, snake_body0)\u001b[0m\n\u001b[1;32m 149\u001b[0m \u001b[38;5;66;03m# 如果当前没有吃到食物\u001b[39;00m\n\u001b[1;32m 150\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m currentlocation \u001b[38;5;241m!=\u001b[39m food_position:\n\u001b[1;32m 151\u001b[0m \n\u001b[1;32m 152\u001b[0m \u001b[38;5;66;03m# 记录下一步可以走到的位置,不考虑蛇身更新情况\u001b[39;00m\n\u001b[0;32m--> 153\u001b[0m l1 \u001b[38;5;241m=\u001b[39m \u001b[43mwalkable\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcurrentlocation\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msnake_body\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 155\u001b[0m \u001b[38;5;66;03m# 遍历下一步的所有可能\u001b[39;00m\n\u001b[1;32m 156\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m l1:\n",
434
+ "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36mwalkable\u001b[0;34m(location0, snake_body0)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[38;5;66;03m# 向左\u001b[39;00m\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m location0[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m20\u001b[39m \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m [location0[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m20\u001b[39m, location0[\u001b[38;5;241m1\u001b[39m]] \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m snake_body0:\n\u001b[0;32m--> 105\u001b[0m \u001b[43ml\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mappend\u001b[49m([location0[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m20\u001b[39m, location0[\u001b[38;5;241m1\u001b[39m]])\n\u001b[1;32m 107\u001b[0m \u001b[38;5;66;03m# 向下\u001b[39;00m\n\u001b[1;32m 108\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m location0[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m20\u001b[39m \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m300\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m [location0[\u001b[38;5;241m0\u001b[39m], location0[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m20\u001b[39m] \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m snake_body0:\n",
435
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
436
+ ]
437
+ }
438
+ ],
439
+ "source": [
440
+ "import pygame\n",
441
+ "import sys\n",
442
+ "import random\n",
443
+ "import time\n",
444
+ "from pygame.locals import *\n",
445
+ "\n",
446
+ "# 初始化Pygame库\n",
447
+ "pygame.init()\n",
448
+ "\n",
449
+ "# 初始化一个游戏界面窗口\n",
450
+ "DISPLAY = pygame.display.set_mode((300, 300))\n",
451
+ "\n",
452
+ "# 设置游戏窗口的标题\n",
453
+ "pygame.display.set_caption('贪吃蛇')\n",
454
+ "\n",
455
+ "# 定义一个变量来控制游戏速度\n",
456
+ "FPSCLOCK = pygame.time.Clock()\n",
457
+ "\n",
458
+ "# 初始化游戏界面内使用的字体\n",
459
+ "BASICFONT = pygame.font.SysFont(\"SIMYOU.TTF\", 80)\n",
460
+ "font = pygame.font.SysFont('simsunnsimsun', 80)\n",
461
+ "\n",
462
+ "# 设置颜色\n",
463
+ "BLACK = pygame.Color(0, 0, 0)\n",
464
+ "WHITE = pygame.Color(255, 255, 255)\n",
465
+ "RED = pygame.Color(255, 0, 0)\n",
466
+ "GREY = pygame.Color(150, 150, 150)\n",
467
+ "GREEN = pygame.Color(0, 255, 255)\n",
468
+ "YELLOW = pygame.Color(255, 255, 0)\n",
469
+ "FORESTGREEN = pygame.Color(34, 139, 34)\n",
470
+ "Purple3 = pygame.Color(125, 38, 205)\n",
471
+ "\n",
472
+ "# 贪吃蛇头的初始位置\n",
473
+ "snake_Head = [100, 100]\n",
474
+ "\n",
475
+ "# 初始化贪吃蛇的身体 (注:这里以20*20为一个标准小格子)\n",
476
+ "snake_Body = [[80, 100], [60, 100], [40, 100]]\n",
477
+ "\n",
478
+ "# 指定蛇初始前进的方向 -- 向右\n",
479
+ "direction = \"RIGHT\"\n",
480
+ "\n",
481
+ "\n",
482
+ "def randomfood(snake_Body):\n",
483
+ " \"\"\"随机生成食物\"\"\"\n",
484
+ "\n",
485
+ " # 随机生���食物的位置\n",
486
+ " x = random.randrange(1, 15)\n",
487
+ " y = random.randrange(1, 15)\n",
488
+ " Position = [int(x * 20), int(y * 20)]\n",
489
+ "\n",
490
+ " # 如果食物在贪吃蛇的身体里,则重新随机\n",
491
+ " while Position in snake_Body:\n",
492
+ " x = random.randrange(1, 15)\n",
493
+ " y = random.randrange(1, 15)\n",
494
+ " Position = [int(x * 20), int(y * 20)]\n",
495
+ " return Position\n",
496
+ "\n",
497
+ "\n",
498
+ "# 随机出现第一个食物\n",
499
+ "food_Position = randomfood(snake_Body)\n",
500
+ "\n",
501
+ "# 标记食物\n",
502
+ "food_flag = 1\n",
503
+ "\n",
504
+ "\n",
505
+ "def GameOver():\n",
506
+ " \"\"\"游戏结束\"\"\"\n",
507
+ "\n",
508
+ " # 设置GameOver的显示颜色与文字\n",
509
+ " if (len(snake_Body) - 3) <= 10:\n",
510
+ " GameOver_Surf = font.render('太菜了', True, GREY)\n",
511
+ " elif 10 <= (len(snake_Body) - 3) <= 20:\n",
512
+ " GameOver_Surf = font.render('一般般', True, GREY)\n",
513
+ " else:\n",
514
+ " GameOver_Surf = font.render('很棒!', True, GREY)\n",
515
+ "\n",
516
+ " # 设置GameOver的位置\n",
517
+ " GameOver_Rect = GameOver_Surf.get_rect()\n",
518
+ " GameOver_Rect.midtop = (320, 200)\n",
519
+ "\n",
520
+ " # 绑定以上设置到句柄\n",
521
+ " DISPLAY.blit(GameOver_Surf, GameOver_Rect)\n",
522
+ "\n",
523
+ " # 刷新画面\n",
524
+ " pygame.display.flip()\n",
525
+ "\n",
526
+ " # 等待3秒\n",
527
+ " time.sleep(3)\n",
528
+ " # 退出游戏\n",
529
+ " pygame.quit()\n",
530
+ " # 退出程序\n",
531
+ " sys.exit()\n",
532
+ "\n",
533
+ "\n",
534
+ "def walkable(location0, snake_body0):\n",
535
+ " \"\"\"返回下一步可以到达的位置\"\"\"\n",
536
+ " l = []\n",
537
+ "\n",
538
+ " # 向右\n",
539
+ " if location0[0] + 20 < 300 and [location0[0] + 20, location0[1]] not in snake_body0:\n",
540
+ " l.append([location0[0] + 20, location0[1]])\n",
541
+ "\n",
542
+ " # 向左\n",
543
+ " if location0[0] - 20 >= 0 and [location0[0] - 20, location0[1]] not in snake_body0:\n",
544
+ " l.append([location0[0] - 20, location0[1]])\n",
545
+ "\n",
546
+ " # 向下\n",
547
+ " if location0[1] + 20 < 300 and [location0[0], location0[1] + 20] not in snake_body0:\n",
548
+ " l.append([location0[0], location0[1] + 20])\n",
549
+ "\n",
550
+ " # 向上\n",
551
+ " if location0[1] - 20 >= 0 and [location0[0], location0[1] - 20] not in snake_body0:\n",
552
+ " l.append([location0[0], location0[1] - 20])\n",
553
+ "\n",
554
+ " return l\n",
555
+ "\n",
556
+ "\n",
557
+ "def changexy(position0):\n",
558
+ " \"\"\"改变位置\"\"\"\n",
559
+ " position = position0.copy()\n",
560
+ " return position[1] * 15 + position[0]\n",
561
+ "\n",
562
+ "\n",
563
+ "def BFS(food_position0, snake_body0):\n",
564
+ " # 获取食物的位置\n",
565
+ " food_position = food_position0.copy()\n",
566
+ " # 获取贪吃蛇的身体信息\n",
567
+ " snake_body = snake_body0.copy()\n",
568
+ "\n",
569
+ " # 是否可以吃到食物\n",
570
+ " flag = 0\n",
571
+ "\n",
572
+ " # 存储路径\n",
573
+ " path = []\n",
574
+ "\n",
575
+ " # 搜索栈,广度优先搜索\n",
576
+ " search_stack = []\n",
577
+ "\n",
578
+ " # {下一个位置:当前位置}\n",
579
+ " visited_dict = {}\n",
580
+ " search_stack.append([snake_body[0][0], snake_body[0][1]])\n",
581
+ " visited_dict[changexy([snake_body[0][0], snake_body[0][1]])] = -1\n",
582
+ "\n",
583
+ " # 进行广度优先遍历\n",
584
+ " while len(search_stack) > 0:\n",
585
+ " # 获取贪吃蛇头当前位置\n",
586
+ " currentlocation = search_stack.pop(0)\n",
587
+ "\n",
588
+ " # 如果当前没有吃到食物\n",
589
+ " if currentlocation != food_position:\n",
590
+ "\n",
591
+ " # 记录下一步可以走到的位置,不考虑蛇身更新情况\n",
592
+ " l1 = walkable(currentlocation, snake_body)\n",
593
+ "\n",
594
+ " # 遍历下一步的所有可能\n",
595
+ " for i in l1:\n",
596
+ " if visited_dict.get(changexy(i)) == None:\n",
597
+ " search_stack.append(i)\n",
598
+ " visited_dict[changexy(i)] = currentlocation\n",
599
+ "\n",
600
+ " # 如果吃到该食物\n",
601
+ " else:\n",
602
+ " flag = 1\n",
603
+ " break\n",
604
+ "\n",
605
+ " # 添加食物位置进路径\n",
606
+ " path.append(currentlocation)\n",
607
+ "\n",
608
+ " # 不断将前一个位置添加进路径\n",
609
+ " while visited_dict[changexy(currentlocation)] != -1:\n",
610
+ " currentlocation = visited_dict[changexy(currentlocation)]\n",
611
+ " path.append(currentlocation)\n",
612
+ "\n",
613
+ " # 反转 --> 贪吃蛇的头到食物的路径\n",
614
+ " path.reverse()\n",
615
+ "\n",
616
+ " return path, flag\n",
617
+ "\n",
618
+ "\n",
619
+ "def tailBFS(tail_position0, snake_body0):\n",
620
+ " # 蛇尾、蛇身\n",
621
+ " tail_position = tail_position0.copy()\n",
622
+ " snake_body = snake_body0.copy()\n",
623
+ "\n",
624
+ " # 记录是否能到蛇尾\n",
625
+ " flag = 0\n",
626
+ "\n",
627
+ " # 记录到蛇尾的路径\n",
628
+ " path = []\n",
629
+ "\n",
630
+ " # 搜索栈\n",
631
+ " search_stack = []\n",
632
+ "\n",
633
+ " # {下一个位置:当前位置}\n",
634
+ " visited_dict = {}\n",
635
+ "\n",
636
+ " # 初始化\n",
637
+ " search_stack.append([snake_body[0][0], snake_body[0][1]])\n",
638
+ " visited_dict[changexy([snake_body[0][0], snake_body[0][1]])] = -1\n",
639
+ "\n",
640
+ " # 开始广度优先遍历\n",
641
+ " while len(search_stack) > 0:\n",
642
+ " # 获取当前位置\n",
643
+ " currentlocation = search_stack.pop(0)\n",
644
+ "\n",
645
+ " if currentlocation != tail_position:\n",
646
+ " # l1存储下一步可走的位置,蛇尾是可到达的\n",
647
+ " tail = snake_body.pop()\n",
648
+ " l1 = walkable(currentlocation, snake_body)\n",
649
+ " snake_body.append(tail)\n",
650
+ "\n",
651
+ " # 遍历各个可走位置\n",
652
+ " for i1 in l1:\n",
653
+ " if visited_dict.get(changexy(i1)) == None:\n",
654
+ " # 更新搜索栈\n",
655
+ " search_stack.append(i1)\n",
656
+ " # 记录位置点间的关系\n",
657
+ " visited_dict[changexy(i1)] = currentlocation\n",
658
+ " else:\n",
659
+ " flag = 1\n",
660
+ " break\n",
661
+ "\n",
662
+ " return flag\n",
663
+ "\n",
664
+ "\n",
665
+ "def exploretheway(food_position0, snake_body0):\n",
666
+ " # 获取蛇身与食物位置\n",
667
+ " snake_body = snake_body0.copy()\n",
668
+ " food_position = food_position0.copy()\n",
669
+ "\n",
670
+ " while True:\n",
671
+ " # 获取觅食路径\n",
672
+ " path, flag1 = BFS(food_position, snake_body)\n",
673
+ "\n",
674
+ " # 更新蛇头\n",
675
+ " snake_body.insert(0, path[1])\n",
676
+ "\n",
677
+ " # 如果吃到食物\n",
678
+ " if snake_body[0][0] == food_position[0] and snake_body[0][1] == food_position[1]:\n",
679
+ "\n",
680
+ " # 当前位置是否能找到尾巴\n",
681
+ " flag2 = tailBFS(snake_body[-1], snake_body)\n",
682
+ "\n",
683
+ " break\n",
684
+ " # 未吃到食物,则更新蛇尾\n",
685
+ " else:\n",
686
+ " snake_body.pop()\n",
687
+ " return flag2\n",
688
+ "\n",
689
+ "\n",
690
+ "def longest_tail_path(snake_body0):\n",
691
+ " \"\"\"返回最长的蛇尾路径\"\"\"\n",
692
+ "\n",
693
+ " # 获取蛇身\n",
694
+ " snake_body = snake_body0.copy()\n",
695
+ "\n",
696
+ " # 获取下一步可以走的路,蛇尾是可以走的\n",
697
+ " tail = snake_body.pop()\n",
698
+ " l0 = walkable(snake_body[0], snake_body)\n",
699
+ " snake_body.append(tail)\n",
700
+ "\n",
701
+ " longest = -1\n",
702
+ "\n",
703
+ " # 遍历下一步可能的情况,如A\\B\\C\n",
704
+ " # 再分别利用广度优先搜索,在A\\B\\C的情况下,找出到蛇尾的最短路径\n",
705
+ " # 如果A情况下最短路径是最长的,则下一步往A处走\n",
706
+ " for i0 in l0:\n",
707
+ " flag = 0\n",
708
+ " path = []\n",
709
+ " search_stack = []\n",
710
+ " visited_dict = {}\n",
711
+ "\n",
712
+ " # 搜索栈 -- 广度优先遍历\n",
713
+ " search_stack.append(i0)\n",
714
+ "\n",
715
+ " # {下一个位置:当前位置}\n",
716
+ " visited_dict[changexy(i0)] = -1\n",
717
+ "\n",
718
+ " # 广度搜索\n",
719
+ " while len(search_stack) > 0:\n",
720
+ "\n",
721
+ " # 更新当前位置\n",
722
+ " currentlocation = search_stack.pop(0)\n",
723
+ "\n",
724
+ " # 如果不在蛇尾\n",
725
+ " if currentlocation != snake_body[-1]:\n",
726
+ "\n",
727
+ " # l1记录所有下一位置的可能\n",
728
+ " tail = snake_body.pop()\n",
729
+ " l1 = walkable(currentlocation, snake_body)\n",
730
+ " snake_body.append(tail)\n",
731
+ "\n",
732
+ " # 遍历下一位置的可能\n",
733
+ " for j in l1:\n",
734
+ " if visited_dict.get(changexy(j)) == None:\n",
735
+ "\n",
736
+ " # 更新搜索栈\n",
737
+ " search_stack.append(j)\n",
738
+ "\n",
739
+ " # 更新各位置的先后关系\n",
740
+ " visited_dict[changexy(j)] = currentlocation\n",
741
+ "\n",
742
+ " else:\n",
743
+ " # 找到了一条到达蛇尾的路径\n",
744
+ " flag = 1\n",
745
+ " break\n",
746
+ "\n",
747
+ " # 记录路径信息\n",
748
+ " path.append(currentlocation)\n",
749
+ " while visited_dict[changexy(currentlocation)] != -1:\n",
750
+ " currentlocation = visited_dict[changexy(currentlocation)]\n",
751
+ " path.append(currentlocation)\n",
752
+ " path.reverse()\n",
753
+ "\n",
754
+ " # 如果有更长的��径,则更新最长路径\n",
755
+ " if flag == 1:\n",
756
+ " if len(path) > longest:\n",
757
+ " longest = len(path)\n",
758
+ " longest_path = path\n",
759
+ "\n",
760
+ " return longest_path\n",
761
+ "\n",
762
+ "\n",
763
+ "# 漫步函数,随机选择蛇头周围四个方向中可走的方向走一步\n",
764
+ "def wander(snake_body0):\n",
765
+ " # 获取下一步可以走的路径\n",
766
+ " l = walkable(snake_body0[0], snake_body0)\n",
767
+ "\n",
768
+ " # 随机走一步\n",
769
+ " if len(l) > 1:\n",
770
+ " x = random.randrange(0, len(l))\n",
771
+ " elif len(l) == 1:\n",
772
+ " x = 0\n",
773
+ " else:\n",
774
+ " return l\n",
775
+ " return l[x]\n",
776
+ "\n",
777
+ "def drawSnake(snake_body0):\n",
778
+ "\n",
779
+ " # 画蛇头(x, y, width, length)\n",
780
+ " pygame.draw.rect(DISPLAY, Purple3, Rect(\n",
781
+ " snake_body0[0][0], snake_body0[0][1], 20, 20))\n",
782
+ "\n",
783
+ " # 画蛇身\n",
784
+ " for j in range(1, len(snake_body0)-1):\n",
785
+ " pygame.draw.rect(DISPLAY, FORESTGREEN, Rect(\n",
786
+ " snake_body0[j][0], snake_body0[j][1], 20, 20))\n",
787
+ "\n",
788
+ " # 画蛇尾\n",
789
+ " pygame.draw.rect(DISPLAY, GREEN, Rect(\n",
790
+ " snake_body0[-1][0], snake_body0[-1][1], 20, 20))\n",
791
+ "\n",
792
+ "def drawFood(food_position0):\n",
793
+ " # 画食物\n",
794
+ " pygame.draw.rect(DISPLAY, RED, Rect(\n",
795
+ " food_position0[0], food_position0[1], 20, 20))\n",
796
+ "\n",
797
+ "def drawScore(score):\n",
798
+ " # 设置分数的显示颜色\n",
799
+ " score_Surf = BASICFONT.render('%s' % (score), True, GREY)\n",
800
+ " # 设置分数的位置\n",
801
+ " score_Rect = score_Surf.get_rect()\n",
802
+ " score_Rect.midtop = (50, 20)\n",
803
+ " # 绑定以上设置到句柄\n",
804
+ " DISPLAY.blit(score_Surf, score_Rect)\n",
805
+ "\n",
806
+ "while True:\n",
807
+ " # 蛇身不动,蛇头动,是否可以吃到食物,返回最短路径\n",
808
+ " food_path, food_flag = BFS(food_Position, snake_Body)\n",
809
+ "\n",
810
+ " # 蛇身不动,蛇头动,尾巴是否可到达\n",
811
+ " tail_flag = tailBFS(snake_Body[-1], snake_Body)\n",
812
+ "\n",
813
+ " # 当前布局能找到吃food_path\n",
814
+ " if food_flag == 1:\n",
815
+ " # 虚拟蛇探路,吃到食物后如果能找到tail_path则返回1\n",
816
+ " virtual_tail_flag = exploretheway(food_Position, snake_Body)\n",
817
+ "\n",
818
+ " # 虚拟蛇吃完食物后找得到tail_path\n",
819
+ " if virtual_tail_flag == 1:\n",
820
+ " print(\"安全路径吃食物\")\n",
821
+ "\n",
822
+ " # 真实的蛇沿最短路径向食物走一步\n",
823
+ " snake_Body.insert(0, food_path[1])\n",
824
+ "\n",
825
+ " # 吃到食物,则重新随机食物位置\n",
826
+ " if snake_Body[0][0] == food_Position[0] and snake_Body[0][1] == food_Position[1]:\n",
827
+ " food_Position = randomfood(snake_Body)\n",
828
+ "\n",
829
+ " # 未吃到食物,则更新蛇尾\n",
830
+ " else:\n",
831
+ " snake_Body.pop()\n",
832
+ "\n",
833
+ " # 虚拟蛇吃完食物后找不到tail_path\n",
834
+ " else:\n",
835
+ " print(\"直接吃不安全,找蛇尾\")\n",
836
+ "\n",
837
+ " # 真实的蛇沿最长路径向蛇尾走一步\n",
838
+ " longest_path = longest_tail_path(snake_Body)\n",
839
+ "\n",
840
+ " # 更新蛇身\n",
841
+ " snake_Body.insert(0, longest_path[0])\n",
842
+ " if snake_Body[0][0] == food_Position[0] and snake_Body[0][1] == food_Position[1]:\n",
843
+ " food_Position = randomfood(snake_Body)\n",
844
+ " else:\n",
845
+ " snake_Body.pop()\n",
846
+ "\n",
847
+ " # 当前布局能找到tail_path(找不到food_path)\n",
848
+ " elif tail_flag == 1:\n",
849
+ " print(\"找到蛇尾\")\n",
850
+ " # 真实的蛇沿最长路径向蛇尾走一步\n",
851
+ " longest_path = longest_tail_path(snake_Body)\n",
852
+ "\n",
853
+ " # 更新蛇身\n",
854
+ " snake_Body.insert(0, longest_path[0])\n",
855
+ " snake_Body.pop()\n",
856
+ "\n",
857
+ " # 找不到food_path也找不到tail_path时。随机走一步\n",
858
+ " elif food_flag != 1 and tail_flag != 1:\n",
859
+ " print(\"找不到食物也找不到蛇尾\")\n",
860
+ "\n",
861
+ " # 无路可走\n",
862
+ " if len(wander(snake_Body)) == 0:\n",
863
+ " print(\"走投无路\")\n",
864
+ " GameOver()\n",
865
+ " break\n",
866
+ "\n",
867
+ " # 有路可走\n",
868
+ " else:\n",
869
+ " snake_Body.insert(0, wander(snake_Body))\n",
870
+ " snake_Body.pop()\n",
871
+ "\n",
872
+ " # 画布为黑色\n",
873
+ " DISPLAY.fill(BLACK)\n",
874
+ "\n",
875
+ " # 画出贪吃蛇\n",
876
+ " drawSnake(snake_Body)\n",
877
+ "\n",
878
+ " # 画出食物的位置\n",
879
+ " drawFood(food_Position)\n",
880
+ "\n",
881
+ " # 打印出玩家的分数\n",
882
+ " drawScore(len(snake_Body) - 3)\n",
883
+ "\n",
884
+ " # 刷新Pygame的显示层,贪吃蛇与食物的每一次移动,都会进行刷新显示层的操作来显示。\n",
885
+ " pygame.display.flip()\n",
886
+ "\n",
887
+ " # 控制游戏速度\n",
888
+ " FPSCLOCK.tick(20)\n",
889
+ "\n",
890
+ " # 蛇碰到边缘,游戏结束\n",
891
+ " if snake_Body[0][0] < 0 or snake_Body[0][0] > 300:\n",
892
+ " GameOver()\n",
893
+ " if snake_Body[0][1] < 0 or snake_Body[0][1] > 300:\n",
894
+ " GameOver()\n",
895
+ "\n",
896
+ " # 贪吃蛇头触碰到自己,游戏结束\n",
897
+ " for i in snake_Body[1:]:\n",
898
+ " if snake_Body[0][0] == i[0] and snake_Body[0][1] == i[1]:\n",
899
+ " GameOver()"
900
+ ]
901
+ }
902
+ ],
903
+ "metadata": {
904
+ "kernelspec": {
905
+ "display_name": "Python 3 (ipykernel)",
906
+ "language": "python",
907
+ "name": "python3"
908
+ },
909
+ "language_info": {
910
+ "codemirror_mode": {
911
+ "name": "ipython",
912
+ "version": 3
913
+ },
914
+ "file_extension": ".py",
915
+ "mimetype": "text/x-python",
916
+ "name": "python",
917
+ "nbconvert_exporter": "python",
918
+ "pygments_lexer": "ipython3",
919
+ "version": "3.9.12"
920
+ },
921
+ "toc": {
922
+ "base_numbering": 1,
923
+ "nav_menu": {},
924
+ "number_sections": true,
925
+ "sideBar": true,
926
+ "skip_h1_title": false,
927
+ "title_cell": "Table of Contents",
928
+ "title_sidebar": "Contents",
929
+ "toc_cell": false,
930
+ "toc_position": {
931
+ "height": "calc(100% - 180px)",
932
+ "left": "10px",
933
+ "top": "150px",
934
+ "width": "248.1875px"
935
+ },
936
+ "toc_section_display": true,
937
+ "toc_window_display": true
938
+ }
939
+ },
940
+ "nbformat": 4,
941
+ "nbformat_minor": 5
942
+ }
data/QObject.qss ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ QLabel#notice {
2
+ font-size: 20px;
3
+ color: gray;
4
+ border: 1px solid gray;
5
+ border-radius: 8px;
6
+ }
7
+ QLabel#notice[notice_level="normal"] {
8
+ color: green;
9
+ border-color: green;
10
+ }
11
+ QLabel#notice[notice_level="warning"] {
12
+ color: yellow;
13
+ border-color: yellow;
14
+ }
15
+ QLabel#notice[notice_level="error"] {
16
+ color: red;
17
+ border-color: red;
18
+ }
data/style.qss ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ QMainWindow{
2
+ background-color: yellow
3
+ }
4
+
5
+ QToolTip{
6
+ border: 1px solid rgb(45, 45, 45);
7
+ background: white;
8
+ color: red;
9
+ }
10
+
11
+
12
+
13
+ QPushButton#btnID{
14
+ border-radius: 30px;
15
+ background-image: url('./images/left.png');
16
+ }
17
+
18
+ #btnID:hover{
19
+ border-radius: 30px;
20
+ background-image: url('./images/leftHover.png');
21
+ }
22
+
23
+ QPushButton#btnID:Pressed{
24
+ border-radius: 30px;
25
+ background-image: url('./images/leftPressed.png');
26
+ }
27
+
28
+ QPushButton {
29
+ background-color: red
30
+ }
31
+
32
+ QPushButton[name="btnProperty"] {
33
+ background-color: blue
34
+ }
35
+
36
+ QGroupBox {
37
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #E0E0E0, stop: 1 #FFFFFF);
38
+ border: 2px solid gray;
39
+ border-radius: 5px;
40
+ margin-top: 1ex; /* leave space at the top for the title */
41
+ }
42
+
43
+ QGroupBox::title {
44
+ subcontrol-origin: margin;
45
+ subcontrol-position: top center; /* position at the top center */
46
+ padding: 0 3px;
47
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #FF0ECE, stop: 1 #FFFFFF);
48
+ }
49
+ QGroupBox>QCheckBox{
50
+ background-color: yellow
51
+ }
52
+ QGroupBox>QPushButton{
53
+ border: 2px solid #8f8f91;
54
+ border-radius: 6px;
55
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
56
+ stop: 0 #f6f7fa, stop: 1 #dadbde);
57
+ min-width: 80px;
58
+ }
59
+
60
+ QGroupBox>QPushButton:pressed {
61
+ background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
62
+ stop: 0 #dadbde, stop: 1 #f6f7fa);
63
+ }
64
+
65
+ QGroupBox>QPushButton:flat {
66
+ border: none; /* no border for a flat push button */
67
+ }
68
+
69
+ QGroupBox>QPushButton:default {
70
+ border-color: navy; /* make the default button prominent */
71
+ }
72
+
73
+
74
+ QComboBox {
75
+ border: 1px solid gray;
76
+ border-radius: 3px;
77
+ padding: 1px 18px 1px 3px;
78
+ min-width: 6em;
79
+ }
80
+
81
+ QComboBox::drop-down {
82
+ subcontrol-origin: padding;
83
+ subcontrol-position: top right;
84
+ width: 25px;
85
+ background: red;
86
+ border-left-width: 1px;
87
+ border-left-color: darkgray;
88
+ border-left-style: solid; /* just a single line */
89
+ border-top-right-radius: 3px; /* same radius as the QComboBox */
90
+ border-bottom-right-radius: 3px;
91
+ }
92
+
93
+ QComboBox::down-arrow {
94
+ image: url(./images/dropdown.png);
95
+ }
96
+
data/xxx.png ADDED
data//344/272/213/344/273/266/346/234/272/345/210/266/345/233/276/350/247/243.png ADDED
images/.DS_Store ADDED
Binary file (10.2 kB). View file
 
images/Bold.png ADDED
images/IOS.png ADDED
images/Italic.png ADDED
images/android.png ADDED
images/bao0.png ADDED
images/bao1.png ADDED
images/bao10.png ADDED
images/bao11.png ADDED
images/bao12.png ADDED
images/bao13.png ADDED
images/bao14.png ADDED
images/bao2.png ADDED
images/bao3.png ADDED
images/bao4.png ADDED
images/bao5.png ADDED
images/bao6.png ADDED
images/bao7.png ADDED
images/bao8.png ADDED
images/bao9.png ADDED
images/boy.png ADDED
images/cartoon1.ico ADDED
images/cartoon2.ico ADDED
images/cartoon3.ico ADDED
images/cartoon4.ico ADDED
images/cartoon5.ico ADDED
images/cartoon6.ico ADDED
images/cartoon7.ico ADDED
images/cartoon8.ico ADDED
images/close.ico ADDED
images/close.png ADDED
images/copy.png ADDED
images/cut.png ADDED
images/doc.ico ADDED
images/down3.png ADDED
images/dropdown.png ADDED