A few days ago wrote a 2048 program, is based on Python3 + Pygame implementation, for beginners to learn Python should be a good practice project, now will share the source code to everyone, add a clear note, I believe you can see it is very clear

The running effect is as follows:

The complete code is as follows, if you need to download the material (such as images, fonts, can go to www.itprojects.cn/detail.html… To download)

import random
import sys
from collections import可迭代from functools import reduce

import pygame

# Screen size
WIDTH, HEIGHT = (650.370)
# Background color
BG_COLOR = '#92877d'
# Data needed for the board
MARGIN_SIZE = 10  # Spacing size
BLOCK_SIZE = 80  # Chess position size


def draw_tips(screen) :
    """ Display prompt information """
    # display "score:"
    tips_img = pygame.image.load("resources/images/tips.png")
    screen.blit(tips_img, (375.200))


def get_score(chess_nums_temp) :
    """ Calculates the total score of the current board. ""

    def sum_all(x, y) :
        if isinstance(x, Iterable):
            return sum(x) + sum(y)
        return x + sum(y)

    return reduce(sum_all, chess_nums_temp)


def draw_score(screen, score) :
    """ Show the score """
    # display number
    font_size_big = 60
    font_color = (0.255.255)
    font_big = pygame.font.Font("resources/font/Gabriola.ttf", font_size_big)
    score = font_big.render(str(score), True, font_color)
    screen.blit(score, (470.25))
    # display "score:"
    score_img = pygame.image.load("resources/images/score.png")
    screen.blit(score_img, (370.30))


def show_game_over(screen) :
    font_size_big = 60
    font_size_small = 30
    font_color = (255.255.255)
    font_big = pygame.font.Font("resources/font/Gabriola.ttf", font_size_big)
    font_small = pygame.font.Font("resources/font/Gabriola.ttf", font_size_small)
    surface = screen.convert_alpha()
    surface.fill((127.255.212.2))
    text = font_big.render('Game Over! '.True, font_color)
    text_rect = text.get_rect()
    text_rect.centerx, text_rect.centery = WIDTH / 2, HEIGHT / 2 - 50
    surface.blit(text, text_rect)
    button_width, button_height = 100.40
    button_start_x_left = WIDTH / 2 - button_width - 20
    button_start_x_right = WIDTH / 2 + 20
    button_start_y = HEIGHT / 2 - button_height / 2 + 20
    pygame.draw.rect(surface, (0.255.255), (button_start_x_left, button_start_y, button_width, button_height))
    text_restart = font_small.render('Restart'.True, font_color)
    text_restart_rect = text_restart.get_rect()
    text_restart_rect.centerx, text_restart_rect.centery = button_start_x_left + button_width / 2, button_start_y + button_height / 2
    surface.blit(text_restart, text_restart_rect)
    pygame.draw.rect(surface, (0.255.255), (button_start_x_right, button_start_y, button_width, button_height))
    text_quit = font_small.render('Quit'.True, font_color)
    text_quit_rect = text_quit.get_rect()
    text_quit_rect.centerx, text_quit_rect.centery = button_start_x_right + button_width / 2, button_start_y + button_height / 2
    surface.blit(text_quit, text_quit_rect)
    clock = pygame.time.Clock()
    while True:
        screen.blit(surface, (0.0))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
                if text_quit_rect.collidepoint(pygame.mouse.get_pos()):
                    sys.exit()
                if text_restart_rect.collidepoint(pygame.mouse.get_pos()):
                    return True
        pygame.display.update()
        clock.tick(60)


def judge_game_over(field) :
    """ As long as you can move in one direction, the game is not over. """
    return not any([judge_move_left(field), judge_move_right(field), judge_move_up(field), judge_move_down(field)])


def judge_move_up(chess_nums_temp) :
    # transpose the numbers in the checkerboard so that the numbers in row 2 and column 3 become row 3 and column 2
    # zip: implementation
    # *chess_nums_temp unpacks the list
    chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    return judge_move_left(chess_nums_temp)


def judge_move_down(chess_nums_temp) :
    Logic: determine if you can move down, that is, transpose the elements and determine if the board can move to the right after transpose.
    # 1. Row and column transpose
    chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    # 2. Determine if you can move to the right
    return judge_move_right(chess_nums_temp)


def judge_move_left(chess_nums_temp) :
    # Return True as long as any row of the board can be moved to the left
    for row in chess_nums_temp:
        for i in range(3) :Judge each line 3 times
            # if the number on the left is 0 and the number on the right is not 0, then we can move to the left.
            if row[i] == 0 and row[i + 1] != 0:
                return True
            elifrow[i] ! =0 and row[i + 1] == row[i]:
                # if the number on the left is not 0, and the two numbers on the left and right are equal, then we can move to the left.
                return True
    return False


def judge_move_right(chess_nums_temp) :
    # Invert each line of the checkerboard, then you can use the function to the left
    return judge_move_left([row[::-1] for row in chess_nums_temp])


def move_left(chess_nums_temp) :
    for i, row in enumerate(chess_nums_temp):
        # 1. Put the non-zero digits in this line forward and the zeros back. [0, 2, 2, 2]-->[2, 2, 2, 0]
        row = sorted(row, key=lambda x: 1 if x == 0 else 0)

        # 2. Check if two numbers are equal. If the first number is equal to 2, the second number is 0. For example [2, 2, 2, 0]-->[4, 0, 2, 0]
        for index in range(3) :if row[index] == row[index + 1]:
                row[index] *= 2
                row[index + 1] = 0

        # 3. Remove the gap after the merge, i.e. non-0 to the left and 0 to the right. For example [4, 0, 2, 0]-->[4, 2, 0, 0]
        row = sorted(row, key=lambda x: 1 if x == 0 else 0)
        # 4. Update the list of numbers because this line is already after the operation
        chess_nums_temp[i] = row
    return chess_nums_temp


def move_right(chess_nums_temp) :
    # Scroll first
    chess_nums_temp = [row[::-1] for row in chess_nums_temp]
    # Then call functions like left move
    move_left(chess_nums_temp)
    # Finally, flip it again to achieve the previous look
    return [row[::-1] for row in chess_nums_temp]


def move_up(chess_nums_temp) :
    Row and column transpose
    chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    # Move left
    chess_nums_temp = move_left(chess_nums_temp)
    Undo by transposing row and column again
    return [list(row) for row in zip(*chess_nums_temp)]


def move_down(chess_nums_temp) :
    Row and column transpose
    chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    # Move right
    chess_nums_temp = move_right(chess_nums_temp)
    Undo by transposing row and column again
    return [list(row) for row in zip(*chess_nums_temp)]


def move(chess_nums_temp, direction) :
    """ Move the number according to the direction """
    # store the function that determines whether each direction can be moved
    judge_move_func_dict = {
        'left': judge_move_left,
        'right': judge_move_right,
        'up': judge_move_up,
        'down': judge_move_down
    }
    # Store functions that move in all directions
    move_func_dict = {
        'left': move_left,
        'right': move_right,
        'up': move_up,
        'down': move_down
    }

    Call the corresponding function to determine if it can move in this direction
    ret = judge_move_func_dict[direction](chess_nums_temp)
    print("%s direction can move:" % direction, ret)
    if ret:
        chess_nums_temp = move_func_dict[direction](chess_nums_temp)
        create_random_num(chess_nums_temp)

    # return the list, if updated is new, if not updated is the previous one
    return chess_nums_temp


def get_num_color(num) :
    According to the current number to display, extract the background color and font color corresponding to the number: [grid background color, font color in the grid] ""
    color_dict = {
        2: ['#eee4da'.'#776e65'].4: ['#ede0c8'.'#776e65'].8: ['#f2b179'.'#f9f6f2'].16: ['#f59563'.'#f9f6f2'].32: ['#f67c5f'.'#f9f6f2'].64: ['#f65e3b'.'#f9f6f2'].128: ['#edcf72'.'#f9f6f2'].256: ['#edcc61'.'#f9f6f2'].512: ['#edc850'.'#f9f6f2'].1024: ['#edc53f'.'#f9f6f2'].2048: ['#edc22e'.'#f9f6f2'].4096: ['#eee4da'.'#776e65'].8192: ['#edc22e'.'#f9f6f2'].16384: ['#f2b179'.'#776e65'].32768: ['#f59563'.'#776e65'].65536: ['#f67c5f'.'#f9f6f2'].0: ['#9e948a'.None]}return color_dict[num]


def create_random_num(nums_temp) :
    "" generate a random number in the checkerboard. ""
    # Store all empty locations
    positions = list(a)for row, line in enumerate(nums_temp):
        for col, num in enumerate(line):
            if num == 0:
                positions.append((row, col))

    # Select a random location from the list of empty locations and unpack it
    row, col = random.choice(positions)
    nums_temp[row][col] = random.choice([2.4.2])  # Pick 2 2's and 1 4's at random, so 2's are twice as likely to be picked as 4's


def draw_nums(screen, chess_nums_temp) :
    """ Display the numbers on the board """
    # Prepare fonts, etc
    font_size = BLOCK_SIZE - 10
    font = pygame.font.Font("./resources/font/Gabriola.ttf", font_size)
    # iterate over numbers
    for i, line in enumerate(chess_nums_temp):
        for j, num in enumerate(line):
            ifnum ! =0:
                # Calculate display position (x coordinates, y coordinates)
                x = MARGIN_SIZE * (j + 1) + BLOCK_SIZE * j
                y = MARGIN_SIZE * (i + 1) + BLOCK_SIZE * i
                # get color
                font_color = pygame.Color(get_num_color(num)[1])
                # display number
                text = font.render(str(num), True, font_color)
                text_rect = text.get_rect()
                text_rect.centerx, text_rect.centery = x + BLOCK_SIZE / 2, y + BLOCK_SIZE / 2
                # Redraw this box with the corresponding numeric background color
                pygame.draw.rect(screen, pygame.Color(get_num_color(num)[0]), (x, y, BLOCK_SIZE, BLOCK_SIZE))
                screen.blit(text, text_rect)


def draw_chess_board(screen) :
    """ Display board """
    for i in range(4) :for j in range(4):
            x = MARGIN_SIZE * (j + 1) + BLOCK_SIZE * j
            y = MARGIN_SIZE * (i + 1) + BLOCK_SIZE * i
            pygame.draw.rect(screen, pygame.Color('#f9f6f2'), (x, y, BLOCK_SIZE, BLOCK_SIZE))


def run(screen) :
    # define a list of all numbers on the current board, 0 if there are no numbers
    chess_nums = [[0 for _ in range(4)] for _ in range(4)]
    # Generate a random number
    create_random_num(chess_nums)
    create_random_num(chess_nums)
    Record the current score
    score = get_score(chess_nums)
    # Create timer (prevent while loop too fast, use too much CPU)
    clock = pygame.time.Clock()
    while True:
        # Event detection (mouse click, keyboard press, etc.)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key in [pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT]:
                    direction = {pygame.K_UP: 'up', pygame.K_DOWN: 'down', pygame.K_LEFT: 'left', pygame.K_RIGHT: 'right'}[event.key]
                    print("Pressed the arrow key :", direction)
                    chess_nums = move(chess_nums, direction)
                    if judge_game_over(chess_nums):
                        print("Game over....")
                        return
                    # Recalculate every time you press the arrow key
                    score = get_score(chess_nums)

        Display the background color
        screen.fill(pygame.Color(BG_COLOR))

        # display checkerboard
        draw_chess_board(screen)

        # Display the numbers on the board
        draw_nums(screen, chess_nums)

        # display score
        draw_score(screen, score)

        # display action prompt
        draw_tips(screen)

        # refresh the display (at which point the window will actually display)
        pygame.display.update()

        # FPS (number of frames displayed per second)
        clock.tick(60)  # Through a certain delay, to achieve 1 second can cycle 60 times


def main() :
    # Game initialization
    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    while True:
        # Run the game once
        run(screen)
        # show the end of the game, whether to restart
        show_game_over(screen)


if __name__ == '__main__':
    main()
Copy the code