1

Series entry

"Python3 Programming Actual Tetris Robot"

game class

The game logic control class is the glue between the interface and the Tetris class. It accepts the mouse and keyboard events of the interface, operates the Tetris class, and implements the game logic. The operation of a single block has been implemented in Tetris. The game class mainly implements the elimination algorithm, the generation of new blocks, and the speed control of the game.

Design ideas

The simple processing of the layer elimination algorithm is to find a line and eliminate it. This project uses a little trick, first find all the rows that can be eliminated, store the row numbers in the array, and eliminate them all at once. The speed of the game is controlled by using timers, but I found that python timers are somewhat different from other languages, and new timer objects will continue to be generated. At first, I felt something was wrong, but I did not pay attention to it. It was later confirmed that this would cause a memory leak, which was replaced by tkinter.after.

Correlation constant

SCORES = (0,1,3,7,10)      # 消层分值设定

STEPUPSCORE = 50           # 每增长50分,速度加快一个等级
STEPUPINTERVAL = 100       # 每增长一个等级,定时器间隔时间减少100毫秒

Implementation

Game state variables

game.gameRunningStatus

  • 0: The game has not started
  • 1: Manual game
  • 2: Game playback
  • 5: The game is paused

Start the game

def start(self):
    self.gameRunningStatus = 1
    self.gameSpeedInterval = 1000        # 初始游戏速度
    self.gameSpeed = 1                   # 游戏速度等级
    self.gameLevels = 0                  # 消层数
    self.gameScores = 0                  # 总得分
    self.app.updateGameInfo(1,0,0)       # 初始化界面信息
    self.canvas.delete(ALL)              # 清空游戏空间
    self.nextCanvas.delete(ALL)          # 下一方块空间清空
    initGameRoom()                       # 初始化游戏空间数据

    self.tetris = Tetris(self.canvas, 4, 0, random.randint(0,6))             # 随机生成第一个方块
    for i in range(random.randint(0,4)):                                     # 旋转随机次数,方块出场式
        self.tetris.rotate()
    self.nextTetris = Tetris(self.nextCanvas, 1, 1, random.randint(0,6))     # 随机生成下一方块
    for i in range(random.randint(0,4)):                                     # 下一方块初始形态(随机)
        self.nextTetris.rotate()

    self.tick = Timer(self.gameSpeedInterval / 1000, self.tickoff)           # 控制游戏速度定时器
    self.tick.start()

Generate the next block

The main function of game control. After the cube drops to the bottom, it performs layer removal, statistical score, speed level determination, determination of whether the game is over, and the next cube is moved into the game space and a cube is generated and displayed in the next cube display space.

def generateNext(self):
    cleanLevels = self.clearRows()                               # 统计可消除层数
    if cleanLevels > 0:                                          # 有可消层,计算分值
        self.gameLevels += cleanLevels
        self.gameScores += SCORES[cleanLevels]
        if self.gameScores / STEPUPSCORE >= self.gameSpeed:
            self.gameSpeed += 1
            self.gameSpeedInterval -= STEPUPINTERVAL
        self.app.updateGameInfo(self.gameSpeed, self.gameLevels, self.gameScores)
    self.tetris = Tetris(self.canvas, 4, 0, self.nextTetris.getTetrisShape())    # 复制nexTetris到游戏空间
    for i in range(self.nextTetris.getRotateCount()):
        if not self.tetris.rotate():
            break
    if self.tetris.canPlace(4, 0):                   # 判定游戏是否结束
        self.nextCanvas.delete(ALL)                  # 游戏未结束,生成新的方块放入下一方块空间
        self.nextTetris = Tetris(self.nextCanvas, 1, 1, random.randint(0,6))
        for i in range(random.randint(0,4)):
            self.nextTetris.rotate()
    else:                                            # 游戏结束
        self.gameRunningStatus = 0
        self.canvas.create_text(150, 200, text = "Game is over!", fill="white", font = "Times 28 italic bold")
        self.app.setStartButtonText("Start")
        print("game is over!")

