- 新增双升(DoubleUp)完整实现:引擎 double-up-core.ts / double-up.ts、牌桌 UI DoubleUpBoard.tsx、单测与 E2E - 甩牌跟牌修复:validateFollow 新增 mixed 分支,甩牌后其他家可正常跟单张同花色牌 - start.sh:PORT=5173 单端口一键启动(Koa 同时 serve 页面+/api+/socket.io),浏览器直连无需 Vite 代理 - 玩家名 server 权威化(join 时写入 G.playerNames);LobbyRoomList 支持 fixedSeats(双升固定 4 人) - 文档:新增 dev-log/14、更新 dev-log README 索引与进度、更新根 README 启动/部署说明 - .gitignore 忽略根目录 data/*.db 运行时 SQLite
14 KiB
双升(两副牌升级 / 拖拉机 / 80分)玩法规则汇总
本文是给 CodeBuddy(及后续实现双升游戏
packages/engine/src/games/double-up.ts)的玩法依据。 来源:QQ游戏"升级"规则说明、百度百科"升级/拖拉机"词条、知乎升级(拖拉机)规则说明。 所有 【已拍板】 标记的规则为用户在 2026-08-27 确认的选型,实现时以此为准。
0. 一句话定义
四人两两结对,用两副含大小王的扑克牌(108张),通过"亮主定花色(无人亮则翻牌兜底)→ 庄家扣底 → 轮流出牌捡分(5/10/K)→ 抠底翻番"的流程,按闲家得分决定庄家队伍是否升级,先打到 A 的一方获胜。
1. 牌具与发牌
- 牌数:108 张(两副 × 54 张,含大小王)。【已拍板 V1:含大小王】
- 人数:4 人,固定 4 人(双升不支持其他人数),对家为一队(南北 vs 东西)。
- 分牌分值:5 = 5 分,10 = 10 分,K = 10 分。两副牌总分 = 200 分。
1.1 发牌流程(逐张发 + 实时排序 + 可亮主)
发牌是一个独立阶段(deal),与出牌严格分离。流程:
- 洗牌后,一张一张发给 4 家(按座位顺序轮转)。每发出一张,收到牌的玩家手牌立即按「花色分组(主牌在前、副牌按 ♠>♥>♦>♣)、同花色点数从大到小」重排(由
sortHand保证,玩家无需手动整理)。 - 发到 100 张时停止(每人 25 张),剩余 8 张作为底牌,不再发给任何人。
- 发牌过程中(deal 阶段内任意时刻)任何玩家可亮主(见 §3)。亮出的牌从手牌移到该玩家座位前的「亮主公开区」(桌上所有人可见),被更高优先级反主时自动归还其手牌。
- 发牌结束(满 100 张、剩 8 张底牌)自动定主:按亮主公开区里「当前最大优先级」的亮主记录定主花色+庄家(无人亮则翻底牌第一张兜底,见 §3 V3),亮主区牌归还各自主人,庄家收底,直接进入 bottom 阶段。不再有独立的「确认主花色」按钮/阶段(Request B)。
- 主花色确认后进入 bottom 阶段:庄家把 8 张底牌全部收入自己手牌(临时 33 张),按主花色在前、副花色在后重新排序,再挑 8 张扣出作为新底牌。这 8 张扣底牌只有庄家可见,其他人不可见(结算抠底时才公开)。
- 扣底完成后进入 play 阶段开始出牌。
工程实现注(v2026-08-27 重构):发牌改为真正的一张一张发——房主端 UI 通过
dealOnemove 以 ~150ms 间隔自动驱动(DoubleUpBoard内useEffect监听phase==='deal' && me==='0'),发满 100 张(dealIndex>=100)自动调用resolveTrump定主并转bottom。亮主为公开亮主区(revealed[pid]):declareTrump把牌从手牌移入该区(所有人可见),更高优先级反主时先把被反者亮主区的牌归还其手牌再落新亮主;resolveTrump(发满 100 张自动触发,或旧confirmTrumpmove 兜底)把所有亮主区牌归还各自主人、确定主花色/庄家、庄家收底。playerView对所有人隐藏deck(未发的整副牌)与bottom(非庄家),但revealed公开。
阶段机:
waiting(进房)→deal(一张一张发+可亮主,发满自动定主)→bottom(庄家收底+扣 8 张隐藏)→play(出牌)→score(结算)。trump阶段已合并进deal末尾自动处理。
2. 级牌 / 主牌 / 副牌
- 级数:双方从 2 级起步;庄家队伍的当前级别 = 本局级牌点数。序列:2,3,4,5,6,7,8,9,10,J,Q,K,A。打到 A 且获胜即整场获胜。
- 级牌:点数等于当前级数的一切牌(四种花色都有),无论是否主花色皆为主牌。
- 主牌:大小王(无花色,恒为主)+ 级牌(四花色皆主)+ 主花色所有牌(亮主决定)。
- 副牌:除主牌之外其余三花色的牌。
- 2 是否常主:【已拍板 V2:2 为常主】所有花色的 2 均为主牌,分"主2"(主花色里的2)与"副2"(其他花色的2)。
主牌大小顺序(2为常主,从大→小):
大王 > 小王 > 主级牌 > 副级牌 > 主2 > 副2 > A > K > Q > J > 9 > 8 > 7 > 6 > 5 > 4 > 3
副牌大小顺序(从大→小,花色间无大小):
A > K > Q > J > 9 > 8 > 7 > 6 > 5 > 4 > 3 > 2
同级牌"先出者大"(出牌时同点同花色以先落者为大)。
3. 亮主 / 反主(确定主花色)
时机:在 deal 阶段(发牌过程中) 即可亮主;亮出的牌从手牌移到该玩家座位前的「亮主公开区」(桌面可见,所有人都能看到),被反主时立即归还其手牌——不在手中、也不在暗处。发牌满 100 张时自动定主(亮主区最大优先级胜出,否则翻底牌第一张兜底),无需手动确认。
抢亮优先级由低到高(V5 四级抢亮,"2 张反 1 张"):
| 等级 | 亮出的牌 | 效果 |
|---|---|---|
| 1 | 单张级牌 | 定该花色为主 |
| 2 | 一对级牌 | 反主(覆盖单张) |
| 3 | 一对小王 | 反主(覆盖级牌对,等同"反无主"方向) |
| 4 | 一对大王 | 反主(最高,打无主) |
- 后亮者用更高优先级的牌可反主改花色;同优先级不能再反(以先出现者为准)。
- "自保":直接亮对级牌同花色,除反无主外不能被反;"自反":本队再次用更高牌反。
- 一对小王/大王亮出 = 打无主(无主花色,仅大小王+级牌+2 为主)。
- 庄家认定:本局庄家 = 最后成功亮主(优先级最高)的玩家。第一局若无人亮主,庄家按轮庄默认值(初始 P0);之后每局按轮庄顺序(§8)依次更替。
- 【已拍板 V3:翻第四张兜底】 仅当全程无人亮主时,发牌满 100 张后从底牌翻一张牌兜底定主:
- 翻到普通花色牌 → 该花色为主花色。
- 翻到大小王 → 打无主。
- 翻到级牌 → 该级牌的花色为主花色。
- 有人亮过主(含无主)则不翻牌,直接采用已亮主结果。
- 定主由
resolveTrump在发满 100 张时自动完成(旧confirmTrumpmove 作为手动兜底保留但未在 UI 暴露)。定案后庄家收底牌(§4)。
4. 扣底(庄家收底 + 扣 8 张隐藏)
- 主花色确认后,进入 bottom 阶段:庄家先将 8 张底牌全部收入自己手牌(手牌临时变为 25+8 = 33 张),按主花色在前、副花色在后重新排序。
- 庄家从这 33 张中挑 8 张扣出,作为新底牌(
unshownBottom)。这 8 张只有庄家可见,其他人不可见(不放在明面),直到结算抠底(§6)才公开。 - 庄家扣底后手牌回到 25 张,进入 play 阶段。
- 扣底策略:藏分牌或藏大牌防被抠。
- 底牌分数归属在 §6 抠底时结算。
工程实现注:底牌 8 张在 playerView 中对非庄家返回空数组,确保 secrecy;仅庄家
myHand含其完整 33→25 牌。
5. 出牌规则
- 首家(首引):每局第一轮由庄家先出;之后每轮由上一轮赢家先出。
- 首家可出任意牌型:单张、对子、拖拉机(连对)、或"甩牌"。
- 跟牌义务:
- 首家出单张:有该花色必须跟单张(无该花色可毙/垫)。
- 首家出对子:有对子必须出对子(含拖拉机中的对)。
- 首家出拖拉机:有拖拉机必须出拖拉机;无拖拉机则必须出对子;无对子才能出别的。
- 首家出副牌花色而某家无此门花色:可出主牌毙(牌型须匹配:对子用主对、拖拉机用主拖拉机),或出别的副牌垫(垫牌永远最小)。
- ⚠️ 若手上有首出花色牌,不能直接用主牌毙,必须先跟该花色。
- 毙牌(主牌压副牌):
- 仅当无首出花色时可出主牌;主牌天然大于副牌。
- 毙牌牌型须 ≥ 首出牌型(拖拉机数、对子数都不能少于首家),否则算垫牌。
- 多家毙牌时比毙牌中拖拉机/对子大小,大者"盖毙"。
- 甩牌规则:
- 甩出的所有牌必须全大(同花色无人能压)。
- 甩牌失败(他人有更大的)→ 强制出被压制牌型中的最小牌。
- 【已拍板 V4:甩牌失败罚分】 额外按被压制的牌型张数罚分:
- 单张被压:罚 10 分。
- 对子被压:罚 20 分。
- 拖拉机被压(长度 n):罚 20 × n 分。
- 结算方向:庄家甩牌失败 → 闲家得分(罚分加给闲家);闲家甩牌失败 → 闲家扣分(从闲家总分中扣除)。
- 甩牌失败的具体最小牌型仍须按规则打出(软惩罚 + 罚分并存)。
- 一轮 4 家出齐(或对应牌型张数),最大者赢墩并收走本圈所有分牌。
UI 出牌交互(v2026-08-27):本人手牌可自由多选;
DoubleUpBoard实时调用parsePlay+validateFollow解析当前选择并提示牌型(单张/对子/拖拉机/甩牌)与合法性(如"必须跟领出花色")。「出牌」按钮仅当选择合法时才点亮。首出可任意牌型;跟牌若持有领出花色/对子/拖拉机则必须匹配,断门才可毙/垫。这样对子、拖拉机、甩牌均可在 UI 正常打出(之前缺实时提示导致只能出单张)。
6. 拖拉机(连对)定义
- 定义:同花色、点数连续、每点两张(对子)的联牌。必须全主或全副,主副不可混。
- 副牌例:
KKQQ、JJ99、554433。 - 主牌例:
一对小王+一对主级牌、主级牌对+副级牌对、主级牌对+主A对。
- 副牌例:
- 级牌相邻可连:主花色级牌 7 可与红桃 66、红桃 88 连成拖拉机;副级牌 7 与副花色 7 可连(同为主)。
- 王的特殊:大王+小王 可成对/连(在主牌序相邻)。
- 不算拖拉机:
554、544、5533、JJQ、两对副10(不相邻)、JJ1010、AA22(常主时 AA 与 22 间隔 KQJ9…8,不相邻)。
连续长度 n:两连对 n=2,三连对 n=3,以此类推。
7. 抠底(底牌翻番)
- 最后一圈若闲家方赢,触发抠底:底牌中的分牌按倍数计入闲家得分。
- 倍数 = 2 的(最后一圈最大牌型张数)次方。【已拍板 V6:封顶 64 倍】(即 6 张 / 三连对封顶)。
- 单张抠底:2¹ = 2 倍。
- 对子抠底(如 44):2² = 4 倍。
- 两连对(如 4455):2⁴ = 16 倍。
- 三连对(如 444555):2⁶ = 64 倍(封顶)。
- 甩牌抠底:只按甩牌中的最大牌型算倍数(如甩 AKQJ109877,按对子 77 算 4 倍;甩 AKQJ 按单张算 2 倍)。
- 最后一圈庄家方大 → 不抠底(底牌分归庄家,闲家不得分)。
底牌分 = 底牌中 5/10/K 的面值之和 × 倍数,加入闲家总分后参与 §8 升级判定。
8. 得分与升级判定
以闲家(抓分方)本局总得分计(含捡到的分牌 + 抠底翻番分 + 甩牌罚分加减),总分 200:
| 闲家得分 | 结果 |
|---|---|
| 0 | 庄家方连升 3 级 |
| 1 – 39(不满40) | 庄家方升 2 级 |
| 40 – 79 | 庄家方升 1 级 |
| 80 – 119 | 轮换庄家(闲家上台,不升级) |
| 120 – 159 | 闲家方升 1 级 |
| 160 – 199 | 闲家方连升 2 级 |
| ≥200(每多40) | 闲家方再加升相应级数 |
台阶累计逻辑:0/40/80/120/160/200… 各对应 庄+3/庄+2/庄+1/闲+1/闲+2/闲+3…,之后每 40 分再 +1 级。
轮庄:
- 庄家升级(保庄):下一副由对家当庄家。
- 闲家上台:下一副由本副庄家的下家当庄家。
胜负:某队打到 A 且本局获胜(升级过 A 或闲家上台时已达 A)→ 该队整场胜。
注:A 级特殊回流(如被用 A 抠底则下局级牌回退到 J)属进阶变体,【待确认,默认不实现】。
9. 已拍板的规则选型汇总
| # | 项目 | 选型 |
|---|---|---|
| V1 | 是否含大小王 | 含(108张) |
| V2 | 2 是否常主 | 是(主2/副2) |
| V3 | 无人亮牌兜底 | 翻第四张兜底(有人亮则不打翻牌);翻到王=无主,翻到花色/级牌=该花色主 |
| V4 | 甩牌失败惩罚 | 罚分(单张10/对子20/拖拉机20×n);庄家失败闲家得、闲家失败闲家扣 |
| V6 | 抠底倍数上限 | 64 倍封顶 |
未拍板项(实现时采用主流默认,后续可调整):
- V5 亮主机制:四级抢亮(单级牌<对级牌<对小王<对大王)+ 自保/自反。
- V7 级牌起始:从 2 级起步。
- A 级回流变体:默认不实现。
- 翻牌兜底翻到"级牌"时是否允许再反主:默认不允许(翻牌为最终兜底,等同于定主)。
10. 与现有代码库的对接要点(实现提示,非规则)
- 新游戏文件:
packages/engine/src/games/double-up.ts,导出TTSGame,name: 'DoubleUp',engineName: 'DoubleUp',minPlayers/maxPlayers: 4,setup发 108 张牌(含大小王),留 8 张底。 game.ui:需声明主/副牌区、4 家手牌区、底牌区、已出牌区、当前级牌/主花色指示。因 4 人 + 拖拉机复杂度高,建议先实现逻辑引擎(moves:亮主/翻牌兜底/扣底/出牌/跟牌校验/收分/甩牌罚分/抠底/升级判定),UI 可先用game.ui.actions+ 简化 Board,或仿HoldemBoard写专用DoubleUpBoard。- 必实现的核心校验(出牌合法性):跟牌花色义务、对子/拖拉机跟牌、毙牌牌型匹配、甩牌全大判定 + 罚分、主副大小比较、抠底倍数(封顶64)。
- 需注册到
packages/server/src/index.ts的两个数组(Server({games})与GAMES_ENTRIES),及apps/web/src/App.tsx的GAMES表(maxSeats=4)。 - 建议配套:在
packages/engine/src/games/double-up.test.ts写单元测试覆盖亮主/反主、跟牌校验、拖拉机判定、抠底倍数、升级判定表、甩牌罚分。
11. 参考来源
- QQ游戏"升级"规则说明(minigameimg.qq.com/help/rule36.html)
- 百度百科:升级/拖拉机(baike.baidu.com/item/升级/3563150)
- 知乎:升级(拖拉机)规则(zhuanlan.zhihu.com/p/2035707530240844260,甩牌罚分精确算法来源)
- 知乎:升级(拖拉机)游戏基本游戏规则(zhuanlan.zhihu.com/p/101357920)
- 知乎:四人升级(80分/拖拉机/双升)游戏规则说明(zhuanlan.zhihu.com/p/1900479633754948317)