texas-poker-core 1.4.28 → 1.4.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -59
- package/dist/Player/allowedActions.js +3 -1
- package/package.json +1 -1
- package/types/Player/allowedActions.d.ts +1 -0
package/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# Texas-Poker-Core
|
|
2
2
|
|
|
3
3
|
无服务、无持久化的 **德州扑克(Texas Hold’em)对局引擎**:房间与座位、盲注与角色、发牌、行动轮次、阶段推进、摊牌比牌、奖池与边池分配等规则均在库内完成。
|
|
4
|
-
**不包含**:账号系统、WebSocket/HTTP、数据库、匹配、UI
|
|
4
|
+
**不包含**:账号系统、WebSocket/HTTP、数据库、匹配、UI、节拍与 `sleep`;这些由业务层解释 **领域事件** 并驱动 **`pendingFlowOps`** 队列实现。
|
|
5
5
|
|
|
6
|
-
- **入口类**:`Texas` — 组装 `Pool`、`Dealer`、`Controller`、`Room
|
|
7
|
-
-
|
|
8
|
-
-
|
|
6
|
+
- **入口类**:`Texas` — 组装 `Pool`、`Dealer`、`Controller`、`Room`;状态变更写入事件缓冲,由业务 **`drainDomainEvents()`** 取出后落库 / 推送。
|
|
7
|
+
- **指令入口**:玩家行动统一走 **`dispatchCommand(TableCommand)`**(自愿下注、超时、离场弃牌、入座大盲等)。
|
|
8
|
+
- **节奏解耦**:进街与交权进入 **`pendingFlowOps`**(`stage_advance` | `turn_handoff`),业务按产品节拍调用 **`applyPendingStageAdvance`** / **`flushPendingTurnHandoff`**(单测可 **`flushAllPendingFlowOps`** 一次排空)。
|
|
9
|
+
- **错误模型**:规则或状态不满足时 **`TexasError`(`TexasCoreErrorCode`)** fail-fast;`message` 为默认中文,业务可用 `code` + `payload` 做 i18n。
|
|
10
|
+
|
|
11
|
+
**详细 API 清单**:[CORE_API.md](./CORE_API.md)。**与参考服务端的一整局事件流**:[docs/integration-core-wish-event-flow.md](./docs/integration-core-wish-event-flow.md)。
|
|
9
12
|
|
|
10
13
|
---
|
|
11
14
|
|
|
@@ -24,12 +27,12 @@ npm install texas-poker-core
|
|
|
24
27
|
|
|
25
28
|
| 对象 | 职责 |
|
|
26
29
|
| ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
-
| `Texas` |
|
|
30
|
+
| `Texas` | 会话门面:`setPlayerRoles` / `dealCards` / `start` / `dispatchCommand` / `drainDomainEvents` / 消费 `pendingFlowOps` / `reset` 等 |
|
|
28
31
|
| `Room` | 成员加入/观战/入座/离座、`initialRoles`(末 `lockSeats`)/ `rotateRoles`(不锁座)、`RoomStatus`(`seats_open` / `seats_locked`) |
|
|
29
|
-
| `Dealer` |
|
|
30
|
-
| `Controller` |
|
|
31
|
-
| `Pool` | 奖池与支付(`texas.settle()` 时 `pool.pay()`)
|
|
32
|
-
| `Player` |
|
|
32
|
+
| `Dealer` | 盲注、庄家、角色顺序、发牌(通常经 `Texas` / `Room` 访问) |
|
|
33
|
+
| `Controller` | 一手牌 `HandLifecycle`、当前街 `stage`、`activePlayer`、事件缓冲与 `pendingFlowOps` 队列 |
|
|
34
|
+
| `Pool` | 奖池与支付(`texas.settle()` 时 `pool.pay()`,并缓冲 `PotAwarded`) |
|
|
35
|
+
| `Player` | 单座筹码、手牌、允许行动集合;**勿**直接调 `check`/`bet`/…,统一经 `Texas#dispatchCommand` |
|
|
33
36
|
|
|
34
37
|
---
|
|
35
38
|
|
|
@@ -52,30 +55,22 @@ npm install texas-poker-core
|
|
|
52
55
|
|
|
53
56
|
## 典型对局流程(使用手册)
|
|
54
57
|
|
|
55
|
-
以下为常见顺序;具体校验与错误码以运行时 `TexasError` 为准。
|
|
58
|
+
以下为常见顺序;具体校验与错误码以运行时 `TexasError` 为准。
|
|
59
|
+
**约定**:下列 `setPlayerRoles` / `dealCards` / `start` / `dispatchCommand` 等会 **同步返回** 本步 `TexasDomainEvent[]`(等价于随即 `drainDomainEvents()`);若你自行多次调用 Core 再统一 drain,也可用 `texas.drainDomainEvents()` 取出缓冲。
|
|
56
60
|
|
|
57
61
|
### 1. 创建牌桌
|
|
58
62
|
|
|
59
63
|
```ts
|
|
60
|
-
import { Texas } from 'texas-poker-core'
|
|
64
|
+
import { Texas, type TableCommand } from 'texas-poker-core'
|
|
61
65
|
|
|
62
66
|
const texas = new Texas({
|
|
63
67
|
user: { id: 1, name: '房主' },
|
|
64
68
|
lowestBetAmount: 20,
|
|
65
69
|
maximumCountOfPlayers: 9,
|
|
66
|
-
initialChips: 2000
|
|
67
|
-
// 可选:与 WS/动画对齐
|
|
68
|
-
beforeNextPlayerTurn: async () => {
|
|
69
|
-
/* await sleep(...) */
|
|
70
|
-
},
|
|
71
|
-
beforeStageAdvance: async () => {
|
|
72
|
-
/* ... */
|
|
73
|
-
}
|
|
70
|
+
initialChips: 2000
|
|
74
71
|
})
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
// 日志、监控;随后仍会 throw
|
|
78
|
-
})
|
|
73
|
+
// 业务侧:try/catch TexasError,写日志 / 监控 / 映射 HTTP 状态码
|
|
79
74
|
```
|
|
80
75
|
|
|
81
76
|
### 2. 注册用户与入座
|
|
@@ -84,62 +79,155 @@ texas.onError((err) => {
|
|
|
84
79
|
const p2 = texas.createPlayer({ id: 2, name: '玩家2' })
|
|
85
80
|
texas.room.join(p2)
|
|
86
81
|
texas.room.seat(p2)
|
|
87
|
-
// join =
|
|
82
|
+
// join = 进房(默认观战);seat = 上桌,须 seats_open(一手收尾 reset 后)
|
|
88
83
|
```
|
|
89
84
|
|
|
90
85
|
### 3. 锁座、分配角色、发牌
|
|
91
86
|
|
|
92
87
|
```ts
|
|
93
|
-
texas.setPlayerRoles('initial')
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
88
|
+
const events1 = texas.setPlayerRoles('initial')
|
|
89
|
+
// 首局:Room.initialRoles(定庄 + setOthers + lockSeats)
|
|
90
|
+
// 或 'rearrange'(仅 reArrangeRoles,须已有庄);可选 { buttonUserId }
|
|
91
|
+
await interpret(events1) // 业务:落库 / WS;下同
|
|
92
|
+
|
|
93
|
+
const events2 = texas.dealCards()
|
|
94
|
+
// → HoleCardsDealt(byUserId 含全员手牌;出站前按 viewer 过滤)
|
|
95
|
+
|
|
96
|
+
// 批量 seat/remove 后:Dealer 已各调 reArrangeRoles;可再 texas.reArrangeRoles() 统一推角色
|
|
97
|
+
// (不缓冲 RolesAssigned,需自行读 dealer 各席 role)
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
### 4. 开始本手
|
|
100
101
|
|
|
101
102
|
```ts
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
//
|
|
103
|
+
// 要求:至少 2 人 on-set、seats_locked、controller.idle
|
|
104
|
+
const events3 = texas.start()
|
|
105
|
+
// → HandStarted、BlindsPosted、PotUpdated…;队列入队首人 turn_handoff(尚无 TurnOffered)
|
|
106
|
+
|
|
107
|
+
await drainAndConsumePendingFlow(texas, events3)
|
|
108
|
+
// 生产:先解释 events3,再按节拍 applyPendingStageAdvance / flushPendingTurnHandoff 并 drain 新事件
|
|
109
|
+
// 单测:texas.flushAllPendingFlowOps() 一次排空
|
|
110
|
+
|
|
111
|
+
// 有待贴「入座大盲」队列时,用原子 API 替代 start + 循环 PostBigBlind:
|
|
112
|
+
// texas.startPreflopWithJoiningBigBlinds([userId, ...])
|
|
105
113
|
```
|
|
106
114
|
|
|
107
|
-
### 5.
|
|
115
|
+
### 5. 玩家行动
|
|
108
116
|
|
|
109
|
-
|
|
110
|
-
该玩家可调用(均为 `async`,内部会校验合法行动并可能推进阶段/终局):
|
|
117
|
+
当前行动方:`texas.controller.activePlayer`(须已消费队头 `turn_handoff`,否则 `status` 非 `active`,`dispatchCommand` 会被拒)。
|
|
111
118
|
|
|
112
|
-
|
|
119
|
+
```ts
|
|
120
|
+
const cmd: TableCommand = { type: 'Call', playerId: 2 }
|
|
121
|
+
const events = texas.dispatchCommand(cmd)
|
|
122
|
+
// 自愿:Fold | Check | Call | Bet | Raise | AllIn
|
|
123
|
+
// 超时:FoldDueToTimeout | CheckDueToTimeout(须当前行动方)
|
|
124
|
+
// 离场:FoldDueToLeave(须当前行动方;TurnEnded.reason === 'leave')
|
|
125
|
+
// 入座大盲:PostBigBlind(翻前、本街尚未入池;金额由 stakes 推导)
|
|
126
|
+
|
|
127
|
+
await drainAndConsumePendingFlow(texas, events)
|
|
128
|
+
```
|
|
113
129
|
|
|
114
|
-
|
|
130
|
+
轮到谁、允许哪些操作:解释缓冲中的 **`TurnOffered`**(`allowedActions`、`restrict`)。
|
|
131
|
+
行动结果:**`PlayerActed`** + 紧邻 **`PotUpdated`**;交权结束:**`TurnEnded`**。
|
|
115
132
|
|
|
116
133
|
### 6. 本手结束与清理
|
|
117
134
|
|
|
118
|
-
|
|
119
|
-
之后业务侧通常:
|
|
135
|
+
终局时缓冲会出现 **`HandEnded`**(`outcome`、`pokesRevealed`、`bestPokes` 等)。业务通常在解释该事件时:
|
|
120
136
|
|
|
121
137
|
```ts
|
|
122
|
-
texas.settle() // pool.pay()
|
|
123
|
-
|
|
124
|
-
|
|
138
|
+
const awarded = texas.settle() // pool.pay() → PotAwarded
|
|
139
|
+
await interpret(awarded)
|
|
140
|
+
|
|
141
|
+
texas.reset() // pool + dealer + controller → idle,unlockSeats
|
|
142
|
+
// 下一手:reset → rotateRolesForNewHand → …局间 seat/reArrange… → lockSeats
|
|
143
|
+
// → setPlayerRoles('rearrange')? → dealCards → start() 或 startPreflopWithJoiningBigBlinds
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 7. 业务侧最小循环(示意)
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import type { Texas, TexasDomainEvent } from 'texas-poker-core'
|
|
150
|
+
|
|
151
|
+
async function drainAndConsumePendingFlow(
|
|
152
|
+
texas: Texas,
|
|
153
|
+
firstBatch: TexasDomainEvent[]
|
|
154
|
+
) {
|
|
155
|
+
await interpret(firstBatch)
|
|
156
|
+
while (texas.getPendingFlowOps().length > 0) {
|
|
157
|
+
await sleep(actionRequiredMs) // 产品节拍,Core 内无 sleep
|
|
158
|
+
const head = texas.getPendingFlowOps()[0]
|
|
159
|
+
const more =
|
|
160
|
+
head.kind === 'stage_advance'
|
|
161
|
+
? texas.applyPendingStageAdvance()
|
|
162
|
+
: texas.flushPendingTurnHandoff()
|
|
163
|
+
await interpret(more)
|
|
164
|
+
if (head.kind === 'stage_advance' && texas.getPendingFlowOps().length > 0) {
|
|
165
|
+
await sleep(stageChangedMs)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
125
169
|
```
|
|
126
170
|
|
|
171
|
+
单测可省略 `sleep`,在每次 `dispatchCommand` 后调用 **`texas.flushAllPendingFlowOps()`**。
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 领域事件(`TexasDomainEvent`)
|
|
176
|
+
|
|
177
|
+
类型定义:`src/domain/handDomainEvents.ts`(包内从 `texas-poker-core` 导出 `TexasDomainEvent` / `HandDomainEvent`)。
|
|
178
|
+
本手事件均带 **`handId`**(`start` 时分配,如 `h1`)与单调 **`seq`**,供 `(matchId, handId, seq)` 幂等落库与回放。
|
|
179
|
+
|
|
180
|
+
| 事件 | 典型触发时机 |
|
|
181
|
+
| ---- | ------------ |
|
|
182
|
+
| `RolesAssigned` | `setPlayerRoles` |
|
|
183
|
+
| `HoleCardsDealt` | `dealCards` |
|
|
184
|
+
| `HandStarted` | `start` / `startPreflopWithJoiningBigBlinds` |
|
|
185
|
+
| `PostedJoiningBigBlinds` | 入座大盲汇总(`startPreflopWithJoiningBigBlinds` 恒一条;`PostBigBlind` 每次一条) |
|
|
186
|
+
| `BlindsPosted` | 桌 SB/BB 贴盲后 |
|
|
187
|
+
| `PlayerActed` | 自愿行动 / 超时行动(盲注路径不发) |
|
|
188
|
+
| `PotUpdated` | 每次入池后(紧跟对应 `PlayerActed` 或盲注行) |
|
|
189
|
+
| `StageAdvanced` | 消费 `stage_advance`:正常进街或跑马揭示 |
|
|
190
|
+
| `TurnOffered` | 消费 `turn_handoff`:`getControl` 后 |
|
|
191
|
+
| `TurnEnded` | 行动方交权结束(`reason`: `action` \| `timeout` \| `leave` 等) |
|
|
192
|
+
| `PotAwarded` | `settle()` |
|
|
193
|
+
| `HandEnded` | 独赢弃牌或摊牌/跑马结束 |
|
|
194
|
+
|
|
195
|
+
**不再提供** `onGameEnd` / `onAction` / `onPreAction` 等实例回调;节奏由业务解释事件 + 消费 `pendingFlowOps` 完成。
|
|
196
|
+
|
|
197
|
+
回放与持久化辅助:`toPersistedDomainEventRows`、`projectCompositeReadModel`、`interpret` 等见包导出与 [docs/replay-app-server-core-coordination.md](./docs/replay-app-server-core-coordination.md)。
|
|
198
|
+
|
|
127
199
|
---
|
|
128
200
|
|
|
129
|
-
##
|
|
201
|
+
## `TableCommand`(`dispatchCommand`)
|
|
202
|
+
|
|
203
|
+
| `type` | 说明 |
|
|
204
|
+
| ------ | ---- |
|
|
205
|
+
| `Fold` / `Check` / `Call` / `Bet` / `Raise` / `AllIn` | 自愿行动;`Bet`/`Raise` 带 `amount` / `additionalAmount` |
|
|
206
|
+
| `FoldDueToTimeout` / `CheckDueToTimeout` | 计时到期;须当前行动方 |
|
|
207
|
+
| `FoldDueToLeave` | 当前行动方离场弃牌;可先 `canFoldDueToLeave(userId)` |
|
|
208
|
+
| `PostBigBlind` | 翻前补入座大盲;可选 `allowWhenCurrentActor`(服务端可信队列) |
|
|
130
209
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| `onRolesAssigned` | `setPlayerRoles` 成功后,携带 `userId` / `role` / `actionIndex` |
|
|
135
|
-
| `onDealCards` | `dealCards` 成功后,各玩家手牌(业务可据此推送私密牌) |
|
|
136
|
-
| `onPreAction` | 轮到玩家行动前(注册到所有当前 `Player`) |
|
|
137
|
-
| `onAction` | 玩家完成一次合法行动后(写库、广播;默认盲注行为可能不触发,以实现为准) |
|
|
138
|
-
| `onGameStart` | 本手 `controller.start()` 内、盲注与首回合开始前 |
|
|
139
|
-
| `onGameEnd` | 一手结束,携带公牌、摊牌信息、`pokesRevealed` 等 |
|
|
140
|
-
| `onNextStage` | 翻牌 / 转牌 / 河牌等阶段推进,携带本段新亮公牌 |
|
|
210
|
+
JSON 解析:`parseTableCommandFromJson` / `parseTableCommandFromUnknown`。
|
|
211
|
+
|
|
212
|
+
---
|
|
141
213
|
|
|
142
|
-
`
|
|
214
|
+
## `Texas` 会话 API 速查
|
|
215
|
+
|
|
216
|
+
| 方法 | 说明 |
|
|
217
|
+
| ---- | ---- |
|
|
218
|
+
| `drainDomainEvents()` | 取出并清空事件缓冲(无副作用) |
|
|
219
|
+
| `dispatchCommand(cmd)` | 玩家行动唯一推荐入口;返回本步事件 |
|
|
220
|
+
| `getPendingFlowOps()` | 流程队列快照(`stage_advance` \| `turn_handoff`) |
|
|
221
|
+
| `applyPendingStageAdvance()` | 消费队头进街/跑马;队头不对抛 `CTRL_FLOW_PENDING_MISMATCH` |
|
|
222
|
+
| `flushPendingTurnHandoff()` | 消费队头交权 → `TurnOffered`;队头不对则静默 return |
|
|
223
|
+
| `flushAllPendingFlowOps()` | 无 sleep 排空队列(单测/批处理) |
|
|
224
|
+
| `setPlayerRoles` / `dealCards` / `start` / `settle` | 返回本步领域事件;`start` 后须消费队列才有 `TurnOffered` |
|
|
225
|
+
| `startPreflopWithJoiningBigBlinds(ids)` | 含入座大盲的原子开局 |
|
|
226
|
+
| `rotateRolesForNewHand()` | `reset` 后局间移庄,不锁座 |
|
|
227
|
+
| `reArrangeRoles()` | 按庄位重算角色,不写 `RolesAssigned` |
|
|
228
|
+
| `lockSeats` / `unlockSeats` | 显式锁座;`reset()` 会 `unlockSeats` |
|
|
229
|
+
| `removePlayerByIdAsSystem` | 系统级踢人(可绕过 `seats_locked`) |
|
|
230
|
+
| `canFoldDueToLeave` / `end` | 离场弃牌预判 / 强制结束到 `between_hands` |
|
|
143
231
|
|
|
144
232
|
---
|
|
145
233
|
|
|
@@ -150,8 +238,8 @@ texas.reset() // pool + dealer + controller 清理,controller → idle,并 u
|
|
|
150
238
|
- `watch` / `watchById`:回观战(须 `seats_open`)
|
|
151
239
|
- `remove` / `removeById`:离房;**仅观战(`hang`)锁座时也可离房**;**已入座**须 `seats_open`(**房主需业务先 `setOwner` 再 remove**)
|
|
152
240
|
- `initialRoles`:定庄 + 盲位并 **lockSeats**;`rotateRoles`:局间移庄,**不** lock(下一手前由业务 `lockSeats`)
|
|
153
|
-
- `lockSeats` / `unlockSeats
|
|
154
|
-
- `setOwner` / `setOwnerById` / `
|
|
241
|
+
- `lockSeats` / `unlockSeats`:也可经 `texas.lockSeats()` / `texas.unlockSeats()`
|
|
242
|
+
- `setOwner` / `setOwnerById` / `getPlayerById` / `getPlayersBySeatStatus` 等
|
|
155
243
|
|
|
156
244
|
---
|
|
157
245
|
|
|
@@ -159,18 +247,26 @@ texas.reset() // pool + dealer + controller 清理,controller → idle,并 u
|
|
|
159
247
|
|
|
160
248
|
```ts
|
|
161
249
|
Texas.configureEngine({
|
|
162
|
-
|
|
250
|
+
simulation: {
|
|
251
|
+
// 单测/集成脚本:如 immediateDefaultActionOnTurn、allowSingleSeatedPlayer 等
|
|
252
|
+
}
|
|
163
253
|
})
|
|
164
|
-
Texas.resetEngineContext()
|
|
254
|
+
Texas.resetEngineContext() // jest.setup 已 beforeEach 调用
|
|
165
255
|
```
|
|
166
256
|
|
|
167
|
-
|
|
257
|
+
导出中还包含牌型/阶段/行动枚举、读模型 reducer(`reduceCommunityBoardFromDomainEvents` 等)、`createStandardDeckPokes`、`rankSignatureToDisplayGroups`、`isFatalTexasErrorCode`、`texasErrorCategory` 等。
|
|
168
258
|
|
|
169
259
|
---
|
|
170
260
|
|
|
171
261
|
## 更多文档
|
|
172
262
|
|
|
173
|
-
|
|
263
|
+
| 文档 | 内容 |
|
|
264
|
+
| ---- | ---- |
|
|
265
|
+
| [CORE_API.md](./CORE_API.md) | 对外 API 与不变量摘要 |
|
|
266
|
+
| [docs/integration-core-wish-event-flow.md](./docs/integration-core-wish-event-flow.md) | 与参考服务端的整局事件流、节拍、落库 |
|
|
267
|
+
| [docs/refactor-maintainer-reference.md](./docs/refactor-maintainer-reference.md) | 队列语义与维护速查 |
|
|
268
|
+
| [docs/replay-app-server-core-coordination.md](./docs/replay-app-server-core-coordination.md) | 回放磁带与 App/Server 配合 |
|
|
269
|
+
| [docs/How to refactor to be side-effect-free/](./docs/How%20to%20refactor%20to%20be%20side-effect-free/) | 事件化架构与路线图 |
|
|
174
270
|
|
|
175
271
|
---
|
|
176
272
|
|
|
@@ -616,4 +712,10 @@ fix: 修复结算金额分配异常
|
|
|
616
712
|
reject modulo bias
|
|
617
713
|
|
|
618
714
|
## 1.4.28
|
|
619
|
-
|
|
715
|
+
|
|
716
|
+
贴盲与 game start 原子化
|
|
717
|
+
|
|
718
|
+
## 1.4.29
|
|
719
|
+
修复post bb玩家过牌导致的异常
|
|
720
|
+
## 1.4.30
|
|
721
|
+
同步README接入文档
|
|
@@ -33,7 +33,9 @@ function resolveAllowedActions(ctx) {
|
|
|
33
33
|
});
|
|
34
34
|
return _helper(player !== null && player !== void 0 ? player : null);
|
|
35
35
|
}
|
|
36
|
-
if (lastPlayer.getAction().type === _constant.ActionTypeEnum.CHECK
|
|
36
|
+
if (lastPlayer.getAction().type === _constant.ActionTypeEnum.CHECK && lastPlayer.currentStageTotalAmount === 0) {
|
|
37
|
+
return [_constant.ActionTypeEnum.ALL_IN, _constant.ActionTypeEnum.BET, _constant.ActionTypeEnum.CHECK, _constant.ActionTypeEnum.FOLD];
|
|
38
|
+
}
|
|
37
39
|
if (ctx.selfBalance + ctx.selfCurrentStageTotal <= ctx.maxOthersStageBet) {
|
|
38
40
|
return [_constant.ActionTypeEnum.ALL_IN, _constant.ActionTypeEnum.FOLD];
|
|
39
41
|
}
|
package/package.json
CHANGED