Statistics can eliminate the layer

The clearRows function finds the layers that can be eliminated, eliminates them, and returns the total number of layers that can be eliminated.

def clearRows(self):
    occupyLines = []                  # 存储可消除层行号
    h = 20
    while h > 0:
        allOccupy = 0
        for i in range(1, 11):
            if GameRoom[h][i]:
                allOccupy += 1        # block统计
        if allOccupy == 10:           # 行满
            occupyLines.append(h)     # 存储行号
        elif allOccupy == 0:          # 有一个空位,跳过些行
            break
        h -= 1
    if len(occupyLines) > 0:          # 有可消层
        self.doCleanRows(occupyLines) # 消除可消层
    return len(occupyLines)

De-layer function

Elimination layer function, according to the clearRows function to eliminate the full row of the game space. The difficulty of the algorithm is to control two variables at the same time. One is to traverse the game space from bottom to top, and the other is to move the space data above the full line down, and the step size of the down shift is the number of lines that have been eliminated.

def doCleanRows(self, lines):
    index = 0                                 # 存储已经消除了多少行
    h = lines[index]                          # 满行行号数据
    while h >= 0:                             # 只需要从最下面一满行开始即可
        if index < len(lines) and h == lines[index]:         # 找到一可消行
            index += 1                        # 已消行总数加1
            for j in range(1, 11):
                GameRoom[h][j] = 0            # 游戏空间数据消行
                for b in self.canvas.find_closest(\         # Canvas元件消除
                    j * BLOCKSIDEWIDTH - HALFBLOCKWIDTH, \
                    h  * BLOCKSIDEWIDTH - HALFBLOCKWIDTH):
                    self.canvas.delete(b)
        else:                                 # 移动游戏空间数据
            count = 0                         # 空位统计,全空,可以提前结束循环
            for j in range(1, 11):
                if GameRoom[h][j] == 1:
                    count += 1
                    GameRoom[h + index][j] = GameRoom[h][j]     # 注意index变量,这是移动步长,与已经消除行数有关
                    GameRoom[h][j] = 0
                    for b in self.canvas.find_closest(j * BLOCKSIDEWIDTH - HALFBLOCKWIDTH, h  * BLOCKSIDEWIDTH - HALFBLOCKWIDTH):
                        self.canvas.move(b, 0, index * BLOCKSIDEWIDTH)
            if count == 0:                   # 发现整行位全空,提前退出
                break
        h -= 1

Block control

The block control has been implemented in the Tetris class. In the game class, just forward the event to the current block. The only one more moveDownEnd-block straight down function.

def moveDownEnd(self):
    while self.moveDown():      # 循环下落,直到不能再落
        pass

Game speed control

The speed control of the game is easy to implement, just trigger the down function periodically.

def tickoff(self):
    if self.gameRunningStatus == 1:
        self.moveDown()
        self.tick = Timer(self.gameSpeedInterval / 1000, self.tickoff)
        self.tick.start()

Content preview

The use of timers will introduce new threads and resource conflicts will occur. Ordering will resolve thread conflicts.

project address

https://gitee.com/zhoutk/ptetris
或
https://github.com/zhoutk/ptetris

Operation method

1. install python3, git
2. git clone https://gitee.com/zhoutk/ptetris (or download and unzip source code)
3. cd ptetris
4. python3 tetris

This project surpport windows, linux, macOs

on linux, you must install tkinter first, use this command:  
sudo apt install python3-tk

Related items

C++ version has been implemented, project address:

https://gitee.com/zhoutk/qtetris

zhoutk
2.6k 声望1.2k 粉丝

自由程序员,技术路线c,delphi,c++,c#,java,php,node.js,python,golang,typescript;超喜欢react.js的设计思路,全栈开发成为我的终极目标。开发机器macbook pro,或装ubuntu、fedora的机器,编程用vim...