用 p5.js 复刻经典扫雷游戏:Minesweeper Coding Challenge 71 源码级实战解析
教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载导读本文基于 Coding TrainThe Coding Train网站归档仓库中的第 71 号编程挑战文档071-minesweeper.md及其配套源码完整拆解如何用 JavaScript 与 p5.js 库从零实现经典扫雷Minesweeper游戏。文中将覆盖二维数组与对象原型构建棋盘数据结构、随机布雷算法、相邻地雷计数、洪水填充Flood Fill自动展开、鼠标交互与游戏结束判定等核心环节并对照 Processing 移植版本给出跨语言实现要点。读完本文你将掌握一套可直接复制运行的 p5.js 扫雷实现并理解其背后的网格算法设计。挑战背景Coding Challenge #71该挑战发布于 2017 年 5 月 18 日视频编号为 71video_idLFU5ZlrR21E对应仓库为CC_071_minesweeper并配有 p5.js Web Editor 在线草图web_editor-mz-ePoqd。正如原文档所述In this challenge, I attempt to code the classic game Minesweeper in JavaScript with the p5.js library. This video builds on some previous tutorials related to the Prototypes and multi-dimensional arrays in JavaScript.即本挑战使用 p5.js 复刻经典扫雷游戏并建立在 JavaScript 的Prototype原型与二维数组2D Arrays两个前置教程的基础上——分别对应仓库中的 9.19-Prototypes-in-javascript.md 与 9.15-2d-arrays-in-javascript.md。因此本文既是一份扫雷实现指南也是二维数组与原型链在真实游戏项目中的综合演练。仓库中的完整实现分为两个版本p5.js 版本P5/sketch.js、P5/cell.js、P5/index.htmlProcessing 移植版本Processing/CC_071_minesweeper/CC_071_minesweeper.pde、Processing/CC_071_minesweeper/Cell.pde游戏规则与整体设计经典扫雷的规则可以概括为三条棋盘上随机埋设若干地雷本实现中以蜜蜂 bee代指地雷下文沿用源码命名翻开任意格子若该格是雷游戏结束若该格周围 8 个方向存在雷则显示周围雷的数量若周围没有雷则自动递归展开相邻格子玩家通过推理数字与雷的分布最终安全翻开所有非雷格子即获胜本实现未额外实现胜利判定与右键标雷属于基础版。本实现的整体设计非常精简由两部分构成棋盘管理层sketch.js创建二维数组网格、随机布雷、处理鼠标点击、触发游戏结束、驱动逐帧渲染格子对象层cell.js定义单个格子的状态坐标、尺寸、是否雷、是否已翻开、邻雷计数及其绘制、计数、展开行为。棋盘参数在 sketch.js 中集中定义参数值说明w20每个格子的像素边长createCanvas(401, 401)401×401画布尺寸20×20400 边长为 20 的格子留 1px 余量cols/rows由floor(width / w)计算实际网格为 20 列 × 20 行totalBees30总共埋设的地雷数量由于width为 401、w为 20cols floor(401 / 20) 20、rows 20因此棋盘实际是 20×20 400 个格子其中埋设 30 颗雷。数据结构二维数组 Cell 原型对象1. 二维数组工具函数sketch.js 定义了make2DArray用于创建一个cols × rows的二维数组function make2DArray(cols, rows) { var arr new Array(cols); for (var i 0; i arr.length; i) { arr[i] new Array(rows); } return arr; }这里采用外层数组的每个元素再嵌套一个数组的方式形成grid[i][j]的访问结构其中i为列索引水平方向、j为行索引垂直方向。这正是二维数组教程9.15-2d-arrays-in-javascript.md中演示的经典模式。2. Cell 构造函数与原型方法cell.js 定义了Cell构造函数采用 ES5 的原型链Prototype风格——这是本挑战特意演示的 JavaScript 语言特性function Cell(i, j, w) { this.i i; this.j j; this.x i * w; this.y j * w; this.w w; this.neighborCount 0; this.bee false; this.revealed false; }每个格子维护如下状态属性含义i/j格子在网格中的列 / 行索引x/y格子在画布上的像素坐标由索引乘以格子尺寸得到w格子边长neighborCount周围 8 个格子中的地雷数量雷格子记为 -1bee是否为地雷revealed是否已被翻开所有行为方法通过Cell.prototype挂载到原型上包括show、countBees、contains、reveal、floodFill这正是原型教程9.19-Prototypes-in-javascript.md中所讲的通过 prototype 为自定义对象附加方法的实际应用。初始化流程网格创建与随机布雷setup() 完成整个棋盘的初始化分为三步第一步创建网格function setup() { createCanvas(401, 401); cols floor(width / w); rows floor(height / w); grid make2DArray(cols, rows); for (var i 0; i cols; i) { for (var j 0; j rows; j) { grid[i][j] new Cell(i, j, w); } } ... }通过嵌套for循环为每个网格位置grid[i][j]实例化一个Cell。第二步随机布雷无重复选取技巧布雷的关键在于每个位置最多只能埋一颗雷。源码采用了一个优雅的技巧先把所有坐标[i, j]放进一个options数组然后每次随机取出一个下标、放置地雷后立即用splice将该坐标从候选中删除// Pick totalBees spots var options []; for (var i 0; i cols; i) { for (var j 0; j rows; j) { options.push([i, j]); } } for (var n 0; n totalBees; n) { var index floor(random(options.length)); var choice options[index]; var i choice[0]; var j choice[1]; // Deletes that spot so its no longer an option options.splice(index, 1); grid[i][j].bee true; }这种候选池 随机下标 删除已选项的做法等价于从 400 个格子中无放回地抽取 30 个作为雷位避免了重复布雷导致地雷数量不足的隐患。第三步计算所有格子的邻雷数for (var i 0; i cols; i) { for (var j 0; j rows; j) { grid[i][j].countBees(); } }邻域计数countBees 的实现与边界处理cell.js 中的countBees遍历以当前格子为中心的 3×3 邻域xoff、yoff均取 -1、0、1统计其中bee true的格子数量Cell.prototype.countBees function() { if (this.bee) { this.neighborCount -1; return; } var total 0; for (var xoff -1; xoff 1; xoff) { var i this.i xoff; if (i 0 || i cols) continue; for (var yoff -1; yoff 1; yoff) { var j this.j yoff; if (j 0 || j rows) continue; var neighbor grid[i][j]; if (neighbor.bee) { total; } } } this.neighborCount total; };实现要点雷格子的计数约定为 -1方便后续reveal逻辑判断该格是否触发洪水填充同时避免把雷自身计入数字边界守卫if (i 0 || i cols) continue;与if (j 0 || j rows) continue;确保位于棋盘边缘的格子不会越界访问grid这是二维数组遍历中最常见的易错点。渲染show 方法与状态可视化cell.js 的show方法负责绘制每个格子未翻开的格子只画边框翻开的格子根据状态显示内容Cell.prototype.show function() { stroke(0); noFill(); rect(this.x, this.y, this.w, this.w); if (this.revealed) { if (this.bee) { fill(127); ellipse(this.x this.w * 0.5, this.y this.w * 0.5, this.w * 0.5); } else { fill(200); rect(this.x, this.y, this.w, this.w); if (this.neighborCount 0) { textAlign(CENTER); fill(0); text(this.neighborCount, this.x this.w * 0.5, this.y this.w - 6); } } } };未翻开仅描边矩形stroke(0)noFill()呈现待探索状态翻开的雷灰色填充 在格子中心绘制椭圆表示地雷翻开的非雷格浅灰填充fill(200)并在neighborCount 0时居中绘制数字neighborCount 0的格子留白。在 draw() 中每帧先background(255)清屏再通过双重循环调用每个格子的show()function draw() { background(255); for (var i 0; i cols; i) { for (var j 0; j rows; j) { grid[i][j].show(); } } }核心算法reveal 与 floodFill 洪水填充这是扫雷体验的灵魂——点击一个邻雷数为 0 的格子时应自动连锁展开一大片无雷区域。cell.js 的reveal负责翻开当前格子并在neighborCount 0时触发floodFillCell.prototype.reveal function() { this.revealed true; if (this.neighborCount 0) { // flood fill time this.floodFill(); } };cell.js 的floodFill遍历 3×3 邻域对每个未翻开的邻居递归调用reveal从而形成深度优先的连锁展开Cell.prototype.floodFill function() { for (var xoff -1; xoff 1; xoff) { var i this.i xoff; if (i 0 || i cols) continue; for (var yoff -1; yoff 1; yoff) { var j this.j yoff; if (j 0 || j rows) continue; var neighbor grid[i][j]; // Note the neighbor.bee check was not required. // See issue #184 if (!neighbor.revealed) { neighbor.reveal(); } } } };两个值得注意的实现细节递归终止条件floodFill只对!neighbor.revealed的格子调用reveal已被翻开的格子会被跳过从而保证递归必然终止不会无限循环源码注释明确说明不需要判断 neighbor 是否为雷issue #184由于reveal只在neighborCount 0时才触发floodFill而雷格子的neighborCount被置为 -1因此雷格子永远不会作为floodFill的发起者自然也不会被 0 值区域递归展开。这一注释是仓库源码中留给我们理解算法正确性的关键线索。这种中心格为 0 时向四周扩散的算法正是原文档 links 中引用的Flood fill洪水填充算法在扫雷场景下的应用——常用于图像处理与连通区域标记这里是它在网格游戏中的典型落地。交互与胜负判定mousePressed 与 gameOversketch.js 通过 p5.js 的mousePressed回调实现点击翻开function mousePressed() { for (var i 0; i cols; i) { for (var j 0; j rows; j) { if (grid[i][j].contains(mouseX, mouseY)) { grid[i][j].reveal(); if (grid[i][j].bee) { gameOver(); } } } } }命中检测依赖 cell.js 的contains方法Cell.prototype.contains function(x, y) { return x this.x x this.x this.w y this.y y this.y this.w; };即鼠标坐标落在格子像素矩形范围内即视为点击该格。若点中地雷则调用 gameOver() 把整个棋盘全部置为revealed true将所有地雷位置公开展示function gameOver() { for (var i 0; i cols; i) { for (var j 0; j rows; j) { grid[i][j].revealed true; } } }注意点击事件采用遍历全部格子逐个做矩形命中测试的朴素实现。对于 20×20 400 个格子的规模这种方式完全够用若棋盘规模大幅增大可以改用由鼠标坐标直接反推格子索引floor(mouseX / w)的 O(1) 命中方式作为优化方向。运行方式与文件组织p5.js 版本的入口页面是 P5/index.html它通过 CDN 引入 p5.js 并依次加载两个脚本!DOCTYPE html html head script typetext/javascript srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/p5.min.js/script script typetext/javascript srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/addons/p5.sound.min.js/script script typetext/javascript srcsketch.js/script script typetext/javascript srccell.js/script /head body /body /html运行方式有两种本地运行将index.html、sketch.js、cell.js三个文件放在同一目录用浏览器直接打开index.html或通过本地静态服务器如python -m http.server访问脚本加载顺序为sketch.js先于cell.js两者均使用全局变量与全局函数协作在线编辑在 p5.js Web Editor 中打开草图web_editor-mz-ePoqd即可在线运行与修改。由于本项目主要依赖 CDN 上的 p5.js 1.4.1运行时需要网络环境加载 p5 库代码本身使用 ES5 语法var、构造函数 原型方法不依赖 ES6 特性兼容性良好。Processing 移植版本对照仓库还提供了与 p5.js 版逻辑完全对应的 Processing 移植版Processing/CC_071_minesweeper/CC_071_minesweeper.pde主程序包含setup、gameOver、mousePressed、draw其中文件头部注释写明Originally written using p5.jsProcessing/CC_071_minesweeper/Processing/CC_071_minesweeper/Cell.pdeCell类定义含show、countBees、contains、reveal、floodFill。两个版本的核心差异在于语言层面的映射概念p5.js 版Processing 版网格声明var grid;make2DArray(cols, rows)Cell[][] grid;对象定义function Cell(...)Cell.prototype.methodclass Cell { ... }布雷候选池var options []options.splice(index, 1)ArrayListint[] optionsoptions.remove(index)画布createCanvas(401, 401)size(401, 401)随机random(n)random(n)可以看出Processing 版几乎是一比一的直译相同的 20×20 网格、30 颗雷、相同的neighborCount -1雷格约定、相同的递归floodFill逻辑连不需要检查 neighbor.beeissue #184的注释都保留了下来。这对于理解同一套游戏逻辑如何在不同语言/框架间移植是极好的对照材料。前置知识与延伸学习原文档明确指出本挑战建立在以下两个前置教程之上二者在仓库中均有对应页面9.15-2d-arrays-in-javascript.md讲解 JavaScript 二维数组的创建、初始化与嵌套 for 循环遍历——本挑战的make2DArray与双重循环正是其直接应用9.19-Prototypes-in-javascript.md讲解如何通过 prototype 为对象附加方法、原型链与原型式继承——本挑战的Cell.prototype.show等五个方法即是原型用法的示范。此外仓库中还有相关的延伸主题可供继续探索例如 9.24-inheritance-with-prototype.md 讨论了基于原型的继承而 162-self-avoiding-walk.md 等挑战文档中也把本挑战列为相关视频引用。扫雷中用到的邻域遍历与洪水填充思想在迷宫生成如 010.1-maze-dfs-p5.md等网格类挑战中同样频繁出现。社区贡献与扩展方向原文档的contributions字段记录了社区围绕该挑战产出的丰富变体这些实现覆盖了多种语言与玩法扩展可作为进一步学习与改写的灵感来源以下仅列举代表性方向不包含外部链接玩法变体TypeScript 带颜色移植版、3D 扫雷、基于六边形的 HexSweeper、把地雷替换成无害小精灵的 Trollsweeper、扩展版 Beesweeper 等语言变体Processing.py、Python PyGame、Unity3D 等不同技术栈的复刻实现界面与交互变体Mine-Buddy、带 UI 控制的实现、在线可玩版本等。对于想在本实现基础上继续扩展的读者仓库源码也指明了几个自然的改进点增加右键标雷与胜利判定、用坐标反推优化命中检测、增加地雷数量/棋盘尺寸的可调参数、引入倒计时与剩余雷数显示等——这些都可以在不改变现有Cell数据结构的前提下增量实现。小结本挑战以不到 200 行代码p5.js 版两文件合计约 170 行完整复现了扫雷的核心玩法二维数组 原型对象建模棋盘、候选池随机布雷、3×3 邻域计数、0 值触发的递归洪水填充以及点击交互与全局翻盘的游戏结束判定。从 sketch.js 与 cell.js 的源码出发再对照 Processing 移植版即可把网格算法 对象原型这套组合拳灵活迁移到任意类似的棋盘类游戏中去。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐用 Processing 复刻经典游戏 PongCoding Challenge 67 源码级实战解析用 Processing 复刻经典游戏 PongCoding Challenge 67 源码级实战解析 在这篇技术指南中我们将以 Coding Train教程文档示例工程用 p5.js 复刻 Agar.io 基础游戏机制Coding Challenge 32.1 全解析用 p5.js 复刻 Agar.io 基础游戏机制Coding Challenge 32.1 全解析 这篇技术指南围绕 Coding TrainThe Co教程文档示例工程探秘经典用Rust重构的扫雷游戏——minesweeper-rs探秘经典用Rust重构的扫雷游戏—— minesweeper rs 1、项目介绍 在快速发展的编程世界中复古游戏的魅力依然不减。 minesweeper r上一篇如何快速解决 Nix Software Center 常见问题终极故障排除指南下一篇SiYuan 图片 OCR 完整指南粘贴图片后文字直接可搜索创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

用 Git 版本控制管理架构决策记录(ADR):从 mkdir 到 Commit 的完整实战

用 Git 版本控制管理架构决策记录(ADR):从 mkdir 到 Commit 的完整实战

【免费下载链接】architecture-decision-record Architecture decision record (ADR) examples for software planning, IT leadership, and template documentation 项目地址: https://gitcode.com/gh_mirrors/ar/architecture-decision-record 点击查看 免费下载 …

2026/10/12 1:23:45 阅读更多 →
REA 完整导览:一条命令让编码 Agent 接入本地逆向工程

REA 完整导览:一条命令让编码 Agent 接入本地逆向工程

REA 完整导览:一条命令让编码 Agent 接入本地逆向工程 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea REA(Reverse Enginee…

2026/10/12 1:23:45 阅读更多 →
Learn-Web-Hacking:Windows 本地认证机制与密码存储全解析(winlogon、lsass、SAM 与 SPNEGO)

Learn-Web-Hacking:Windows 本地认证机制与密码存储全解析(winlogon、lsass、SAM 与 SPNEGO)

文档网络安全教程 【免费下载链接】Learn-Web-Hacking Study Notes For Web Hacking / Web安全学习笔记 项目地址: https://gitcode.com/gh_mirrors/le/Learn-Web-Hacking 点击查看 免费下载 本篇技术指南以 Learn-Web-Hacking 仓库中 Windows 认证笔记 为核心&…

2026/10/12 1:23:45 阅读更多 →

最新新闻

数据结构 - > 排序算法

数据结构 - > 排序算法

1. 排序的概念1.1 常见的排序算法1.2 排序算法的评价指标复杂度:评价排序算法的第一大指标就是时间复杂度和空间复杂度,它衡量算法的时间效率和空间效率。稳定性:假定在待排序的数据元素中有两个元素 Ri 和 Rj,它们对应的关键字为…

2026/10/12 3:39:10 阅读更多 →
ccg-workflow Shell 技能指南:Bash 脚本自动化、系统管理与多模型协作实战

ccg-workflow Shell 技能指南:Bash 脚本自动化、系统管理与多模型协作实战

【免费下载链接】ccg-workflow 多模型协作工作流引擎 — /ccg:go 一个命令,AI 自动分析意图、选择策略、编排 Codex Gemini Claude 协作执行 项目地址: https://gitcode.com/gh_mirrors/cc/ccg-workflow 点击查看 免费下载 导读 本文基于 ccg-workfl…

2026/10/12 3:39:10 阅读更多 →
2026年软件测试趋势:AI Agent、质量内建与可观测性重塑质量保障

2026年软件测试趋势:AI Agent、质量内建与可观测性重塑质量保障

做测试这行,每年年底都在猜明年的技术方向,但2026年这次不太一样。我最近和不少测试负责人、开发团队聊下来,大家最焦虑的已经不是又冒出了什么新工具,而是整个质量体系正在被 AI 和平台工程重构,很多沿用多年的测试方…

2026/10/12 3:39:10 阅读更多 →
netdxf实战:DXF文字注释与尺寸标注的创建与修改

netdxf实战:DXF文字注释与尺寸标注的创建与修改

接触过DXF开发的人应该都有这种感觉:画直线、画圆、画多段线都属于“基本功”,真正让图纸变得可读、可传递设计意图的,是文字注释和尺寸标注。这一篇是整个netdxf系列里我比较想写的一篇,因为注释和标注的处理逻辑和普通几何实体完…

2026/10/12 3:39:10 阅读更多 →
文华财经主升浪买点指标公式拆解:多条件共振识别趋势启动

文华财经主升浪买点指标公式拆解:多条件共振识别趋势启动

1. 文华财经主升浪买点指标的实战拆解做期货日内或者波段的朋友,应该都听过“主升浪”这个词。行情走主升浪的时候,速度最快、幅度最大,但也是最难拿得住的一段。很多朋友在文华财经软件里翻遍了各类指标公式,要么信号滞后&#x…

2026/10/12 3:39:10 阅读更多 →
ppt-master 的 IBM 品牌身份预设解析:从 Carbon Blue 设计规范到可执行的 design_spec

ppt-master 的 IBM 品牌身份预设解析:从 Carbon Blue 设计规范到可执行的 design_spec

AI 技能人工智能 【免费下载链接】ppt-master AI 把任意文档生成真正可编辑的 PowerPoint —— 原生形状与动画、演讲者备注可合成音频旁白、还能参考你自己的 .pptx 模板,而不是一张张图片 何雨果出品 项目地址: https://gitcode.com/hugohe3/ppt-master 点击查看…

2026/10/12 3:38:09 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →