用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!

用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!(结尾附完整代码)

简介

2048 是一个简单而又令人上瘾的数字拼图游戏。玩家通过滑动方块使相同数字的方块合并,目标是创造出数字 2048!在这篇博客中,我们将用 Python 的 Tkinter 库 从零开始实现这款游戏,涵盖从界面设计到逻辑实现的每一个细节,帮助你全面了解背后的开发思路。

游戏特点

  • 经典玩法:滑动合并相同数字,尽可能达到 2048。
  • 随机生成新数字:每次滑动后随机生成 2 或 4。
  • 直观的图形界面:使用 Tkinter 绘制游戏网格与方块,带来清晰的视觉效果。
  • 开发环境准备

    在开始之前,确保你的环境已安装 Python,并包含以下必要库:

    pip install tk

    Tkinter 是 Python 内置的 GUI 工具包,因此大多数环境无需额外安装。 

    项目实现步骤

    我们将按照以下步骤构建游戏:

    1. 设计网格界面
    2. 实现核心逻辑
    3. 绑定键盘事件
    4. 添加游戏结束检测
    5. 优化用户体验

    1. 设计网格界面

    首先,我们定义游戏的基本参数,包括网格大小、方块的尺寸、间距和颜色等。随后,通过 Tkinter 创建一个网格界面,每个格子将用矩形和文本元素表示。

    import tkinter as tk
    from tkinter import messagebox
    import random
    
    # 常量定义
    GRID_SIZE = 4  # 网格为 4x4
    TILE_SIZE = 100  # 每个方块的像素大小
    PADDING = 10  # 方块之间的间距
    BACKGROUND_COLOR = "#92877d"
    TILE_COLORS = {
        0: "#9e948a", 2: "#eee4da", 4: "#ede0c8",
        8: "#f2b179", 16: "#f59563", 32: "#f67c5f",
        64: "#f65e3b", 128: "#edcf72", 256: "#edcc61",
        512: "#edc850", 1024: "#edc53f", 2048: "#edc22e",
    }
    
    # 初始化游戏界面
    class Game2048:
        def __init__(self, master):
            self.master = master
            self.master.title("2048")
            self.master.geometry(f"{GRID_SIZE * TILE_SIZE + PADDING * (GRID_SIZE + 1)}x{GRID_SIZE * TILE_SIZE + PADDING * (GRID_SIZE + 1)}")
            self.master.resizable(False, False)
            self.init_grid()
            self.reset_game()
    
        def init_grid(self):
            """创建 4x4 网格"""
            self.canvas = tk.Canvas(self.master, bg=BACKGROUND_COLOR, bd=0, highlightthickness=0)
            self.canvas.pack(fill=tk.BOTH, expand=True)
            self.tiles = []
            for i in range(GRID_SIZE):
                row = []
                for j in range(GRID_SIZE):
                    x1 = PADDING + j * (TILE_SIZE + PADDING)
                    y1 = PADDING + i * (TILE_SIZE + PADDING)
                    x2 = x1 + TILE_SIZE
                    y2 = y1 + TILE_SIZE
                    rect = self.canvas.create_rectangle(x1, y1, x2, y2, fill=TILE_COLORS[0], outline="")
                    text = self.canvas.create_text(x1 + TILE_SIZE // 2, y1 + TILE_SIZE // 2, text="", font=("Helvetica", 30), fill="#776e65")
                    row.append((rect, text))
                self.tiles.append(row)
    

     运行效果:运行后你会看到一个 4×4 的灰色网格,每个格子代表一个方块。

     

    2. 实现核心逻辑

    游戏的逻辑主要包括:

  • 压缩和合并数字:将相邻的数字合并。
  • 滑动方向操作:实现上下左右滑动功能。
  • 随机生成新数字:在空格中随机生成 2 或 4。
  • 压缩与合并

    每次滑动操作可以拆分为两部分:压缩(去掉空格)合并(合并相邻的相同数字)

    def compress(row):
        """将行向左压缩,去掉 0"""
        new_row = [num for num in row if num != 0]
        new_row += [0] * (GRID_SIZE - len(new_row))
        return new_row
    
    def merge(row):
        """合并相邻相同的数字"""
        for i in range(len(row) - 1):
            if row[i] == row[i + 1] and row[i] != 0:
                row[i] *= 2
                row[i + 1] = 0
        return row
    
    滑动操作

    根据方向执行不同操作(上下左右)。

    def move_left(grid):
        """左移操作"""
        new_grid = []
        for row in grid:
            compressed_row = compress(row)
            merged_row = merge(compressed_row)
            new_grid.append(compress(merged_row))
        return new_grid
    
    随机生成数字

    每次滑动后随机选择一个空格,填入 2 或 4。

    def spawn_new_tile(grid):
        """随机生成一个新的数字方块(2 或 4)"""
        empty_cells = [(i, j) for i in range(GRID_SIZE) for j in range(GRID_SIZE) if grid[i][j] == 0]
        if empty_cells:
            i, j = random.choice(empty_cells)
            grid[i][j] = 2 if random.random() < 0.9 else 4
    

    3. 绑定键盘事件

    为了让游戏动起来,我们需要捕获用户的按键(上下左右方向键),并根据按键更新网格状态。

    def key_press(event):
        """处理键盘输入"""
        key = event.keysym
        if key in ["Up", "Down", "Left", "Right"]:
            if key == "Up":
                game.grid = move_left(rotate_left(game.grid))
                game.grid = rotate_right(game.grid)
            elif key == "Down":
                game.grid = move_left(rotate_right(game.grid))
                game.grid = rotate_left(game.grid)
            elif key == "Left":
                game.grid = move_left(game.grid)
            elif key == "Right":
                game.grid = move_left(reverse_rows(game.grid))
                game.grid = reverse_rows(game.grid)
            spawn_new_tile(game.grid)
            game.update_ui()
            if is_game_over(game.grid):
                messagebox.showinfo("Game Over", "No more moves left!")
    

    4. 添加游戏结束检测

    当所有格子都被填满且没有可合并的数字时,游戏结束。

    def is_game_over(grid):
        """判断是否还有有效操作"""
        for i in range(GRID_SIZE):
            for j in range(GRID_SIZE):
                if grid[i][j] == 0:  # 空格存在
                    return False
                if j + 1 < GRID_SIZE and grid[i][j] == grid[i][j + 1]:  # 横向可合并
                    return False
                if i + 1 < GRID_SIZE and grid[i][j] == grid[i + 1][j]:  # 纵向可合并
                    return False
        return True
    

    5. 完整代码

    将上述代码整合的完整代码如下:运行后即可得到完整的 2048 游戏!

    import tkinter as tk
    from tkinter import messagebox
    import random
    
    # 常量定义
    GRID_SIZE = 4
    TILE_SIZE = 100
    PADDING = 10
    BACKGROUND_COLOR = "#92877d"
    TILE_COLORS = {
        0: "#9e948a", 2: "#eee4da", 4: "#ede0c8",
        8: "#f2b179", 16: "#f59563", 32: "#f67c5f",
        64: "#f65e3b", 128: "#edcf72", 256: "#edcc61",
        512: "#edc850", 1024: "#edc53f", 2048: "#edc22e",
    }
    
    class Game2048:
        def __init__(self, master):
            self.master = master
            self.master.title("2048")
            self.master.geometry(f"{GRID_SIZE * TILE_SIZE + PADDING * (GRID_SIZE + 1)}x{GRID_SIZE * TILE_SIZE + PADDING * (GRID_SIZE + 1)}")
            self.master.resizable(False, False)
            self.init_grid()
            self.reset_game()
    
        def init_grid(self):
            """初始化网格界面"""
            self.canvas = tk.Canvas(self.master, bg=BACKGROUND_COLOR, bd=0, highlightthickness=0)
            self.canvas.pack(fill=tk.BOTH, expand=True)
            self.tiles = []
            for i in range(GRID_SIZE):
                row = []
                for j in range(GRID_SIZE):
                    x1 = PADDING + j * (TILE_SIZE + PADDING)
                    y1 = PADDING + i * (TILE_SIZE + PADDING)
                    x2 = x1 + TILE_SIZE
                    y2 = y1 + TILE_SIZE
                    rect = self.canvas.create_rectangle(x1, y1, x2, y2, fill=TILE_COLORS[0], outline="")
                    text = self.canvas.create_text(x1 + TILE_SIZE // 2, y1 + TILE_SIZE // 2, text="", font=("Helvetica", 30), fill="#776e65")
                    row.append((rect, text))
                self.tiles.append(row)
    
        def reset_game(self):
            """重置游戏状态"""
            self.grid = [[0] * GRID_SIZE for _ in range(GRID_SIZE)]
            self.spawn_new_tile()
            self.spawn_new_tile()
            self.update_ui()
    
        def spawn_new_tile(self):
            """随机生成一个新的数字方块(2 或 4)"""
            empty_cells = [(i, j) for i in range(GRID_SIZE) for j in range(GRID_SIZE) if self.grid[i][j] == 0]
            if empty_cells:
                i, j = random.choice(empty_cells)
                self.grid[i][j] = 2 if random.random() < 0.9 else 4
    
        def update_ui(self):
            """更新界面显示"""
            for i in range(GRID_SIZE):
                for j in range(GRID_SIZE):
                    value = self.grid[i][j]
                    rect, text = self.tiles[i][j]
                    self.canvas.itemconfig(rect, fill=TILE_COLORS.get(value, TILE_COLORS[2048]))
                    self.canvas.itemconfig(text, text=str(value) if value != 0 else "")
    
    def compress(row):
        """将行向左压缩,去掉 0"""
        new_row = [num for num in row if num != 0]
        new_row += [0] * (GRID_SIZE - len(new_row))
        return new_row
    
    def merge(row):
        """合并相邻相同的数字"""
        for i in range(len(row) - 1):
            if row[i] == row[i + 1] and row[i] != 0:
                row[i] *= 2
                row[i + 1] = 0
        return row
    
    def move_left(grid):
        """左移操作"""
        new_grid = []
        for row in grid:
            compressed_row = compress(row)
            merged_row = merge(compressed_row)
            new_grid.append(compress(merged_row))
        return new_grid
    
    def rotate_right(grid):
        """矩阵顺时针旋转 90°"""
        return [list(row) for row in zip(*grid[::-1])]
    
    def rotate_left(grid):
        """矩阵逆时针旋转 90°"""
        return [list(row) for row in zip(*grid)][::-1]
    
    def reverse_rows(grid):
        """矩阵水平翻转"""
        return [row[::-1] for row in grid]
    
    def is_game_over(grid):
        """判断是否还有有效操作"""
        for i in range(GRID_SIZE):
            for j in range(GRID_SIZE):
                if grid[i][j] == 0:  # 空格存在
                    return False
                if j + 1 < GRID_SIZE and grid[i][j] == grid[i][j + 1]:  # 横向可合并
                    return False
                if i + 1 < GRID_SIZE and grid[i][j] == grid[i + 1][j]:  # 纵向可合并
                    return False
        return True
    
    def key_press(event):
        """处理键盘输入"""
        key = event.keysym
        if key in ["Up", "Down", "Left", "Right"]:
            if key == "Up":
                game.grid = move_left(rotate_left(game.grid))
                game.grid = rotate_right(game.grid)
            elif key == "Down":
                game.grid = move_left(rotate_right(game.grid))
                game.grid = rotate_left(game.grid)
            elif key == "Left":
                game.grid = move_left(game.grid)
            elif key == "Right":
                game.grid = move_left(reverse_rows(game.grid))
                game.grid = reverse_rows(game.grid)
            game.spawn_new_tile()
            game.update_ui()
            if is_game_over(game.grid):
                messagebox.showinfo("Game Over", "No more moves left!")
    
    root = tk.Tk()
    game = Game2048(root)
    root.bind("<Key>", key_press)
    root.mainloop()

    通过本篇博客,我们从头实现了经典的 2048 游戏。不仅让你掌握了 Tkinter 界面开发,还深入理解了游戏逻辑的实现过程。如果你喜欢这篇博客,请分享给更多的 Python 爱好者吧!

    作者:一位小说男主

    物联沃分享整理
    物联沃-IOTWORD物联网 » 用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!

    发表回复