20244113咸润杰 实验四Python综合实践

课程:《Python程序设计》
班级: 2441
姓名: 咸润杰
学号:20244113
实验教师:王志强
实验日期:2025年5月13日
必修/选修: 公选课

1. 实验内容以及分析设计

(1)实验内容
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。
本次python作业我选择编写一个小游戏五子棋以及海龟绘图这两个程序。

(2)实验分析
①选题分析。本次实验并没有明确规定实验内容,可以选择编写小游戏,我个人喜欢下五子棋,同时,五子棋的逻辑较为简单,适合于本人的水平,而且,python支持该游戏的编程,因此,我选择编写一个五子棋的游戏。其次,我本人非常感谢王老师,想做一个程序表达谢意,因此我自学海龟绘图,反复研究练习,终于做出了一个程序。
②游戏分析。五子棋的逻辑为:绘制棋盘(棋盘由纵横各15条线交叉组成,线条清晰,布局合理)、交替落子(通过鼠标点击棋盘的交叉点,在对应位置落下黑白两色棋子,且落子位置需准确,不能超出棋盘范围,还要避免在已有棋子的位置重复落子)、五子连珠(当某一方的五个同色棋子在横、竖、斜方向上连成一线时,判定该方获胜)因此,我需要根据这样的逻辑进行程序编写。
③绘图分析。绘制一朵花,附有文字“追光而遇、沐光而行!”

(3)实验设计
①五子棋的设计。整体而言,我打算按照五子棋的逻辑进行,将其分为若干个环节,对每一个环节分别编写,最后整合到一起,从而完成游戏编写。整个过程分为了绘制棋盘、交替落子、五子连珠等环节。而对于每一个环节,我都要进行额外的设计,在绘制棋盘中,我需要调用相应的绘图函数来绘制棋盘线条和棋子图形,并设置合适的颜色以及大小等。在交替落子环节,我需要利用算法将鼠标点击的屏幕坐标转换为棋盘上的交叉点坐标,还要对计算得到的行和列进行边界检查,确保其在棋盘的有效范围内,同时避免在已有棋子的位置重复落子。在五子连珠环节,我需要利用算法遍历行、列、主对角线、副对角线等,判断是否为五子连珠。
②海龟绘图设计。绘制一朵花,附有文字“追光而遇、沐光而行!”尽量使得花居于中间位置,体现位置上的美感。充分利用偏移角度、前往、抬笔落笔等,确保美观性。设计合适的速度,使得绘画过程得以完全展现。

2. 实验过程以及结果(附代码以及视频)

(1)五子棋起源于中国,是一种两人对弈的策略型棋类游戏。五子棋具有容易上手,老少皆宜,趣味横生的特点,它不仅能增强思维能力,而且富含哲理,有助于修身养性。因此,我计划编写一个五子棋的程序。
①下载插件
由于该程序需要使用pygame以及numpy插件,所以首先要在python中安装这两个插件。如图所示,在python软件包中搜索,之后便可以安装。其中,pygame是一个用于编写游戏的python模块集合,它允许开发者使用python编写具有完整功能的游戏和多媒体软件。numpy则是python中基于数组对象的科学计算库,支持大量的维度数组与矩阵运算。

②绘制棋盘
运用import导入pygame、sys模块,sys模块用于提供接口,用于访问python解释器自身使用和维护的变量,同时模块中还提供部分函数,可以与解释器进行交互。之后我初始化pygame,获取对显示系统的访问,并创建一个窗口,我将窗口设置为670×670,又设置了画布的颜色为(238,154,73)。但是,这样的话,程序一经运行便会关闭,因此,有必要对其进行训练刷新画布,此时,我运用了while循环函数以及if函数,当按键或者关闭窗口时,程序会退出。之后会清屏以及刷新显示。

此时,运行程序,图像如下。

③增加格线
初步的画布已经完成,接下来需要绘制网格线,这需要用到函数对其颜色、长度等进行定义,line_color表示线条颜色,[0,0]表示起始位置,[670,670]表示终止位置,2表示线条粗细。五子棋一般棋盘大小为15x15,而初始化的窗口大小为670x670,出于美观的需要,每个棋子之间需要存在一定的间隔。于是我定义每个棋子的半径大小为20,每颗棋子间隔为2,相邻的两条横线或者竖线之间间隔为44,最边缘的两条线和棋盘边缘距离为27。我利用if函数开始画15条棋盘的线,从27开始,循环间隔为44。为此,我用了for循环,变量i从27开始,到670之前结束,每次循环增加44,同时,如果i等于27或者等于670-27(即绘制最左边和最右边的边框),线条的粗细为4像素,否则,线条的粗细为2像素。此外,我还在棋盘中心的位置作一个标记,即一个黑色的实心小圆。pygame.draw.circle(screen, line_color,[27+447,27+447], 8,0),[27+447,27+447]为圆心位置,即棋盘正中心,8为圆的半径大小,0表示实心圆,如果设置为1则输出空心圆。

运行之后,结果如下。

④模拟落子
现在需要根据鼠标信息落子,即点击鼠标左键落子,为了美化效果,应当实时获取鼠标的位置信息,显示出当前落子的位置。即代码x,y = pygame.mouse.get_pos(),获取鼠标坐标信息之后,需要找到这个坐标具体落入的位置,因此需要进行判断:首先循环遍历棋盘的格子中心点,之后判断输入点是否在当前格子范围内,其中,L1=i-22和L2=i+22定义当前格子在水平方向上的边界,而R1=j-22和R2=j+22则定义当前格子在垂直方向上的边界,if x>=L1and x<= L2 and y>=R1 and y<=R2表示如果输入的点 (x, y) 落在当前格子的范围内,则认为该点属于这个格子,最后返回结果,如果找到符合条件的格子,则返回格子的中心点坐标 (i, j),表示为“可以落子的位置”,如果没有找到符合条件的格子,则返回原始输入坐标 (x, y)。

返回可以落子的位置后,需要将其显示,我选择使用一个正方形框出来,正方形框的代码为pygame.draw.rect(screen,[0 ,229 ,238 ],[x-22,y-22,44,44],2,1),同画圆的函数基本一样。[0 ,229 ,238 ]是颜色的RGB,[x-22,y-22,44,44]表示从位置(x-22,y-22)开始向右向下分别延长44的长度,得到一个正方形,2表示线条的粗细程度,1表示空心的正方形。

得到具体的位置(x,y)之后,获取鼠标左键信息,如果按下左键就落子,之后使用画圆的函数在这个位置画圆,由于落子之后的位置便不能再次落子,所以需要储存已经落子的位置。定义over_pos表示已经落子的位置,val[0][0]以及val[0][1]分别表示坐标中的x,y分量,落子时会检查是否相等,如果有匹配的坐标,则返回false,表示该位置已经被占用,如果没有相匹配的坐标,则可以进行落子。因为每次循环时间间隔很短,容易导致明明只按一次左键,却被多次获取,所以使用tim进行延时。

同时,五子棋为黑白交替落子,因此我使用len(over_pos)%2函数,判断当前棋盘状态中已经落子的数量是否为偶数,若为偶数,则黑棋落子,若为奇数,白棋落子。最后,显示所有落下的棋子,刷新显示。

运行之后,结果如下。

⑤五子连珠
落子之后,需要判断五子连珠。首先,初始化棋盘矩阵,mp=np.zeros([15,15],dtype=int),创建一个15×15的二维数组mp,表示棋盘,棋盘的每个位置初始值为0,表示空格,黑子用1表示,白子用2表示。其次,将over_pos的坐标映射到棋盘数组,通过公式x=int((val[0][0]-27)/44)和y=int((val[0][1]-27)/44) 将实际坐标映射到棋盘数组的索引,根据棋子颜色赋值到mp中。之后通过四种方式判断有无五子连珠,即水平、垂直、主对角线、副对角线,原理是类似的,遍历棋盘的每一行(列)(对角线),检查是否存在连续的五个黑子或白子,如果找到五子连珠,则返回胜利方的编号(1表示黑子胜利,2表示白子胜利)以及连珠的坐标列表。如果没有五子连珠,返回[0, []]表示没有玩家获胜。

⑥修饰注释
我添加了个人水印,由于以上截图中并没有添加注释,所以我又对代码添加注释,进行了详细的解释。

以下为代码和运行视频。
代码添加了十分详细的注释,视频则是上传至我的B站账号。

#20244113咸润杰
#简易五子棋游戏
import pygame#调用pygame库,用于编写游戏
import sys#导入sys模块,退出程序
from pygame.locals import QUIT,KEYDOWN#调用常量,用于退出以及按键
import numpy as np#导入numpy库,并将其别名设置为np
pygame.init()#初始化pygame
screen = pygame.display.set_mode((670,670))#创建一个窗口,大小为670x670
screen_color=[238,154,73]#设置画布颜色,[238,154,73]对应为棕黄色
line_color = [0,0,0]#设置线条颜色,[0,0,0]对应黑色
def check_win(over_pos):#判断五子连珠
    mp=np.zeros([15,15],dtype=int)#创建一个15×15的二维数组mp,表示棋盘
    #将实际坐标映射到棋盘数组
    for val in over_pos:
        x=int((val[0][0]-27)/44)
        y=int((val[0][1]-27)/44)
        #根据棋子颜色赋值到mp
        if val[1]==white_color:
            mp[x][y]=2#表示白子
        else:
            mp[x][y]=1#表示黑子
    #遍历棋盘的每一行,检查是否存在连续的五个黑子或白子
    for i in range(15):
        pos1=[]
        pos2=[]
        for j in range(15):
            if mp[i][j]==1:
                pos1.append([i,j])
            else:
                pos1=[]
            if mp[i][j]==2:
                pos2.append([i,j])
            else:
                pos2=[]
            if len(pos1)>=5:#黑子五子连珠
                return [1,pos1]
            if len(pos2)>=5:#白子五子连珠
                return [2,pos2]
    #遍历棋盘的每一列,检查是否存在连续的五个黑子或白子
    for j in range(15):
        pos1=[]
        pos2=[]
        for i in range(15):
            if mp[i][j]==1:
                pos1.append([i,j])
            else:
                pos1=[]
            if mp[i][j]==2:
                pos2.append([i,j])
            else:
                pos2=[]
            if len(pos1)>=5:#黑子五子连珠
                return [1,pos1]
            if len(pos2)>=5:#白子五子连珠
                return [2,pos2]
    #遍历棋盘的每个起点,检查主对角线方向(从左上到右下)是否有五子连珠
    for i in range(15):
        for j in range(15):
            pos1=[]
            pos2=[]
            for k in range(15):
                if i+k>=15 or j+k>=15:
                    break
                if mp[i+k][j+k]==1:
                    pos1.append([i+k,j+k])
                else:
                    pos1=[]
                if mp[i+k][j+k]==2:
                    pos2.append([i+k,j+k])
                else:
                    pos2=[]
                if len(pos1)>=5:#黑子五子连珠
                    return [1,pos1]
                if len(pos2)>=5:#白子五子连珠
                    return [2,pos2]
    #遍历棋盘的每个起点,检查副对角线方向(从右上到左下)是否有五子连珠
    for i in range(15):
        for j in range(15):
            pos1=[]
            pos2=[]
            for k in range(15):
                if i+k>=15 or j-k<0:
                    break
                if mp[i+k][j-k]==1:
                    pos1.append([i+k,j-k])
                else:
                    pos1=[]
                if mp[i+k][j-k]==2:
                    pos2.append([i+k,j-k])
                else:
                    pos2=[]
                if len(pos1)>=5:#黑子五子连珠
                    return [1,pos1]
                if len(pos2)>=5:#白子五子连珠
                    return [2,pos2]
    return [0,[]]#没有五子连珠,没有玩家获胜
#找到可以落子的位置
def find_pos(x,y):
    for i in range(27,670,44):
        for j in range(27,670,44):
            L1=i-22
            L2=i+22
            R1=j-22
            R2=j+22
            if x>=L1 and x<=L2 and y>=R1 and y<=R2:
                return i,j
    return x,y#如果鼠标位置在某个区域,返回该区域的中心坐标
#检查当前位置是否已经落子
def check_over_pos(x,y,over_pos):
    for val in over_pos:
        if val[0][0]==x and val[0][1]==y:#遍历已落子的坐标
            return False# 如果该位置已落子,返回False
    return True#如果该位置未落子,返回True
flag=False#标志,用于判断是否可以落子
tim=0#延时计数器
over_pos=[]#表示已经落子的位置
white_color=[255,255,255]#白棋颜色
black_color=[0,0,0]#黑棋颜色
while True:#不断训练刷新画布
    # 获取事件,如果鼠标点击右上角关闭按钮,关闭
    for event in pygame.event.get():
        if event.type in (QUIT,KEYDOWN):
            sys.exit()
    screen.fill(screen_color)#清屏
    #绘制格线
    for i in range(27,670,44):
        #先画竖线
        if i==27 or i==670-27:#边缘线稍微粗一些
            pygame.draw.line(screen,line_color,[i,27],[i,670-27],4)
        else:
            pygame.draw.line(screen,line_color,[i,27],[i,670-27],2)
        #再画横线
        if i==27 or i==670-27:#边缘线稍微粗一些
            pygame.draw.line(screen,line_color,[27,i],[670-27,i],4)
        else:
            pygame.draw.line(screen,line_color,[27,i],[670-27,i],2)
    #在棋盘中心画个小圆表示正中心位置
    pygame.draw.circle(screen, line_color,[27+44*7,27+44*7], 8,0)
    for val in over_pos:#显示所有落下的棋子
        pygame.draw.circle(screen, val[1],val[0], 20,0)
    #判断是否存在五子连心
    res=check_win(over_pos)
    if res[0]!=0:
        for pos in res[1]:
            pygame.draw.rect(screen,[238,48,167],[pos[0]*44+27-22,pos[1]*44+27-22,44,44],2,1)
        pygame.display.update()#刷新显示
        continue#游戏结束,停止下面的操作
    #获取鼠标坐标信息
    x,y = pygame.mouse.get_pos()
    x,y=find_pos(x,y)
    if check_over_pos(x,y,over_pos):#判断是否可以落子,之后显示
        pygame.draw.rect(screen,[0 ,229 ,238 ],[x-22,y-22,44,44],2,1)
    keys_pressed = pygame.mouse.get_pressed()#获取鼠标按键信息
    #鼠标左键表示落子,tim用于延时,因为每次循环时间间隔很短,容易导致明明按一次左键,却被多次获取
    if keys_pressed[0] and tim==0:
        flag=True
        if check_over_pos(x,y,over_pos):#判断是否可以落子,之后落子
            if len(over_pos)%2==0:#黑子
                over_pos.append([[x,y],black_color])
            else:
                over_pos.append([[x,y],white_color])
    #鼠标左键延时作用
    if flag:
        tim+=1
    if tim%50==0:#延时200ms
        flag=False
        tim=0
    pygame.display.update()#刷新显示

点击查看五子棋的视频

(2)虽然编写一个五子棋的游戏已经满足了期末要求,但是怀着对王老师的感恩,我打算再编写一个程序,以此表达我的感激。我计划使用海龟绘图来绘画图案,表达对老师的感激。由于我们课程之中并没有学到海龟绘图,因此我耗费几天的时间在网络中寻找资源进行自学。水平有限,还请老师多多包涵。
①自学海龟绘图
turtle即为海龟之意,海龟绘图可以这样理解:海龟处于横轴为x、纵轴为y的坐标系原点(0,0)位置,它根据一组函数指令的控制,在平面坐标系中移动,在它爬行的路径上绘制了图形。使用海龟绘图之时,首先创建画布,在画布上,默认有一个坐标原点为画布中心的坐标轴,坐标原点上有一只面朝x轴正方向的海龟,海龟绘图中,就是使用位置以及方向描述海龟(画笔)的状态。我们可以设置画笔的颜色,粗细以及速度等。同时,我们可以通过画笔运动命令、画笔控制命令、全局控制命令来进行绘图。
②设置初始位置
我首先导入海龟绘图的模块,设置了绘图速度、画笔粗细、背景颜色等。为之后绘画提供便利。

