您尚未登录。

楼主 #1 2020-01-28 10:18:46

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Python pygame 俄罗斯方块(tetris)游戏

离线

楼主 #2 2020-01-28 10:20:30

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: Python pygame 俄罗斯方块(tetris)游戏

classes.py

#########################################
# File name: Classes.py                 #
# Author: David Gurevich                #
# Course: ICS3U                         #
# Instructor: D. Mavrodin               #
# --------------------------------------#
# Last Modified: 11/12/2017 @ 20:52     #
#########################################
import pygame

BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
ORANGE = (255, 127, 0)
CYAN = (0, 183, 235)
MAGENTA = (255, 0, 255)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
TRANS_WHITE = (255, 255, 255, 50)
COLOURS = [BLACK, RED, GREEN, BLUE, ORANGE, CYAN, MAGENTA, YELLOW, WHITE]
CLR_names = ['black', 'red', 'green', 'blue', 'orange', 'cyan', 'magenta', 'yellow', 'white']
figures = [None, 'Z', 'S', 'J', 'L', 'I', 'T', 'O', None]


class Block(object):
    """ A square - basic building block
        data:               behaviour:
            col - column        move left/right/up/down
            row - row           drawself.blocks
            clr - colour
    """

    def __init__(self, col=1, row=1, clr=1):
        self.col = col
        self.row = row
        self.clr = clr

    def __str__(self):
        return '(' + str(self.col) + ',' + str(self.row) + ') ' + CLR_names[self.clr]

    def __eq__(self, other):
        if self.col == other.col and self.row == other.row:
            return True

    def draw(self, surface, gridsize=20, shadow=False):
        x = self.col * gridsize
        y = self.row * gridsize
        CLR = COLOURS[self.clr]
        if not shadow:
            pygame.draw.rect(surface, CLR, (x, y, gridsize - 2, gridsize - 2), 0)
            pygame.draw.rect(surface, WHITE, (x, y, gridsize, gridsize), 2)

        else:
            pygame.draw.rect(surface, TRANS_WHITE, (x, y, gridsize, gridsize), 3)

    def move_down(self):
        self.row = self.row + 1

    # --------------------------------------- #


class Cluster(object):
    """ Collection of blocks
        data:
            col - column where the anchor block is located
            row - row where the anchor block is located
            blocksNo - number of blocks
    """

    def __init__(self, col=1, row=1, blocksNo=1):
        self.col = col
        self.row = row
        self.clr = 0
        self.blocks = [Block()] * blocksNo
        self._colOffsets = [0] * blocksNo
        self._rowOffsets = [0] * blocksNo

    def _update(self):
        for i in range(len(self.blocks)):
            blockCOL = self.col + self._colOffsets[i]
            blockROW = self.row + self._rowOffsets[i]
            blockCLR = self.clr
            self.blocks[i] = Block(blockCOL, blockROW, blockCLR)

    def draw(self, surface, gridsize, shadow=False):
        for block in self.blocks:
            block.draw(surface, gridsize, shadow)

    def collides(self, other):
        """ Compare each block from a cluster to all blocks from another cluster.
            Return True only if there is a location conflict.
        """
        for block in self.blocks:
            for obstacle in other.blocks:
                if block == obstacle:
                    return True
        return False

    def append(self, other):
        """ Append all blocks from another cluster to this one.
        """
        for block in other.blocks:
            self.blocks.append(block)


# -------------------------------------- #


class Obstacles(Cluster):
    """ Collection of tetrominoe blocks on the playing field, left from previous shapes.
        
    """

    def __init__(self, col=0, row=0, blocksNo=0):
        Cluster.__init__(self, col, row, blocksNo)

    def show(self):
        print("\nObstacle: ")
        for block in self.blocks:
            print((block)._colOffsets)

    def findFullRows(self, top, bottom, columns):
        fullRows = []
        rows = []
        for block in self.blocks:
            rows.append(block.row)

        for row in range(top, bottom):
            if rows.count(row) == columns:
                fullRows.append(row)
        return fullRows

    def removeFullRows(self, fullRows):
        for row in fullRows:
            for i in reversed(range(len(self.blocks))):
                if self.blocks[i].row == row:
                    self.blocks.pop(i)
                elif self.blocks[i].row < row:
                    self.blocks[i].move_down()


