方格中的混沌与秩序:用线性代数思想构建 2048 游戏引擎

2014年,Gabriele Cirulli 开发的《2048》席卷了全球。玩家在一个 $4 \times 4$ 的网格中滑动数字,相同的数字碰撞合并,试图拼凑出 2048。

作为一个计算机科学的观察者,当我们剥离其鲜艳的 UI 外壳,会发现它的内核极其纯粹:这是一个状态机,其状态转换由矩阵操作定义。

今天,我们将不使用复杂的嵌套循环来分别处理“上下左右”,而是利用线性代数的对称性,构建一个优雅的 Python 游戏引擎。

第一部分:数学建模——将游戏抽象为矩阵

游戏棋盘本质上是一个 $4 \times 4$ 的矩阵 $M$。

$$
M = \begin{bmatrix}
0 & 2 & 0 & 2 \
4 & 4 & 8 & 0 \
0 & 0 & 2 & 2 \
0 & 0 & 0 & 0
\end{bmatrix}
$$

1.1 核心操作的原子化

2048 的移动逻辑看起来很复杂,但其实可以分解为两个原子操作:

  1. 压缩 (Compress):将非零元素向一侧堆叠,挤掉中间的零。
  2. 合并 (Merge):相邻且相同的元素结合,$A + A \rightarrow 2A$。

1.2 维度的降维打击:只写一个方向

初学者最容易犯的错误是写四个函数:move_left, move_right, move_up, move_down。这会导致代码重复且难以维护。

如果我们利用矩阵的几何变换,我们只需要写一个 move_left(左移)。

  • 向右移:等同于 $\text{Reverse}(M) \rightarrow \text{Left} \rightarrow \text{Reverse}(M)$
  • 向上移:等同于 $\text{Transpose}(M) \rightarrow \text{Left} \rightarrow \text{Transpose}(M)$
  • 向下移:等同于 $\text{Transpose}(M) \rightarrow \text{Right} \rightarrow \text{Transpose}(M)$

通过转置(行列互换)和镜像(左右翻转),我们将二维空间的四个方向问题,坍缩为单一方向的一维数组处理问题。

第二部分:Python 逻辑引擎实现

首先,我们实现不依赖于任何图形库的纯逻辑核心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import random

class LogicEngine:
def __init__(self):
self.grid = [[0] * 4 for _ in range(4)]
self.score = 0
self.add_new_tile()
self.add_new_tile()

def add_new_tile(self):
"""在空白处随机生成一个 2 (90%) 或 4 (10%)"""
empty_cells = [(r, c) for r in range(4) for c in range(4) if self.grid[r][c] == 0]
if not empty_cells:
return
r, c = random.choice(empty_cells)
self.grid[r][c] = 2 if random.random() < 0.9 else 4

def compress(self, grid):
"""原子操作:压缩非零元素到左侧"""
new_grid = [[0] * 4 for _ in range(4)]
for r in range(4):
pos = 0
for c in range(4):
if grid[r][c] != 0:
new_grid[r][pos] = grid[r][c]
pos += 1
return new_grid

def merge(self, grid):
"""原子操作:合并相邻相同元素"""
for r in range(4):
for c in range(3):
if grid[r][c] != 0 and grid[r][c] == grid[r][c+1]:
grid[r][c] *= 2
grid[r][c+1] = 0
self.score += grid[r][c]
return grid

def reverse(self, grid):
"""矩阵镜像翻转"""
return [row[::-1] for row in grid]

def transpose(self, grid):
"""矩阵转置 (行列互换)"""
return [list(row) for row in zip(*grid)]

def move_left(self, grid):
"""核心逻辑:压缩 -> 合并 -> 再压缩"""
grid = self.compress(grid)
grid = self.merge(grid)
grid = self.compress(grid)
return grid

def step(self, direction):
"""
统一接口:根据方向变换矩阵,应用左移逻辑,再还原
direction: 'Left', 'Right', 'Up', 'Down'
"""
# 1. 变换坐标系
if direction == 'Up':
self.grid = self.transpose(self.grid)
elif direction == 'Down':
self.grid = self.transpose(self.grid)
self.grid = self.reverse(self.grid)
elif direction == 'Right':
self.grid = self.reverse(self.grid)

# 2. 应用核心逻辑 (Left)
new_grid = self.move_left(self.grid)

# 3. 检查是否有变化 (决定是否生成新数字)
changed = new_grid != self.grid
self.grid = new_grid

