- 新增桥牌(Contract Bridge)完整实现:固定 4 人分南北/东西两队
- 引擎 packages/engine/src/games/bridge.ts:
- 52 张牌均分,每人 13 张,共 13 墩
- 阶段机 waiting → bidding → play → score
- 叫牌:Pass / Bid(1♣..7NT 35 个叫品)/ Double / Redouble
* Bid 必须高于上一叫品(阶数优先,同阶则花色等级 ♣<♦<♥<♠<NT)
* Double 只能针对敌方叫品;Redouble 只能针对己方被加倍的叫品
* 连续 3 家 Pass 结束叫牌;4 家全 Pass 作废重发
- 打牌:强制跟花色 / 将牌赢墩 / NT 不可将吃;明手 13 张牌摊开由庄家控制
- 计分:完整实现成局 ≥100、超墩、满贯(小/大)、加倍/再加倍、罚分表(含易局/非易局)
- 橡胶局(Rubber Bridge):完成且成局记 1 局;赢 2 局胜一盘,重置易局
- playerView 隐藏他人手牌,明手牌公开对所有人可见
- UI apps/web/src/BridgeBoard.tsx:
- 椭圆牌桌 + 4 家固定 N/E/S/W 方位(参照双升 / 拱猪 UI 风格)
- 真实姓名显示:G.playerNames(引擎 setName 写入)+ prop(ctx.players)+ setName 兜底
- 常驻计分板:NS / EW 队员姓名 + 本副赢墩数 + 累计分 + 队总分 + 易局 + 盘数
- 顶部状态条:当前副数 / 局数 / 易局 / 契约 / 庄家明手 / 大字号 NS vs EW 墩数对比
- 座位卡:每玩家本副赢墩数 + 手牌张数 + 角色(庄/明/防)
- 叫牌:35 个 bid 按钮 + Pass / X / XX;叫牌历史 hover 显示真实姓名
- 牌桌内显示:上一墩(3 秒高亮后由 collectTrick 收牌)+ 当前墩 + 领出花色
- 明手手牌区:庄家控制时高亮可点;明手本人不可自己出牌
- 文档 docs/bridge/00-rules.md:桥牌规则调研 + 10 条实现选型 + 与标准桥牌的差异说明
- 测试 packages/engine/src/games/bridge.test.ts:49 个用例覆盖牌组 / 叫品合法性 / 跟牌 / 赢墩 / 计分 / 成局
- App.tsx:GAMES 表新增 bridge(fixedSeats=4),GAME_ORDER 插入;BridgeLocalView / BridgeOnlineView
- server/src/index.ts:games 数组与 GAMES_ENTRIES 注册 Bridge,启动日志同步
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
317 lines
16 KiB
Markdown
317 lines
16 KiB
Markdown
# 桥牌(Contract Bridge)玩法规则与实现选型汇总
|
||
|
||
> 本文档是给 CodeBuddy 实现桥牌游戏 `packages/engine/src/games/bridge.ts` 的玩法依据。
|
||
> 来源:WBF《Laws of Duplicate Bridge 2017》、ACBL《Rubber Bridge》官方规则、维基百科 Contract Bridge 词条、Bridge Base Online(在线桥牌平台)规则页。
|
||
> 所有 **【已拍板】** 标记的规则为实现选型(按本地 4 人联机场景做了合理化裁剪),引擎实现以此为准;与真实桥牌有出入的条目在第十节「与标准桥牌的差异」中单独说明。
|
||
|
||
---
|
||
|
||
## 0. 一句话定义
|
||
|
||
四人两两结对(南北 NS vs 东西 EW),用**一副 52 张标准牌**(每人 13 张,共 13 墩),按「**发牌 → 叫牌(确定定约方/将牌/阶数/加倍状态)→ 打牌(庄家指挥明手赢墩)→ 计分**」的流程决出胜负。计分采用**橡胶桥**(Rubber Bridge)模式:完成 ≥100 墩分即成一局,赢两局胜一盘。
|
||
|
||
---
|
||
|
||
## 1. 牌具与座位
|
||
|
||
- **牌**:52 张标准牌,4 花色 × 13 点(A K Q J 10 9 8 7 6 5 4 3 2),**无大小王**。
|
||
- **人数**:**固定 4 人**,不支持 2/3 人。
|
||
- **座位**:4 人围坐一圈,按顺时针编号为 N(北)→ E(东)→ S(南)→ W(西)。
|
||
- 搭档固定:**N + S**(南北队,NS)、**E + W**(东西队,EW)。
|
||
- 相对而坐,互为对手。
|
||
- **手牌**:每人 **13 张**,共 **13 墩**(每墩 4 张)。
|
||
|
||
---
|
||
|
||
## 2. 发牌阶段
|
||
|
||
1. 房主点「开始游戏」时系统**洗一副 52 张牌**,按座位顺序一张一张均分,每人 13 张。
|
||
2. 发牌后立即进入**叫牌阶段**,**发牌人(首局的 N)先叫**。
|
||
3. 发牌阶段(deal)只是一次性原子动作,不需要一张一张动画(与双升不同,桥牌发牌后所有 13 张一次摊给玩家,立即开始叫牌)。
|
||
|
||
---
|
||
|
||
## 3. 叫牌阶段(Auction)
|
||
|
||
叫牌是顺时针轮流进行的公开过程,每位玩家依次说出**一个叫品**(pass / bid / double / redouble),直到**连续三人 pass**(Pass Out Pass)后叫牌结束。
|
||
|
||
### 3.1 叫品结构
|
||
|
||
- **Pass(P / 不叫)**:放弃本次叫牌机会,不构成定约。
|
||
- **Bid(叫品)** = **阶数(1~7)+ 花色(♣/♦/♥/♠/NT)**,承诺该方能在该阶数 + 6 墩的基础上完成定约(如 `1♥` = 承诺至少赢 7 墩,且 ♥ 为将牌)。
|
||
- 阶数:1、2、3、4、5、6、7(共 7 档,对应 7~13 墩)。
|
||
- 花色等级(从低到高):**♣ < ♦ < ♥ < ♠ < NT**。
|
||
- **Double(X / 加倍)**:对**敌方**最后一个叫品加倍。
|
||
- **Redouble(XX / 再加倍)**:本方叫品被敌方加倍后,对该加倍再翻倍。
|
||
|
||
### 3.2 叫品合法性规则
|
||
|
||
1. **必须高于之前的叫品**——阶数优先;同阶数则花色等级必须更高。
|
||
- 例:`1♣ → 1♦ → 1♥ → 1♠ → 1NT → 2♣ → 2♦ → ... → 7NT`。
|
||
2. **Double**:必须**针对敌方的最后一个叫品**;不能加倍己方叫品;不能加倍 Pass 后的「叫品」(Pass 不构成叫品)。
|
||
3. **Redouble**:必须**针对敌方对自己叫品的 Double**;其它情况不允许。
|
||
4. **Pass**:随时可叫,**总是合法**。
|
||
5. 一家叫出非 Pass 后,后续各家在没新叫品出现前,**不能再次叫同等或更低**的叫品(包括花色等级)。
|
||
|
||
### 3.3 叫牌结束条件
|
||
|
||
| 情况 | 结果 |
|
||
| --- | --- |
|
||
| 连续 **3 家 Pass** | 叫牌结束 |
|
||
| 4 家全部 Pass | **Pass Out(PO)**——本局作废,分数清零,开始新一手 |
|
||
| 最后叫品后被连续 Pass | 进入打牌阶段 |
|
||
|
||
### 3.4 定约方与术语
|
||
|
||
- **庄家(Declarer)** = **首先叫出最终定约花色的一方玩家**(若最终叫品为 NT,则为首先叫 NT 的玩家)。
|
||
- 例:叫牌过程 `P-1♣-P-1♥-P-1♠-P-P-P`,最终定约 `1♠`,则庄家 = 首先叫 ♠ 的那位玩家(不是必然最后叫的人)。
|
||
- **明手(Dummy)** = 庄家的**搭档**。
|
||
- **定约方(Declaring Side)** = 庄家 + 明手(搭档二人);他们需要完成定约。
|
||
- **防守方(Defending Side)** = 敌方二人(对手)。
|
||
- **首攻方(Opening Leader)** = 庄家**左侧**的那位玩家(属于防守方之一)。
|
||
|
||
---
|
||
|
||
## 4. 将牌(Trump)
|
||
|
||
- 叫牌确定的**花色**为**将牌花色(trump)**;叫 `X NT` 则**无将(No Trump)**。
|
||
- **将牌永远大于其他花色**——任何将牌都比非将牌花色的最大牌更大。
|
||
- **无将(NT)**:没有将牌花色,只能用**领出花色**中的最大牌赢墩;领出花色外的牌是废牌(无法赢墩,除非用将吃——但 NT 不可将吃)。
|
||
- 实际出牌时,**将牌赢墩优先级最高**:若领出方出非将牌花色,下家可以**将吃(ruff)**——出一张将牌盖掉(无论点数高低),赢得本墩。
|
||
|
||
---
|
||
|
||
## 5. 打牌阶段(Play)
|
||
|
||
### 5.1 流程
|
||
|
||
1. 打牌共 **13 墩**。第一墩由**首攻方**(庄家左手玩家)领出一张牌(面朝上)。
|
||
2. 之后按顺时针各家各出一张,构成一墩。
|
||
3. 4 人出完一墩后,比较大小,**赢墩者收走该墩并领出下一墩**。
|
||
4. 13 墩打完,本副牌结束,进入计分。
|
||
|
||
### 5.2 跟牌规则(强制)
|
||
|
||
- 若手中有**领出花色(lead suit)**的牌,**必须出该花色**(包括领出方出牌后续的所有人)。
|
||
- 若手中**没有**领出花色:
|
||
- 若有**将牌**:可以**将吃(ruff)**——出一张将牌(无论点数),赢得本墩。
|
||
- 若既无领出花色又无将牌:可以**垫牌(discard)**——任意其他花色牌(永远不能赢墩,除非被将吃,但此处已将牌没有)。
|
||
- **无将(NT)**:不能将吃——若没有领出花色,只能垫牌,永远不能赢此墩。
|
||
|
||
### 5.3 赢墩规则
|
||
|
||
按出牌顺序,第 4 张牌出完后比较本墩:
|
||
|
||
| 情况 | 赢墩者 |
|
||
| --- | --- |
|
||
| **有人将吃** | **最大将牌**赢墩(无论点数) |
|
||
| **无人将吃,有将牌花色在内** | **最大将牌**赢墩 |
|
||
| **无人将吃,纯领出花色** | **最大领出花色**赢墩 |
|
||
| **NT 局且都是垫牌** | 第一张牌(领出者)赢墩(其领出花色牌最大) |
|
||
|
||
> 注:首攻方出的领出花色牌永远是该花色最大或唯一存在该花色的牌(否则首攻方不出该花色)。
|
||
|
||
### 5.4 明手(Dummy)
|
||
|
||
- 打牌阶段,**明手的 13 张牌明牌摊开**在桌上,所有人可见。
|
||
- **明手牌由庄家**指挥出牌(庄家对明手和**自己的牌**共 26 张都有控制权)。
|
||
- 明手玩家不能自主出牌——UI 上把明手的牌视为「庄家控制的牌堆」,庄家每轮选明手该出哪张。
|
||
- 防守方(敌方二人)各自出自己手中的牌。
|
||
|
||
---
|
||
|
||
## 6. 计分(Scoring)
|
||
|
||
桥牌计分比较复杂,分为 **完成定约奖励(Contract Points)** 和 **罚分(Penalty Points)**。本文档采用**橡胶桥**计分法。
|
||
|
||
### 6.1 墩分(Trick Points)
|
||
|
||
完成定约所要求的 **6 + 阶数** 墩叫「合约墩」,每超过 6 墩的部分叫「**奇墩(odd tricks)**」。每奇墩的得分:
|
||
|
||
| 定约花色 | 每奇墩得分 |
|
||
| --- | --- |
|
||
| ♣ 或 ♦ | **20** |
|
||
| ♥ 或 ♠ | **30** |
|
||
| NT(无将) | **首墩 40**,**之后每墩 30** |
|
||
|
||
**加倍/再加倍** 时奇墩分翻倍:
|
||
|
||
| 状态 | 墩分 |
|
||
| --- | --- |
|
||
| 未加倍 | 上述原值 |
|
||
| 加倍(X) | 墩分 ×2 |
|
||
| 再加倍(XX) | 墩分 ×4 |
|
||
|
||
### 6.2 成局与未成局
|
||
|
||
- **合约墩分 ≥ 100**:**成局(Game)**。
|
||
- **合约墩分 < 100**:**未成局(Part-Score / Partial)**。
|
||
|
||
| 定约 | 合约墩分 | 是否成局 |
|
||
| --- | --- | --- |
|
||
| 3NT | 70 + 30×2 = **100** | ✅ 成局 |
|
||
| 4♥ / 4♠ | 30×4 = **120** | ✅ 成局 |
|
||
| 5♣ / 5♦ | 20×5 = **100** | ✅ 成局 |
|
||
| 4♣ / 4♦ | 20×4 = **80** | ❌ 未成局 |
|
||
| 1NT / 2NT | 40 + 30×(n-1) | 1NT=70 未成局;2NT=90 未成局;3NT=100 成局 |
|
||
|
||
### 6.3 完成定约奖励
|
||
|
||
定约方完成(或超过)合约墩数时的奖励分:
|
||
|
||
| 类型 | 未成局(Part-Score) | 成局(Game) |
|
||
| --- | --- | --- |
|
||
| 完成定约基本分(未加倍) | **50** | **300**(小满贯/大满贯另算) |
|
||
| 完成定约基本分(加倍 X) | **50** | **500** |
|
||
| 完成定约基本分(再加倍 XX) | **100** | **1000** |
|
||
|
||
> 注:完成定约基本分与奇墩分**分开记**——基本分是奖励,奇墩分计入成局线(≥100 即成局)。
|
||
|
||
### 6.4 超墩奖励(Overtricks)
|
||
|
||
多得墩数(合约外的赢墩)的奖励:
|
||
|
||
| 状态 | 每超墩得分 |
|
||
| --- | --- |
|
||
| 未加倍 | 与原墩分相同(♣♦20,♥♠30,NT30) |
|
||
| 加倍(X) | 易局 **200**;非易局 **100** |
|
||
| 再加倍(XX) | 易局 **400**;非易局 **200** |
|
||
|
||
### 6.5 满贯奖励(Slam Bonus)
|
||
|
||
完成 12 墩 = **小满贯(Small Slam)**;完成 13 墩 = **大满贯(Grand Slam)**。
|
||
|
||
| 类型 | 未成局/非易局 | 易局 |
|
||
| --- | --- | --- |
|
||
| 小满贯(12 墩) | **500** | **750** |
|
||
| 大满贯(13 墩) | **1000** | **1500** |
|
||
|
||
> 满贯奖励**与加倍状态无关**——只跟是否达成 12/13 墩以及是否易局相关。
|
||
|
||
### 6.6 失败罚分(Undertricks)
|
||
|
||
定约方未达到合约墩数时(即输 N 墩),**敌方**获得罚分(按"输墩的次数"算):
|
||
|
||
| 输墩数 | 非易局未加 | 非易局加倍 | 非易局再加倍 | 易局未加 | 易局加倍 | 易局再加倍 |
|
||
| --- | --- | --- | --- | --- | --- | --- |
|
||
| 第 1 墩 | **50** | **100** | **200** | **100** | **200** | **400** |
|
||
| 第 2 墩 | **50** | **200** | **400** | **100** | **300** | **600** |
|
||
| 第 3 墩 | **50** | **200** | **400** | **100** | **300** | **600** |
|
||
| 第 4+ 墩(每) | **50** | **200** | **400** | **100** | **300** | **600** |
|
||
|
||
> **易局(Vulnerable)**:当一方已经赢了一局(成局)后,下一局该方自动为易局,**直到该盘结束**。
|
||
> 详细规则见 §6.8 橡胶桥规则。
|
||
|
||
### 6.7 简化计分(本文档采用)
|
||
|
||
为了本地 4 人联机的简单性,本实现对标准桥牌的计分做了**适度简化**(详见 §10)。核心规则:
|
||
|
||
1. 完成定约奖励、超墩奖励、满贯奖励、罚分均按上述标准表。
|
||
2. **墩分和奖励分合并计入完成方的总分**——便于 UI 一目了然。
|
||
3. **罚分单独计入敌方总分**(负数表示被扣分)。
|
||
|
||
### 6.8 橡胶桥规则(一盘制胜)
|
||
|
||
- **一盘(Rubber)= 一局 + 双方完成各自的局数**。
|
||
- 橡胶桥采用**累计分制**:
|
||
- 任一方先**赢 2 局**(Game)即**赢一盘**。
|
||
- 一盘结束,双方易局状态重置,新一盘重新开始。
|
||
- 简单起见,本实现把"局"理解为:只要有一方成局(≥100 合约墩分)即记录「1 局胜利」;第一个赢 2 局的队伍胜一盘。
|
||
- 本地游戏可以**无限进行**(无终局条件),玩家自行决定结束。
|
||
|
||
---
|
||
|
||
## 7. 玩法变体
|
||
|
||
- **橡胶桥(Rubber Bridge)** ✅ 本实现采用:累计局数决胜。
|
||
- **复式桥(Duplicate Bridge)**:俱乐部/比赛用,同手牌多桌对比,不在本地联机场景使用。
|
||
- **芝加哥桥(Chicago Bridge)**:4 局一盘,不在本实现范围。
|
||
|
||
---
|
||
|
||
## 8. 策略基础
|
||
|
||
### 8.1 4 种赢墩方式
|
||
|
||
1. **建立长套(Establishment)**:打一花色树立长套,后期必赢。
|
||
2. **打大牌(Top of the Line)**:持有对方无法超越的大牌直接赢墩。
|
||
3. **飞牌(Finesse)**:利用大牌位置优势(明手/自己持有大牌,争取让对方在大牌之间出小牌)。
|
||
4. **将吃(Ruff)**:用将牌赢对方领出的非将牌花色。
|
||
|
||
### 8.2 牌力估算(Milton Work Count)
|
||
|
||
| 牌 | 点数 |
|
||
| --- | --- |
|
||
| A | 4 |
|
||
| K | 3 |
|
||
| Q | 2 |
|
||
| J | 1 |
|
||
|
||
- 全副牌共 40 点(4×4+3×4+2×4+1×4=40)。
|
||
- 通常 12+ 点开叫,搭档合计 25+ 点可成局。
|
||
|
||
> 本实现**不实现**任何叫牌体系(SA / Acol / 2/1 / Precision),完全由玩家自由叫牌。AI 队友(如果有)也只做基本决策。
|
||
|
||
---
|
||
|
||
## 9. 已拍板的规则选型
|
||
|
||
| # | 项目 | 选型 |
|
||
| --- | --- | --- |
|
||
| V1 | 人数 | **固定 4 人**,不支持 2/3 |
|
||
| V2 | 牌组 | **52 张**标准牌(无大小王) |
|
||
| V3 | 叫牌体系 | **自由叫牌**——不实现任何体系(玩家自决);AI(若加)只做基本开叫决策 |
|
||
| V4 | 叫牌结束 | 连续 3 家 Pass 或 4 家全 Pass(PO 重新开始) |
|
||
| V5 | 定约方/庄家 | 首先叫出最终定约花色的玩家(标准规则) |
|
||
| V6 | 打牌 | **13 墩**,首攻方 = 庄家左手;明手牌公开由庄家控制 |
|
||
| V7 | 跟牌规则 | 标准强制跟牌 / 将吃 / 垫牌;NT 不可将吃 |
|
||
| V8 | 计分 | **橡胶桥制**:完成 ≥100 合约墩分成局;赢 2 局胜一盘 |
|
||
| V9 | 易局 | 一方赢一局后该方在剩余该盘易局,直到新盘 |
|
||
| V10 | 终局条件 | **无终局**——连续多盘进行,累计分数显示在 UI,玩家自决 |
|
||
|
||
未拍板项(实现时采用主流默认):
|
||
- 叫牌超时机制:本地联机玩家每叫有思考时间,无强制超时。
|
||
- 非法叫品拦截:UI 显示可叫品(基于当前叫牌历史),点击非法叫品直接拒绝。
|
||
|
||
---
|
||
|
||
## 10. 与标准桥牌的差异
|
||
|
||
为了在 4 人联机的浏览器环境下顺利实施,本实现做了**适度简化**,不影响核心玩法:
|
||
|
||
1. **无规则违规惩罚**:标准桥牌中有「违规/修正」机制(lead out of turn 等),本实现**不实现**,只校验叫品合法性、出牌合法性、跟牌合法性。违反顺序的 move 在 server 端被忽略。
|
||
2. **无 Claim(声称已胜)机制**:标准桥牌允许玩家在中途声称已胜局并摊牌,本实现要求打满 13 墩。
|
||
3. **无慢速打牌/喊单超时**:本地玩家无思考时间限制;超时则自动 Pass。
|
||
4. **无慢速叫牌叫品(Slow Bid)限制**:标准桥牌禁止某些拖延叫品,本实现不限制。
|
||
5. **易局按「本盘」累计**:标准橡胶桥是「一盘(Rubber)」重置易局;本实现用相同规则。
|
||
6. **复式桥牌模式不支持**:只有橡胶桥。
|
||
|
||
---
|
||
|
||
## 11. 与现有代码库的对接要点
|
||
|
||
- 新游戏文件:`packages/engine/src/games/bridge.ts`,导出 `TTSGame`,`name: 'Bridge'`,`engineName: 'Bridge'`,`minPlayers/maxPlayers: 4`。
|
||
- `setup`:建 4 个空 hands 桶、初始化 callHistory / finalContract / declarer / leadPlayer / vulnerability / rubber 状态。
|
||
- 阶段机:`waiting`(进房)→ `bidding`(叫牌)→ `play`(打牌)→ `score`(结算)。`bidding` 阶段维护 `callHistory`,结束时定庄家/定约方。
|
||
- `playerView`:
|
||
- 隐藏:他人 hands(除明手外,明手一旦摊开就对所有人可见)。
|
||
- 公开:`currentTrick`、`callHistory`、`finalContract`、`declarer`、`dummy`、`vulnerability`、`scores`。
|
||
- 必实现的核心校验:
|
||
- 叫品合法性(必须高于上一叫品;Double/Redouble 仅对敌方/己方被加倍叫品)。
|
||
- 跟牌规则(有领出花色必须跟;NT 局不可将吃;将吃用最大将牌赢墩)。
|
||
- 明手控制(庄家指挥明手出牌)。
|
||
- 计分(按 §6.1~§6.6 表格;橡胶局制胜按 §6.8)。
|
||
- 需注册到 `packages/server/src/index.ts` 的 `games` 数组与 `GAMES_ENTRIES` 数组,及 `apps/web/src/App.tsx` 的 `GAMES` 表(maxSeats=4,fixedSeats=4)。
|
||
- 配套单元测试 `packages/engine/src/games/bridge.test.ts`:发牌、叫牌合法性、跟牌规则、赢墩判定、计分表(成局/满贯/加倍/超墩/罚分)、橡胶局胜。
|
||
|
||
---
|
||
|
||
## 12. 参考来源
|
||
|
||
- 维基百科:Contract Bridge(en.wikipedia.org/wiki/Contract_bridge)
|
||
- WBF《Laws of Duplicate Bridge 2017》(worldbridge.org)
|
||
- ACBL《Rubber Bridge》规则页(acbl.org)
|
||
- Bridge Base Online 规则页(bridgebase.com)
|
||
- 百度百科「桥牌」词条
|
||
- 知乎「桥牌玩法」专栏
|