Files
huajishe-tts/docs/bridge/00-rules.md
e2hang 57594dbec5 feat(bridge): 桥牌完整实现(4 人联机,橡胶局计分)+ 椭圆牌桌 UI
- 新增桥牌(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>
2026-09-05 21:45:40 +08:00

317 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 桥牌(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)
- 百度百科「桥牌」词条
- 知乎「桥牌玩法」专栏