Python 利用线性代数思想构建 2048 游戏引擎
2014 年,Gabriele Cirulli 开发的《2048》席卷了全球。玩家在一个 4×4 的网格中滑动数字,相同的数字碰撞合并,试图拼凑出 2048。
作为一个计算机科学的观察者,当我们剥离其鲜艳的 UI 外壳,会发现它的内核极其纯粹:这是一个状态机,其状态转换由矩阵操作定义。
今天,我们将不使用复杂的嵌套循环来分别处理'上下左右',而是利用线性代数的对称性,构建一个优雅的 Python 游戏引擎。
第一部分:数学建模——将游戏抽象为矩阵
游戏棋盘本质上是一个 4×4 的矩阵 M。
0 2 0 2
4 4 8 0
0 0 2 2
0 0 0 0
1.1 核心操作的原子化
2048 的移动逻辑看起来很复杂,但其实可以分解为两个原子操作:
- 压缩 (Compress):将非零元素向一侧堆叠,挤掉中间的零。
- 合并 (Merge):相邻且相同的元素结合,A+A→2A。
1.2 维度的降维打击:只写一个方向
初学者最容易犯的错误是写四个函数:move_left, move_right, move_up, move_down。这会导致代码重复且难以维护。
如果我们利用矩阵的几何变换,我们只需要写一个 move_left(左移)。
- 向右移:等同于 Reverse(M) → Left → Reverse(M)
- 向上移:等同于 Transpose(M) → Left → Transpose(M)
- 向下移:等同于 Transpose(M) → Right → Transpose(M)
通过转置(行列互换)和镜像(左右翻转),我们将二维空间的四个方向问题,坍缩为单一方向的一维数组处理问题。
第二部分:Python 逻辑引擎实现
首先,我们实现不依赖于任何图形库的纯逻辑核心。
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) r () c () .grid[r][c] == ]
empty_cells:
r, c = random.choice(empty_cells)
.grid[r][c] = random.random() <
():
new_grid = [[] * _ ()]
r ():
pos =
c ():
grid[r][c] != :
new_grid[r][pos] = grid[r][c]
pos +=
new_grid
():
r ():
c ():
grid[r][c] != grid[r][c] == grid[r][c+]:
grid[r][c] *=
grid[r][c+] =
.score += grid[r][c]
grid
():
[row[::-] row grid]
():
[(row) row (*grid)]
():
grid = .compress(grid)
grid = .merge(grid)
grid = .compress(grid)
grid
():
direction == :
.grid = .transpose(.grid)
direction == :
.grid = .transpose(.grid)
.grid = .reverse(.grid)
direction == :
.grid = .reverse(.grid)
new_grid = .move_left(.grid)
changed = new_grid != .grid
.grid = new_grid
direction == :
.grid = .transpose(.grid)
direction == :
.grid = .reverse(.grid)
.grid = .transpose(.grid)
direction == :
.grid = .reverse(.grid)
changed:
.add_new_tile()
changed