# ---------------------------------------#
class Shape(Cluster):
    """ A tetrominoe in one of the shapes: Z,S,J,L,I,T,O; consists of 4 x Block() objects
        data:               behaviour:
            col - column        move left/right/up/down
            row - row           draw
            clr - colour        rotate
                * figure/shape is defined by the colour
            rot - rotation             
    """

    def __init__(self, col=1, row=1, clr=1, rot=1, shadow=False):
        Cluster.__init__(self, col, row, 4)
        self.clr = clr
        self.shadow = shadow
        self._rot = rot
        self._colOffsets = [-1, 0, 0, 1]
        self._rowOffsets = [-1, -1, 0, 0]
        self._rotate()

    def __str__(self):
        return figures[self.clr] + ' (' + str(self.col) + ',' + str(self.row) + ') ' + CLR_names[self.clr]

    def _rotate(self):
        """ offsets are assigned starting from the farthest (most distant) block in reference to the anchor block """
        if self.clr == 1:
            _colOffsets = [[-1, -1, 0, 0], [-1, 0, 0, 1], [1, 1, 0, 0], [1, 0, 0, -1]]
            _rowOffsets = [[1, 0, 0, -1], [-1, -1, 0, 0], [-1, 0, 0, 1], [1, 1, 0, 0]]
        elif self.clr == 2:
            _colOffsets = [[-1, -1, 0, 0], [1, 0, 0, -1], [1, 1, 0, 0], [-1, 0, 0, 1]]
            _rowOffsets = [[-1, 0, 0, 1], [-1, -1, 0, 0], [1, 0, 0, -1], [1, 1, 0, 0]]
        elif self.clr == 3:
            _colOffsets = [[-1, 0, 0, 0], [-1, -1, 0, 1], [1, 0, 0, 0], [1, 1, 0, -1]]
            _rowOffsets = [[1, 1, 0, -1], [-1, 0, 0, 0], [-1, -1, 0, 1], [1, 0, 0, 0]]
        elif self.clr == 4:
            _colOffsets = [[-1, 0, 0, 0], [1, 1, 0, -1], [1, 0, 0, 0], [-1, -1, 0, 1]]
            _rowOffsets = [[-1, -1, 0, 1], [-1, 0, 0, 0], [1, 1, 0, -1], [1, 0, 0, 0]]
        elif self.clr == 5:
            _colOffsets = [[0, 0, 0, 0], [2, 1, 0, -1], [0, 0, 0, 0], [-2, -1, 0, 1]]
            _rowOffsets = [[-2, -1, 0, 1], [0, 0, 0, 0], [2, 1, 0, -1], [0, 0, 0, 0]]
        elif self.clr == 6:
            _colOffsets = [[0, -1, 0, 0], [-1, 0, 0, 1], [0, 1, 0, 0], [1, 0, 0, -1]]  #
            _rowOffsets = [[1, 0, 0, -1], [0, -1, 0, 0], [-1, 0, 0, 1], [0, 1, 0, 0]]  #
        elif self.clr == 7:
            _colOffsets = [[-1, -1, 0, 0], [-1, -1, 0, 0], [-1, -1, 0, 0], [-1, -1, 0, 0]]
            _rowOffsets = [[0, -1, 0, -1], [0, -1, 0, -1], [0, -1, 0, -1], [0, -1, 0, -1]]
        self._colOffsets = _colOffsets[self._rot]
        self._rowOffsets = _rowOffsets[self._rot]
        self._update()

    def move_left(self):
        self.col = self.col - 1
        self._update()

    def move_right(self):
        self.col = self.col + 1
        self._update()

    def move_down(self):
        self.row = self.row + 1
        self._update()

    def move_up(self):
        self.row = self.row - 1
        self._update()

    def rotateClkwise(self):
        self._rot = (self._rot + 1) % 4

    def rotateCntclkwise(self):
        self._rot = (self._rot - 1) % 4


# --------------------------------------- #


class Floor(Cluster):
    """ Horizontal line of blocks
        data:
            col - column where the anchor block is located
            row - row where the anchor block is located
            blocksNo - number of blocks 
    """

    def __init__(self, col=1, row=1, blocksNo=1):
        Cluster.__init__(self, col, row, blocksNo)
        for i in range(blocksNo):
            self._colOffsets[i] = i
        self._update()


# --------------------------------------- #


class Wall(Cluster):
    """ Vertical line of blocks
        data:
            col - column where the anchor block is located
            row - row where the anchor block is located
            blocksNo - number of blocks 
    """

    def __init__(self, col=1, row=1, blocksNo=1):
        Cluster.__init__(self, col, row, blocksNo)
        for i in range(blocksNo):
            self._rowOffsets[i] = i
        self._update()

