Continue juejin… cn/post / 702207… Code update of

First, do evaluation after elimination

In the previous article, we evaluated the position of the block and the void that came out after the block fell, but these evaluations were not calculated for elimination, so that the robot now does not consider elimination that leaves no void at all, such as the one below.But we know that this elimination does not create a void. So when we calculate the evaluation, it’s best to calculate the evaluation after elimination. We simply add a do_clear function to the Matrix function to eliminate it

    def do_clear(self):
        for i in range(self.rows-1,-1,-1):
            if sum(self.cols*i:self.cols*(i+1))==self.cols:
                self.matrix[self.cols:self.cols*(i+1)]=self.matrix[0:self.cols*i]
Copy the code

Then add a line after clone_matrix.fill_block(center_shape, xdiff=xdiff, ydiff=max_yindex)

clone_matrix.do_clear()
Copy the code

Now the robot is a little smarter than before, but there are still problems, as long as there are the following two problems: 1. When there was a better elimination option available, the robot did not choose a better elimination option (for example, it could have eliminated two rows, but the robot chose to eliminate one row). 2. People try to avoid stacking empty columns too high on either side and prioritize filling in columns far from empty. Let’s make these changes.

Second, consider getting a higher score when eliminating

We add a clear_num to the do_clear function of the Matrix to calculate how many rows are eliminated

    def do_clear(self):
        clear_num = 0
        for i in range(self.rows-1,-1,-1):
            if sum(self.data[self.cols*i:self.cols*(i+1)])==self.cols:
                self.data[self.cols:self.cols*(i+1)]=self.data[0:self.cols*i]
                clear_num+=1
        return clear_num
Copy the code

Add two lines like this to the calculation

clear_num=clone_matrix.do_clear()
score += clear_num * 5
Copy the code

In this way, each additional elimination line will get an extra 5 points, which will motivate the robot to find a better elimination solution in a single elimination.

3. Avoid packing near empty columns

First of all, we can think of the definition of empty column, as shown in the figure below, the position of 1 cannot be considered as a complete empty column, because there are two high Spaces on the right of the column, and the chance of saving is much greater than that of the position of 2. Both sides of 2 have already been raised by 3 Spaces, and if it goes on like this, we have to wait for a long bar.So we add an empty column fetch function to the Matrix

    def get_empty_col(self):
        miny_arr=[]
        for x in range(self.cols):
            miny=19
            for y in range(self.rows):
                miny=y
                if self.get_val(x,y) == 1:break
            miny_arr.append(miny)
        empty_arr=[]
        if miny_arr[1] - miny_arr[0] > 2: empty_arr.append((self.cols,miny_arr[1] - miny_arr[0]))
        if miny_arr[self.cols-2] - miny_arr[self.cols-1] > 2: empty_arr.append((miny_arr[self.cols-2] - miny_arr[self.cols-1],self.cols))
        for x in range(1,self.cols-1):
            if miny_arr[x-1]-miny_arr[x]>2 or miny_arr[x+1]-miny_arr[x]>2: empty_arr.append((miny_arr[x-1]-miny_arr[x],miny_arr[x+1]-miny_arr[x]))
        return empty_arr
Copy the code

Add get_cost_of_emptycol to AIPlayer

    def get_cost_of_emptycol(self, empty_arr):
        cost = 0
        for l,r in empty_arr:
            if l>2 and r>2: cost += (l+r)*2
            elif l>2: cost += l
            else: cost += r
        return cost
Copy the code

Add two lines where the score is calculated

empty_arr = clone_matrix.get_empty_col()
score -= self.get_cost_of_emptycol(empty_arr)
Copy the code

Now that the robot is better, it can no longer play gameover when there is no bad situation

Four, has become a void above the location to avoid

Such as belowWe will definitely avoid stacking in the position of red box as much as possible, because this will help us to enter the void for new elimination as soon as possible after eliminating the squares above the void. However, the robot does not consider this at present, so we continue to add the calculation of square blocks on the void in the Matrix. Function implementation is as follows:

    def get_block_above_hole(self):
        blocks=0
        for x in range(0,self.cols):
            for y in range(1,self.rows):
                if self.get_val(x,y) == 0 and self.get_val(x,y-1) == 1: 
                    blocks += sum(self.data[x:(y*self.cols+x):self.cols])

        return blocks
