Files
huajishe-tts/docs/texas/04-allin-hand-comparison.md
e2hang 04d0af515c feat: 登录系统落地 + 踢人修正 + 单房间约束 + 权限收口
问题8 登录系统:
- node:sqlite 账号存储(scrypt 哈希 + HMAC 签名 token),preset admin 改为 e2hang/evan1115
- /api/auth/register|login|me,me 返回 room 供重联
- 网关拦截 create/join 需登录(401),列表保持开放

踢人逻辑修正(问题2 延伸):
- kickMove 真正释放座位(folded=false, name=null)而非锁死
- war/mill 去掉永久封禁语义

管理员越权:管理员 token 可删任意房间、踢任意房间内的人

单房间约束(用户新需求):
- 同一用户同时只能在一个房间,create/join 他房返回 409
- 踢人后若房间仅剩踢人者则自动解散并释放其记录(修复 2 人局踢光对手后被困 409)
- 陈旧房间记录兜底清理

UI 清理:移除"改名"按钮与"你的名字"输入框;删除房间按钮仅房主/管理员可见

docs: 新增 12-auth-kick-single-room.md 汇总本会话全部需求/根因/修复/边界测试

Co-Authored-By: Claude (CodeBuddy) <noreply@codebuddy.ai>
2026-08-25 14:13:59 +08:00

91 lines
5.7 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.
# 问题 4all-in 与比牌归属权side-pot 分配)问题
## 现象(用户反馈)
“德州扑克有 all in 问题和牌判断大小问题,归属权不行。”
拆成两件事:
1. **all-in 相关的归属权**:多个玩家不同筹码 all-in 时边池side pot赢家归属不对 / 钱分错。
2. **比牌判断大小**比牌evaluate / compare结果不对或平局分池奇数筹码归属不对。
## 现状分析(基于 `holdem.ts` + `holdem-eval.ts` 精读)
### 4.1 牌型评估器(`holdem-eval.ts`)—— 重点排查
- `evaluate5`:逐档判断(同花顺→四条→葫芦→同花→顺子→三条→两对→一对→高牌),
`ranks` 是降序用于同档比较,`compareHands` 先比 `category` 再逐位比 `ranks`
**未发现明显 bug**wheel(A-2-3-4-5) 在 `straightHigh` 里正确返回 5同花只数 suit葫芦/四条的 kicker 取降序正确。
- 需补的**确定性单测**(证明或证伪):
- 皇家同花顺 vs 同花顺、四条 vs 葫芦、葫芦 vs 同花、两对 kicker 次序、A 高 vs K 高;
- **平局**:完全相同的 7 张 → `compareHands === 0`(已在 `holdem.test.ts` 间接覆盖,需独立断言)。
- **A 既当高又当低**A-2-3-4-5 与 A-K-Q-J-T 应都识别为顺子且后者更大。
### 4.2 边池构建(`buildPots``holdem.ts:293`)—— 归属权核心
-`totalBetThisHand` 的贡献层级切池;`eligible` = 该层 `inHand && !folded` 的贡献者。
- 逻辑基本正确(单测 `3 人 all-in 100/300/300``未跟注返还` 已通过)。
- 潜在问题点:
- **空层 / 全弃层**`winElig.length === 0` 时把整层还给 `contrib[0]`(唯一贡献者)。这在“某层所有人弃牌、但有人更早 all-in 死钱”的极端情况下可能把钱退错人。
标准规则:若某一贡献层级上所有人都弃牌,该层金额应并入**仍能摊牌的最大边池**或退给未弃牌者。
现有实现退给 `contrib[0]`(该层第一贡献者)不够严谨。
- **边池的赢家归属**`resolveHand` 对每个 `pot``pot.eligible` 里比牌定赢家(正确)。
- **奇数筹码归属**`orderFromButton` 按“距按钮顺时针距离”排序,`resolveHand``extra` 从左往右 +1。
标准:奇数筹码给**按钮左侧第一个赢家**(顺时针)。当前 `seatDistance(from=btn, pid)` 定义是顺时针距离,
`orderFromButton` 升序即“离按钮最近的顺时针赢家先拿”,与标准一致(已在 `holdem.test.ts:493` 验证)。
### 4.3 all-in 街道推进(`roundComplete` / `advance`)—— **已定位真实 bug**
`roundComplete` 三个分支(`holdem.ts:198`
- `list.length === 0` → true正确
- `list.length === 1``return G.players[list[0]].betThisRound >= G.currentBet;`
- `list.length >= 2` → 全部 `acted && betThisRound === currentBet`
**问题在 `list.length === 1` 分支:它只看 `betThisRound >= currentBet`,完全忽略了 `acted`**
若唯一可行动者已经跟平了当前注、但 `acted === false`(例如他 limp 后别人 all-in、街道尚未因级联结束
该分支会误判 `roundComplete = true`**提前结束本街**,导致该玩家被跳过、轮不到他过牌/确认。
**已用确定性单测复现**(见 `holdem-probe.test.ts`
```
stacks=[0,0,500], bets=[100,100,100], totals=[100,100,100],
acted=[true,true,false], currentBet=100, dealerButton='0'
→ roundComplete 返回 true错误应为 false
```
`acted=[true,true,true]` 时返回 true正确。说明 bug 仅在“单可行动者 + 已 match 但未 acted”时触发。
**这正是“归属权不行 / all-in 出问题”的根因之一**:本应继续行动的人被跳过,边池在错误时机结算。
**修复**`holdem.ts`
```ts
if (list.length === 1) {
const p = G.players[list[0]!]!;
const stillNeedsToAct = p.betThisRound < G.currentBet || G.acted[list[0]!] === false;
return !stillNeedsToAct;
}
```
即:单可行动者仍“未完成(需跟注或尚未 acted”时不算 roundComplete。
## 拟采用方案
1. **修 `roundComplete` 的 `list.length === 1` 分支**
改为“该玩家是否还能行动call 不为 0 且未 all-in
```ts
if (list.length === 1) {
const p = G.players[list[0]];
const canStillCall = p.betThisRound < G.currentBet; // 还有注要跟
return !canStillCall;
}
```
这样只剩 1 人且面对未 match 的 all-in 注时,仍要等他 call/fold 才结束。
2. **加固 `buildPots` 的空层处理**
- 若某层 `winElig.length === 0` 且存在其他仍有 `inHand && !folded` 的玩家,
该层金额应并入“下一个有赢家的边池”或按总贡献退还给未弃牌者。
- 简化稳妥版:空层时,把金额加进**主池的赢家集合**(即所有未弃牌者摊牌),避免凭空退给某一个人。
- 同时补单测覆盖“某层全弃牌”场景。
3. **扩充 `holdem-eval` 单测 + 加 `compareHands` 平局/大小断言**,证明比牌正确;
若发现 bug 当场修。重点加A-2-3-4-5 顺、同花顺>四条、两对踢脚次序、完全相同的手牌 `compare===0`。
4. **`resolveHand` 平局/边池综合单测**:构造 3 人不同 all-in + 平局,断言每个池的赢家与金额。
## 验证
- 新增单测覆盖:`roundComplete` 单可行动者面对大 all-in 注 → 返回 false
`buildPots` 空层;`evaluate7` 各类牌型大小;`resolveHand` 多池 + 平局资金守恒(总发出 = 总池)。
- 资金守恒断言:`Σ stack + pot` 在每手前后恒定(已有 `holdem.test.ts:558`,扩展)。
- 联机手测3 人不同筹码 all-in → 摊牌后各池赢家与金额符合预期。