欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

python 数据逆时针旋转270度_Python自动耍俄罗斯方块

发布时间:2025/3/20 50 豆豆
生活随笔 收集整理的这篇文章主要介绍了 python 数据逆时针旋转270度_Python自动耍俄罗斯方块 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

小笨聪前面的文章总是用 Python 来进行爬虫,今天就换一下口味,体验一下 Python 自动耍俄罗斯方块。

小游戏 | Python自动玩俄罗斯方块​mp.weixin.qq.com

俄罗斯方块(Tetris) 规则:由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。(来自百度百科)

俄罗斯方块游戏界面

我们此次的任务就是在这条规则的基础上,利用 Python 和简单的 AI 算法,实现自动寻找最优位置和调整方块形态,达到快速高效得分的目的。(最终效果见文末视频)

代码分三部分:

  • utils.py:基础设置和界面设计
  • ai.py:AI算法和具体方式
  • AITetris.py:实现游戏主循环
  • 一.基础设置和界面设计

    1.定义一个俄罗斯方块

    方块共有7种形状,每块包含四个人小方块,分别写出小方块的相对坐标、定义获取小方块旋转角度、绝对坐标和相对坐标边界的函数。

    1class tetrisShape():2 def __init__(self, shape=0):3 # 空块4 self.shape_empty = 05 # 一字型块6 self.shape_I = 17 # L型块8 self.shape_L = 29 # 向左的L型块 10 self.shape_J = 3 11 # T型块 12 self.shape_T = 4 13 # 田字型块 14 self.shape_O = 5 15 # 反向Z型块 16 self.shape_S = 6 17 # Z型块 18 self.shape_Z = 7 19 # 每种块包含的四个小方块相对坐标分布 20 self.shapes_relative_coords = [ 21 [[0, 0], [0, 0], [0, 0], [0, 0]], 22 [[0, -1], [0, 0], [0, 1], [0, 2]], 23 [[0, -1], [0, 0], [0, 1], [1, 1]], 24 [[0, -1], [0, 0], [0, 1], [-1, 1]], 25 [[0, -1], [0, 0], [0, 1], [1, 0]], 26 [[0, 0], [0, -1], [1, 0], [1, -1]], 27 [[0, 0], [0, -1], [-1, 0], [1, -1]], 28 [[0, 0], [0, -1], [1, 0], [-1, -1]] 29 ] 30 self.shape = shape 31 self.relative_coords = self.shapes_relative_coords[self.shape] 32 '''获得该形状当前旋转状态的四个小方块的相对坐标分布''' 33 def getRotatedRelativeCoords(self, direction): 34 # 初始分布 35 if direction == 0 or self.shape == self.shape_O: 36 return self.relative_coords 37 # 逆时针旋转90度 38 if direction == 1: 39 return [[-y, x] for x, y in self.relative_coords] 40 # 逆时针旋转180度 41 if direction == 2: 42 if self.shape in [self.shape_I, self.shape_Z, self.shape_S]: 43 return self.relative_coords 44 else: 45 return [[-x, -y] for x, y in self.relative_coords] 46 # 逆时针旋转270度 47 if direction == 3: 48 if self.shape in [self.shape_I, self.shape_Z, self.shape_S]: 49 return [[-y, x] for x, y in self.relative_coords] 50 else: 51 return [[y, -x] for x, y in self.relative_coords]

    2.内部板块

    游戏主界面分为内部版块和外部板块,将游戏进行中的数据记录与数据处理部分定义在内部板块类中,将游戏数据可视化定义在外部板块类中。在内部版块类中,我们可以操作方块向左、向右、顺时针、逆时针、向下和坠落等运动。

    在方块移动时,也要实时判断方块是否越界或落地并及时处理,如果落地则将方块合并并整行消除,再创建新的方块,以此循环。

    1class InnerBoard():2 def __init__(self, width=10, height=22):3 # 宽和长, 单位长度为小方块边长4 self.width = width5 self.height = height6 self.reset()7 '''判断当前俄罗斯方块是否可以移动到某位置'''8 def ableMove(self, coord, direction=None):9 assert len(coord) == 2 10 if direction is None: 11 direction = self.current_direction 12 for x, y in self.current_tetris.getAbsoluteCoords(direction, coord[0], coord[1]): 13 # 超出边界 14 if x >= self.width or x < 0 or y >= self.height or y < 0: 15 return False 16 # 该位置有俄罗斯方块了 17 if self.getCoordValue([x, y]) > 0: 18 return False 19 return True 20 '''向右移动''' 21 def moveRight(self): 22 if self.ableMove([self.current_coord[0]+1, self.current_coord[1]]): 23 self.current_coord[0] += 1 24 '''向左移动''' 25 def moveLeft(self): 26 if self.ableMove([self.current_coord[0]-1, self.current_coord[1]]): 27 self.current_coord[0] -= 1 28 '''顺时针转''' 29 def rotateClockwise(self): 30 if self.ableMove(self.current_coord, (self.current_direction-1) % 4): 31 self.current_direction = (self.current_direction-1) % 4 32 '''逆时针转''' 33 def rotateAnticlockwise(self): 34 if self.ableMove(self.current_coord, (self.current_direction+1) % 4): 35 self.current_direction = (self.current_direction+1) % 4 36 '''向下移动''' 37 def moveDown(self): 38 removed_lines = 0 39 if self.ableMove([self.current_coord[0], self.current_coord[1]+1]): 40 self.current_coord[1] += 1 41 else: 42 x_min, x_max, y_min, y_max = self.current_tetris.getRelativeBoundary(self.current_direction) 43 # 简单起见, 有超出屏幕就判定游戏结束 44 if self.current_coord[1] + y_min < 0: 45 self.is_gameover = True 46 return removed_lines 47 self.mergeTetris() 48 removed_lines = self.removeFullLines() 49 self.createNewTetris() 50 return removed_lines

    3.外部板块和侧面板

    1class ExternalBoard(QFrame):2 score_signal = pyqtSignal(str)3 def __init__(self, parent, grid_size, inner_board):4 super().__init__(parent)5 self.grid_size = grid_size6 self.inner_board = inner_board7 self.setFixedSize(grid_size * inner_board.width, grid_size * inner_board.height)8 self.initExternalBoard()9 '''外部板块初始化''' 10 def initExternalBoard(self): 11 self.score = 0 12 '''把内部板块结构画出来''' 13 def paintEvent(self, event): 14 painter = QPainter(self) 15 for x in range(self.inner_board.width): 16 for y in range(self.inner_board.height): 17 shape = self.inner_board.getCoordValue([x, y]) 18 drawCell(painter, x * self.grid_size, y * self.grid_size, shape, self.grid_size) 19 for x, y in self.inner_board.getCurrentTetrisCoords(): 20 shape = self.inner_board.current_tetris.shape 21 drawCell(painter, x * self.grid_size, y * self.grid_size, shape, self.grid_size) 22 painter.setPen(QColor(0x777777)) 23 painter.drawLine(0, self.height()-1, self.width(), self.height()-1) 24 painter.drawLine(self.width()-1, 0, self.width()-1, self.height()) 25 painter.setPen(QColor(0xCCCCCC)) 26 painter.drawLine(self.width(), 0, self.width(), self.height()) 27 painter.drawLine(0, self.height(), self.width(), self.height()) 28 '''数据更新''' 29 def updateData(self): 30 self.score_signal.emit(str(self.score)) 31 self.update() 32 33 34 ''' 35 侧面板, 右边显示下一个俄罗斯方块的形状 36 ''' 37class SidePanel(QFrame): 38 def __init__(self, parent, grid_size, inner_board): 39 super().__init__(parent) 40 self.grid_size = grid_size 41 self.inner_board = inner_board 42 self.setFixedSize(grid_size * 5, grid_size * inner_board.height) 43 self.move(grid_size * inner_board.width, 0) 44 '''画侧面板''' 45 def paintEvent(self, event): 46 painter = QPainter(self) 47 x_min, x_max, y_min, y_max = self.inner_board.next_tetris.getRelativeBoundary(0) 48 dy = 3 * self.grid_size 49 dx = (self.width() - (x_max - x_min) * self.grid_size) / 2 50 shape = self.inner_board.next_tetris.shape 51 for x, y in self.inner_board.next_tetris.getAbsoluteCoords(0, 0, -y_min): 52 drawCell(painter, x * self.grid_size + dx, y * self.grid_size + dy, shape, self.grid_size) 53 '''更新数据''' 54 def updateData(self): 55 self.update()

    二、AI算法和具体方式

    此处AI 算法基本思想就是,遍历当前可操作的俄罗斯方块和下一个可操作的俄罗斯方块(根据不同的策略,即选择不同的位置和旋转角度)下落到底部后组成的所有可能的未来场景,从这些未来场景中选择一个最优的,其对应的当前可操作的俄罗斯方块的行动策略即为当前解,具体的代码实现如下:

    1# 简单的AI算法2for d_now in current_direction_range:3 x_now_min, x_now_max, y_now_min, y_now_max = self.inner_board.current_tetris.getRelativeBoundary(d_now)4 for x_now in range(-x_now_min, self.inner_board.width - x_now_max):5 board = self.getFinalBoardData(d_now, x_now)6 for d_next in next_direction_range:7 x_next_min, x_next_max, y_next_min, y_next_max = self.inner_board.next_tetris.getRelativeBoundary(d_next)8 distances = self.getDropDistances(board, d_next, range(-x_next_min, self.inner_board.width-x_next_max))9 for x_next in range(-x_next_min, self.inner_board.width-x_next_max): 10 score = self.calcScore(copy.deepcopy(board), d_next, x_next, distances) 11 if not action or action[2] < score: 12 action = [d_now, x_now, score] 13return action

    未来场景优劣评定考虑的因素有:

    1)可消除的行数;

    2)堆积后的俄罗斯方块内的虚洞数量;

    3)堆积后的俄罗斯方块内的小方块数量;

    4)堆积后的俄罗斯方块的最高点;

    5)堆积后的俄罗斯方块的高度(每一列都有一个高度)标准差;

    6)堆积后的俄罗斯方块的高度一阶前向差分;

    7)堆积后的俄罗斯方块的高度一阶前向差分的标准差;

    9)堆积后的俄罗斯方块的最高点和最低点之差。

    代码实现如下:

    1 # 下个俄罗斯方块以某种方式模拟到达底部2 board = self.imitateDropDown(board, self.inner_board.next_tetris, d_next, x_next, distances[x_next])3 width, height = self.inner_board.width, self.inner_board.height4 # 下一个俄罗斯方块以某方案行动到达底部后的得分(可消除的行数)5 removed_lines = 06# 空位统计7 hole_statistic_0 = [0] * width8 hole_statistic_1 = [0] * width9 # 方块数量 10 num_blocks = 0 11 # 空位数量 12 num_holes = 0 13 # 每个x位置堆积俄罗斯方块的最高点 14 roof_y = [0] * width 15 for y in range(height-1, -1, -1): 16 # 是否有空位 17 has_hole = False 18 # 是否有方块 19 has_block = False 20 for x in range(width): 21 if board[x + y * width] == tetrisShape().shape_empty: 22 has_hole = True 23 hole_statistic_0[x] += 1 24 else: 25 has_block = True 26 roof_y[x] = height - y 27 if hole_statistic_0[x] > 0: 28 hole_statistic_1[x] += hole_statistic_0[x] 29 hole_statistic_0[x] = 0 30 if hole_statistic_1[x] > 0: 31 num_blocks += 1 32 if not has_block: 33 break 34 if not has_hole and has_block: 35 removed_lines += 1 36 ......

    三、实现游戏主循环

    定义俄罗斯方块游戏类初始化(包括块大小、下落速度、水平布局、AI控制等),并含有开始、暂停、界面更新等事件。

    1class TetrisGame(QMainWindow):2 def __init__(self):3 super().__init__()4 # 是否暂停ing5 self.is_paused = False6 # 是否开始ing7 self.is_started = False8 self.initUI()9 '''界面初始化''' 10 def initUI(self): 11 # 块大小 12 self.grid_size = 22 13 # 游戏帧率 14 self.fps = 100 15 self.timer = QBasicTimer() 16 # 水平布局 17 layout_horizontal = QHBoxLayout() 18 self.inner_board = InnerBoard() 19 self.external_board = ExternalBoard(self, self.grid_size, self.inner_board) 20 21 . 22 . 23 . 24 25 '''按键事件''' 26 def keyPressEvent(self, event): 27 if not self.is_started or self.inner_board.current_tetris == tetrisShape().shape_empty: 28 super(TetrisGame, self).keyPressEvent(event) 29 return 30 key = event.key() 31 # P键暂停 32 if key == Qt.Key_P: 33 self.pause() 34 return 35 if self.is_paused: 36 return 37 else: 38 super(TetrisGame, self).keyPressEvent(event) 39 self.updateWindow() 40 41 42if __name__ == '__main__': 43 app = QApplication([])

    最终效果视频展示

    Python自动耍俄罗斯方块https://www.zhihu.com/video/1158808534270595072

    以上就是本次Python自动耍俄罗斯方块的过程,微信公众号“学编程的金融客”后台回复“ 俄罗斯方块 ”即可获取源码。【完】

    小游戏 | Python自动玩俄罗斯方块​mp.weixin.qq.com

    往期推荐

    1.爬取流浪地球影评

    2.爬取《明日之子》评论和弹幕

    3.北上广深租房图鉴

    4.爬取抖音视频

    5.母亲节祝福代码

    你的点赞和关注就是对我最大的支持!

    保存扫码关注公众号呗

    总结

    以上是生活随笔为你收集整理的python 数据逆时针旋转270度_Python自动耍俄罗斯方块的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。