Skip to content

PyGame-俄罗斯方块

难度:⭐⭐⭐⭐

1. 题目描述

题目描述

使用 Pygame 库实现俄罗斯方块

2. 关键点

  • pygame 的使用;运行 pip install pygame 进行安装
  • 方块形状的定义

3. 代码实现

点击显示代码
python
import pygame
import random

# 方块颜色
colors = [
    (0, 240, 240),
    (240, 0, 0),
    (0, 240, 0),
    (0, 0, 240),
    (240, 160, 0),
    (160, 0, 240),
    (240, 240, 0),
]


class Figure:
    """方块类"""
    x = 0
    y = 0

    # 方块形状
    figures = [
        [[1, 5, 9, 13], [4, 5, 6, 7]],
        [[4, 5, 9, 10], [2, 6, 5, 9]],
        [[6, 7, 9, 10], [1, 5, 6, 10]],
        [[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]],
        [[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]],
        [[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]],
        [[1, 2, 5, 6]],
    ]

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.type = random.randint(0, len(self.figures) - 1)
        self.color = random.randint(1, len(colors) - 1)
        self.rotation = 0

    def image(self):
        return self.figures[self.type][self.rotation]

    def rotate(self):
        self.rotation = (self.rotation + 1) % len(self.figures[self.type])


class Tetris:
    level = 2
    x = y = 0
    zoom = 30  # 方块大小
    figure = None

    def __init__(self, height, width):
        self.height = height
        self.width = width
        self.field = []
        self.state = "start"
        for i in range(height):
            new_line = []
            for j in range(width):
                new_line.append(0)
            self.field.append(new_line)

    def new_figure(self):
        self.figure = Figure(3, 0)

    def intersects(self):
        """判断是否到底"""
        intersection = False
        for i in range(4):
            for j in range(4):
                if i * 4 + j in self.figure.image():
                    if i + self.figure.y > self.height - 1 or \
                            j + self.figure.x > self.width - 1 or \
                            j + self.figure.x < 0 or \
                            self.field[i + self.figure.y][
                                    j + self.figure.x] > 0:
                        intersection = True
        return intersection

    def break_lines(self):
        """消除"""
        lines = 0
        for i in range(1, self.height):
            zeros = 0
            for j in range(self.width):
                if self.field[i][j] == 0:
                    zeros += 1
            if zeros == 0:
                lines += 1
                for i1 in range(i, 1, -1):
                    for j in range(self.width):
                        self.field[i1][j] = self.field[i1 - 1][j]

    def go_space(self):
        """一键落下"""
        while not self.intersects():
            self.figure.y += 1
        self.figure.y -= 1
        self.freeze()

    def go_down(self):
        self.figure.y += 1
        if self.intersects():
            self.figure.y -= 1
            self.freeze()

    def freeze(self):
        """触底之后留住方块"""
        for i in range(4):
            for j in range(4):
                if i * 4 + j in self.figure.image():
                    self.field[i + self.figure.y][j + self.figure.x] \
                            = self.figure.color
        self.break_lines()
        self.new_figure()
        if self.intersects():
            self.state = "gameover"

    def go_side(self, dx):
        old_x = self.figure.x
        self.figure.x += dx
        if self.intersects():
            self.figure.x = old_x

    def rotate(self):
        old_rotation = self.figure.rotation
        self.figure.rotate()
        if self.intersects():
            self.figure.rotation = old_rotation


pygame.init()

screen_width = 12
screen_height = 20
screen_color = (0, 0, 0)
size = (
    screen_width*Tetris.zoom+Tetris.x*2,
    screen_height*Tetris.zoom+Tetris.y*2
)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("俄罗斯方块")

# Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()
fps = 25
game = Tetris(screen_height, screen_width)
counter = 0

pressing_down = False

while not done:
    if game.figure is None:
        game.new_figure()
    counter += 1
    if counter > 100000:
        counter = 0

    if counter % (fps // game.level // 2) == 0 or pressing_down:
        if game.state == "start":
            game.go_down()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                game.rotate()
            if event.key == pygame.K_DOWN:
                pressing_down = True
            if event.key == pygame.K_LEFT:
                game.go_side(-1)
            if event.key == pygame.K_RIGHT:
                game.go_side(1)
            if event.key == pygame.K_SPACE:
                game.go_space()
            if event.key == pygame.K_ESCAPE:
                game.__init__(screen_height, screen_width)

    if event.type == pygame.KEYUP:
        if event.key == pygame.K_DOWN:
            pressing_down = False

    screen.fill(screen_color)

    for i in range(game.height):
        for j in range(game.width):
            if game.field[i][j] > 0:
                pygame.draw.rect(screen, colors[game.field[i][j]], [
                    game.x + game.zoom * j + 1,
                    game.y + game.zoom * i + 1, game.zoom - 2, game.zoom - 1])

    if game.figure is not None:
        for i in range(4):
            for j in range(4):
                p = i * 4 + j
                if p in game.figure.image():
                    pygame.draw.rect(screen, colors[game.figure.color], [
                        game.x + game.zoom * (j + game.figure.x) + 1,
                        game.y + game.zoom * (i + game.figure.y) + 1,
                        game.zoom - 2, game.zoom - 2])

    if game.state == "gameover":
        done = True
    pygame.display.flip()
    clock.tick(fps)

pygame.quit()

4. 运行示例

5. 进阶思考

  1. 按住左右键的时候可连续移动
  2. 记录分数