20244108 实验四《Python程序设计》实验报告
20244108 2024-2025《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 2441
姓名: 方昱霖
学号:20244108
实验教师:王志强
实验日期:2025年5月13日
必修/选修: 公选课
1、实验要求
Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
注:在Windows/Linux系统上使用VIM、PDB、IDLE、Pycharm等工具编程实现。
2、实验内容
综合Python各版块知识和个人兴趣等因素,我在本次实验中选择制作俄罗斯方块小游戏。
俄罗斯方块是有不同形状(如长方形、T形等,在本次实验中均有运用)的方块从游戏区域的上方落下,玩家通过操作使方块在下落过程中旋转、移动,使其排列更为紧密。同时,玩家还可以通过方向键来使方块旋转、调整下落速度。
进行这个程序的编写需要综合运用各方面的Python知识,同时也是一个不小的挑战。
3、实验过程
(1)为了满足游戏进行的需要,在程序编写前须先在Pycharm上下载pygame。

(2)使用import函数导入模块,其中random模块用于随机选择下落的方块形状。为了使用pygame的功能,这里用了pygame.init()对pygame库进行初始化。

(3)设置游戏窗口:
首先分别定义游戏窗口的宽度和高度为400和600像素。
再使用screen = pygame.display.set_mode((screen_width, screen_height))创建指定大小的游戏窗口。
再使用pygame.display.set_caption("俄罗斯方块")设置游戏窗口的标题为“俄罗斯方块”。

(4)定义方块的颜色:
用RGB元组形式表示游戏中会用到的几种形式,我选择了蓝色和红色作为方块的颜色。

(5)定义方块大小和网格大小:
使用block_size = 20 来定义游戏中方块的大小,即每个方块是一个边长为 20 像素的正方形。
然后再使用grid_width = screen_width // block_size 和 grid_height = screen_height // block_size 两个函数来根据窗口宽度和高度以及方块大小计算出游戏区域在水平和垂直方向上的网格数量,用于后续方块位置的计算和布局。

(6)定义方块形状:
俄罗斯方块中的方块共有七种形状,为了定义这七种形状,这里我运用到了列表的知识。
shapes是一个列表,其中每个元素代表一种俄罗斯方块的形状,每个形状由一个或多个子列表表示,子列表中的数字代表方块在形状中的位置,数字相同表示属于同一个方块部分。
比如第一个,第一行三个“1”,第二行居中一个“1”,很容易看出这是一个“T”形。同理,根据数字的位置,我们可以很直观地看出剩下几个方块的形状:第二个和第三个都是上面两个下面两个但方向相反,不知道它们应该叫什么学名所以就不赘述了;第四个和第五个都像横着的“L”,同样呈轴对称形状;第六个是一个“一”,第七个则是一个2*2的正方形。
如此,就完成了七种方块形状的定义。

(7)生成新方块:
当一个方块落到底部之后,上端会出现一个新的方块开始下落。
这里定义了一个新的函数new_block来实现这个功能,随机从上面的shapes列表中选择一种方块形状,并返回该形状以及它在游戏区域中的初始x、y坐标,即最上方的中间位置。

(8)检查方块位置:
在游戏中,方块需在指定区域内下落,不能超出界面范围,也不能与其他方块的位置重叠。所以需要设置函数用以检查方块位置,通过遍历方块的每个部分,检查其对应棋盘位置是否已被占用或者超出棋盘边界,如果有则返回False ,表示位置不合法,程序将会报错;如果所有部分都合法则返回True ,程序可正常运行。
这里的自定义函数def check_position(board, shape, offset) 用于检查方块在指定位置是否合法。
board是游戏棋盘的状态,是一个二维列表,记录哪些网格位置已经被占用。
shape是要检查位置的方块形状。
offset是方块相对于棋盘左上角的偏移量,根据x、y坐标进行判断。

(9)方块放置:
这里就是自定义一个join_matrix的函数,作用是把一个方块矩阵拼到游戏棋盘矩阵上,也就是游戏中的放置方块。
其中mat1是目标棋盘矩阵,mat2是要拼上去的方块矩阵;mat2_off是方块相对于棋盘的偏移量,比如往左/右移几格、往上/下移几格。
函数会逐个检查方块里的每个元素,如果这个元素不是0,就按偏移量把它的值“叠”到棋盘对应的位置上,最后返回拼好的棋盘矩阵,方块放置完毕。

(10)清除满行的方块:
在俄罗斯方块中,如果一行被方块填满了,那该行就可以被消除。这里使用了def clear_rows(board)来实现这个功能,定义函数用于检查并清除游戏棋盘中的满行。
首先遍历棋盘的每一行,使用 all 函数判断该行是否所有位置都被占用(即满行),如果是则将该行索引添加到full_rows列表中,full_rows用于存储所有满行的索引。
然后遍历full_rows列表,调用remove_row函数依次删除这些满行,最后返回更新后的棋盘,这样下来满行的方块就被清除完毕了。

(11)游戏初始化:
定义一个叫main的主函数,程序大部分关键操作和流程都要写在这个函数里。
然后用board = [[0 for _ in range(grid_width)] for _ in range(grid_height)]函数来规划棋盘,棋盘上每个小格子一开始的值都是0,棋盘的宽和高呢就看grid_width 和grid_height这两个变量定的数,也就是我前面定义的400像素和600像素。
再调用new_block这个函数生成一个方块来,方块的生成位置是其初始位置,也就是第一行的中间。
再用pygame里的功能创建一个时钟,这时钟就是用来把控游戏画面更新速度的。
当呈现出running时就是运行状态正常时,此时方块的下落速度为500。

(12)游戏主体循环:
游戏运行起来后,只要running这个标志是True,就会进入while running的主循环,一直执行里面的代码。此处我运用了while语句来实现这个循环。
首先,把游戏屏幕填充为黑色。
然后通过foreventinpygame.event.get()去查看pygame收集到的所有事件。要是碰到事件类型是关闭窗口,running就会成为False,游戏自动结束,退出循环。
如果事件类型是按下按键,那就继续判断按的是哪个键。
要是按下左箭头键,先算出方块新的横坐标new_x,再用check_position函数判断新位置合不合法,合法的话就更新方块的横坐标block_x;
按下右箭头键时,操作和左箭头键类似,都是计算新位置并检查,合法就更新横坐标。
按下下箭头键时,算出方块新的纵坐标new_y,位置合法就更新纵坐标block_y。
而按下上箭头键时,先通过切片和zip操作把方块转一下,然后检查旋转后方块的位置合不合法,合法就更新方块block。

(13)方块下落及处理:
在游戏过程中,方块会自动下落,在程序里具体表现为它的纵坐标增加1,也就是执行block_y+=1。
下落一格后,程序会通过ifnotcheck_position(board,block,(block_x,block_y))来检查方块当前位置是否合法。如果不合法,就调用join_matrix函数,把方块合并到棋盘上,不过合并的实际位置是上一格,因为刚下落一格后的位置不合法,即执行board=join_matrix(board,block,(block_x,block_y-1))。
然后使用clear_rows函数除棋盘上那些填满的行,对应代码是board=clear_rows(board)。
随后会再生成一个新的方块,新的方块也随之开始下落。
最后,再检查这个新方块的初始位置合不合法,不合法则游戏结束,退出循环,这一步由ifnotcheck_position(board,block,(block_x,block_y))及其后续操作来实现。

(14)方块填充:
我设置下落的方块为红色,下落完成的方块为蓝色。
遍历棋盘board,如果棋盘格子的值不为0,就用蓝色绘制一个矩形表示该格子在屏幕上的位置。
再遍历当前方块block,如果方块格子的值不为0,就用红色绘制一个矩形表示该方块格子在屏幕上的位置,位置根据方块当前坐标block_x和block_y计算。

(15)刷新与延迟:
使用函数pygame.display.flip() 来更新屏幕显示内容,并根据game_spee设置的时间进行延迟,控制游戏节奏。
pygame.quit()则用于游戏结束时,退出pygame。

4.实验结果
游戏运行较为成功,方块生成与下落均正常,可以通过按键控制方块方向与下落速度。

程序源代码如下:
点击查看代码
import random
import pygame
pygame.init()
screen_width = 400
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("俄罗斯方块")
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
block_size = 20
grid_width = screen_width // block_size
grid_height = screen_height // block_size
shapes = [
[[1, 1, 1],
[0, 1, 0]],
[[0, 2, 2],
[2, 2, 0]],
[[3, 3, 0],
[0, 3, 3]],
[[4, 0, 0],
[4, 4, 4]],
[[0, 0, 5],
[5, 5, 5]],
[[6, 6, 6, 6]],
[[7, 7],
[7, 7]]
]
def new_block():
return random.choice(shapes), 0, 0
def check_position(board, shape, offset):
off_x, off_y = offset
for y, row in enumerate(shape):
for x, cell in enumerate(row):
try:
if cell and board[y + off_y][x + off_x]:
return False
except IndexError:
return False
return True
def join_matrix(mat1, mat2, mat2_off):
off_x, off_y = mat2_off
for y, row in enumerate(mat2):
for x, cell in enumerate(row):
if cell:
mat1[y + off_y][x + off_x] = cell
return mat1
def remove_row(board, row):
del board[row]
return [[0 for _ in range(grid_width)]] + board
def clear_rows(board):
full_rows = []
for i, row in enumerate(board):
if all(row):
full_rows.append(i)
for row in full_rows:
board = remove_row(board, row)
return board
def main():
board = [[0 for _ in range(grid_width)] for _ in range(grid_height)]
block, block_x, block_y = new_block()
clock = pygame.time.Clock()#使用pygame.time.Clock()
running = True
game_speed = 500
while running:
screen.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
new_x = block_x - 1
if check_position(board, block, (new_x, block_y)):
block_x = new_x
if event.key == pygame.K_RIGHT:
new_x = block_x + 1
if check_position(board, block, (new_x, block_y)):
block_x = new_x
if event.key == pygame.K_DOWN:
new_y = block_y + 1
if check_position(board, block, (block_x, new_y)):
block_y = new_y
if event.key == pygame.K_UP:
rotated_block = list(zip(*block[::-1]))
if check_position(board, rotated_block, (block_x, block_y)):
block = [list(row) for row in rotated_block]
block_y += 1
if not check_position(board, block, (block_x, block_y)):
board = join_matrix(board, block, (block_x, block_y - 1))
board = clear_rows(board)
block, block_x, block_y = new_block()
if not check_position(board, block, (block_x, block_y)):
running = False
for y, row in enumerate(board):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, BLUE,
(x * block_size, y * block_size, block_size, block_size),
0)
for y, row in enumerate(block):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, RED,
((block_x + x) * block_size, (block_y + y) * block_size, block_size, block_size),
0)
pygame.display.flip()
pygame.time.delay(game_speed)
pygame.quit()
if __name__ == "__main__":
main()
【视频作业】:[https://www.bilibili.com/video/BV17D7nzDEyB/?vd_source=d7994f29e58b5aef7c29510eac4929e3]
5.实验中遇到的问题和解决过程
(1)代码较为复杂,有一些地方不太会写。
解决方案:求助了大数据和b站、小红书等平台上的教学视频,避免一味的复制粘贴,而是理解掌握了每行代码的用法。
(2)对方块满行之后如何消除没有头绪
解决方案:在网上查找了相关资料,得知可以通过索引定位到需要消除的那一行从而进行消除。
其他(感悟、思考等)
(1)实验总结:本次Python实验课要求我们自由选择程序进行编写,在最开始,我十分茫然,不清楚自己应该编写什么样的程序。在观看了社媒平台上的一些教学视频以及结合自身兴趣之后,我选择编写了俄罗斯方块。通过编写这个游戏,我深刻体会到理论与实践结合的重要性。
从设计方块的下落、旋转,到实现消行和检测位置功能,每一步都考验了我对Python知识的理解和运用。在实验过程中我遇到了诸多难题,比如生成方块、定义颜色等。我通过寻求帮助,观看视频,查找资料等方式主动迎接挑战并解决问题,最后也得出了比较让我自己满意的成果。在这个不断发现问题、解决问题的过程中,我的能力有了很大的进步,对于Python的理解也更加深入,同时也收获了成功的喜悦和满足。
(2)课程总结:在Python这门课的学习中我学到了很多。
首先是Python的一些基本知识和原理,例如自定义函数,循环语句等。还记得第一节课编写最简单的程序时,我因为程序报错而感到手足无措,最后才发现是运用了中文冒号的缘故。而现在,经过了这么多节课程的学习,我已经可以熟练编写一些简单的程序,例如剪刀石头布和猜数字小游戏。这进一步锻炼了我的实践能力和知识水平,让我接触到了之前作为一名文科生从未探索过的领域,也让我发现了Python的乐趣。
在开始时我对Python了解得很少,仅仅局限于高中的信息技术课本上。这也让我在最初的学习中感到有些吃力,有时候被老师提问也答不太上来。我很感谢老师用心的教导,每节课都把相关知识点讲得很清楚,提问后也会带领我们进一步复习巩固,帮助我们不断学习新的知识、收获新的成功,让我们受益匪浅。
在这一学习的过程中,我的思维和习惯也得到了很好的培养。Python的学习培养了我的理科思维。让我学着借助数据和实验来认识问题和解决问题,从已知条件出发,借助逻辑推理和数据分析来得到自己想要的结果。同时这也培养了我细心细致的习惯,从最开始的总是遗忘诸如标点符号一类的细节而导致程序不断报错,到现在的细致编写每一行代码、基本做到不在基础性细节上犯错,我变得更加认真严谨。同时,在那一次次的遇到挫折并尝试着解决问题的过程中,我学着迎难而上、越挫越勇,而在面对着诸多困难完成最后这个实验之后,回望这一学期的Python学习课程,那种成就感和满足感是难以言表的。
还有一个很重要的收获就是学会摆脱对大数据的依赖。ai工具的确方便,但我们不能因此过度依靠ai,更需要注重自己的思考与行动,自己去探索、自己去学习、自己去实践,如此方能有所收获。
正如老师在最后一节课上所言,“那些看似波澜不惊的日复一日,终将在某天,让我们看到坚持的意义。”未来的大学生活和Python的学习都是如此,只有通过不断的努力,才能获得成功。“功不唐捐,玉汝于成”,在未来的学习中,我也将继续运用了解Python知识,将理论融于实践,进一步求索。
(3)意见和建议
前面的基础知识比较容易理解,但是后面就都比较难了,比如用socket函数编制服务器和客户端的那个实验,当初就不是特别理解,老师以后讲的时候可以稍微放慢一点速度!
还有有时候老师编写代码的速度比较快,感觉有点跟不上,建议老师稍微停留几秒或者提醒大家记一下重点步骤。
对于文科生来说Python的学习难度确实很大,感觉光听课还是不够的,老师可以在课前几天发布预习内容让大家提早学习一下下节课要讲的重点内容,也可以推荐一些相关的资料,这样应该可以帮助同学们在课堂上跟上进度!
最后,老师讲得特别好,感谢您一个学期以来的教导!

浙公网安备 33010602011771号