③绘制花蕊
现在绘制花蕊,我设置花蕊填充颜色为红色,然后多次调用circle和fd,以及方向调整命令(left 和 right),绘制出一个复杂的花蕊形状。在turtle.circle(a,b)中,a半径,b表示弧度;在turtle.left(d)中,d表示角度;在turtle.fd(e)中,e表示前进的像素。

程序运行之后,结果如下。

④绘制叶子
绘制花蕊之后,我想绘制叶子,但是此时海龟的位置并不在我想要他在的位置,于是,我使用turtle.goto(0,-7),使之到达相应的位置,但是如果直接过去,会产生痕迹,于是,我使用了turtle.penup()以及turtle.pendown(),使之没有痕迹,此外,我使用turtle.setheading(-83),调整海龟的头部方向。

程序运行之后,结果如下。

⑤添加语句
同样的道理,我使用turtle.goto(-200,-230),使之到达相应的位置,turtle.write可以用于输出文字,之后的font可以指定字体、字号以及字体风格等。之后,我使用了turtle.hideturtle()来隐藏箭头显示,turtle.done()则用于在绘图完成后保持窗口打开,直到用户手动关闭窗口。

程序运行之后,结果如下。

⑥修饰注释
我添加了个人水印,虽然以上截图中有一定的注释,但是不够详细,所以我又对代码添加注释,进行了详细的解释。

以下为代码和运行视频。
同五子棋一样,代码添加了详细的注释,视频则是上传至我的B站账号

#20244113咸润杰
#追光而遇,沐光而行
import turtle#导入海龟绘图
turtle.speed(10)#设置绘图速度
turtle.bgcolor('skyblue')
#设置初始位置
turtle.penup()#抬起画笔,移动时不画线
turtle.pensize(3)#设置画笔粗细
turtle.left(90)#向左转90度
turtle.fd(200)#向前移动200像素
turtle.pendown()#落笔
turtle.right(90)#右转90度
#花蕊
#多次调用画弧、前进、调整方向等函数
turtle.fillcolor("red")#设置填充颜色
turtle.begin_fill()#开始填充
turtle.circle(10, 180)
turtle.circle(25, 110)
turtle.left(50)
turtle.circle(60, 45)
turtle.circle(20, 170)
turtle.right(24)
turtle.fd(30)
turtle.left(10)
turtle.circle(30, 110)
turtle.fd(20)
turtle.left(40)
turtle.circle(90, 70)
turtle.circle(30, 150)
turtle.right(30)
turtle.fd(15)
turtle.circle(80, 90)
turtle.left(15)
turtle.fd(45)
turtle.right(165)
turtle.fd(20)
turtle.left(155)
turtle.circle(150, 80)
turtle.left(50)
turtle.circle(150, 90)
turtle.end_fill()#结束填充
turtle.penup()#抬笔
turtle.goto(0,-7)#前往(0,-7)
turtle.setheading(-83)#调整方向
turtle.pendown()#落笔
#叶子1
#多次调用画弧、前进、调整方向等函数
turtle.fd(30)
turtle.left(90)
turtle.fd(25)
turtle.left(45)
turtle.fillcolor("green")#设置填充颜色
turtle.begin_fill()#开始填充
turtle.circle(-80, 90)
turtle.right(90)
turtle.circle(-80, 90)
turtle.end_fill()#结束填充
turtle.right(135)
turtle.fd(60)
turtle.left(180)
turtle.fd(85)
turtle.left(90)
turtle.fd(80)
# 叶子2
#多次调用画弧、前进、调整方向等函数
turtle.right(90)
turtle.right(45)
turtle.fillcolor("green")#设置填充颜色
turtle.begin_fill()#开始填充
turtle.circle(80, 90)
turtle.left(90)
turtle.circle(80, 90)
turtle.end_fill()#结束填充
turtle.left(135)
turtle.fd(60)
turtle.left(180)
turtle.fd(60)
turtle.right(90)
turtle.circle(200, 60)
#追光而遇,沐光而行!
turtle.pencolor('darkred')
turtle.goto(-200,-230)#前往坐标(-200,-230)
turtle.write('追光而遇,沐光而行!',font=('华文行楷',20,'bold italic'))#输出文字
turtle.hideturtle()#隐藏箭头
turtle.done()#保持窗口打开

点击查看海龟绘图视频

3. 实验困难以及解决方式

  • 问题1:部分代码书写较为困难。
  • 问题1解决方案:求救于AI,尽管如此,我并没有照搬照抄,而是深入的理解与思考,正因如此,我对代码做了详细的注释,表达我对代码的理解。
  • 问题2:书写实验报告之时,不会规范的粘贴代码。
  • 问题2解决方案:询问同学之后,能够规范的粘贴代码。
  • 问题三:学习海龟绘图,比较困难。
  • 问题3解决方案:自主学习,不断地寻找资料,终于掌握了一点皮毛。(之所以没有询问老师,是因为想整一个惊喜)
  • 问题四:使用海龟绘图进行绘画时,十分困难。
  • 问题4解决方案:依托于强大的耐心,我一点一点的调试,终于完成了花。

4. 课程总结

(1)本次实验感悟
我最大的感受是,利用Python写游戏好难啊!尽管如此,我还是尽力克服了困难,尽自己最大的努力,完成了五子棋的程序编写。海龟绘图也十分的困难,主要体现在绘图过程中画笔的角度、前进距离等,想要画出一朵花是十分困难的,真的好难!
其次的感受是,坚持是有力量的。尽管这两个程序很难,但是我一直坚持下去,终于完成了这两个程序的编写,完成之时的成就感是无与伦比的。
再次是我深化了对于五子棋游戏以及海龟绘图的理解。原来五子棋的代码是这样写的!原来一朵花是这样这样画出来的!
最后,终于轮到了个人能力提升的环节,虽然有一些困难,但我还是独立完成了代码编写,个人的能力确实有了一定的提高。

(2)课程感想体会
坦白来讲,我当初对于是否选修python有很强的犹豫,因为我觉得作为一种编程语言,python会很难,事实证明,的确很难。
万幸的是,有王老师这样的好老师。上课之时,王老师能够领着我们写一遍代码,便于我们理解。十二周课以来,每一节课都坚持下来了(因为确实很难),虽然有些部分难以理解,但跟着老师的思路,边听边写,也初步掌握了一些python的基本技能,受益匪浅。虽然很不好意思没有把老师讲的内容学到精通,但也学到了很多新的知识,比如说网络爬虫的运用、socket技术、小游戏的设计等,这加深我们对python的认识,开阔了自己的视野,还了解到更多获取资料和解决问题的方法。
总而言之,在王老师的指导下,我进行了很多的编程实践,虽然没有完成很庞大的项目,但是在课程学习的过程中,我掌握了一些知识,收获很多,也体会到了python写程序的成就感。希望自己能够在以后的时间中,继续学习python,亲手写出更多有意思的程序,在python学习之路上继续前行。
再一个就是,老师组织的英语打卡活动不仅能够帮助我们的python学习,而且也督促大家学习英语,为四六级考试打下基础,这一点十分感谢老师。
回忆整个课程,最重要的是收获了满满的感动,很荣幸能够跟着王老师一起学习python课程,再次感谢老师的辛勤付出与谆谆教诲!
祝老师工作顺利,万事顺遂,永远帅气!

(3)意见和建议

  • 放慢教学速度,本学期之中有些部分讲的比较快,部分同学(比如说我)没有跟上,需要课下训练。
  • 给下一级增加作业量,个人认为,作业量比较少,对课程的训练较少,不利于同学们能力的提升。
  • 将平时成绩公示,让每个同学能知道自己的平时成绩,这样不仅可以增加同学们的学习积极性,而且可以给同学们一定的警醒。
  • 增加课程之中的具体案例,将python与实际生活相结合,增加课堂中的趣味性。
  • 增强与课本的联系,能够兼顾课本。
posted @ 2025-05-18 19:30  Sakura-樱良  阅读(112)  评论(0)    收藏  举报