撤销为什么只需要 pop:用事件重放实现离线台球计分
📅 2026/7/29 19:17:58
👁️ 次浏览
计分板最常见的 bug不是按钮没响应而是比分、开球方、让局和撤销各维护一份状态某个分支忘记同步其中一项。台球工具选择只记录“发生了什么”把“现在是什么状态”全部重算出来。一、先把事实和结果分开以中式八球抢局制为例页面需要展示两名选手当前比分本局开球方是否已经达到抢局目标炸清、接清等特殊胜局次数撤销上一局后的所有状态。直觉实现会在点击“选手 A 获胜”时同时执行scoreA、切换开球方、更新特殊次数并判断获胜。这样做的问题是撤销时必须精确执行所有反操作新增一种开球规则又会影响多个分支。这个思路最初用于「零碎百宝箱」的台球计分功能目标是在球房断网时仍能完整记下一场比赛。项目只保存不可再分的事实frames:[{winner:A,type:normal,at:1720000000000},{winner:B,type:clear,at:1720000060000}]比分、胜者和开球方都是派生值functionscoreOf(match,side){consthandicapsideA?match.handicapA:match.handicapB;returnhandicapmatch.frames.filter(ff.winnerside).length;}functionmatchWinner(match){if(scoreOf(match,A)match.raceTo)returnA;if(scoreOf(match,B)match.raceTo)returnB;returnnull;}撤销上一局因此只需frames.pop()随后所有派生函数自然得到上一时刻的状态。二、事件重放消除了“双重真相”事件模型的核心并不是用了数组而是规定数组是唯一事实来源。如果同时持久化frames和scoreA二者迟早会不一致。frames 事件序列比分当前开球方比赛胜者特殊胜局统计结算详情开球方也从历史推导轮流开球已完成局数的奇偶决定当前开球方胜方开球取上一局赢家负方开球取上一局赢家的另一方。这些函数可以独立测试。页面组件不需要知道各种规则怎样组合只负责渲染计算结果和追加事件。三、两套玩法共用一个思想台球工具同时支持中式八球抢局和九球追分。追分不是简单的“多人比分加减”它有普胜、银九、大金、小金、犯规等事件不同事件决定谁向谁转移多少分还会影响下一局出杆顺序。追分对局保存{players:[A,B,C],initialScore:100,values:{normal:5,silver:10,golden:20},firstOrder:[0,1,2],events:[{type:normal,winner:0,payer:1,at:1720000000000},{type:golden,winner:2,payer:null,at:1720000060000}]}payernull表示其余所有人都向赢家支付。重放函数从每人的初始分开始逐条转移for(consteventofmatch.events){constvaluematch.values[event.type];if(event.payernull){for(leti0;iplayers.length;i){if(i!event.winner){scores[i]-value;scores[event.winner]value;}}}else{scores[event.winner]value;scores[event.payer]-value;}}这段算法天然维持一个不变量sum(scores) initialScore * playerCount如果任何操作后总分变化就说明转移规则实现有误。把业务规则转化为可断言的不变量是提高计分类软件可靠性的有效方法。四、出杆顺序也可以重放追分模式的当前顺序并不单纯按局数轮换。赢家应排到首位单一输家排第二其余人保持上一局相对顺序大金没有单一输家则只把赢家提到首位。犯规属于局中处罚不改变下一局顺序。实现用事件类型集合区分哪些事件会结束一局再从firstOrder依次重放constORDER_EVENTSnewSet([normal,silver,gold9,golden]);for(consteventofevents){if(!ORDER_EVENTS.has(event.type))continue;constrestorder.filter(ii!event.winneri!event.payer);orderevent.payernull?[event.winner,...order.filter(ii!event.winner)]:[event.winner,event.payer,...rest];}分数和顺序都来自同一串事件因此撤销一个犯规只恢复分数不会错误改变顺序撤销一局胜负则两者一起回到正确状态。五、兼容旧存档要看数据形状软件升级后同一个玩法代码可能改变计分模型。项目曾经存在九球抢局存档后来九球改为追分。如果只根据mode nine-ball判断旧存档会被错误送进需要players/events的追分页面。实现改为按数据形状判断exportfunctionisChase(match){return!!(matchArray.isArray(match.players));}这是一种实用的兼容策略当持久数据没有明确 schema version 时使用不会歧义的结构特征识别旧格式。更长期的方案是在新数据中加入schemaVersion并编写显式迁移但在已有用户存档无法补字段时形状检测仍是必要兜底。六、本地是主存储云端只是备份球房网络不稳定时点击记分必须立即生效。当前对局、设置和最近 100 条历史都先写入带billiards:命名空间的本地 storage。比赛结束后再静默尝试上传。每场比赛创建一个客户端幂等键m${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}服务端按 openid 和clientKey去重所以网络重试不会制造重复比赛。同步流程为处理此前离线删除留下的待删队列拉取云端记录与本地按clientKey合并推送所有未同步且已经完赛的本地记录。删除也不能只操作本地。已同步记录删除后会把clientKey放进pendingDeletes下次联网时补删云端在删除完成前拉取逻辑会跳过同 key 的远端记录防止它“复活”。这种设计明确牺牲了多人同时编辑一场比赛的能力换取球桌上最重要的体验任何时候都能记分杀进程后也能恢复当前对局。七、结算数据为何可以适度冗余进行中状态坚持事件唯一来源但历史列表需要快速展示最终比分。完赛入库时抢局模式会附加scoreA、scoreB和winner追分模式附加finalScores。这不是重新引入双重真相因为它们是事件序列在“完赛时刻”的不可变快照不再参与后续计分。历史卡片可以直接读取详情页仍能用事件复核。判断是否可以冗余的标准是源数据是否保留快照是否只在明确的生命周期节点生成快照生成后是否还会被独立修改。满足这三点适度反规范化能简化查询否则就会变成两个可写字段互相打架。八、事件溯源不等于必须上复杂基础设施这里没有消息队列、事件总线或专用事件数据库只有普通 JavaScript 数组和 JSON 持久化但已经获得了事件建模的核心收益状态计算是确定性的撤销成本低新统计可以从旧事件补算业务不变量容易测试云端可以保存完整过程而非只有最终分数。它也有边界。事件无限增长会让每次重放变慢复杂业务还要处理事件版本迁移。台球一场比赛的事件数量有限直接从头重放最简单若扩展到数万事件的长期系统再引入周期快照。九、可复用的设计判断计分工具适合事件重放通常因为它同时满足三点操作可以表达为离散事件当前状态能由事件确定计算用户有频繁撤销需求。实现时应守住以下约束只持久化事件和必要初始条件不持久化可变派生状态把所有规则写成无副作用的推导函数为零和、局数和顺序等业务不变量编写测试明确事件 schema升级时识别或迁移旧存档把离线删除也建模为需要补偿的同步动作。当撤销需要写一大串反向逻辑时通常不是撤销太复杂而是状态保存得太多了。
Path of Building终极指南:5分钟掌握流放之路离线构筑模拟器 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/gh_mirrors/pat/PathOfBuilding
想要在《流放之路》中打造完美角色却总是浪费资源&am…
📅 2026/7/29 19:17:58
RaZ引擎资源管理最佳实践:模型、纹理与动画加载优化策略 【免费下载链接】RaZ Modern & multiplatform 3D game engine 项目地址: https://gitcode.com/gh_mirrors/ra/RaZ
RaZ是一款现代化跨平台3D游戏引擎,提供高效的资源管理系统帮助开发者…
📅 2026/7/29 19:17:58
在上一篇《全球供应链风险加剧,IT基础设施如何从容应对价格变量?》中,我们探讨了在算力硬件紧缺、预算吃紧的大背景下,用户可以通过“老旧服务器资源优化路径,在极小性能损耗下,实现主机可用内存的翻倍。 这…
📅 2026/7/29 19:16:58
Loop终极指南:如何用免费开源工具彻底改变你的Mac窗口管理体验 【免费下载链接】Loop Window management made elegant. 项目地址: https://gitcode.com/GitHub_Trending/lo/Loop
你是否厌倦了在Mac上不断拖拽、调整窗口的繁琐操作?每天面对杂乱无…
📅 2026/7/29 20:09:20
3步打造智能媒体库:MetaTube插件完全指南 【免费下载链接】jellyfin-plugin-metatube MetaTube Plugin for Jellyfin/Emby 项目地址: https://gitcode.com/gh_mirrors/je/jellyfin-plugin-metatube
MetaTube插件是专为Jellyfin和Emby媒体服务器设计的智能元数…
📅 2026/7/29 20:09:20
一、基础定义
Modbus Poll 是 Modbus 主站(Master)仿真调试软件,由 Modbustools 公司开发,Windows 平台专用工业调试工具,
核心作用:模拟 PLC / 上位机,主动下发 Modbus 指令,读写…
📅 2026/7/29 20:09:20
微信聊天记录备份终极方案:WechatBakTool完整使用指南 【免费下载链接】WechatBakTool 基于C#的微信PC版聊天记录备份工具,提供图形界面,解密微信数据库并导出聊天记录。 项目地址: https://gitcode.com/gh_mirrors/we/WechatBakTool
…
📅 2026/7/29 20:09:20
3分钟搞定Windows与Office激活:KMS智能脚本全攻略 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO
还在为Windows系统激活弹窗烦恼吗?或是Office办公软件提示"产品未…
📅 2026/7/29 20:09:20
终极指南:如何用MAA明日方舟助手实现游戏自动化,每天节省3小时游戏时间 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clien…
📅 2026/7/29 20:08:20
解密Seq的核心功能:如何利用Pipeline实现高效基因组数据处理 【免费下载链接】seq A high-performance, Pythonic language for bioinformatics 项目地址: https://gitcode.com/gh_mirrors/se/seq
Seq作为一款高性能的生物信息学专用语言,其Pipel…
📅 2026/7/29 0:00:00
Flask-Blogging插件开发指南:打造属于你的个性化博客功能 【免费下载链接】Flask-Blogging A Markdown Based Python Blog Engine as a Flask Extension. 项目地址: https://gitcode.com/gh_mirrors/fl/Flask-Blogging
Flask-Blogging是一个基于Markdown的Py…
📅 2026/7/29 0:00:00
近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…
📅 2026/7/29 0:01:00
更多请点击:
https://codechina.net
第一章:AI帮助理解数学概念 人工智能正以前所未有的方式重塑数学学习的路径。通过自然语言处理与符号计算的深度融合,AI不仅能解析抽象定义,还能将定理、证明和几何直觉转化为可交互、可验证的…
📅 2026/7/29 1:14:44
1. 项目背景与核心价值去年参与的一个短剧项目让我深刻体会到传统创作流程的痛点:编剧团队花了三周打磨剧本,角色设计反复修改了七版,最后成片时又因为演员档期问题不得不临时调整分镜。这种低效的创作模式在快节奏的内容行业越来越难以为继。…
📅 2026/7/29 1:14:44
remix-i18next TypeScript类型安全实践:确保翻译键与类型定义同步 【免费下载链接】remix-i18next The easiest way to translate your React Router framework mode apps 项目地址: https://gitcode.com/gh_mirrors/re/remix-i18next
在开发多语言应用时&am…
📅 2026/7/29 1:14:46
目录
第一步:选对模板,省心一半
第二步:打开扫码点餐功能
开启功能按钮
桌台管理与桌码生成
第三步:个性化设计,打造品牌感
调整点餐页面
设置点餐规则 你还在让顾客站着排队点餐吗?2025年ÿ…
📅 2026/7/29 7:15:11
在业务中快速构建一个能理解私有文档、准确回答专业问题的智能助手,是很多开发团队面临的共同挑战。传统方案往往需要从零开始搭建复杂的 RAG(检索增强生成)系统,涉及文档解析、向量化、检索、大模型调用等多个环节,整…
📅 2026/7/29 17:15:46
FAE放射组学分析工具:医学影像特征探索的完整解决方案 【免费下载链接】FAE FeAture Explorer 项目地址: https://gitcode.com/gh_mirrors/fae/FAE
你是否曾经面对海量医学影像数据感到无从下手?想要从CT、MRI等影像中提取有价值的定量特征&#…
📅 2026/7/29 5:15:05