Tetris.py

#########################################
# File name: Tetris.py                  #
# Author: David Gurevich                #
# Course: ICS3U                         #
# Instructor: D. Mavrodin               #
# --------------------------------------#
# Last Modified: 11/12/2017 @ 21:02     #
#########################################

import sys
from random import randint, choice

from Classes import *

pygame.init()

HEIGHT = 600
WIDTH = 575
GRIDSIZE = HEIGHT // 24
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris - David Gurevich")

LVL_1, LVL_2, LVL_3, LVL_4, LVL_5, LVL_6, LVL_7, LVL_8, LVL_9 = 45, 20, 10, 7, 5, 4, 3, 2, 1

LEVELS = [LVL_1, LVL_2, LVL_3, LVL_4, LVL_5, LVL_6, LVL_7, LVL_8, LVL_9]

SCORE = 0

# ---------------------------------------#

COLUMNS = 14
ROWS = 24
LEFT = 0
RIGHT = LEFT + COLUMNS
MIDDLE = LEFT + COLUMNS // 2
TOP = 1
FLOOR = TOP + ROWS

# -------------IMAGES and MUSIC--------------------#

pygame.mixer.set_num_channels(6)

# Channel 0: Background Music
# Channel 1: Block Rotation
# Channel 2: Force Hit
# Channel 3: Line Remove
# Channel 4: Slow Hit
# Channel 5: Tetris Remove


# ---- BACKGROUND IMAGES ---- #
tetris_img = pygame.image.load('images/Tetris.jpg')
grid_img = pygame.image.load('images/gridbg.jpg')

intro_screen = pygame.image.load('images/Intro.jpg')
outro_screen = pygame.image.load('images/Outro.jpg')
# --------------------------- #

# ---- SOUND EFFECTS ---- #
block_rotate = pygame.mixer.Sound('Sounds/block-rotate.ogg')
force_hit = pygame.mixer.Sound('Sounds/force-hit.ogg')
line_remove = pygame.mixer.Sound('Sounds/line-remove.ogg')
slow_hit = pygame.mixer.Sound('Sounds/slow-hit.ogg')
tetris_remove = pygame.mixer.Sound('Sounds/tetris-remove.ogg')
# ----------------------- #

# ---- BACKGROUND MUSIC ---- #
kalinka = pygame.mixer.Sound('Music/kalinka.ogg')
katyusha = pygame.mixer.Sound('Music/katyusha.ogg')
korobushka = pygame.mixer.Sound('Music/korobushka.ogg')
smuglianka = pygame.mixer.Sound('Music/smuglianka.ogg')

bg_music = choice([kalinka, katyusha, korobushka, smuglianka])
# -------------------------- #

# ---- BLOCK PREVIEWS ---- #
cube_block = pygame.image.load('Previews/cube-block.png').convert_alpha()
i_block = pygame.image.load('Previews/i-block.png').convert_alpha()
j_block = pygame.image.load('Previews/j-block.png').convert_alpha()
L_block = pygame.image.load('Previews/L-block.png').convert_alpha()
r_s_block = pygame.image.load('Previews/r-s-block.png').convert_alpha()
s_block = pygame.image.load('Previews/s-block.png').convert_alpha()
t_block = pygame.image.load('Previews/t-block.png').convert_alpha()

block_img_lst = [r_s_block, s_block, L_block, j_block, i_block, t_block, cube_block]  # MUST MATCH LIST IN CLASSES.PY
# ------------------------ #

# ---- FAVICON ---- #
favicon = pygame.image.load('images/favicon.png').convert_alpha()
pygame.display.set_icon(favicon)
# ----------------- #

# ---- FONTS ---- #
pygame.font.init()
my_font = pygame.font.SysFont('Arial Black', 21)


# --------------- #

# ------------- FUNCTIONS -------------------- #


def draw_grid():
    """ Draw horisontal and vertical lines on the entire game window.
        Space between the lines is GRIDSIZE.
    """
    for i in range(15):
        pygame.draw.line(screen, BLACK, (i * GRIDSIZE, 0), (i * GRIDSIZE, HEIGHT), 1)

    for i in range(24):
        pygame.draw.line(screen, BLACK, (0, i * GRIDSIZE), (GRIDSIZE * 24, i * GRIDSIZE), 1)


def redraw_screen():
    score_text = my_font.render(str(SCORE), True, WHITE)
    timer_text = my_font.render(str(round(pygame.time.get_ticks() / 1000, 2)), True, WHITE)
    level_text = my_font.render(str(level + 1), True, WHITE)

    screen.blit(grid_img, (0, 0))
    draw_grid()
    screen.blit(tetris_img, (GRIDSIZE * 14, 0))
    shape.draw(screen, GRIDSIZE)
    shadow.draw(screen, GRIDSIZE, True)
    obstacles.draw(screen, GRIDSIZE)

    # BLIT FONTS
    screen.blit(score_text, ((GRIDSIZE * 14) + 90, 460))
    screen.blit(timer_text, ((GRIDSIZE * 14) + 85, 538))
    screen.blit(level_text, ((GRIDSIZE * 14) + 100, 380))

    # BLIT NEXT SHAPE
    screen.blit(block_img_lst[nextShapeNo - 1], ((GRIDSIZE * 14) + 72, 240))

    pygame.display.flip()


def drop(my_shape):
    flow = False
    while not flow:
        my_shape.move_down()
        if my_shape.collides(floor) or my_shape.collides(obstacles):
            my_shape.move_up()
            flow = True

    if not my_shape.shadow:
        pygame.mixer.Channel(2).play(force_hit)


# -------------------------------------------- #

# ------------- MAIN PROGRAM -------------------- #

counter = 0

shapeNo = randint(1, 7)
nextShapeNo = randint(1, 7)

shape = Shape(MIDDLE, TOP, shapeNo)
floor = Floor(LEFT, ROWS, COLUMNS)
leftWall = Wall(LEFT - 1, 0, ROWS)
rightWall = Wall(RIGHT, 0, ROWS)
obstacles = Obstacles(LEFT, FLOOR)
inPlay = False
hasPlayed = False
level = 0

PREV_TETRIS = False

pygame.mixer.Channel(0).play(bg_music, -1)

# ---- INTRO SCREEN ---- #
while not inPlay and not hasPlayed:
    screen.blit(intro_screen, (0, 0))
    pygame.display.flip()

    screen.blit(intro_screen, (0, 0))
    pygame.display.flip()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit(0)

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                inPlay = True
                hasPlayed = True

# ---------------------- #

