{
  "lead": "「落沙游戏」是一类经典的像素沙盒：屏幕上每个格子是一粒材料，沙子会下落堆积，水会流动摊平，火会点燃木头并化作烟。整个世界没有物理引擎，只靠每个格子根据邻居执行几条简单规则，复杂的流动、燃烧、沉淀现象就自然「涌现」出来。",
  "formulas": [["下 → 斜下 → 横向", "每种材料按优先级依次尝试移动方向"]],
  "terms": [
    ["沙子", "先尝试向下，被挡住就随机选一侧斜向下滑，所以会堆成约 45° 的沙丘。沙子可以和水交换位置，因此会沉到水底。"],
    ["水", "和沙子一样先往下、再斜往下；都走不了时向左右流动，每帧最多流 3 格，所以一桶水倒下去会很快摊平成水面。"],
    ["火与烟", "火有寿命，每帧有一定概率点燃上下左右的木头；旁边还有燃料时留在原地燃烧，没有燃料就向上飘。寿命耗尽变成烟，烟会上升并逐渐消散。火旁边只要有水就立刻熄灭，变成一缕烟。"],
    ["扫描顺序", "每帧从下往上逐行扫描，这样下落的颗粒一帧只走一步；每一行的扫描方向左右交替，否则所有东西都会慢慢偏向一边。每个格子记录「本帧已经移动过」，防止同一颗粒在一帧里被搬两次。"]
  ],
  "flow": [
    ["js", "拖拽时 JS 把两次指针事件之间的连线插值成一串点，对每个点调用 <code>sand_paint(x, y, 半径, 材料)</code>，快速划动也不会断开。"],
    ["rust", "每帧调用 <code>sand_step()</code>，Rust 按上面的规则更新全部 30,000 个格子。材料、寿命、颜色抖动分别存在几个紧凑的 <code>Vec&lt;u8&gt;</code> 里。"],
    ["rust", "<code>sand_render()</code> 按材料和每格固定的随机明暗值输出 RGBA，沙子因此呈现颗粒感。"],
    ["js", "JS 把 200×150 的小图关闭平滑放大 4 倍绘制，保留像素风格；每秒读一次 <code>sand_counts()</code> 显示各材料的数量。"]
  ],
  "perf": [
    "规则都是局部的、没有浮点运算，200×150 的世界每帧通常只需零点几毫秒，大部分时间花在绘制上。",
    "随机数来自共享的 xorshift 生成器（<code>crates/common</code>），种子固定，所以同样的操作每次得到同样的结果，测试可以精确复现。",
    "测试里验证了几条「守恒律」：沙子和水的总量在运动中保持不变，墙永远不动，火最终会熄灭。"
  ],
  "src": [["crates/games/falling-sand/src/lib.rs", ""], ["www/falling-sand/index.js", ""]]
}
