页面能打开,不代表规则正确:单 HTML 游戏的纯规则核心与不变量测试
我以前给浏览器小游戏做回归时最容易得到一种虚假的安全感页面能打开点击开始后没有报错Canvas 不是空白手机宽度没有横向滚动条。这些都应该检查但它们只能证明界面大致活着。它们回答不了另外三个问题编程解谜发布的 12 个关卡是否真的都存在一条符合预算的解证件审查显示允许入境时违规原因是否一定为空生态模拟连续推进数百年后是否会出现负种群、NaN或超过上限的环境值这三个问题无法靠截图回答也不适合全部交给端到端点击脚本穷举。我最后采用的做法很朴素把规则核心从 DOM 事件里拆出来让界面和测试调用同一套函数。项目仍然是零依赖、单 HTML 文件。没有为了测试把它改成框架工程也没有增加后端变化只发生在代码边界。本次验证范围这次重构对应三个不同类型的规则系统规则类型核心问题验证样本核心入口编程解谜关卡是否可解指令预算是否真实12 个参考程序simulate(levelIndex, program)证件审查裁定与违规原因是否一致6 日 30 宗档案evaluateCase(day, data)生态模拟长期状态是否始终处于合法边界6 个生态区 × 45 年advanceYear(input, scenario, weatherIndex)专项测试还会实际操作三个页面并检查三星、跨设备档案、Canvas 像素和 390×844 触屏布局。随后执行全仓审计共覆盖 100 个游戏在桌面和手机上的 200 个页面组合。结果为加载失败 0、JavaScript 错误 0、控制台错误 0、横向溢出 0、viewport 缺失 0。这里需要区分两层证据模型测试证明特定规则约束成立浏览器审计证明这些规则接入真实页面后没有破坏运行和布局。两者不能互相替代。先划清四层边界图 1场景数据进入纯规则函数得到结构化结果再交给 DOM、Canvas 和存档层三个专项检查都发生在规则核心而不是复制一份测试专用逻辑。原型阶段的代码通常长这样button.onclick () { // 读取页面字段 // 修改全局变量 // 计算结果 // 更新 DOM // 顺便保存 localStorage };它不是写错了而是把五种职责塞进了同一个入口。当内容只有一关、一个旅客或一个年度时这种写法很快。内容量增加以后问题开始出现测试必须点击按钮才能触发规则想验证一个边界要先把页面操作到指定状态结果文本可能和真实布尔裁定来自两套if随机输入让失败难以复现存档导入后很难单独确认数据清洗是否正确。重构后的边界分成四层场景数据关卡、日规、气候序列和目标纯规则函数只接收输入返回新状态或裁定结果结构化结果状态、原因、趋势和星级所需指标外层适配DOM、Canvas、音效、键盘、触屏和localStorage。测试不再模拟另一套规则而是直接调用第二层。三种规则三种验证方式图 2三个页面分别处理指令执行、证件裁定和长期模拟截图证明它们真实运行规则正确性则由页面内同源核心和专项测试共同给出。这三类玩法不能只写一个通用的测试成功断言。它们需要不同的可验证对象。1. 可执行关卡夹具参考解必须走同一解释器编程解谜最危险的数据问题不是页面崩溃而是关卡看起来完整却没有合法解。我给每个关卡保留一条参考程序{ name: 最终上传, map: [ ############, #..S~.O####, ############, ####O..D.cG#, ############ ], budget: 8, par: 11, solution: F3JFF33F }参考程序不是显示给玩家的答案而是关卡数据的可执行夹具。页面启动时校验器会检查地图每一行宽度一致起点和终点各有一个传送环成对出现参考程序没有超过源码预算程序通过真实解释器执行后收齐芯片并到达终点实际执行步数没有超过关卡目标。核心模拟函数与玩家点击运行时使用的是同一个function simulate(levelIndex, program) { const expanded expandProgram( Array.isArray(program) ? program : [...program] ); const runtime createRuntime(levelIndex); if (!expanded.ok) { return { ...runtime, reason: expanded.reason, expanded: [] }; } for (const token of expanded.commands) { executeCommand(runtime, token); if (runtime.stopped || runtime.won) break; } return { ...runtime, expanded: expanded.commands }; }这次校验确实抓到过问题部分参考解把×2/×3理解成重复整个分组而解释器实现的是重复上一条原子指令。页面可以正常打开地图也能画出来但 10 个参考解与真实语义不一致。如果没有让参考解通过同一解释器这类问题很容易被当成以后手测关卡再说。2. 原因型规则函数结果和解释必须同源证件审查的旧结构会先随机制造旅客再在多个位置判断姓名、国籍、有效期和许可章。这种写法容易出现一种尴尬状态界面说拒绝日志却给出无拒绝理由。新的规则函数不只返回布尔值而是先收集原因function evaluateCase(day, data) { const rule DAY_RULES[day]; const reasons []; if (data.expiry rule.expiry) { reasons.push(护照有效期早于 ${rule.expiry}); } if (rule.banned data.nation rule.banned) { reasons.push(${rule.banned}公民暂停入境); } if (rule.permit (!data.hasPermit || data.permitName ! data.name)) { reasons.push(data.hasPermit ? 通行证姓名与护照不符 : 缺少临时通行证); } return { allowed: reasons.length 0, reasons }; }这样有两个直接收益。第一allowed不再和解释分开维护。只要原因数组非空就必须拒绝。第二UI、日志和测试都能复用reasons。玩家收到的是具体规则反馈测试也能检查item.allowed (item.reasons.length 0)六日 30 宗档案使用确定性索引生成。同一个day slot重复构造得到的对象必须完全一致。这里的确定性不是取消变化而是让变化来自可追踪的规则和数据而不是无法复现的Math.random()。3. 纯状态转移长期模拟先守住不变量生态模拟的问题不是某一年算对就结束了。一次很小的比例错误可能在第 20 年、第 40 年才变成负种群或NaN。如果每次推进都依赖 DOM 和动画长时间压力测试会很慢也很难定位。年度演替因此被抽成纯状态转移function advanceYear(input, scenario, weatherIndex) { const weather WEATHER[weatherIndex]; const next { ...input, pop: [...input.pop], projects: { ...input.projects }, extinctions: [...input.extinctions] }; // 根据营养层、环境和气候计算 next.pop next.water Math.round(clamp(next.water, 10, 100)); next.soil Math.round(clamp(next.soil, 10, 100)); next.habitat Math.round(clamp(next.habitat, 10, 100)); next.year input.year 1; return next; }函数不读取按钮、不绘制 Canvas也不写存档。测试可以从六个生态区的初始状态出发各自连续推进 45 年共检查 270 个模拟年for (let year 0; year 45; year) { sample advanceYear( sample, scenario, scenario.weather[year % scenario.weather.length] ); assert(sample.pop.every((value) value 0 value 999)); assert([sample.water, sample.soil, sample.habitat] .every((value) value 0 value 100)); }这不是为了证明数值平衡完美而是先守住最基本的不变量所有数值有限种群不为负环境指标不越界输入状态不会被意外原地污染同一气候序列能够重复运行。浏览器测试仍然需要把规则函数拆出来不代表可以删除 Playwright。纯函数不知道下面这些事情玩家按键是否真的进入程序队列裁定按钮是否调用了正确的旅客数据十五次推进一年是否更新了图表与结算档案码是否能经过页面入口导出、清洗和导入Canvas 是否绘制了非空且有颜色差异的像素390×844 手机视口是否横向溢出。因此专项脚本保留真实浏览器层node promo-video/scripts/check-rule-driven-games.mjs图 3专项测试验证 12 个参考程序、30 宗确定性档案和 270 个模拟年全仓审计覆盖桌面与手机共 200 个页面组合。本轮浏览器错误、加载失败和横向溢出均为 0。专项测试实际执行了这些动作在编程页面点击四次前进运行后确认首关获得三星按规则完成第一日五宗裁定确认 5/5、三星和下一日解锁建成滴灌网络连续点击推进 15 年确认四项目标、零灭绝和三星对BYTE2、CHECK2、ECO2三种档案码执行往返在桌面和触屏上下文中收集浏览器错误并检查布局。然后再运行全仓审计node promo-video/scripts/audit-games.mjs模型测试负责规则是否满足约束浏览器测试负责真实页面是否正确接入。这比让端到端脚本承担所有组合更快也更容易在失败时定位到具体层。这套方法没有证明什么自动化结果必须和它真正覆盖的范围一致。参考程序不等于关卡质量12 个参考程序全部可解只能证明至少存在一条合法路径。它不能证明关卡只有一条解难度曲线一定合理玩家能自然理解新指令最优解没有更短。要回答这些问题需要搜索算法、游玩数据或人工测试而不是把可解写成好玩。270 个模拟年不等于生态模型真实不变量测试能发现负数、越界和非数值但不能证明生态参数符合现实世界。这里的模型服务于策略游戏反馈不是科学预测工具。文章中的 270 年是自动化执行规模不是实验或现实数据。确定性档案不等于覆盖所有证件组合30 宗档案覆盖当前六日规则但姓名、国籍、目的和多重违规仍然存在更多排列。后续如果规则继续增加更适合引入属性测试或组合生成而不是无限手写固定案例。无浏览器错误不等于所有设备兼容本轮真实检查的是 Playwright 驱动的桌面 1440×900 与移动端 390×844 上下文。它不能替代 Safari、低性能设备、辅助技术和真实触摸延迟测试。什么时候值得这样拆不是每个 200 行小游戏都需要完整测试架构。当项目出现下面任意两项时我会优先拆规则核心相同规则被 UI、日志、结算和存档重复使用内容数据开始批量增加存在随机或长时间演化失败后很难判断是界面错误还是规则错误需要跨设备档案或版本迁移每次改数值都必须手工重玩十几分钟。最小可行改法也不复杂让规则函数接收明确输入返回结构化结果不直接修改 DOM给场景数据保存可执行样例或预期原因为长期状态写出不会被破坏的不变量最后用少量真实浏览器流程验证接线。结语单 HTML 不等于只能写一次性脚本。即使所有 CSS、界面和 JavaScript 都在一个文件里仍然可以划分清楚的模型边界数据描述问题纯函数执行规则结构化结果解释发生了什么页面只负责交互和呈现。这样做最直接的价值不是测试数量更多而是规则开始能够独立回答这关为什么可解这个人为什么被拒绝这个状态为什么仍然合法相关游戏源码、专项脚本和全仓审计都在开源仓库https://github.com/wangzifan396-wzf/mini-browser-games当页面能打开时测试才刚刚开始。真正需要被验证的是按钮背后那套会持续产生结果的规则。

相关新闻

SAGE框架:子目标条件化动作生成在强化学习规划中的应用

SAGE框架:子目标条件化动作生成在强化学习规划中的应用

在强化学习和机器人控制领域,如何让智能体在复杂环境中高效规划并执行动作一直是个核心挑战。传统的规划方法往往面临计算复杂度高或难以处理高维状态空间的困境。近期提出的 SAGE(Subgoal-Conditioned Action Generation)框架,通…

2026/10/17 4:29:15 阅读更多 →
Linear Loops自动化工作流:提升团队开发效率的完整指南

Linear Loops自动化工作流:提升团队开发效率的完整指南

在项目迭代和团队协作中,重复性的任务流转、状态同步和跨工具数据搬运往往消耗大量开发时间。Linear 最新推出的 Loops 功能,正是瞄准了这一痛点,旨在通过自动化工作流简化循环工程操作。本文将完整解析 Loops 的核心概念、适用场景&#xff…

2026/10/16 12:45:55 阅读更多 →
60%的知识库文档从未被检索过——你在用20%的文档回答100%的问题

60%的知识库文档从未被检索过——你在用20%的文档回答100%的问题

核心观点:知识库不是“越多越好”。我去跑了一个查询,盯了半天——六成的文档,过去30天一次都没被搜过。 我去做了件大多数人不会做的事:给知识库里每一条文档,查一下它过去30天被检索过多少次。 1200条FAQ。我按检索…

2026/10/16 11:50:06 阅读更多 →

最新新闻

2026年AI编程工具全解析:33款主流助手与IDE选型指南

2026年AI编程工具全解析:33款主流助手与IDE选型指南

2024年大家还在争论“AI能不能写代码”,到2026年这个问题基本不需要讨论了。我今年在好几个项目里把AI编程工具从“偶尔试用”变成了“日常主力”,从代码补全到自动化测试再到完整功能模块的实现,工具链已经成熟得超出预期。但问题也变成了另…

2026/10/17 21:02:08 阅读更多 →
Vibe Coding第二课:零基础搭建个人主页和数字分身

Vibe Coding第二课:零基础搭建个人主页和数字分身

Vibe Coding这个词,最近在开发者圈子里被反复讨论,但对真正零基础的人来说,它最诱人的点其实特别简单:你不需要先把编程学个半吊子,就能做出能给别人看的东西。第二课我们直接来干一件特别实在的事——从0到1搭一个你自…

2026/10/17 21:02:08 阅读更多 →
分步傅里叶法解NLS方程:MATLAB仿真孤子传输实战

分步傅里叶法解NLS方程:MATLAB仿真孤子传输实战

简介:非线性光纤光学与光通信领域的学习者、研究人员,常需掌握分步傅里叶法求解非线性薛定谔方程(NLS)的数值实现。该资源包提供了一份MATLAB源代码及详细解析文档,内容涵盖NLS方程建模、分步傅里叶法原理、代码结构梳…

2026/10/17 21:02:08 阅读更多 →
MATLAB调用外部软件:控件化封装与COM/system/MEX选型实战

MATLAB调用外部软件:控件化封装与COM/system/MEX选型实战

1. 项目缘起与整体设计思路1.1 为什么要在MATLAB里“调用”外部软件做过工程仿真或者数据处理的朋友大概率遇到过这种场景:手头有一套用MATLAB写的算法主流程,但某个关键环节依赖一个独立的第三方软件——可能是某个专业的求解器、一个老旧的C计算程序、…

2026/10/17 21:02:08 阅读更多 →
全网最全Python课程PDF如何高效利用:拆目录、验代码、避坑指南

全网最全Python课程PDF如何高效利用:拆目录、验代码、避坑指南

简介:一份以 PDF 文档形式整理的全网 Python 系统课程学习路线,适合零基础入门到希望进阶的开发者使用。文档提炼了 637 集课程的整体框架,按 Python 基础、进阶、终极、爬虫四篇展开,覆盖 Python 基础语法、坦克大战实战、网络并…

2026/10/17 21:02:08 阅读更多 →
约瑟夫环C语言循环链表实现:从建环到出圈的完整方案

约瑟夫环C语言循环链表实现:从建环到出圈的完整方案

简介:这是一份面向C语言初学者与数据结构学习者的约瑟夫死亡游戏完整实现资料,围绕循环链表这一核心结构展开,帮助读者理解环形链表的创建、遍历与结点删除等经典操作。资源包内仅含1个docx文档,大小约18KB,以文字形式…

2026/10/17 21:01:07 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →