发布时间:2026/8/29 7:17:01
【生命游戏】从零实现一个可交互的Web前端模拟器 1. 生命游戏简介与实现思路生命游戏Game of Life是英国数学家约翰·康威在1970年提出的一种细胞自动机。它由一个二维网格组成每个格子代表一个细胞细胞有两种状态存活或死亡。游戏的规则非常简单孤单死亡如果细胞的邻居小于等于1个则该细胞在下一次状态将死亡拥挤死亡如果细胞的邻居在4个及以上则该细胞在下一次状态将死亡稳定如果细胞的邻居为2个或3个则下一次状态为稳定存活复活如果某位置原无细胞存活而该位置的邻居为2个或3个则该位置将复活一个细胞这些简单的规则却能产生极其复杂和有趣的行为模式这也是生命游戏经久不衰的魅力所在。实现一个Web版的生命游戏模拟器我们需要考虑以下几个核心部分网格系统用二维数组表示细胞状态渲染系统将细胞状态可视化展示规则引擎计算每个细胞的下一个状态交互系统允许用户与模拟器互动控制面板提供开始/暂停、速度调节等功能2. 搭建基础HTML结构我们先从最基础的HTML结构开始。创建一个index.html文件包含以下内容!DOCTYPE html html head meta charsetutf-8 title生命游戏模拟器/title link relstylesheet hrefstyle.css /head body div classcontainer h1生命游戏模拟器/h1 div classgame-container div classgame-intro h2游戏规则/h2 p生命游戏为1970年由英国数学家J. H. Conway所提出.../p !-- 规则说明 -- /div div classgame-grid idgrid/div div classgame-controls div classsettings div classsetting label网格大小:/label input typenumber idgrid-size min10 max100 value30 /div div classsetting label演化速度(ms):/label input typenumber idspeed min50 max2000 value200 /div /div div classbuttons button idstart开始/button button idpause暂停/button button idreset重置/button button idrandom随机填充/button button idnext下一步/button /div /div /div /div script srcscript.js/script /body /html这个结构包含了三个主要部分游戏规则说明区游戏网格展示区控制面板区3. 使用CSS美化界面接下来我们添加一些基本的样式。创建style.css文件* { margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif; } body { background-color: #f0f0f0; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; } h1 { text-align: center; margin: 20px 0; color: #333; } .game-container { display: flex; gap: 20px; margin-top: 20px; } .game-intro { flex: 1; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .game-grid { flex: 2; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); display: grid; grid-template-columns: repeat(30, 1fr); grid-template-rows: repeat(30, 1fr); aspect-ratio: 1/1; } .cell { border: 1px solid #eee; cursor: pointer; } .cell.alive { background-color: #4CAF50; } .game-controls { flex: 1; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .settings { margin-bottom: 20px; } .setting { margin-bottom: 15px; } .setting label { display: block; margin-bottom: 5px; font-weight: bold; } .setting input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } .buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } .buttons button { padding: 10px; border: none; border-radius: 4px; background-color: #4CAF50; color: white; cursor: pointer; transition: background-color 0.3s; } .buttons button:hover { background-color: #45a049; } .buttons button:active { background-color: #3e8e41; }4. JavaScript核心逻辑实现现在我们来编写JavaScript代码。创建script.js文件// 游戏状态 const gameState { gridSize: 30, speed: 200, isRunning: false, grid: [], nextGrid: [], timer: null }; // 初始化游戏 function initGame() { const gridElement document.getElementById(grid); gridElement.innerHTML ; gridElement.style.gridTemplateColumns repeat(${gameState.gridSize}, 1fr); gridElement.style.gridTemplateRows repeat(${gameState.gridSize}, 1fr); // 初始化网格数组 gameState.grid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); gameState.nextGrid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); // 创建网格元素 for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { const cell document.createElement(div); cell.className cell; cell.dataset.row i; cell.dataset.col j; cell.addEventListener(click, toggleCell); gridElement.appendChild(cell); } } } // 切换细胞状态 function toggleCell(e) { if (gameState.isRunning) return; const row parseInt(e.target.dataset.row); const col parseInt(e.target.dataset.col); gameState.grid[row][col] gameState.grid[row][col] ? 0 : 1; e.target.classList.toggle(alive); } // 计算下一代 function nextGeneration() { // 计算每个细胞的下一个状态 for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { const neighbors countNeighbors(i, j); // 应用生命游戏规则 if (gameState.grid[i][j] 1) { gameState.nextGrid[i][j] (neighbors 2 || neighbors 3) ? 1 : 0; } else { gameState.nextGrid[i][j] (neighbors 3) ? 1 : 0; } } } // 更新网格和UI updateGrid(); } // 计算邻居数量 function countNeighbors(row, col) { let count 0; for (let i -1; i 1; i) { for (let j -1; j 1; j) { if (i 0 j 0) continue; const newRow row i; const newCol col j; if (newRow 0 newRow gameState.gridSize newCol 0 newCol gameState.gridSize) { count gameState.grid[newRow][newCol]; } } } return count; } // 更新网格显示 function updateGrid() { const cells document.querySelectorAll(.cell); for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { gameState.grid[i][j] gameState.nextGrid[i][j]; const index i * gameState.gridSize j; if (gameState.grid[i][j] 1) { cells[index].classList.add(alive); } else { cells[index].classList.remove(alive); } } } } // 随机填充网格 function randomFill() { const cells document.querySelectorAll(.cell); for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { gameState.grid[i][j] Math.random() 0.7 ? 1 : 0; const index i * gameState.gridSize j; if (gameState.grid[i][j] 1) { cells[index].classList.add(alive); } else { cells[index].classList.remove(alive); } } } } // 重置游戏 function resetGame() { stopGame(); initGame(); } // 开始游戏 function startGame() { if (gameState.isRunning) return; gameState.isRunning true; gameState.timer setInterval(nextGeneration, gameState.speed); document.getElementById(start).disabled true; document.getElementById(pause).disabled false; } // 暂停游戏 function stopGame() { if (!gameState.isRunning) return; gameState.isRunning false; clearInterval(gameState.timer); document.getElementById(start).disabled false; document.getElementById(pause).disabled true; } // 更新游戏速度 function updateSpeed() { gameState.speed parseInt(document.getElementById(speed).value); if (gameState.isRunning) { stopGame(); startGame(); } } // 更新网格大小 function updateGridSize() { const newSize parseInt(document.getElementById(grid-size).value); if (newSize 10 newSize 100) { gameState.gridSize newSize; resetGame(); } } // 初始化事件监听 function initEvents() { document.getElementById(start).addEventListener(click, startGame); document.getElementById(pause).addEventListener(click, stopGame); document.getElementById(reset).addEventListener(click, resetGame); document.getElementById(random).addEventListener(click, randomFill); document.getElementById(next).addEventListener(click, nextGeneration); document.getElementById(speed).addEventListener(change, updateSpeed); document.getElementById(grid-size).addEventListener(change, updateGridSize); } // 初始化应用 function initApp() { initEvents(); initGame(); } // 启动应用 window.onload initApp;5. 性能优化与Canvas实现虽然上面的实现已经可以工作但当网格变大时使用DOM元素渲染性能会下降。我们可以改用Canvas来提升性能// 替换原来的game-grid div为canvas div classgame-area canvas idgame-canvas/canvas /div // 更新CSS .game-area { flex: 2; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); position: relative; } #game-canvas { width: 100%; height: 100%; display: block; } // 修改JavaScript部分 const canvas document.getElementById(game-canvas); const ctx canvas.getContext(2d); let cellSize 0; function initGame() { // 设置canvas尺寸 const container document.querySelector(.game-area); canvas.width container.clientWidth; canvas.height container.clientHeight; cellSize canvas.width / gameState.gridSize; // 初始化网格数组 gameState.grid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); gameState.nextGrid Array(gameState.gridSize).fill().map(() Array(gameState.gridSize).fill(0) ); // 绘制网格 drawGrid(); // 添加点击事件 canvas.addEventListener(click, handleCanvasClick); } function drawGrid() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制背景 ctx.fillStyle white; ctx.fillRect(0, 0, canvas.width, canvas.height); // 绘制网格线 ctx.strokeStyle #eee; ctx.lineWidth 0.5; for (let i 0; i gameState.gridSize; i) { // 垂直线 ctx.beginPath(); ctx.moveTo(i * cellSize, 0); ctx.lineTo(i * cellSize, canvas.height); ctx.stroke(); // 水平线 ctx.beginPath(); ctx.moveTo(0, i * cellSize); ctx.lineTo(canvas.width, i * cellSize); ctx.stroke(); } // 绘制存活细胞 ctx.fillStyle #4CAF50; for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { if (gameState.grid[i][j] 1) { ctx.fillRect( j * cellSize 1, i * cellSize 1, cellSize - 2, cellSize - 2 ); } } } } function handleCanvasClick(e) { if (gameState.isRunning) return; const rect canvas.getBoundingClientRect(); const x e.clientX - rect.left; const y e.clientY - rect.top; const col Math.floor(x / cellSize); const row Math.floor(y / cellSize); if (row 0 row gameState.gridSize col 0 col gameState.gridSize) { gameState.grid[row][col] gameState.grid[row][col] ? 0 : 1; drawGrid(); } } function updateGrid() { for (let i 0; i gameState.gridSize; i) { for (let j 0; j gameState.gridSize; j) { gameState.grid[i][j] gameState.nextGrid[i][j]; } } drawGrid(); }6. 添加更多功能与优化现在我们的生命游戏模拟器已经基本完成但还可以添加一些有用的功能模式选择预定义一些有趣的初始模式颜色主题允许用户自定义细胞颜色网格显示切换可以隐藏网格线历史记录记录演化历史导出/导入保存和加载特定模式// 在控制面板添加新按钮 div classbuttons !-- 原有按钮... -- button idpatterns模式/button button idtheme切换主题/button button idtoggle-grid隐藏网格/button /div // 添加模式选择对话框 div idpattern-modal classmodal div classmodal-content span classclosetimes;/span h3选择模式/h3 div classpattern-list div classpattern>

相关新闻

2026/8/29 7:17:01

Wireshark网络数据分析实战:从FTP/Telnet协议解析到攻击链还原

1. 项目概述与核心价值“2019年全国职业院校技能大赛中职组‘网络空间安全’-数据分析3”,这个标题对于圈内人来说,一眼就能看出其分量。它不是一个简单的练习题,而是直接对标国内顶尖职业技能竞赛的实战场景。这类赛题的核心,从来…

2026/8/29 7:17:01

第23篇-macOS伴侣应用.md

【OpenClaw 从入门到精通】第 23 篇:macOS 伴侣应用(OpenClaw.app) 本系列定位:零基础入门,从安装配置到高级架构全覆盖。 本篇你将学到 macOS 伴侣应用功能菜单栏控制Voice Wake 唤醒词macOS 权限设置 一、OpenClaw.…

2026/8/29 7:17:01

Gemini Live代管复杂任务:从语音指令到数字代理的跃迁

去年年底有几个晚上,我几乎每天都在反复切换手机上的语音助手和各类效率工具,只为了把一件其实并不复杂的事情理顺:安排一次跨三个城市的出差行程,同时要处理两封需要不同措辞的邮件,还要在间隙里找到时间把当天的会议…

2026/8/29 7:27:02

2026 年5款企业数字人软件横评:多语种跨境场景适配实测对比

一、引文与摘要跨境贸易企业做海外短视频营销,最头疼的不是内容创意,而是多语种内容的生产效率。2026年实测5款主流企业数字人软件后,一个明确的结论是:晟诺科讯达在多语种适配、克隆效率和成本控制三个维度的综合表现领先&#x…

2026/8/29 7:27:02

2026 年支持品牌定制5 款数字人平台盘点:企业级适配方案对比

引文 | 预算有限的企业该怎么选定制数字人平台眼下企业做短视频营销和品牌推广,数字人已成标配工具。根据恒州诚思调研统计,2024年全球数字克隆人直播市场规模约257.7亿元,预计到2031年将接近1912.6亿元。但面对市面上形形色色的品牌定制数字…

2026/8/29 7:27:02

Java后端双线作战:华为校招与阿里社招全流程复盘

1. 为什么9月同时投华为校招和阿里社招:我的求职背景与策略 9月这个时间节点挺特殊的。华为校招的机考和性格测试集中在8月底到9月中旬铺开,而阿里那边很多部门的社招HC(Headcount,招聘名额)也在9月做最后的盘点&#…

2026/8/29 7:27:02

CAN总线简述

1. 引言:什么是CAN总线? 控制器局域网(Controller Area Network,简称CAN)是一种广泛应用于汽车、工业自动化、医疗设备等领域的串行通信总线标准。它由德国博世公司(Bosch)于1983年开发&#xf…

2026/8/29 7:27:02

Java秋招面经大合集:基础八股、算法框架与实战避坑全复盘

我的Java秋招面经大合集 又到了一年一度的秋招季,这段时间后台私信里问Java面试准备的同学特别多。我这个合集本来只是自己随手记的复盘笔记,后来发现身边好几个朋友靠它突击拿到了offer,干脆整理出来分享给大家。内容覆盖Java基础八股、常见…

2026/8/28 16:16:17

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/28 16:16:21

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/28 16:16:22

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/29 0:01:10

etc目录下的profile.d文件目录设置环境变量和全局脚本shell

一、设置环境变量etc目录下的profile.d文件目录 /etc/profile.d1、编写 vi test.sh文件内容# jdk变量 export ZHK_HOME/root export PATH$PATH:$ZHK_HOME/test # 可以取出来ZHK_HOME变量给ZZZ_HOME赋值 export ZZZ_HOME${ZHK_HOME}/test2、刷新 执行source /etc/profile 命令使…

2026/8/29 0:01:10

【JavaScript】内存管理-垃圾回收机制-内存泄露

内存管理 C 语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()。 而 JavaScript 是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。释放的过程称为垃圾回收。 整…

2026/8/29 0:01:10

Labgrid-MCP:为嵌入式硬件实验室接入AI Agent操控能力

Labgrid-MCP 的目标是把 MCP(Model Context Protocol)能力延伸到真实嵌入式硬件实验室:AI Agent 通过一个标准化的 MCP Server,就能查看目标板状态、控制上电断电、复位开发板、读取串口日志,甚至执行镜像刷写。对于经…

2026/8/28 16:16:48

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/28 16:16:50

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/28 11:06:45

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…