Copy the code

Let’s add another line where we calculate the score

score -= clone_matrix.get_block_above_hole()
Copy the code

The robot will then consider having as few squares above the void as possible. Attached is the current complete procedure.

# -*- coding=utf-8 -*-
import random
import pygame
from pygame.locals import KEYDOWN,K_LEFT,K_RIGHT,K_UP,K_DOWN,K_SPACE
import pickle,os

ROW_COUNT=20
COL_COUNT=10
SCORE_MAP=(100,300,800,1600)

class Matrix(object):
    rows = 0
    cols = 0
    data = []
    def __init__(self, rows, cols, data=None):
        self.rows = rows
        self.cols = cols 
        if data is None: data = [0 for i in range(rows*cols)]
        self.data = data

    def set_val(self, x, y, val):
        self.data[y*self.cols+x] = val

    def get_val(self, x, y):
        return self.data[y*self.cols+x]
    
    def cross_block(self, rect_arr, xdiff=0, ydiff=0):
        for x,y in rect_arr:
            #if x+xdiff>=0 and x+xdiff<self.cols and y+ydiff>=0 and y+ydiff<self.rows:
            if self.get_val(x+xdiff,y+ydiff) == 1: return True
        return False

    def get_block_above_hole(self):
        blocks=0
        for x in range(0,self.cols):
            for y in range(1,self.rows):
                if self.get_val(x,y) == 0 and self.get_val(x,y-1) == 1: 
                    blocks += sum(self.data[x:(y*self.cols+x):self.cols])

        return blocks

    def get_hole_number(self):
        hole_num=0
        for x in range(0,self.cols):
            for y in range(1,self.rows):
                if self.get_val(x,y) == 0 and self.get_val(x,y-1) == 1: 
                    hole_num+=1
        return hole_num

    def clone(self):
        clone_matrix=Matrix(self.rows, self.cols, list(self.data))
        return clone_matrix

    def fill_block(self, rect_arr, xdiff=0, ydiff=0):
        for x,y in rect_arr:
            self.set_val(x+xdiff,y+ydiff, 1)

    def do_clear(self):
        clear_num = 0
        for i in range(self.rows-1,-1,-1):
            if sum(self.data[self.cols*i:self.cols*(i+1)])==self.cols:
                self.data[self.cols:self.cols*(i+1)]=self.data[0:self.cols*i]
                clear_num+=1
        return clear_num

    def get_empty_col(self):
        miny_arr=[]
        for x in range(self.cols):
            miny=19
            for y in range(self.rows):
                miny=y
                if self.get_val(x,y) == 1:break
            miny_arr.append(miny)
        empty_arr=[]
        if miny_arr[1] - miny_arr[0] > 2: empty_arr.append((self.cols,miny_arr[1] - miny_arr[0]))
        if miny_arr[self.cols-2] - miny_arr[self.cols-1] > 2: empty_arr.append((miny_arr[self.cols-2] - miny_arr[self.cols-1],self.cols))
        for x in range(1,self.cols-1):
            if miny_arr[x-1]-miny_arr[x]>2 or miny_arr[x+1]-miny_arr[x]>2: empty_arr.append((miny_arr[x-1]-miny_arr[x],miny_arr[x+1]-miny_arr[x]))
        return empty_arr

    def print_matrix(self):
        for i in range(self.rows):
            print self.data[self.cols*i:self.cols*(i+1)]

class Player(object):
    auto_mode=False
    def __init__(self):
        pass
    def run(self, panel): 
        pass

class HumanPlayer(Player):
    def __init__(self):
        super(Player, self).__init__()

