一、爱因斯坦问题:一个形状的无限拼图

想象一个看似简单的问题:能否找到一种形状,用它铺满整个平面,却永远不产生重复的图案?

“Einstein”在德语中意为“一块石头”,恰好是这里所需要的:一块单独的瓦片(ein Stein),一种形状,一个答案。

爱因斯坦问题(Einstein Problem):寻找一种单个形状的瓦片,使得它能且仅能以非周期的方式铺满整个平面——也就是说,无论如何铺设,生成的图案永远不会平移重复自身。

在正式定义中,我们需要区分两种铺砌方式:

周期铺砌(Periodic Tiling)

存在一个最小平移向量,使得整个图案沿该向量平移后与自身完全重合。像方砖、蜂巢这样规律重复的铺法。

非周期铺砌(Aperiodic Tiling)

不存在任何平移向量能让图案与自身重合。图案覆盖整个平面,但永远不会出现两个完全相同的区域。

这个问题的历史可以追溯到 20 世纪 60 年代。1961 年,数学家王浩提出了“王氏砖”(Wang tiles)猜想;1966 年,Robert Berger 找到了第一组非周期瓦片集(需要 20,426 种不同的形状);此后半个多世纪,数学家们不断缩减所需的形状数量——从几千种到几十种,再到著名的彭罗斯铺砌(Penrose tiling)只需 2 种形状。

但终极问题始终悬而未决:一种形状够不够?

直到 2023 年 3 月,退休数学家 David Smith 与合作者给出了答案:够了。一种就够了。他们发现的形状被命名为“Hat”(帽子),因为它看起来像一顶礼帽。几个月后,他们又发现了另一种真正的“吸血鬼爱因斯坦”——Spectre(幽灵),它甚至不需要翻转就能非周期铺砌。

Hat tile on hex grid with vertices numbered

Hat 瓦片:13 个顶点构成的非周期瓦片,标注在六角网格上

Hat vs Spectre tile comparison

Hat(13 顶点)与 Spectre(14 顶点)的对比

二、Spectre Puzzle:把数学变成游戏

当我第一次看到 Hat 瓦片时,脑海中浮现的不是数学论文,而是一个问题:能不能把它做成拼图游戏?

于是就有了 Spectre Puzzle——一个纯静态 Web 应用,零运行时依赖,完全用 TypeScript 手写的几何引擎。

Spectre Puzzle home page

Spectre Puzzle 首页:两个入口——创建拼图和解谜挑战

游戏流程概览

整个游戏分为两步:先创建拼图,再解谜。你可以在创建页面用 Hat 或 Spectre 瓦片生成一个密铺图案,选取感兴趣的区域导出为拼图;然后在解谜页面导入拼图文件,将打乱的瓦片拖拽、旋转、拼回原位。v0.2.0 版本还新增了预设谜题,可以直接开始解谜。

创建拼图

打开 创建页面,左侧是控制面板,右侧是画布。

Create puzzle page sidebar controls

创建页面:左侧控制面板,右侧空白画布等待生成

选择瓦片类型

在侧边栏顶部选择瓦片类型:HatSpectre。Spectre 是默认选项。Hat 瓦片包含 13 个顶点,需要翻转才能完成非周期铺砌;Spectre 有 14 个顶点,不需要翻转。

调整生成参数

三个关键参数控制拼图的生成:

Shape 滑块(仅 Hat 模式可用):在 Hat 和 Spectre 形状之间连续插值。拉到最左是 Hat,最右是 Spectre,中间是两者的过渡形态。

Generation Depth:替换系统的递归深度。深度 1 生成少量瓦片(适合新手),深度 4 生成上千个瓦片(挑战性极强)。建议从 2 开始。

Curved Edges(仅 Spectre):开启后瓦片边缘会变成 Bézier 曲线,像真正的拼图一样互锁。你还可以点击 “Edit Curve” 自定义曲线形状。

生成并选取

点击 Generate 按钮,画布上会渲染完整的密铺图案。

Create page with generated tiling

生成 Spectre 密铺图案后的创建页面,可以看到完整的非周期铺砌

然后切换到 Select Mode,在画布上框选一个矩形区域。系统会将框选区域内的瓦片切割为拼图块。选好后点击 Export 导出为 JSON 文件,或点击 Save 保存到浏览器本地存储。

解谜挑战

打开 解谜页面,有三种方式加载拼图:

预设谜题:右侧边栏的 “Preset Puzzles” 下拉菜单提供 8 个内置谜题,涵盖 Hat 和 Spectre 两种瓦片,分为 easy(3-6 块)、medium(14-22 块)、hard(28-42 块)三个难度。选择后立即加载。

导入文件:点击 Import 按钮,选择创建页面导出的 .json 文件。

本地存档:在 “Saved Puzzles” 中点击之前保存的拼图。

Solve page with preset puzzle loaded

解谜页面:已加载预设谜题,画布上显示目标轮廓,右侧是待拼的碎片

操作方式

加载拼图后,点击画布中央的 Start 按钮开始计时,所有瓦片会被打乱到右侧的 Piece Tray 中。

放置瓦片:从右侧 Piece Tray 中拖拽瓦片到画布上。

移动瓦片:在画布上按住左键拖拽已放置的瓦片。

旋转瓦片:选中瓦片后,拖拽上方的旋转手柄。也可以用鼠标右键或 Shift+左键快速旋转。

多选瓦片:在空白区域按住左键拖拽出选择框(Marquee),框内的瓦片会被一起选中,可以同时移动和旋转。

吸附对齐:当瓦片接近正确位置时,会自动吸附到位。吸附成功的瓦片会变色固定,表示已就位。

平移画布:鼠标中键拖拽,或切换到 Pan 模式后用左键拖拽。滚轮缩放。

辅助功能

Show Solution:显示完整解法,所有瓦片回到正确位置。

Reset:重新打乱所有瓦片。

Save Image:将当前画布保存为 PNG 图片。

v0.2.0 新功能:预设谜题

最新版本新增了预设谜题功能,解谜页面侧边栏的下拉菜单中内置了 8 个精心设计的谜题,涵盖两种瓦片和三个难度级别:

难度

Hat

Spectre

Easy (3-6 块)

-

2 个谜题

Medium (14-22 块)

1 个谜题

2 个谜题

Hard (28-42 块)

2 个谜题

1 个谜题

技术亮点

零依赖:纯 TypeScript 手写几何引擎,从多边形裁剪到仿射变换到吸附逻辑,没有任何第三方库。

边缘计算:部署在 Cloudflare Pages,全球 CDN 加速,静态资源就近分发,加载时间 < 100ms。

曲线边缘:Bézier 曲线 + Catmull-Rom 插值生成 S 形互锁边缘,让拼图块像真正的拼图一样咬合。

双瓦片系统:同时支持 Hat(4-元块替换)和 Spectre(9-元块替换)两种非周期瓦片的生成算法。

三、数学之美:瓦片几何的视觉化

让我们深入 Hat 和 Spectre 瓦片的几何结构,看看它们是如何从简单的数学公式中诞生出如此复杂的非周期图案的。

3.1 六角网格坐标系

Hat 瓦片的 13 个顶点定义在六角网格上。从整数坐标 (x,y)(x, y) 到笛卡尔坐标的映射公式为:

hexPt(x,y)=(x+y2,  32y)\text{hexPt}(x, y) = \left( x + \frac{y}{2},\; \frac{\sqrt{3}}{2} \cdot y \right)

这意味着六角网格上的每一步 (+1,0)(+1, 0) 对应水平距离 11,而 (0,+1)(0, +1) 对应 (12,32)\left(\frac{1}{2}, \frac{\sqrt{3}}{2}\right),恰好是单位圆上 60° 角的投影。这正是正三角形铺砌的数学本质。

Hex coordinate system with Hat outline

六角网格坐标系:蓝色高亮为 Hat 瓦片的 13 个顶点位置

Hat 瓦片的 13 个顶点可以用六角坐标表示为:

Vhat=[(0,0)(1,1)(0,2)(2,2)(2,1)(4,2)(5,1)(4,0)(3,0)(2,2)(0,3)(0,2)(1,2)]V_{\text{hat}} = \begin{bmatrix} (0,0) & (-1,-1) & (0,-2) & (2,-2) & (2,-1) \\ (4,-2) & (5,-1) & (4,0) & (3,0) & (2,2) \\ (0,3) & (0,2) & (-1,2) \end{bmatrix}

3.2 边长分析

由于 Hat 定义在六角网格上,相邻顶点之间的距离只能是两种值:

d(Vi,Vj){1,  3}d(V_i, V_j) \in \{1,\; \sqrt{3}\}

而 Spectre 瓦片使用 32\frac{\sqrt{3}}{2} 偏移系统,其边长分布更加均匀。以下是两种瓦片的边长分布对比:

Edge length distribution for Hat and Spectre

边长分布:Hat 的边长在 {1, √3} 之间跳变,Spectre 更加均匀

3.3 内角分析

对于简单多边形,内角之和遵循:

i=1nθi=(n2)×180°\sum_{i=1}^{n} \theta_i = (n - 2) \times 180°

对于 Hat(n=13n = 13),内角和为 11×180°=1980°11 \times 180° = 1980°。对于 Spectre(n=14n = 14),内角和为 12×180°=2160°12 \times 180° = 2160°。以下是各顶点的内角分布饼图:

Interior angle distribution for Hat and Spectre

内角分布饼图:Hat (Σ=1980°) 和 Spectre (Σ=2160°)

3.4 替换系统:从一块到无穷

