一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习。
声明:本人学习是在扇贝编程通过网络学习的,相关的知识、案例来源于扇贝编程。如果使用请说明来源。
第三十关是基础部分的结业部分,没有讲新的内容,是分步骤讲了一个比较复杂的变成,迷宫游戏。所以没有做导图。
这个拯救哪吒的迷宫游戏是我目前接触的最为复杂的程序了,因此学完之后用了两天的时间消化,才基本弄懂了每个部分。但是如果让我自己写,还是写不出来。一是有的命令什么的不太熟练,二是有的部分想的不够全面。
练习:拯救哪吒
main.py 主程序
from maze import Maze 导入迷宫模块
from nezha import Nezha 导入哪吒模块
from controller import Controller 导入控制程序模块
maze_list = [
[1,1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1], 基础数据迷宫墙的数字化,抽象化
[1,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1,0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[1,0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
[1,1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1],
[1,0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1],
[1,0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1],
[1,0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1,1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1,0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1],
[1,0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
[1,0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1],
[1,1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]
]
Maze(maze_list) 调用maze函数
nezha = Nezha(maze_list, 0, 5, 12, 7) 调用哪吒模块,初始位置和出口位置
Controller(nezha.go_up, nezha.go_down,nezha.go_left, nezha.go_right) 调用控制程序
Maze.py 迷宫模块
from turtle import Turtle 导入画图
class Maze(Turtle): 子承父业,Maze继承Turtle
size = 20 正方形砖块边长为20
def__init__(self, maze_list):
# 需要先调用父类的初始化方法才能在初始化方法中调用父类的方法
Turtle.__init__(self) 初始化
self.maze_list = maze_list
#为了加快绘图速度隐藏海龟,速度设为最快
self.hideturtle()
self.speed(0)
self.draw_walls() 调用画墙函数
defdraw_wall(self):
self.pendown() 先落下再抬笔,这样上下两行之间就不会有一条斜线了。
self.begin_fill()
self.fillcolor('#7392f6')
for i in range(4):
self.forward(self.size)
self.right(90)
self.end_fill()
self.penup() 画出一块砖
defdraw_walls(self):
self.penup()
#从 (-130, 130) 开始
self.goto(-130, 130)
for row in range(13): 13行
for col in range(13): 13列
if self.maze_list[row][col] == 1: 横竖交叉点上是1时,就画砖
self.draw_wall()
self.goto(self.size * (col + 1) - 130,130 - self.size * row) 画下一块砖,下一列
#下移一行
self.goto(-130, 130 - self.size * (row +1)) 画下一行第一块砖的位置
图(1)先抬笔后落笔,由于是抬笔所以没有画出线,只有动作,之所以出现墙,是因为有填充。最后落笔之后再到下一行,就拉出一条斜线在上下行之间,然后抬笔。
图(2)先落笔后抬笔,不仅能画出每块砖的轮廓,上下场之间也不会有斜线,因为画完之后是抬笔再到下一行的。
#右移一列
self.goto(self.size * (col + 1) - 130, 130 - self.size * row) 画下一块砖,下一列
#下移一行
self.goto(-130, 130 - self.size * (row + 1)) 画下一行第一块砖的位置
这两行都去掉的话,整个模块只画出一块砖,没有到下一列,也没有到下一行
如果上一行去掉,只画出第一列,即每行的第一块砖,系统不会跳到下一列
如果只是后一行去掉的话,第一列的会画到最后一列的后面,并且向上错一行,为什么这样呢?最初我的想法应该是第一列不出现的。
经过长期的思考,愚钝的我才弄明白原因,当row=0,cul遍历0-12之后,由于第二个self.goto去掉了,画笔没有到第二行第一个砖块的位置,还在第一行最后一列的后面,此时进入下一个循环,即row=1,col=0是,调用了画砖程序,于是就继续了一块砖,然后画笔跳到第二行的第二列,即row=1,col=1,这样系统从第二行开始就从每一行的第二列开始画,而第一块砖都画到上一行的末尾了,因为没有告诉画笔要转场。
结果见下图(3)
nezha.py 哪吒模块
from turtle import Turtle
class Nezha(Turtle):
def__init__(self, maze_list, start_m, start_n,end_m, end_n):
#前文中没有考虑出口,是没有这两个参数的。
#父类初始化
Turtle.__init__(self)
self.maze_list = maze_list
self.m = start_m
self.n = start_n
self.end_n = end_n
self.end_m = end_m
self.hideturtle()
self.speed(0)
self.penup()
#移到对应的位置
self.goto(self.n * 20 - 120, 120 - self.m *20) #如果用130会怎样呢?
#变成海龟
self.shape('turtle')
self.color('#28bea0')
self.setheading(270)
self.showturtle()
#添加哪吒图片作为形状
screen = self.getscreen() 这句话不理解
screen.addshape('nezha.png') 实际软件环境只能用jpg格式的图片
defreach_exit(self, m, n): 判断到达出口
if m == self.end_m and n == self.end_n:
self.shape('nezha.png')
#再次变身回来成哪吒
defcanmove(self, m, n): 限制移动范围,为零的时候才能走
return self.maze_list[m][n] == 0
defmove(self, m, n): #移动程序
self.m = m
self.n = n
self.goto(self.n * 20 - 120, 120 - self.m * 20)
self.reach_exit(m, n)
defgo_up(self):
if self.canmove(self.m - 1, self.n):
self.setheading(90)
self.move(self.m - 1, self.n)
defgo_down(self):
if self.canmove(self.m + 1, self.n):
self.setheading(270)
self.move(self.m + 1, self.n)
defgo_left(self):
if self.canmove(self.m, self.n - 1):
self.setheading(180)
self.move(self.m, self.n - 1)
defgo_right(self):
if self.canmove(self.m, self.n + 1):
self.setheading(0)
self.move(self.m, self.n + 1)
图(4)self.goto(self.n * 20 - 130,130 - self.m * 20)
图(5)self.goto(self.n * 20 - 120, 120 - self.m *20)
controller.py 控制模块
from turtle import Turtle
class Controller(Turtle):
def__init__(self, go_up, go_down, go_left, go_right):
#父类初始化
Turtle.__init__(self)
#初始值设置
self.go_up = go_up
self.go_down = go_down
self.go_left = go_left
self.go_right = go_right
#绘制控制器
self.hideturtle()
self.speed(0)
self.draw_btn('上', -162, 130)
self.draw_btn('下', 132, 130)
self.draw_btn('左', -162, -100)
self.draw_btn('右', 132, -100)
# 绑定点击事件,通过点击获得相应的位置坐标,指挥海龟的移动。点击区域移动海龟
screen = self.getscreen()
screen.onclick(self.handlescreenclick) 对应下文的区域定义。
defdraw_btn(self, name, x, y):
self.penup()
self.goto(x, y)
self.begin_fill()
self.fillcolor('#000000')
for i in range(4):
self.forward(30)
self.right(90)
self.end_fill()
self.color('#ffffff')
self.goto(x + 7, y - 20)
self.write(name, font = ('SimHei', 12, 'bold'))
def handlescreenclick(self, x,y): #屏幕点击处理,就是点击不同的位置,控制海龟的行动
if x < 0 and y > 0:
self.go_up()
if x > 0 and y > 0:
self.go_down()
if x < 0 and y < 0:
self.go_left()
if x > 0 and y < 0:
self.go_right()
# 这段程序的好处是,点击一个区域就可以调动海龟的移动,这些区域分别是四个象限,而不是只能点击一个图标。
但是不好的地方是不够直观,比如想让海龟向下的时候,点击的是第一象限,而第一象限是在迷宫偏上的位置的。
self.draw_btn('上', -162, 130)
self.draw_btn('下', 132, 130)
self.draw_btn('左', -162, -100)
self.draw_btn('右', 132, -100)
图标画出来在迷宫的四个角,也是不够直观,比如下在右上角,迷宫的上半部分。图(6)
self.draw_btn('上', -15, 162)
self.draw_btn('下', -15, -132)
self.draw_btn('左', -162, 15)
self.draw_btn('右', 132, 15)
图标画出来之后,分别在上下左右,这样和我们的习惯更为一致,图(7)
但是点击上下左右图标,小乌龟不变,原来不是上下左右起作用,是定义的四个象限在起作用,于是我修改了上下左右的范围:
def handlescreenclick(self, x, y):
if -130 < x < 130 and y > 130:
self.go_up()
if -130 < x < 130 and y < -130:
self.go_down()
if x < -130 and -130 < y < 130:
self.go_left()
if x > 130 and -130 < y< 130:
self.go_right()
这样点击迷宫的上下左右区域,就能够指挥小乌龟上下左右的移动了,比较直观,而不是四个象限,总感觉和习惯不一致。
至此,我基本对这个题目中程序理解的比较全面的。但是感觉比较薄弱的地方是,各个模块之间的调用,尤其是参数的设置以及各个模块之间数据的传递等如果不看答案,感觉还是想的不够周全。还有就是初始化的部分
在主程序中导入各个模块,前面是模块名称,后面是各个模块定义的类的名称
Maze(maze_list)
nezha = Nezha(maze_list, 0, 5, 12, 7)
Controller(nezha.go_up, nezha.go_down,nezha.go_left, nezha.go_right)
通过主程序调用各个模块,调用Maze迷宫模块要用到迷宫数字列表maze_list
调用nezha要用的模块数字列表和入口位置0,5,以及户口位置12,7
调用controller模块,要用到nezha模块中的上下左右程序。
每个模块先导入海龟绘画功能
然后建立一个类继承Turtle的画图功能
from turtle import Turtle
class Maze(Turtle):
size = 20
def__init__(self, maze_list): 要用到maze_list
Turtle.__init__(self) 还要对父类进行初始化,才能调用
# 需要先调用父类的初始化方法才能在初始化方法中调用父类的方法
from turtle import Turtle
class Nezha(Turtle):
def__init__(self, maze_list, start_m, start_n, end_m, end_n):
# 要用到maze_list,开始和结束的行列数字
Turtle.__init__(self) 还要对父类进行初始化,才能调用
from turtle import Turtle
class Controller(Turtle):
def__init__(self, go_up, go_down, go_left, go_right):
# 控制要用到后面设置的上下作用范围设置数据
Turtle.__init__(self) 还要对父类进行初始化,才能调用
初始化时设置的特征和初始化中的参数是相关的,在参数中出现,就要在后面进行初始值设置。除了self,其他参数都要进行初始值设置,或者说这些特征都要进行初始化设置。