问题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>
68 lines
4.9 KiB
Markdown
68 lines
4.9 KiB
Markdown
# 问题 5:打牌顺序(标准德州扑克行动顺序调查)
|
||
|
||
> 用户原话:“打牌顺序好像有点问题……现在的逻辑每次都从庄家的下一位开始,但是标准德州扑克是这样的吗?”
|
||
|
||
## 标准德州扑克的行动顺序(权威规则)
|
||
|
||
### 翻牌前(preflop)
|
||
- 小盲(SB)在大盲(BB)**左边**第一个座位,大盲在 SB 左边。
|
||
- **第一个行动者 = 大盲左边的玩家(UTG,Under The Gun)**,即顺时针从庄家(按钮)数:庄家 → SB → BB → **UTG 先行动**。
|
||
- 行动顺时针进行:UTG → … → SB → BB。
|
||
- **大盲有“BB option”**:若无人加注,轮到大盲时他可以选择过牌(看注)或加注。
|
||
|
||
### 翻牌后(flop / turn / river)
|
||
- **第一个行动者 = 小盲(按钮左边第一个仍在手的玩家)**。
|
||
- 行动顺时针:SB → … → 庄家(按钮)。
|
||
- 即:**翻牌后由庄家左边第一个玩家先行动**(与翻牌前不同,翻牌前是 BB 左边)。
|
||
|
||
### 每次有人加注(raise)之后
|
||
- 加注会**重置所有其他玩家的“已行动”标记**(他们必须再次面对这个加注做决定)。
|
||
- 行动从**加注者顺时针的下一位**开始,逐一回到加注者之前那一位;
|
||
所有人都对当前最高注 `call/raise/fold` 且下注额一致、且都“已行动”后,该街才结束。
|
||
- 若某人 all-in 且金额不足以构成完整加注(incomplete raise),则**不重置**已行动者的标记
|
||
(已行动且面对未匹配注的人只能 call 或 fold,不能再加注)—— 本项目已实现此规则(`holdem.ts:438`)。
|
||
|
||
### 单挑(heads-up)特例
|
||
- 只有两人时:**庄家 = 小盲**,大盲是另一人。
|
||
- 翻牌前:庄家(SB)**先**行动。
|
||
- 翻牌后:大盲先行动(因为大盲是“庄家左边第一个”)。
|
||
|
||
## 现有代码核对(`holdem.ts`)
|
||
|
||
| 街 | 代码 `firstToAct` | 是否标准 | 结论 |
|
||
|----|-------------------|----------|------|
|
||
| preflop 多人 | `nextSeat(bbPid, canAct)` = BB 顺时针下一位 = **UTG** | ✅ 标准 | 正确 |
|
||
| preflop 单挑 | `sbPid`(= 庄家)先行动 | ✅ 标准(单挑庄家先) | 正确 |
|
||
| flop/turn/river 多人 | `nextSeat(dealerButton, canAct)` = 庄家顺时针第一人 = **SB** | ✅ 标准 | 正确 |
|
||
| flop/turn/river 单挑 | `nextSeat(dealerButton, canAct)` = 大盲 | ✅ 标准(单挑大盲先) | 正确 |
|
||
|
||
### 每次 raise 后的顺序
|
||
- `raiseMove`:完整加注时 `for pid: G.acted[pid] = false`(`holdem.ts:443`),即重置其他人的已行动标记 → ✅ 标准。
|
||
- `advance()` → `nextActor(G, ctx)` = `nextSeat(ctx.currentPlayer, canAct)`(`holdem.ts:230`):
|
||
从**当前行动者(加注者)顺时针下一位**继续 → ✅ 标准。
|
||
- `roundComplete` 要求全部 `acted && betThisRound === currentBet` → ✅ 标准(不完整加注不重置已行动者也已处理)。
|
||
|
||
## 调查结论
|
||
**现有代码的行动顺序实际上符合标准德州扑克规则**(preflop=UTG、postflop=SB、raise 后重置并顺时针推进)。
|
||
用户感知到的“每次都从庄家的下一位开始”可能来自以下混淆之一:
|
||
|
||
1. **单挑时庄家先行动(preflop)** 被误以为“从庄家下一位开始”——其实单挑就是庄家(SB)先动,标准如此。
|
||
2. **postflop 从 SB(庄家下一位)开始** 被当成“所有街都从庄家下一位”——但 preflop 是从 UTG(庄家下两位),两街起点不同,代码已区分,属正确。
|
||
3. 可能真实存在的 bug 不在“首行动者”,而在 **`advance` 的流转**(例如 `events.endTurn({next})` 在 `ActivePlayers.ALL` 下是否被 boardgame.io 正确采纳)。
|
||
|
||
### 仍需落实的验证(防误判)
|
||
- **加一个确定性集成测试**,打印每一街的 currentPlayer 序列,人工核对:
|
||
- 3 人:preflop 顺序应为 `UTG(0) → SB(1) → BB(2) →`(BB option)→ flop 起 `SB(1) → ...`。
|
||
- 在某玩家中途 raise 后,确认顺序从 raise 者下家继续、且其他人被重新要求行动。
|
||
- 若测试发现 `endTurn({next})` 未被采纳(currentPlayer 回到错误座位),则问题在 boardgame.io 的
|
||
`ActivePlayers.ALL` + `turn.order` 交互,需改用 `events.setActivePlayers` 或显式 `endTurn` 序列。
|
||
|
||
## 拟采用方案
|
||
1. 先写 **顺序验证测试**(不改逻辑),确认当前行为。
|
||
2. 若测试通过与标准一致 → **无需改顺序逻辑**,仅在文档/UI 上澄清(例如在行动条显示“(庄家左 X 位行动)”),
|
||
并在 `HoldemBoard` 高亮当前行动者(已有 `data-actor`)。
|
||
3. 若测试暴露异常 → 定位 `advance`/`turn.order` 问题并修复(届时更新本文件结论)。
|
||
|
||
> 一句话答复用户:**标准德州扑克不是“每次都从庄家下一位”**——翻牌前从大盲下一位(UTG),翻牌后从小盲(庄家下一位);
|
||
> 当前代码已按此实现,但我会用测试再次核实并保留证据。
|