开发环境:TraeCode(Spec 驱动,产出 spec.md / tasks.md / checklist.md 三份规格件)
作品形态:纯前端多文件网页,双击 index.html 即开;零依赖、零构建、零网络请求
出处:应本活动评论 第 141 楼 而做
一、我做了什么
一个叫「骰子酒令」的骰子游戏合集。三套玩法共用一个外壳、一套 CSS 3D 骰子组件、一套可复核的公平机制:
玩法出处一句话
闽南博饼
福建中秋民俗(国家级非遗)
6 颗骰定级,一套 63 个奖品,含状元易主与对堂追饼
大话骰 / 吹牛
酒吧
每人 5 颗暗骰,叫「N 个 M」,可加斋、飞、开、劈、连开、通杀
骰宝(Sic Bo)
赌场 / 酒局
3 或 4 颗骰,押大、小、豹子、和值,按赔率派彩
三套都是单机轮流(pass-and-play),一台设备传着玩,2–12 人。
三个地方是刻意做的:
打开即用不是口号,是硬约束——为了 file:// 双击能开,全程没用 ES Module(会被 CORS 拦),改用 classic <script> 按序加载;没有一个网络请求,没有一步构建。
骰子是可复核的——所有点数来自一个可播种的伪随机数生成器,每次掷骰都进审计日志(局号 / 种子 / 逐颗点数 / 时间戳);审计页可以输入种子复现,对不上会逐颗标出是哪一颗不符。
规则速查页不是手抄的——它由各玩法规则引擎的判定表直接生成。文档和实现共用同一份真值源,从根上断绝「说明写一套、代码判另一套」。
二、灵感来自哪条评论
来自活动帖 第 141 楼:
我想要一个【中秋家庭博饼小工具】,用来解决【中秋全家聚餐想玩博饼,但人在外地凑不齐骰子和瓷碗,规则每年都要现场吵一遍,散场后状元到底被谁抱走了全靠回忆】的问题。最好能有【手机摇一摇掷出六个骰子,带碗底晃动动画和骰子碰撞声,自动判定一秀/二举/四进/三红/对堂/状元,全家人拉进同一局实时积分排行,奖品自定义录入,一局结束自动结算谁抱走状元】。
「规则每年都要现场吵一遍」是我最有共鸣的一句——所以本项目把博饼里所有有争议的判定点都做成了可切换项,而不是替用户选边。
博饼之外,我顺手把酒桌上最常出现的另外两套骰子玩法(大话骰、骰宝)也并进了同一套外壳。理由是:中秋家宴喝完,国庆聚会大概率还要再来一轮,多一副骰子不多事。
三、它解决了什么假期问题
凑不齐骰子和瓷碗:外地过节、视频连线,打开网页就能博。3D 瓷碗、骰子抛入、碗内弹跳收面——气氛不用靠实物补。
规则每年吵一遍:内置判定表 + 争议项可配置。开博前在「设置」里把有分歧的几条定好(例如「六扑红 vs 状元插金花谁大」),整局按此执行。
散场后状元归谁全靠回忆:63 个奖品是一个可视库存网格,谁拿了哪一等、还剩几个一目了然;满盘自动终局,弹出每人所得奖品明细,并标注状元得主。
朋友聚会想玩点别的骰子:大话骰有斋 / 飞 / 劈 / 通杀,骰宝有赔率派彩,都在这一个页面里。
四、技术要点(这部分比界面更费心思)
4.1 打开即用:把「零依赖」当验收项
不用 ES Module(file:// 下 import 会被 CORS 拦),改用 classic script + 全局命名空间 DG.* 按序加载。
零网络请求、零构建步骤、零第三方库。CSS 与 JS 全是本地文件,字体走系统字体栈。
本地存储所有读写都做了 try/catch:隐私模式或限制环境下降级为纯内存,绝不因为存不上就崩掉整局。
4.2 可复核的公平:种子化 RNG + 掷骰审计
PRNG:Mulberry32(状态仅 1 个 32 位整数,周期 2³²,且没有「种子 0」这种退化态)。
字符串种子:FNV-1a 32 位哈希 + 末轮雪崩混合,中文等非 ASCII 也不退化。同字符串必然同种子。
取点数:拒绝采样,不用 % 6——取模会给某些面多一个来源,那本身就是偏差。
审计日志:每次掷骰记 局号 / 种子 / 逐颗点数 / 时间戳;一条记录只由「种子 + 骰数」决定,因此可独立复现。
复现校验:审计页点「复现校验」,用种子重跑并与记录的逐颗比对,不符会标出「第 N 颗:期望 X,声称 Y」。
多批掷骰也各自可查:博饼开局是「先掷 2 颗定起博者,再掷 6 颗」——如果第二批还用第一批的种子入库,就会被误判成作弊。所以导出的是每批的批起始种子,每一批都能单独复核。
4.3 CSS 3D 骰子:动画终态必须等于引擎读数
每颗骰是 6 个面 div 组成的真 3D 立方体(transform-style: preserve-3d),对面和为 7(1↔6、2↔5、3↔4)。
闽南「红四」用红色 pip——这是博饼判定的核心视觉,绝不能所有点数一个色。
掷骰不是刚体物理,是编排动画:抛物线抛入 → 碗内衰减弹跳 → 阻尼收敛到目标面。好处是结果可控。
关键取舍:点数先由引擎算定,动画只是把它演出来;动画收敛时把立方体锁死到该点数对应的精确旋转(settle),所以视觉朝上的面与规则引擎读到的点数逐颗一致,不存在「动画演歪了、结果却是另一个」。
性能:全部骰子由单个 requestAnimationFrame 循环驱动;will-change 只在动画期间挂,结束即移除;位移一律 translate3d 走合成层。骰子数到 60(大话骰 12 人 × 5)时自动缩小尺寸保证落碗。
4.4 规则速查与实现在同一真值源
每个规则引擎都导出一个 ruleRows(),速查页只做渲染、不手写第二份判定表。若某天改了判定条件而忘了改文档,速查页会跟着自动变——因为「写两遍」这件事从结构上被禁止了。
地域分歧显式标注:存在分歧的条目在速查页打上「地域分歧」标记,例如:
分歧点处理
六扑红 vs 状元插金花 孰大可切换,判定表随之重排
六扑黑 = 全员福利 / 关灯抢饼可切换
大话骰起叫门槛(人数+1 / 人数×1.5)可切换
半劈翻倍、投降减半、围骰全圈奖励逐项可配置
五、对照原评论逐条核对
原评论要求本项目实现情况
手机摇一摇掷出六颗骰子未做。改为按钮掷骰(摇一摇涉及 DeviceMotion 与 iOS 手势授权,属另一条实现路径)
碗底晃动动画已做。3D 瓷碗 + 骰子抛物线抛入 → 碗内衰减弹跳 → 阻尼收面
骰子碰撞声未做。列为非目标:音频要等首次交互,会破坏「打开即用」的即时性
自动判定一秀/二举/四进/三红/对堂/状元已做。判定表自高至低「先匹配即止」,覆盖六扑红/状元插金花/五红/五子/四红/四进/对堂/三红/二举/一秀/六扑黑
全家人拉进同一局实时积分排行部分做。没做联网,改为同一设备轮流(pass-and-play)2–12 人,榜单与奖品库存实时更新;异地只能各玩各的
奖品自定义录入未做。奖品为固定六等 63 件
一局结束自动结算谁抱走状元已做。63 件满盘即终局,弹出每人奖品明细并标注状元得主
额外做的(原评论没提,但为了玩法完整):
状元易主:后续玩家博出更高等级的状元,奖品易主,并明示双方等级高低。
对堂追饼:2 个对堂取完后仍博到对堂,从反方向最近的上一位对堂获得者处追回 1 个。
起博者确定:开局掷 2 颗骰得点数 n,从首位玩家起(本人算第 1 人)数到第 n 人。
大话骰完整链路:斋 / 飞 / 开 / 劈 / 反劈 / 连开 / 通杀,码数换算(开 1 码、劈 2、反劈 4)。
骰宝赔率表:3 骰含和值押注(1 赔 6–60),豹子不计入大小。
断点恢复:刷新页面若存在未结束牌局,提示恢复或新开局。
六、用 TraeCode 创作的过程
我这次没有「一句话让它生成整个游戏」,而是先让 TraeCode 产出规格件,再按 Task 逐块实现、逐块验收。
阶段〇 · 写规格,不写代码
先落三份文件:spec.md(要什么、判定表、验收场景)、tasks.md(拆到 SubTask,标依赖关系)、checklist.md(交付前逐项打勾的清单)。判定表在这一步就定死了,避免实现中途「边写边编规则」。
阶段一 · 外壳 + 设计系统 + 存储:入口页、设置页、弹层、提示条;配色走浅灰白底 + 金/琥珀强调(骰子是象牙白、四点为红);localStorage 封装成失败可降级。
阶段二 · 种子化 RNG + 审计:PRNG、拒绝采样、审计日志、复现校验、审计页。
阶段三 · CSS 3D 骰子组件(三套玩法共享的依赖,所以最先做扎实):立方体、红四点、投掷编排动画、终态锁定、单 rAF 循环。
这一步做了一次数值校验而非目测:立方体三组对面和为 7;FACE_ROT 能把 6 个点数面各自转到朝上,且在 4 种自旋角下24/24 全部成立;终态朝上面唯一 6/6 通过。
阶段四 · 三套玩法引擎 + 界面(互不依赖,可并行):博饼、大话骰、骰宝各自的纯函数引擎,加上「开盅前的骰盅扣合态」(旋转页面也看不到点数)。
阶段五 · 规则速查 + 端到端验证:速查页接引擎的 ruleRows();三套玩法各跑通一整局,核对判定结果与动画终态一致。
七、设计取舍与已知限制
多人为什么是同一台设备轮流,不是联网对战?
酒桌上本来就是这个玩法:一台手机传着玩,比各自盯屏幕更接近真实场景;而且没有后端,才可能做到单文件首发、离线可用、发给谁都能直接打开。异地视频连线的场景我如实承认没覆盖——需要真联网版本的话可以后续加。
为什么不做音效? 浏览器自动播放策略要求首次交互后才能出声,和「打开即有反馈」冲突;这一期把它列为非目标,宁可不做也不做半吊子。
骰子物理是表现层动画,不是真实物理模拟——最终定格面由判定结果决定。这是刻意的:保证结果一定正确。
明确未做:摇一摇、骰子碰撞音效、联网实时排行、奖品名自定义、牛九/一六(缺少可靠规则资料,不凭印象实现)。
八、最后
闽南有句话,「博有博无,欢喜就好」。
一秀最容易博,状元最难——但真正让人记住的,是六颗骰子落进碗里那两秒,一桌人一起等结果。
祝双节团圆。也欢迎直接在评论区指出规则错处,我改。
(附:作品体验——将项目目录整体下载,双击 index.html 即可离线运行;无需安装、无需联网。)
assets.zip (65.5 KB)



