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')[1]) 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[0]): _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[0]) // 2, (SCREEN_HEIGHT - gameover_size[1]) // 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:
I want to share with you the learning of my official account. Ha: Python, look at Muzi!