【生命游戏】从零实现一个可交互的Web前端模拟器

发布时间:2026/10/24 17:43:48

【生命游戏】从零实现一个可交互的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/10/24 17:43:24

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

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

2026/10/24 16:33:58

第23篇-macOS伴侣应用.md

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

2026/10/20 13:14:13

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

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

2026/10/24 17:40:36

VMware Thin磁盘空间回收:VMFS5打洞与vMotion迁移实战

简介:这份文档面向使用 VMware 虚拟化平台、并受精简磁盘(Thin)空间膨胀困扰的运维人员与虚拟化学习者,聚焦 VMFS5 文件系统不支持自动回收空间这一痛点,讲解手工回收 VM 精简卷容量的可行路径。压缩包内仅含 1 个 doc…

2026/10/24 17:40:36

MATLAB特征值分解避坑指南:eig顺序、若当标准形与数值稳定性

简介:这份资源面向学习MATLAB数值计算与线性代数的学生及工程人员,聚焦矩阵处理中的特征值、特征向量与对角化等核心问题,适合作为课堂实验或自学练习的配套材料。压缩包内共1个doc文档,约98KB,内容以实验讲义形式组织…

2026/10/24 17:40:36

Windows文件I/O三种模式对比:无缓冲、顺序扫描与异步性能分析

简介:操作系统课程中常用的一份实验参考资料,聚焦无缓冲、有缓冲与异步三种文件读写方式的实现与性能对比,面向计算机专业学生及需要理解Windows文件I/O机制的开发者。文档源自武汉大学计算机学院本科生实验设计报告,完整阐述了实…

2026/10/24 17:40:36

Java万年历记事本课程设计:Swing界面与日期算法实战

简介:这份JAVA万年历源码面向Java初学者与课程设计开发者,以单文件calendar类实现万年历与记事本二合一桌面应用,帮助读者在实战中掌握日期时间处理、Swing界面设计与事件驱动编程。资源包共1个doc文件,约317KB,内容为…

2026/10/24 17:40:36

专利质量要求如何落地?研发自存清单与权利要求撰写实战指南

简介:一份面向企业专利工程师、研发人员与专利代理人的权利要求书质量自查要点,源自华为专利质量要求中的实操整理。文档以“问题—原因—指导”为主线,系统梳理独立权利要求缺少必要技术特征、权利要求不清楚、缺乏新颖性/创造性、保护范围被…

2026/10/24 17:35:35

从词典匹配到二元语法:手把手入门中文分词与NLP基础

简介:这份资源是南京邮电大学自然语言处理课程实验一的完整实验报告,面向正在学习中文分词与句法分析的高校学生及自学者,帮助其理解词典分词与二元语法分词的基本原理并完成实验任务。报告围绕HanLP工具展开,涵盖segment分词指令…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