python 数据逆时针旋转270度_Python自动耍俄罗斯方块
小笨聰前面的文章總是用 Python 來進行爬蟲,今天就換一下口味,體驗一下 Python 自動耍俄羅斯方塊。
小游戲 | Python自動玩俄羅斯方塊?mp.weixin.qq.com俄羅斯方塊(Tetris) 規則:由小方塊組成的不同形狀的板塊陸續從屏幕上方落下來,玩家通過調整板塊的位置和方向,使它們在屏幕底部拼出完整的一條或幾條。這些完整的橫條會隨即消失,給新落下來的板塊騰出空間,與此同時,玩家得到分數獎勵。沒有被消除掉的方塊不斷堆積起來,一旦堆到屏幕頂端,玩家便告輸,游戲結束。(來自百度百科)
俄羅斯方塊游戲界面我們此次的任務就是在這條規則的基礎上,利用 Python 和簡單的 AI 算法,實現自動尋找最優位置和調整方塊形態,達到快速高效得分的目的。(最終效果見文末視頻)
代碼分三部分:
一.基礎設置和界面設計
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_lines3.外部板塊和側面板
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自动耍俄罗斯方块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 完整mes代码(含客户端和server端
- 下一篇: 电脑有回声_专递课堂互动教室现场有回声怎