Rust + WebAssembly 实现康威生命游戏

ops/2025/3/28 7:46:27/

1. 设计思路

1.1 选择有限的世界

康威生命游戏的世界是 无限二维网格,但由于 计算机内存有限,我们可以选择三种有限宇宙方案:

  1. 动态扩展:仅存储“活跃区域”,按需扩展(可能无限增长)。
  2. 固定大小,无边界扩展:边界处的细胞会被“消灭”。
  3. 固定大小,环绕宇宙(Toroidal Universe (我们采用此方案)

环绕宇宙(Toroidal Universe)允许 滑翔机(Gliders) 无限运行,不会被边界限制:

  • 上边界的细胞连接到下边界
  • 左边界的细胞连接到右边界

2. Rust 代码实现

2.1 定义 Cell 结构

rust">#[wasm_bindgen]
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cell {Dead = 0,Alive = 1,
}

说明

  • #[repr(u8)]:让 Cell 占用 1 字节,减少内存浪费。
  • Dead = 0, Alive = 1:使得 Cell 可以直接用整数加法计算邻居数

2.2 定义 Universe

rust">#[wasm_bindgen]
pub struct Universe {width: u32,height: u32,cells: Vec<Cell>,
}

说明

  • widthheight:宇宙的宽度和高度
  • cells: Vec<Cell>:存储所有细胞状态(0=死,1=活)

2.3 计算网格索引

rust">impl Universe {fn get_index(&self, row: u32, column: u32) -> usize {(row * self.width + column) as usize}
}
  • 计算二维网格一维数组中的索引。

2.4 计算活邻居数量

rust">impl Universe {fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {let mut count = 0;for delta_row in [self.height - 1, 0, 1].iter().cloned() {for delta_col in [self.width - 1, 0, 1].iter().cloned() {if delta_row == 0 && delta_col == 0 {continue;}let neighbor_row = (row + delta_row) % self.height;let neighbor_col = (column + delta_col) % self.width;let idx = self.get_index(neighbor_row, neighbor_col);count += self.cells[idx] as u8;}}count}
}

说明

  • 使用 modulo 计算 环绕宇宙,确保邻居索引不会溢出。
  • 避免 if 语句,减少特殊情况处理,提高性能。

2.5 更新下一代状态

rust">#[wasm_bindgen]
impl Universe {pub fn tick(&mut self) {let mut next = self.cells.clone();for row in 0..self.height {for col in 0..self.width {let idx = self.get_index(row, col);let cell = self.cells[idx];let live_neighbors = self.live_neighbor_count(row, col);let next_cell = match (cell, live_neighbors) {(Cell::Alive, x) if x < 2 => Cell::Dead,(Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,(Cell::Alive, x) if x > 3 => Cell::Dead,(Cell::Dead, 3) => Cell::Alive,(otherwise, _) => otherwise,};next[idx] = next_cell;}}self.cells = next;}
}

说明

  • 规则翻译
    • 活细胞 < 2 → 死亡(过少
    • 活细胞 = 2 or 3 → 存活(繁衍
    • 活细胞 > 3 → 死亡(过度拥挤
    • 死细胞 = 3 → 复活(繁殖

2.6 初始化宇宙

rust">#[wasm_bindgen]
impl Universe {pub fn new() -> Universe {let width = 64;let height = 64;let cells = (0..width * height).map(|i| if i % 2 == 0 || i % 7 == 0 { Cell::Alive } else { Cell::Dead }).collect();Universe {width,height,cells,}}pub fn render(&self) -> String {self.to_string()}
}
  • 初始化:创建 64x64 网格,细胞 随机分布
  • 实现 render():在 JavaScript 中调用,返回网格字符串。

3. JavaScript 前端

wasm-game-of-life/www/index.js 编写 Canvas 渲染 代码。

3.1 JavaScript 初始化

import { Universe, Cell, memory } from "wasm-game-of-life";const CELL_SIZE = 5;
const GRID_COLOR = "#CCCCCC";
const DEAD_COLOR = "#FFFFFF";
const ALIVE_COLOR = "#000000";const universe = Universe.new();
const width = universe.width();
const height = universe.height();

3.2 绘制网格

const drawGrid = () => {ctx.beginPath();ctx.strokeStyle = GRID_COLOR;for (let i = 0; i <= width; i++) {ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0);ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1);}for (let j = 0; j <= height; j++) {ctx.moveTo(0, j * (CELL_SIZE + 1) + 1);ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1);}ctx.stroke();
};

3.3 读取 WebAssembly 内存

const drawCells = () => {const cellsPtr = universe.cells();const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);ctx.beginPath();for (let row = 0; row < height; row++) {for (let col = 0; col < width; col++) {const idx = row * width + col;ctx.fillStyle = cells[idx] === Cell.Dead ? DEAD_COLOR : ALIVE_COLOR;ctx.fillRect(col * (CELL_SIZE + 1) + 1, row * (CELL_SIZE + 1) + 1, CELL_SIZE, CELL_SIZE);}}ctx.stroke();
};

3.4 动画渲染

const renderLoop = () => {universe.tick();drawGrid();drawCells();requestAnimationFrame(renderLoop);
};drawGrid();
drawCells();
requestAnimationFrame(renderLoop);

4.运行项目

wasm-pack build
cd www
npm install
npm run start

打开 http://localhost:8080/,你会看到 动态演化的生命游戏
在这里插入图片描述


http://www.ppmy.cn/ops/167413.html

相关文章

Matlab 四分之一车辆被动悬架和模糊pid控制对比

1、内容简介 Matlab 183-四分之一车辆被动悬架和模糊pid控制对比 可以交流、咨询、答疑 2、内容说明 略 3.1 车辆多自由度模型建立 对于车辆动力学&#xff0c;一般都是研究其悬架系统&#xff0c;悬架系统由轮胎&#xff0c;轮胎空气&#xff0c;弹簧&#xff0c;减震器和…

Pytorch使用手册—自定义 C++ 和 CUDA 运算符(专题五十一)

你将学到什么 如何将用 C++/CUDA 编写的自定义运算符与 PyTorch 集成如何使用 torch.library.opcheck 测试自定义运算符先决条件 1. PyTorch 2.4 或更高版本 2. 对 C++ 和 CUDA 编程有基本了解 注意 本教程也适用于 AMD ROCm,无需额外修改。 PyTorch 提供了一个庞大的运算符库…

基于微信小程序的网上商城

4系统概要设计 4.1 概述 本系统基于Web服务模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1 系统工作原理图 4.2 系统结构 本系统架构网站&#xff0c;本系统的…

Linux内核传输层UDP源码分析

一、用户数据包协议&#xff08;UDP&#xff09; 1.UDP数据报头 UDP 提供面向消息的不可靠传输&#xff0c;但没有拥塞控制功能。很多协议都使用 UDP&#xff0c;如用于 IP 网络传输音频和视频的实时传输协议 (Real-time Transport Protocol&#xff0c;RTP)&#xff0c;此类型…

Go语言--安装和环境搭配

一.Go简介 Go 语言&#xff08;又称 Golang&#xff09;是由 Google 开发的一种开源编程语言&#xff0c;于 2009 年正式对外发布。下面从多个方面为你介绍它&#xff1a; 设计目标 Go 语言的设计初衷是为了解决大规模软件开发中的一些问题&#xff0c;比如编译速度慢、并发…

Unity URPShader:实现和PS一样的色相/饱和度调整参数效果

目录 前言&#xff1a; 一、色相&#xff08;Hue&#xff09; 二、饱和度&#xff08;Saturation&#xff09; 三、明度&#xff08;Lightness&#xff09; 四、全代码实现 五、效果对比 前言&#xff1a; PS中的色相/饱和度调节界面可通过快捷键CtrlU快速打开&#xff0…

VSCode C/C++ 环境搭建指南

一、前言 Visual Studio Code&#xff08;简称 VSCode&#xff09;是一款轻量级且功能强大的跨平台代码编辑器&#xff0c;凭借丰富的插件生态和高度的可定制性&#xff0c;深受开发者喜爱。对于 C/C 开发者而言&#xff0c;在 VSCode 中搭建开发环境&#xff0c;能够获得灵活…

TCP、UDP协议的应用、ServerSocket和Socket、DatagramSocket和DatagramPacket

DAY13.1 Java核心基础 TCP协议 TCP 协议是面向连接的运算层协议&#xff0c;比较复杂&#xff0c;应用程序在使用TCP协议之前必须建立连接&#xff0c;才能传输数据&#xff0c;数据传输完毕之后需要释放连接 就好比现实生活中的打电话&#xff0c;首先确保电话打通了才能进…