# 4. 还原坐标系
if direction == 'Up':
self.grid = self.transpose(self.grid)
elif direction == 'Down':
self.grid = self.reverse(self.grid)
self.grid = self.transpose(self.grid)
elif direction == 'Right':
self.grid = self.reverse(self.grid)

# 5. 如果盘面有变动,生成新数字
if changed:
self.add_new_tile()

return changed

第三部分:图形化呈现 (Pygame)

逻辑写好后,我们需要一个“皮囊”。我们将使用 Pygame 来渲染界面。

1
pip install pygame

3.1 颜色配置与渲染循环

我们将颜色映射表硬编码在字典中,以便快速查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import pygame
import sys

# 颜色常量定义
COLORS = {
0: (205, 193, 180),
2: (238, 228, 218),
4: (237, 224, 200),
8: (242, 177, 121),
16: (245, 149, 99),
32: (246, 124, 95),
64: (246, 94, 59),
128: (237, 207, 114),
256: (237, 204, 97),
512: (237, 200, 80),
1024: (237, 197, 63),
2048: (237, 194, 46)
}
BG_COLOR = (187, 173, 160)
TEXT_COLOR = (119, 110, 101)

class GameUI:
def __init__(self):
pygame.init()
self.width, self.height = 400, 500
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.set_caption("2048 - Matrix Engine")
self.clock = pygame.time.Clock()
self.font = pygame.font.SysFont("arial", 40, bold=True)
self.engine = LogicEngine()

def draw_grid(self):
self.screen.fill(BG_COLOR)

# 绘制分数
score_text = self.font.render(f"Score: {self.engine.score}", True, (255, 255, 255))
self.screen.blit(score_text, (20, 20))

# 绘制 4x4 方格
cell_size = 80
padding = 10
start_y = 100

for r in range(4):
for c in range(4):
value = self.engine.grid[r][c]
rect_x = padding + c * (cell_size + padding)
rect_y = start_y + r * (cell_size + padding)

# 绘制方块背景
color = COLORS.get(value, (60, 58, 50))
pygame.draw.rect(self.screen, color, (rect_x, rect_y, cell_size, cell_size), border_radius=5)

# 绘制数字
if value != 0:
text_color = TEXT_COLOR if value <= 4 else (255, 255, 255)
text_surf = self.font.render(str(value), True, text_color)
text_rect = text_surf.get_rect(center=(rect_x + cell_size/2, rect_y + cell_size/2))
self.screen.blit(text_surf, text_rect)

def run(self):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()

if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.engine.step('Left')
elif event.key == pygame.K_RIGHT:
self.engine.step('Right')
elif event.key == pygame.K_UP:
self.engine.step('Up')
elif event.key == pygame.K_DOWN:
self.engine.step('Down')

self.draw_grid()
pygame.display.update()
self.clock.tick(30)

if __name__ == "__main__":
game = GameUI()
game.run()

第四部分:逻辑深度解析——为什么要这么做?

你可能会问:“为什么不直接写四个方向的逻辑?那样不是更直观吗?”

从软件工程的角度来看,重复是万恶之源(DRY Principle)

如果我们分别为四个方向编写合并逻辑,我们不仅增加了 4 倍的代码量,更增加了 4 倍的 Debug 难度。如果在合并逻辑中发现了一个 Bug(例如分数计算错误),在传统写法中,你需要修改四个地方。

而在我们的矩阵变换写法中,所有的合并逻辑都收敛于 move_left 函数。TransposeReverse 只是改变数据的视角,而不改变数据的规则

这种思想在数学上称为同构(Isomorphism)——虽然方向不同,但操作的代数结构是完全一致的。

第五部分:总结与扩展

我们用不到 150 行代码,就复刻了一个具有完整核心逻辑的 2048。

这个项目是一个绝佳的练手案例,它涵盖了:

  1. 数组操作:切片、索引。
  2. 线性代数:转置矩阵的应用。
  3. GUI 编程:事件循环与渲染。
  4. 算法思维:如何将复杂问题(4个方向)约简为简单问题(1个方向)。

下一步的挑战:

  • 增加动画效果: 当前的方块是瞬间移动的。能否引入插值算法,让方块平滑滑动?
  • AI 求解器: 能否编写一个 Expectimax 算法,让电脑自动玩到 2048?

游戏开发不仅仅是娱乐,更是对逻辑思维的极致训练。


方格中的混沌与秩序:用线性代数思想构建 2048 游戏引擎
https://sunfove.xyz/2026/01/18/2026-01-19-building-2048-with-matrix-logic/
Author
Sunfove
Posted on
January 18, 2026
Licensed under