# preface Tetris has been popular for 30 years and has become the best-selling stand-alone game in history.

Why is it so magical and enduring?

Xiaobian summarized some reasons: it is very simple to start, but there are many skills to meet the desire to create order in chaos

Engineer Alexei says people don't realize that simplicity doesn't mean roughness.

Today, let's explore the classic Tetris ♦ Games take you into the world of games one by one! # Tetris

### Introduction to the game:

The basic rule of Tetris is to move, rotate and place all kinds of squares automatically output by the game to form a complete line

Or multiple lines and eliminate the score.

Direction key: move up, down, left and right. upper 👆: Change the shape of the square; lower 👇: Accelerate downward movement; Left 👈: Move left; right 👉: Move right.

### Environment configuration

Python3, Pycharm ,Pygame.

Third party library installation: pip   install pygame

### Effect display:

Start interface one by one Game interface one by one # Code demonstration:

1) Definition of square

```#The design of square shape, I originally made it into 4 ×  4. Because the maximum length and width are 4, we don't consider how to rotate when rotating, that is, replace one figure with another
#In fact, to achieve this function, you only need to fix the coordinates of the upper left corner```
```#Source base:#959755565#
#csdn Account No.: Gu muzia
#The official account: Python, Muzi.

import random
from collections import namedtuple

Point = namedtuple('Point', 'X Y')
Shape = namedtuple('Shape', 'X Y Width Height')
Block = namedtuple('Block', 'template start_pos end_pos name next')

# S-shaped square
S_BLOCK = [Block(['.OO',
'OO.',
'...'], Point(0, 0), Point(2, 1), 'S', 1),
Block(['O..',
'OO.',
'.O.'], Point(0, 0), Point(1, 2), 'S', 0)]
# Z-shaped square
Z_BLOCK = [Block(['OO.',
'.OO',
'...'], Point(0, 0), Point(2, 1), 'Z', 1),
Block(['.O.',
'OO.',
'O..'], Point(0, 0), Point(1, 2), 'Z', 0)]
# Type I block
I_BLOCK = [Block(['.O..',
'.O..',
'.O..',
'.O..'], Point(1, 0), Point(1, 3), 'I', 1),
Block(['....',
'....',
'OOOO',
'....'], Point(0, 2), Point(3, 2), 'I', 0)]
# O-shaped block
O_BLOCK = [Block(['OO',
'OO'], Point(0, 0), Point(1, 1), 'O', 0)]
# J-shaped block
J_BLOCK = [Block(['O..',
'OOO',
'...'], Point(0, 0), Point(2, 1), 'J', 1),
Block(['.OO',
'.O.',
'.O.'], Point(1, 0), Point(2, 2), 'J', 2),
Block(['...',
'OOO',
'..O'], Point(0, 1), Point(2, 2), 'J', 3),
Block(['.O.',
'.O.',
'OO.'], Point(0, 0), Point(1, 2), 'J', 0)]
# L-shaped block
L_BLOCK = [Block(['..O',
'OOO',
'...'], Point(0, 0), Point(2, 1), 'L', 1),
Block(['.O.',
'.O.',
'.OO'], Point(1, 0), Point(2, 2), 'L', 2),
Block(['...',
'OOO',
'O..'], Point(0, 1), Point(2, 2), 'L', 3),
Block(['OO.',
'.O.',
'.O.'], Point(0, 0), Point(1, 2), 'L', 0)]
# T-shaped block
T_BLOCK = [Block(['.O.',
'OOO',
'...'], Point(0, 0), Point(2, 1), 'T', 1),
Block(['.O.',
'.OO',
'.O.'], Point(1, 0), Point(2, 2), 'T', 2),
Block(['...',
'OOO',
'.O.'], Point(0, 1), Point(2, 2), 'T', 3),
Block(['.O.',
'OO.',
'.O.'], Point(0, 0), Point(1, 2), 'T', 0)]

BLOCKS = {'O': O_BLOCK,
'I': I_BLOCK,
'Z': Z_BLOCK,
'T': T_BLOCK,
'L': L_BLOCK,
'S': S_BLOCK,
'J': J_BLOCK}

def get_block():
block_name = random.choice('OIZTLSJ')
b = BLOCKS[block_name]
idx = random.randint(0, len(b) - 1)
return b[idx]

def get_next_block(block):
b = BLOCKS[block.name]
return b[block.next]
```

2) Main program

```import sys
import pygame
from pygame.locals import *
import blocks

SIZE = 30  # Size of each small square
BLOCK_HEIGHT = 25  # Height of game area
BLOCK_WIDTH = 10   # Game area width
BORDER_WIDTH = 4   # Game area border width
BORDER_COLOR = (40, 40, 200)  # Game area border color
SCREEN_WIDTH = SIZE * (BLOCK_WIDTH + 5)  # Width of game screen
SCREEN_HEIGHT = SIZE * BLOCK_HEIGHT      # Height of game screen
BG_COLOR = (40, 40, 60)  # Background color
BLOCK_COLOR = (20, 128, 200)  #
BLACK = (0, 0, 0)
RED = (200, 30, 30)      # Font color of GAME OVER

def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):
imgText = font.render(text, True, fcolor)
screen.blit(imgText, (x, y))

def main():
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Tetris')

font1 = pygame.font.SysFont('SimHei', 24)  # Bold 24
font2 = pygame.font.Font(None, 72)  # Font for GAME OVER
font_pos_x = BLOCK_WIDTH * SIZE + BORDER_WIDTH + 10  # The X coordinate of the font position in the information display area on the right
gameover_size = font2.size('GAME OVER')
font1_height = int(font1.size('score'))

cur_block = None   # Current drop box
next_block = None  # Next box
cur_pos_x, cur_pos_y = 0, 0

game_area = None    # Entire game area
game_over = True
start = False       # Whether to start, when start = True, game_ GAME OVER is displayed only when over = true
score = 0           # score
orispeed = 0.5      # Original speed
speed = orispeed    # Current speed
pause = False       # suspend
last_drop_time = None   # Last fall time
last_press_time = None  # Last press time

def _dock():
nonlocal cur_block, next_block, game_area, cur_pos_x, cur_pos_y, game_over, score, speed
for _i in range(cur_block.start_pos.Y, cur_block.end_pos.Y + 1):
for _j in range(cur_block.start_pos.X, cur_block.end_pos.X + 1):
if cur_block.template[_i][_j] != '.':
game_area[cur_pos_y + _i][cur_pos_x + _j] = '0'
if cur_pos_y + cur_block.start_pos.Y <= 0:
game_over = True
else:
# Calculation elimination
remove_idxs = []
for _i in range(cur_block.start_pos.Y, cur_block.end_pos.Y + 1):
if all(_x == '0' for _x in game_area[cur_pos_y + _i]):
remove_idxs.append(cur_pos_y + _i)
if remove_idxs:
# Calculate score
remove_count = len(remove_idxs)
if remove_count == 1:
score += 100
elif remove_count == 2:
score += 300
elif remove_count == 3:
score += 700
elif remove_count == 4:
score += 1500
speed = orispeed - 0.03 * (score // 10000)
# eliminate
_i = _j = remove_idxs[-1]
while _i >= 0:
while _j in remove_idxs:
_j -= 1
if _j < 0:
game_area[_i] = ['.'] * BLOCK_WIDTH
else:
game_area[_i] = game_area[_j]
_i -= 1
_j -= 1
cur_block = next_block
next_block = blocks.get_block()
cur_pos_x, cur_pos_y = (BLOCK_WIDTH - cur_block.end_pos.X - 1) // 2, -1 - cur_block.end_pos.Y

def _judge(pos_x, pos_y, block):
nonlocal game_area
for _i in range(block.start_pos.Y, block.end_pos.Y + 1):
if pos_y + block.end_pos.Y >= BLOCK_HEIGHT:
return False
for _j in range(block.start_pos.X, block.end_pos.X + 1):
if pos_y + _i >= 0 and block.template[_i][_j] != '.' and game_area[pos_y + _i][pos_x + _j] != '.':
return False
return True

while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_RETURN:
if game_over:
start = True
game_over = False
score = 0
last_drop_time = time.time()
last_press_time = time.time()
game_area = [['.'] * BLOCK_WIDTH for _ in range(BLOCK_HEIGHT)]
cur_block = blocks.get_block()
next_block = blocks.get_block()
cur_pos_x, cur_pos_y = (BLOCK_WIDTH - cur_block.end_pos.X - 1) // 2, -1 - cur_block.end_pos.Y
elif event.key == K_SPACE:
if not game_over:
pause = not pause
elif event.key in (K_w, K_UP):
# rotate
# In fact, I don't remember very clearly, for example
# .0.
# .00
# ..0
# Whether this can rotate when it is on the far right side. I have tried the Tetris on the Internet. It can't rotate. Here we do it according to whether it can't rotate
# We made a lot of blanks in the shape design, so we only need to specify that the whole shape, including the blank part, can be rotated when it is all in the game area
if 0 <= cur_pos_x <= BLOCK_WIDTH - len(cur_block.template):
_next_block = blocks.get_next_block(cur_block)
if _judge(cur_pos_x, cur_pos_y, _next_block):
cur_block = _next_block

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if not game_over and not pause:
if time.time() - last_press_time > 0.1:
last_press_time = time.time()
if cur_pos_x > - cur_block.start_pos.X:
if _judge(cur_pos_x - 1, cur_pos_y, cur_block):
cur_pos_x -= 1
if event.key == pygame.K_RIGHT:
if not game_over and not pause:
if time.time() - last_press_time > 0.1:
last_press_time = time.time()
# The right border cannot be removed
if cur_pos_x + cur_block.end_pos.X + 1 < BLOCK_WIDTH:
if _judge(cur_pos_x + 1, cur_pos_y, cur_block):
cur_pos_x += 1
if event.key == pygame.K_DOWN:
if not game_over and not pause:
if time.time() - last_press_time > 0.1:
last_press_time = time.time()
if not _judge(cur_pos_x, cur_pos_y + 1, cur_block):
_dock()
else:
last_drop_time = time.time()
cur_pos_y += 1

_draw_background(screen)

_draw_game_area(screen, game_area)

_draw_gridlines(screen)

_draw_info(screen, font1, font_pos_x, font1_height, score)
# Draw the next box in the display message
_draw_block(screen, next_block, font_pos_x, 30 + (font1_height + 6) * 5, 0, 0)

if not game_over:
cur_drop_time = time.time()
if cur_drop_time - last_drop_time > speed:
if not pause:
# We should not judge whether the box is falling or not. When we play Tetris, the moment the box falls to the bottom can move left and right
if not _judge(cur_pos_x, cur_pos_y + 1, cur_block):
_dock()
else:
last_drop_time = cur_drop_time
cur_pos_y += 1
else:
if start:
print_text(screen, font2,
(SCREEN_WIDTH - gameover_size) // 2, (SCREEN_HEIGHT - gameover_size) // 2,
'GAME OVER', RED)

# Draw the current drop box
_draw_block(screen, cur_block, 0, 0, cur_pos_x, cur_pos_y)

pygame.display.flip()

# Painting background
def _draw_background(screen):
# Fill background color
screen.fill(BG_COLOR)
# Draw game area separator
pygame.draw.line(screen, BORDER_COLOR,
(SIZE * BLOCK_WIDTH + BORDER_WIDTH // 2, 0),
(SIZE * BLOCK_WIDTH + BORDER_WIDTH // 2, SCREEN_HEIGHT), BORDER_WIDTH)

# Draw grid lines
def _draw_gridlines(screen):
# Draw grid lines and vertical lines
for x in range(BLOCK_WIDTH):
pygame.draw.line(screen, BLACK, (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1)
# Draw grid lines and horizontal lines
for y in range(BLOCK_HEIGHT):
pygame.draw.line(screen, BLACK, (0, y * SIZE), (BLOCK_WIDTH * SIZE, y * SIZE), 1)

# Draw the box that has fallen
def _draw_game_area(screen, game_area):
if game_area:
for i, row in enumerate(game_area):
for j, cell in enumerate(row):
if cell != '.':
pygame.draw.rect(screen, BLOCK_COLOR, (j * SIZE, i * SIZE, SIZE, SIZE), 0)

# Draw a single square
def _draw_block(screen, block, offset_x, offset_y, pos_x, pos_y):
if block:
for i in range(block.start_pos.Y, block.end_pos.Y + 1):
for j in range(block.start_pos.X, block.end_pos.X + 1):
if block.template[i][j] != '.':
pygame.draw.rect(screen, BLOCK_COLOR,
(offset_x + (pos_x + j) * SIZE, offset_y + (pos_y + i) * SIZE, SIZE, SIZE), 0)

# Draw score and other information
def _draw_info(screen, font, pos_x, font_height, score):
print_text(screen, font, pos_x, 10, f'score: ')
print_text(screen, font, pos_x, 10 + font_height + 6, f'{score}')
print_text(screen, font, pos_x, 20 + (font_height + 6) * 2, f'speed: ')
print_text(screen, font, pos_x, 20 + (font_height + 6) * 3, f'{score // 10000}')
print_text(screen, font, pos_x, 30 + (font_height + 6) * 4, f'next:')```

# ending

Bingo_ This simple version of plant vs. zombie is completed ~ the complete project source code material is packaged and waiting for you to get it 👇

## Free source code project:

Source base: click blue font or private letter Xiaobian 06 to get it for free! In the past, the source code was also on the ground~

I want to share with you the learning of my official account. Ha: Python, look at Muzi! Posted by GregL83 on Mon, 22 Nov 2021 05:57:28 -0800