非周期铺砌的核心在于替换规则(substitution rules)。Hat 瓦片使用 4 种元块(metatile):

H(六边形):4 个 Hat    T(三角形):1 个 Hat    P(平行四边形):2 个 Hat    F(五边形):2 个 Hat

替换过程将 4 种元块组合成 29 个子块的 patch,再从 patch 中提取更大的 H、T、P、F。每一轮替换使瓦片数量增长约 7\approx 7 倍,边长增长约 7\sqrt{7} 倍:

N(k)7k,L(k)(7)kN(k) \approx 7^k, \quad L(k) \approx \left(\sqrt{7}\right)^k

其中 N(k)N(k) 是第 kk 轮替换后的瓦片数量,L(k)L(k) 是对应 patch 的特征长度。Spectre 使用更复杂的 9-元块替换系统(Gamma、Delta、Theta、Lambda、Xi、Pi、Sigma、Phi、Psi),每轮产生 8 个子块。

H metatile substitution rule

H 元块替换:虚线轮廓内的 4 个 Hat 瓦片(3 个原始 + 1 个镜像)

3.5 曲线边缘:Bézier 互锁

为了让拼图块看起来像真正的拼图而不是多边形,我为每条边添加了 Bézier 曲线。关键参数是曲线偏移量 δ=0.6\delta = 0.6

B(t)=(1t)3P0+3(1t)2tC1+3(1t)t2C2+t3P3\mathbf{B}(t) = (1-t)^3 P_0 + 3(1-t)^2 t \cdot C_1 + 3(1-t)t^2 \cdot C_2 + t^3 P_3

每条边的控制点沿垂直方向交替偏移,形成 S 形曲线。相邻瓦片的偏移方向相反,确保它们能够像拼图一样互锁。具体地,第 ii 条边的控制点为:

C1(i)=Vi+13(Vi+1Vi)+(1)iδn^iC_1^{(i)} = V_i + \frac{1}{3}(V_{i+1} - V_i) + (-1)^i \delta \cdot \hat{n}_i C2(i)=Vi+23(Vi+1Vi)+(1)iδn^iC_2^{(i)} = V_i + \frac{2}{3}(V_{i+1} - V_i) + (-1)^i \delta \cdot \hat{n}_i

其中 n^i\hat{n}_i 是第 ii 条边的单位法向量。

Bezier curve edge detail with control points

单条边的 Bézier 曲线:P0→CP1→CP2→P3,控制点垂直偏移形成 S 形

更进一步,使用 Catmull-Rom 样条插值可以让曲线更加平滑。对于有序点列 P0,P1,,Pn1P_0, P_1, \ldots, P_{n-1},任意相邻两点 Pi,Pi+1P_i, P_{i+1} 之间的 Bézier 控制点为:

cp1=Pi+Pi+1Pi16\text{cp}_1 = P_i + \frac{P_{i+1} - P_{i-1}}{6} cp2=Pi+1Pi+2Pi6\text{cp}_2 = P_{i+1} - \frac{P_{i+2} - P_i}{6}

边界点通过镜像反射生成虚拟点:P1=2P0P1P_{-1} = 2P_0 - P_1Pn=2Pn1Pn2P_n = 2P_{n-1} - P_{n-2}。这保证了曲线在端点处的平滑连续。

3.6 仿射变换

在拼图游戏中,每个瓦片都需要支持旋转、平移、缩放等操作。这些操作可以用仿射变换矩阵统一表示:

(xy1)=(abtxcdty001)(xy1)\begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix} = \begin{pmatrix} a & b & t_x \\ c & d & t_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x \\ y \\ 1 \end{pmatrix}

对于 60° 旋转(六角对称的基本操作):

R60°=(1232032120001)R_{60°} = \begin{pmatrix} \frac{1}{2} & -\frac{\sqrt{3}}{2} & 0 \\ \frac{\sqrt{3}}{2} & \frac{1}{2} & 0 \\ 0 & 0 & 1 \end{pmatrix}

在 Spectre Puzzle 中,所有瓦片的定位都通过仿射变换链的复合来实现。每一步替换操作都会将父元块的变换传递给子块,最终叶子节点的变换是整条链的复合:

Tfinal=T0T1T2TkT_{\text{final}} = T_0 \circ T_1 \circ T_2 \circ \cdots \circ T_k

结语

从 David Smith 在 2023 年的数学发现,到一个可以在浏览器中玩耍的拼图游戏,再到这篇文章中每一个精确计算的数学可视化——数学之美在于它从简单的规则中诞生无限的复杂性

Hat 瓦片只需要 13 个顶点和一条简单的替换规则,就能创造出永远不会重复的图案。而 Spectre 更加纯粹——它甚至不需要翻转。这种简洁与深邃的统一,正是数学最动人的地方。

来玩一局吧:

puzzle.game.mrwuliu.top

开源代码:github.com/mr-wuliu/spectre-puzzle