class AIPlayer(Player):
    cal_block_id=-1 
    ctl_arr=[] # control arr, 1=change、2=left、3=right、4=down
    auto_mode=True
    ai_diff_ticks = 100 #timespan between two controls
    
    def __init__(self):
        super(Player, self).__init__()
        self.ctl_ticks = pygame.time.get_ticks() + self.ai_diff_ticks

    def get_cost_of_emptycol(self, empty_arr):
        cost = 0
        for l,r in empty_arr:
            if l>2 and r>2: cost += (l+r)*2
            elif l>2: cost += l
            else: cost += r
        return cost

    def cal_best_arr(self, panel):
        matrix = panel.get_rect_matrix()
        cur_shape_id = panel.moving_block.shape_id
        shape_num = panel.moving_block.shape_num
        max_score = -10000
        best_arr = []
        for i in range(shape_num):
            tmp_shape_id = cur_shape_id + i
            if tmp_shape_id >= shape_num: tmp_shape_id = tmp_shape_id % shape_num
            tmp_shape = panel.moving_block.get_shape(sid=tmp_shape_id)
            center_shape = []
            for x,y in tmp_shape: center_shape.append((x+COL_COUNT/2-2,y-2))
            minx = COL_COUNT
            maxx = 0
            miny = ROW_COUNT
            maxy = -2
            for x,y in center_shape:
                if x<minx: minx = x
                if x>maxx: maxx = x
                if y<miny: miny = y
                if y>maxy: maxy = y

            for xdiff in range(-minx,COL_COUNT-maxx): 
                arr = [1 for _ in range(i)] 
                if xdiff < 0: [arr.append(2) for _ in range(-xdiff)]
                if xdiff > 0: [arr.append(3) for _ in range(xdiff)]

                max_yindex = -miny
                for yindex in range(-miny, ROW_COUNT-maxy):
                    if matrix.cross_block(center_shape, xdiff=xdiff, ydiff=yindex):
                        break
                    max_yindex = yindex
                score = sum([y+max_yindex for x,y in center_shape])

                # clone matrix and fill new block to calculate holes
                clone_matrix = matrix.clone()
                clone_matrix.fill_block(center_shape, xdiff=xdiff, ydiff=max_yindex)
                clear_num = clone_matrix.do_clear()
                score -= clone_matrix.get_block_above_hole()
                empty_arr = clone_matrix.get_empty_col()
                score -= self.get_cost_of_emptycol(empty_arr)
                score += clear_num * 5
                score -= clone_matrix.get_hole_number() * COL_COUNT

                if score > max_score: 
                    max_score = score
                    best_arr = arr
        self.ctl_arr = best_arr+[4]

    def run(self, panel):
        if pygame.time.get_ticks() < self.ctl_ticks: return
        self.ctl_ticks += self.ai_diff_ticks
        if panel.block_id == self.cal_block_id: # block_id not change
            if len(self.ctl_arr)>0:
                ctl = self.ctl_arr.pop(0)
                if ctl == 1: panel.change_block()
                if ctl == 2: panel.control_block(-1,0)
                if ctl == 3: panel.control_block(1,0)
                if ctl == 4:
                    flag = panel.move_block()
                    while flag==1: 
                        flag = panel.move_block()

        else: # block_id is new
            self.cal_block_id = panel.block_id
            self.cal_best_arr(panel)
            

class RectInfo(object):
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.color = color

class HintBox(object):
    next_block=None
    def __init__(self, bg, block_size, position):
        self._bg=bg;
        self._x,self._y,self._width,self._height=position
        self._block_size=block_size
        self._bgcolor=[0,0,0]

    def take_block(self):
        block = self.next_block
        if block is None: # make first block
            block = create_block()
    
        self.next_block = create_block()
        return block

    def paint(self):
        mid_x=self._x+self._width/2
        pygame.draw.line(self._bg,self._bgcolor,[mid_x,self._y],[mid_x,self._y+self._height],self._width) 
        bz=self._block_size
        
        if self.next_block:
            arr = self.next_block.get_rect_arr()
            minx,miny=arr[0]
            maxx,maxy=arr[0]
            for x,y in arr:
                if x<minx: minx=x
                if x>maxx: maxx=x
                if y<miny: miny=y
                if y>maxy: maxy=y
            w=(maxx-minx)*bz
            h=(maxy-miny)*bz
            
            cx=self._width/2-w/2-minx*bz-bz/2 
            cy=self._height/2-h/2-miny*bz-bz/2

            for rect in arr:
                x,y=rect
                pygame.draw.line(self._bg,self.next_block.color,[self._x+x*bz+cx+bz/2,self._y+cy+y*bz],[self._x+x*bz+cx+bz/2,self._y+cy+(y+1)*bz],bz)
                pygame.draw.rect(self._bg,[255,255,255],[self._x+x*bz+cx,self._y+y*bz+cy,bz+1,bz+1],1)

class ScoreBox(object):
    total_score = 0
    high_score = 0
    db_file = 'tetris.db'
    def __init__(self, bg, block_size, position):
        self._bg=bg;
        self._x,self._y,self._width,self._height=position
        self._block_size=block_size
        self._bgcolor=[0,0,0]
        
        if os.path.exists(self.db_file): self.high_score = pickle.load(open(self.db_file,'rb'))

    def paint(self):
        myfont = pygame.font.Font(None,36)
        white = 255,255,255
        textImage = myfont.render('High: %06d'%(self.high_score), True, white)
        self._bg.blit(textImage, (self._x,self._y))
        textImage = myfont.render('Score:%06d'%(self.total_score), True, white)
        self._bg.blit(textImage, (self._x,self._y+40))

    def add_score(self, score):
        self.total_score += score
        if self.total_score > self.high_score:
            self.high_score=self.total_score
            pickle.dump(self.high_score, open(self.db_file,'wb+'))

class Panel(object): 
    block_id=0
    rect_arr=[] 
    moving_block=None 
    hint_box=None
    score_box=None
    def __init__(self,bg, block_size, position):
        self._bg=bg;
        self._x,self._y,self._width,self._height=position
        self._block_size=block_size
        self._bgcolor=[0,0,0]
    
    def get_rect_matrix(self):
        matrix = Matrix(ROW_COUNT, COL_COUNT)
        for rect_info in self.rect_arr:
            matrix.set_val(rect_info.x, rect_info.y, 1)
        return matrix

    def add_block(self,block):
        for x,y in block.get_rect_arr():
            self.rect_arr.append(RectInfo(x,y, block.color))

    def create_move_block(self):
        self.block_id+=1
        block = self.hint_box.take_block()
        #block = create_block()
        block.move(COL_COUNT/2-2,-2) # move block to top center
        self.moving_block=block

    def check_overlap(self, diffx, diffy, check_arr=None):
        if check_arr is None: check_arr = self.moving_block.get_rect_arr()
        for x,y in check_arr:
            for rect_info in self.rect_arr:
                if x+diffx==rect_info.x and y+diffy==rect_info.y:
                    return True
        return False

    def control_block(self, diffx, diffy):
        if self.moving_block.can_move(diffx,diffy) and not self.check_overlap(diffx, diffy):
            self.moving_block.move(diffx,diffy)

    def change_block(self):
        if self.moving_block:
            new_arr = self.moving_block.change()
            if new_arr and not self.check_overlap(0, 0, check_arr=new_arr): 
                self.moving_block.rect_arr=new_arr


    def move_block(self):
        if self.moving_block is None: create_move_block()
        if self.moving_block.can_move(0,1) and not self.check_overlap(0,1): 
            self.moving_block.move(0,1)
            return 1
        else:
            self.add_block(self.moving_block)
            self.check_clear()

            for rect_info in self.rect_arr:
                if rect_info.y<0: return 9 # gameover
            self.create_move_block()
            return 2

    def check_clear(self):
        tmp_arr = [[] for i in range(20)]
       
        for rect_info in self.rect_arr:
            if rect_info.y<0: return
            tmp_arr[rect_info.y].append(rect_info)

        clear_num=0
        clear_lines=set([])
        y_clear_diff_arr=[[] for i in range(20)]
        
        for y in range(19,-1,-1):
            if len(tmp_arr[y])==10:
                clear_lines.add(y)
                clear_num += 1
            y_clear_diff_arr[y] = clear_num

        if clear_num>0:
            new_arr=[]
            
            for y in range(19,-1,-1):
                if y in clear_lines: continue
                tmp_row = tmp_arr[y]
                y_clear_diff=y_clear_diff_arr[y]
                for rect_info in tmp_row:
                    #new_arr.append([x,y+y_clear_diff])
                    new_arr.append(RectInfo(rect_info.x, rect_info.y+y_clear_diff, rect_info.color))
            
            self.rect_arr = new_arr
            score = SCORE_MAP[clear_num-1]
            self.score_box.add_score(score)


    def paint(self):
        mid_x=self._x+self._width/2
        pygame.draw.line(self._bg,self._bgcolor,[mid_x,self._y],[mid_x,self._y+self._height],self._width) # 用一个粗线段来填充背景
        
        bz=self._block_size
        for rect_info in self.rect_arr:
            x=rect_info.x
            y=rect_info.y
            pygame.draw.line(self._bg,rect_info.color,[self._x+x*bz+bz/2,self._y+y*bz],[self._x+x*bz+bz/2,self._y+(y+1)*bz],bz)
            pygame.draw.rect(self._bg,[255,255,255],[self._x+x*bz,self._y+y*bz,bz+1,bz+1],1)
       
        if self.move_block:
            for rect in self.moving_block.get_rect_arr():
                x,y=rect
                pygame.draw.line(self._bg,self.moving_block.color,[self._x+x*bz+bz/2,self._y+y*bz],[self._x+x*bz+bz/2,self._y+(y+1)*bz],bz)
                pygame.draw.rect(self._bg,[255,255,255],[self._x+x*bz,self._y+y*bz,bz+1,bz+1],1)

class Block(object):
    sx=0
    sy=0
    def __init__(self):
        self.rect_arr=[]

    def get_rect_arr(self): 
        return self.rect_arr

    def move(self,xdiff,ydiff):
        self.sx+=xdiff
        self.sy+=ydiff
        self.new_rect_arr=[]
        for x,y in self.rect_arr:
            self.new_rect_arr.append((x+xdiff,y+ydiff))
        self.rect_arr=self.new_rect_arr

    def can_move(self,xdiff,ydiff):
        for x,y in self.rect_arr:
            if y+ydiff>=20: return False
            if x+xdiff<0 or x+xdiff>=10: return False
        return True

    def change(self):
        self.shape_id+=1 
        if self.shape_id >= self.shape_num: 
            self.shape_id=0

        arr = self.get_shape()
        new_arr = []
        for x,y in arr:
            if x+self.sx<0 or x+self.sx>=10: 
                self.shape_id -= 1
                if self.shape_id < 0: self.shape_id = self.shape_num - 1
                return None 

            new_arr.append([x+self.sx,y+self.sy])

        return new_arr

class LongBlock(Block):
    shape_id=0
    shape_num=2
    def __init__(self, n=None): 
        super(LongBlock, self).__init__()
        if n is None: n=random.randint(0,1)
        self.shape_id=n
        self.rect_arr=self.get_shape()
        self.color=(50,180,50)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        return [(1,0),(1,1),(1,2),(1,3)] if sid==0 else [(0,2),(1,2),(2,2),(3,2)]

class SquareBlock(Block): 
    shape_id=0
    shape_num=1
    def __init__(self, n=None):
        super(SquareBlock, self).__init__()
        self.rect_arr=self.get_shape()
        self.color=(0,0,255)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        return [(1,1),(1,2),(2,1),(2,2)]

class ZBlock(Block): 
    shape_id=0
    shape_num=2
    def __init__(self, n=None):
        super(ZBlock, self).__init__()
        if n is None: n=random.randint(0,1)
        self.shape_id=n
        self.rect_arr=self.get_shape()
        self.color=(30,200,200)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        return [(2,0),(2,1),(1,1),(1,2)] if sid==0 else [(0,1),(1,1),(1,2),(2,2)]

class SBlock(Block):
    shape_id=0
    shape_num=2
    def __init__(self, n=None):
        super(SBlock, self).__init__()
        if n is None: n=random.randint(0,1)
        self.shape_id=n
        self.rect_arr=self.get_shape()
        self.color=(255,30,255)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        return [(1,0),(1,1),(2,1),(2,2)] if sid==0 else [(0,2),(1,2),(1,1),(2,1)]

class LBlock(Block): 
    shape_id=0
    shape_num=4
    def __init__(self, n=None):
        super(LBlock, self).__init__()
        if n is None: n=random.randint(0,3)
        self.shape_id=n
        self.rect_arr=self.get_shape()
        self.color=(200,200,30)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        if sid==0: return [(1,0),(1,1),(1,2),(2,2)]
        elif sid==1: return [(0,1),(1,1),(2,1),(0,2)]
        elif sid==2: return [(0,0),(1,0),(1,1),(1,2)]
        else: return [(0,1),(1,1),(2,1),(2,0)]

class JBlock(Block):
    shape_id=0
    shape_num=4
    def __init__(self, n=None):
        super(JBlock, self).__init__()
        if n is None: n=random.randint(0,3)
        self.shape_id=n
        self.rect_arr=self.get_shape()
        self.color=(200,100,0)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        if sid==0: return [(1,0),(1,1),(1,2),(0,2)]
        elif sid==1: return [(0,1),(1,1),(2,1),(0,0)]
        elif sid==2: return [(2,0),(1,0),(1,1),(1,2)]
        else: return [(0,1),(1,1),(2,1),(2,2)]

class TBlock(Block):
    shape_id=0
    shape_num=4
    def __init__(self, n=None):
        super(TBlock, self).__init__()
        if n is None: n=random.randint(0,3)
        self.shape_id=n
        self.rect_arr=self.get_shape()
        self.color=(255,0,0)

    def get_shape(self, sid=None):
        if sid is None: sid = self.shape_id
        if sid==0: return [(0,1),(1,1),(2,1),(1,2)]
        elif sid==1: return [(1,0),(1,1),(1,2),(0,1)]
        elif sid==2: return [(0,1),(1,1),(2,1),(1,0)]
        else: return [(1,0),(1,1),(1,2),(2,1)]

def create_block():
    n = random.randint(0,18)
    if n==0: return SquareBlock(n=0)
    elif n==1 or n==2: return LongBlock(n=n-1)
    elif n==3 or n==4: return ZBlock(n=n-3)
    elif n==5 or n==6: return SBlock(n=n-5)
    elif n>=7 and n<=10: return LBlock(n=n-7)
    elif n>=11 and n<=14: return JBlock(n=n-11)
    else: return TBlock(n=n-15)

def run():
    pygame.init()
    space=30
    main_block_size=30
    main_panel_width=main_block_size*COL_COUNT
    main_panel_height=main_block_size*ROW_COUNT
    screencaption = pygame.display.set_caption('Tetris')
    screen = pygame.display.set_mode((main_panel_width+160+space*3,main_panel_height+space*2)) 
    main_panel=Panel(screen,main_block_size,[space,space,main_panel_width,main_panel_height])
    hint_box=HintBox(screen,main_block_size,[main_panel_width+space+space,space,160,160])
    score_box=ScoreBox(screen,main_block_size,[main_panel_width+space+space,160+space*2,160,160])
    
    main_panel.hint_box=hint_box
    main_panel.score_box=score_box

    pygame.key.set_repeat(200, 30)
    main_panel.create_move_block()

    diff_ticks = 300 
    ticks = pygame.time.get_ticks() + diff_ticks

    player = AIPlayer()

    pause=0
    game_state = 1 # game status 1.normal 2.gameover
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                 pygame.quit()
                 exit()
            if event.type == KEYDOWN:
                if event.key==97: pause=1-pause # press a to pause
                if event.key==112: # for debug where press p
                    main_panel.get_rect_matrix().print_matrix()
            if player.auto_mode:continue
            if event.type == KEYDOWN:
                if event.key == K_LEFT: main_panel.control_block(-1,0)
                if event.key == K_RIGHT: main_panel.control_block(1,0)
                if event.key == K_UP: main_panel.change_block()
                if event.key == K_DOWN: main_panel.control_block(0,1)
                if event.key == K_SPACE:
                    flag = main_panel.move_block()
                    while flag==1: 
                        flag = main_panel.move_block()
                    if flag == 9: game_state = 2
       
        screen.fill((100,100,100)) # make background gray
        main_panel.paint() 
        hint_box.paint() 
        score_box.paint() 

        if game_state == 2:
            myfont = pygame.font.Font(None,30)
            white = 255,255,255
            textImage = myfont.render("Game over", True, white)
            screen.blit(textImage, (160,190))

        pygame.display.update() 

        if pause==1: continue
        if game_state == 1: player.run(main_panel)
        if game_state == 1 and pygame.time.get_ticks() >= ticks:
            ticks+=diff_ticks
            if main_panel.move_block()==9: game_state = 2 # gameover

run()
Copy the code

This article will stop here and continue to optimize in the next chapter.