Continue to write the last Tetris game

Set a different color for each square

The most appropriate place to change the color of the fallen Block is in the rect_arr Block subclass of the Panel class. For example, in the TBlock class, add a line to the __init__ function

The self color = (0, 255)Copy the code

Add code to the Panel paint function

If self.move_block: for rect in self.moving_block.get_rect_arr(): X, y = the rect pygame. The draw. The line (self. _bg,,0,255 [0], [self. * bz _x + x + bz / 2, the self. The _y + y * bz], [self. * bz _x + x + bz / 2, the self. The _y + (y + 1) * bz], bz) Pygame. The draw. The rect (self _bg, [255255255], [self. _x + x * bz, self _y + y * bz, bz + 1, bz + 1], 1)Copy the code

In the

Pygame. The draw. The line (self. _bg,0,255 [0], [self. * bz _x + x + bz / 2, the self. The _y + y * bz], [self. * bz _x + x + bz / 2, the self. The _y + (y + 1) * bz], bz) change 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)Copy the code

Rect_arr (x,y, x,y, x); RectInfo (x,y,y, x,y)

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

Change the code stored in rect_arr to

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

And the design recT_ARR can be modifiedPost the complete code so far

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

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

class Panel(object): # 用于绘制整个游戏窗口的版面
    rect_arr=[] # 已经落底下的方块
    moving_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 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):
        block = create_block()
        block.move(5-2,-2) # 方块挪到中间 
        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 # 游戏失败
            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


    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):
        return [(1,0),(1,1),(1,2),(1,3)] if self.shape_id==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):
        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):
        return [(2,0),(2,1),(1,1),(1,2)] if self.shape_id==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):
        return [(1,0),(1,1),(2,1),(2,2)] if self.shape_id==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):
        if self.shape_id==0: return [(1,0),(1,1),(1,2),(2,2)]
        elif self.shape_id==1: return [(0,1),(1,1),(2,1),(0,2)]
        elif self.shape_id==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):
        if self.shape_id==0: return [(1,0),(1,1),(1,2),(0,2)]
        elif self.shape_id==1: return [(0,1),(1,1),(2,1),(0,0)]
        elif self.shape_id==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):
        if self.shape_id==0: return [(0,1),(1,1),(2,1),(1,2)]
        elif self.shape_id==1: return [(1,0),(1,1),(1,2),(0,1)]
        elif self.shape_id==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,19)
    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*10
    main_panel_height=main_block_size*20
    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])

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

    diff_ticks = 300 # 移动一次蛇头的事件,单位毫秒
    ticks = pygame.time.get_ticks() + diff_ticks

    game_state = 1 # 游戏状态1.表示正常 2.表示失败
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                 pygame.quit()
                 exit()
            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)) # 将界面设置为灰色
        main_panel.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() # 必须调用update才能看到绘图显示

        if game_state == 1 and pygame.time.get_ticks() >= ticks:
            ticks+=diff_ticks
            if main_panel.move_block()==9: game_state = 2 # 游戏结束

run()
Copy the code

Two, the next square

To facilitate the drawing of the prompt window for the next box, we define a HintBox class that manages the drawing of the next box and the interface

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 and 3 def take_block(self): block = self.next_block if block is None: Return block def paint(self): create a block = create_block() self.next_block = create_block() 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 # Calculate the offset pixel cx=self._width/2-w/2-minx*bz-bz/2 needed to draw the square in the center of the prompt window 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+ Bz] (y + 1) * and bz) pygame. The draw. The rect (self. _bg, [255255255], [self. * bz _x + x + cx, the self. The _y + y * bz + cy, bz + 1, bz + 1], 1)Copy the code

Add a property to the Panel class

hint_box=None
Copy the code

Add the Panel class inside

Def create_move_block(self): block= create_move_block() block.move(5-2,-2Copy the code

Blocks are generated by hint_box instead

Def create_move_block(self): block= self.hint_box.take_block() block.move(5-2, -1Copy the code

Added programs to initialize hint_box and set main_panel in the run function

Hint_box = HintBox (screen, main_block_size, [main_panel_width + space + space, space, 160160]) main_panel. Hint_box = hint_boxCopy the code

Added the drawing of the next block prompt window to the main loop

Hint_box.paint () # draw the prompt window for the next blockCopy the code

The next block prompt should now display normally

Third, the calculation of scores

The elimination score is calculated as 100 points for 1 line, 300 points for 2 lines, 800 points for 3 lines, 1600 points for 4 lines. Similar to the design of the next box prompt window, we can add a ScoreBox class

class ScoreBox(object): total_score = 0 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 paint(self): Myfont = pygame.font-size (None,36) White = 255,255,255 textImage = myfont. Render ('Score:%06d'%(self.total_score), True, white) self._bg.blit(textImage, (self._x,self._y))Copy the code

Then add the score_box property to the Panel

score_box=None
Copy the code

Define a global SCORE_MAP

SCORE_MAP = (100300800160)Copy the code

In the check_clear function, if there is a square elimination, it is executed

            score = SCORE_MAP[clear_num-1]
            self.score_box.total_score += score
Copy the code

Initialize score_box in the run main function

Score_box = ScoreBox (screen, main_block_size, [main_panel_width + space + space, 160 + space * 2160160]) main_panel.score_box=score_boxCopy the code

Draw a score_box in the game loop

Score_box.paint () # draw the total scoreCopy the code

Fourth, the highest score in history

Prepare to save in the current directory with a tetris.db pickle file so first

import pickle,os
Copy the code

Since the highest score can be drawn with ScoreBox when drawing the current score, add a highest score attribute and a file definition directly to ScoreBox

high_score = 0
db_file = 'tetris.db'
Copy the code

Added pickle loading to ScoreBox’s initialization function

if os.path.exists(self.db_file): self.high_score = pickle.load(open(self.db_file,'rb'))
Copy the code

Add the highest score drawing in Paint

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))Copy the code

Change the previous modification of ScoreBox score directly to encapsulate an add_score function

    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+'))
Copy the code

Modify score in add_score function and judge whether the score exceeds the maximum score. If the score exceeds the maximum score, save the score (of course, you can also judge and save the maximum score at the end of the game or close the interface to reduce disk IO) and take a look at the effect picturePost the full program

# -*- 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

SCORE_MAP=(100,300,800,1600)

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: # 如果还没有方块,先产生一个
            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): # 用于绘制整个游戏窗口的版面
    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 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):
        block = self.hint_box.take_block()
        #block = create_block()
        block.move(5-2,-2) # 方块挪到中间 
        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 # 游戏失败
            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):
        return [(1,0),(1,1),(1,2),(1,3)] if self.shape_id==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):
        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):
        return [(2,0),(2,1),(1,1),(1,2)] if self.shape_id==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):
        return [(1,0),(1,1),(2,1),(2,2)] if self.shape_id==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):
        if self.shape_id==0: return [(1,0),(1,1),(1,2),(2,2)]
        elif self.shape_id==1: return [(0,1),(1,1),(2,1),(0,2)]
        elif self.shape_id==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):
        if self.shape_id==0: return [(1,0),(1,1),(1,2),(0,2)]
        elif self.shape_id==1: return [(0,1),(1,1),(2,1),(0,0)]
        elif self.shape_id==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):
        if self.shape_id==0: return [(0,1),(1,1),(2,1),(1,2)]
        elif self.shape_id==1: return [(1,0),(1,1),(1,2),(0,1)]
        elif self.shape_id==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,19)
    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*10
    main_panel_height=main_block_size*20
    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

    game_state = 1 # 游戏状态1.表示正常 2.表示失败
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                 pygame.quit()
                 exit()
            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)) # 将界面设置为灰色
        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() # 必须调用update才能看到绘图显示

        if game_state == 1 and pygame.time.get_ticks() >= ticks:
            ticks+=diff_ticks
            if main_panel.move_block()==9: game_state = 2 # 游戏结束

run()
Copy the code

Maybe some people will think that the lower right corner empty so big is to do what use? That area I am preparing to do battle display, here is almost the end of the basic chapter, the next chapter is preparing to write AI.