while inPlay:

    shadow = Shape(shape.col, shape.row, shape.clr, shape._rot, True)
    drop(shadow)

    if counter % LEVELS[level] == 0:
        shape.move_down()
        if shape.collides(floor) or shape.collides(obstacles):
            shape.move_up()
            obstacles.append(shape)
            pygame.mixer.Channel(5).play(slow_hit)
            fullRows = obstacles.findFullRows(TOP, FLOOR, COLUMNS)

            # --------- CHECK --------- #
            if 4 > len(fullRows) > 0:
                SCORE += 100 * len(fullRows)
                pygame.mixer.Channel(3).play(line_remove)
            elif len(fullRows) >= 4:
                SCORE += 800 + (100 * (len(fullRows) - 4))
                pygame.mixer.Channel(4).play(tetris_remove)
                PREV_TETRIS = True
            elif len(fullRows) >= 4 and PREV_TETRIS:
                SCORE += 1200 + (100 * (len(fullRows) - 4))
                PREV_TETRIS = True
                pygame.mixer.Channel(4).play(tetris_remove)
            # ------------------------ #

            obstacles.removeFullRows(fullRows)
            shapeNo = nextShapeNo
            nextShapeNo = randint(1, 7)
            if not shape.row <= 1:
                shape = Shape(MIDDLE, TOP, shapeNo)
            else:
                inPlay = False

    for event in pygame.event.get():

        if event.type == pygame.QUIT:
            inPlay = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                shape.rotateClkwise()
                shape._rotate()
                if shape.collides(leftWall) or shape.collides(rightWall) or shape.collides(floor) or shape.collides(
                        obstacles):
                    shape.rotateCntclkwise()
                    shape._rotate()
                else:
                    pygame.mixer.Channel(1).play(block_rotate)

            if event.key == pygame.K_LEFT:
                shape.move_left()
                if shape.collides(leftWall):
                    shape.move_right()
                elif shape.collides(obstacles):
                    shape.move_right()

            if event.key == pygame.K_RIGHT:
                shape.move_right()
                if shape.collides(rightWall):
                    shape.move_left()
                elif shape.collides(obstacles):
                    shape.move_left()

            if event.key == pygame.K_DOWN:
                shape.move_down()
                if shape.collides(floor) or shape.collides(obstacles):
                    shape.move_up()
                    obstacles.append(shape)
                    fullRows = obstacles.findFullRows(TOP, FLOOR, COLUMNS)
                    # --------- CHECK --------- #
                    if 4 > len(fullRows) > 0:
                        SCORE += 100 * len(fullRows)
                        pygame.mixer.Channel(3).play(line_remove)
                    elif len(fullRows) >= 4:
                        SCORE += 800 + (100 * (len(fullRows) - 4))
                        pygame.mixer.Channel(4).play(tetris_remove)
                        PREV_TETRIS = True
                    elif len(fullRows) >= 4 and PREV_TETRIS:
                        SCORE += 1200 + (100 * (len(fullRows) - 4))
                        PREV_TETRIS = True
                        pygame.mixer.Channel(4).play(tetris_remove)
                    # ------------------------- #

                    obstacles.removeFullRows(fullRows)
                    shapeNo = nextShapeNo
                    nextShapeNo = randint(1, 7)
                    shape = Shape(MIDDLE, TOP, shapeNo)
                    shape = Shape(MIDDLE, TOP, shapeNo)

            if event.key == pygame.K_SPACE:
                drop(shape)
                obstacles.append(shape)
                shapeNo = nextShapeNo
                nextShapeNo = randint(1, 7)
                shape = Shape(MIDDLE, TOP, shapeNo)
                fullRows = obstacles.findFullRows(TOP, FLOOR, COLUMNS)
                # --------- CHECK --------- #
                if 4 > len(fullRows) > 0:
                    SCORE += 100 * len(fullRows)
                    pygame.mixer.Channel(3).play(line_remove)
                elif len(fullRows) >= 4:
                    SCORE += 800 + (100 * (len(fullRows) - 4))
                    pygame.mixer.Channel(4).play(tetris_remove)
                    PREV_TETRIS = True
                elif len(fullRows) >= 4 and PREV_TETRIS:
                    SCORE += 1200 + (100 * (len(fullRows) - 4))
                    PREV_TETRIS = True
                    pygame.mixer.Channel(4).play(tetris_remove)
                # ------------------------- #
                obstacles.removeFullRows(fullRows)

    if 1000 >= SCORE >= 500:
        level = 1
    elif 1500 >= SCORE > 1000:
        level = 2
    elif 2000 >= SCORE > 1500:
        level = 3
    elif 2250 >= SCORE > 2000:
        level = 4
    elif 2500 >= SCORE > 2250:
        level = 5
    elif 2750 >= SCORE > 2500:
        level = 6
    elif 3000 >= SCORE > 2750:
        level = 7
    elif 3250 >= SCORE > 3000:
        level = 8
    elif SCORE >= 3250:
        level = 9

    PREV_TETRIS = False
    counter += 1
    redraw_screen()

while not inPlay and hasPlayed:
    start_timer = pygame.time.get_ticks()
    screen.blit(outro_screen, (0, 0))
    pygame.display.flip()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit(0)

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                pygame.quit()
                sys.exit(0)

        if pygame.time.get_ticks() - start_timer >= 2000:
            pygame.quit()
            sys.exit(0)

# ----------------------------------------------- #

pygame.quit()
sys.exit("Exited Final")

离线

楼主 #3 2020-01-28 10:27:00

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: Python pygame 俄罗斯方块(tetris)游戏

2020-01-28_102350.png

打开工程

离线

楼主 #4 2020-01-28 10:27:38

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: Python pygame 俄罗斯方块(tetris)游戏

2020-01-28_102535.png

右击运行.

离线

楼主 #5 2020-01-28 10:28:16

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: Python pygame 俄罗斯方块(tetris)游戏

2020-01-28_102625.png

玩一把游戏.

离线

楼主 #6 2020-01-28 10:33:32

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: Python pygame 俄罗斯方块(tetris)游戏

上面的 python 代码编辑器是 pycharm,

大名鼎鼎的 捷克 jetBrains 公司出品的.

离线

楼主 #7 2020-02-19 13:47:07

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: Python pygame 俄罗斯方块(tetris)游戏

都忘记这个帖子是我自己发的了。

离线

#8 2022-09-20 17:39:57

lyon1998
Moderator
注册时间: 2021-12-01
已发帖子: 108
积分: 55

Re: Python pygame 俄罗斯方块(tetris)游戏

试试在 MCU 跑 Python?

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn