Skip to content

StandardDeckPlugin ​

在 setup 阶段创建一副 52 张牌的 French deck 并放入 deck zone 的标准牌堆插件。

概述 ​

StandardDeckPlugin 位于 src/plugins/StandardDeckPlugin.ts。它在 setup 阶段创建 4 花色 × 13 点数共 52 张标准扑克牌并放入名为 deck 的 zone 中;在 GAME_STARTED 事件触发时自动派发一个 SHUFFLE action,使牌堆在任何抽牌前已被随机洗牌。

它本身不实现任何游戏规则,仅提供"标准牌堆"基础设施。通常与 DrawPlugin 配合使用以支持抽牌。

导出常量 ​

SHUFFLE_ACTION ​

SHUFFLE action 的类型常量,用于触发指定 zone 的洗牌。

ts
export const SHUFFLE_ACTION = 'SHUFFLE' as const
常量值说明
SHUFFLE_ACTION'SHUFFLE'SHUFFLE action 的 type 字符串

STANDARD_DECK_ZONE_ID ​

标准牌堆 zone 的固定 id。

ts
export const STANDARD_DECK_ZONE_ID = 'deck'
常量值说明
STANDARD_DECK_ZONE_ID'deck'标准牌堆的 zone id

ShufflePayload ​

SHUFFLE action 的 payload 结构。

ts
export interface ShufflePayload {
  zoneId?: string
}

字段 ​

字段类型必填说明
zoneIdstring否待洗牌的 zone id;缺省时取 STANDARD_DECK_ZONE_ID(即 'deck')

StandardDeckPlugin 对象 ​

ts
export const StandardDeckPlugin: GamePlugin = {
  id: 'standard-deck',
  name: 'Standard 52-card Deck',
  version: '1.0.0',
  setup, actions, events
}

字段 ​

字段值说明
id'standard-deck'插件唯一标识
name'Standard 52-card Deck'插件名
version'1.0.0'插件版本
setup见下创建牌堆 zone 与 52 张牌
actions[SHUFFLE handler]处理 SHUFFLE action
events[GAME_STARTED handler]在开局时派发 SHUFFLE

setup() ​

创建牌堆 zone 与 52 张标准牌。所有写入均先检查存在性,保证回放模式下 setup 再次执行时保持幂等。

ts
setup(context: GameContext): void

行为 ​

  1. 若 state.zones[STANDARD_DECK_ZONE_ID] 不存在,通过 ctx.addZone(createZone({ id: 'deck', type: 'deck' })) 创建。
  2. 对每个花色 s ∈ [spade, heart, diamond, club] 与每个点数 r ∈ [A, 2, 3, ..., 10, J, Q, K]:
    • 卡牌 id 形如 std-${suit}-${rank}(如 std-spade-A);
    • 若 state.cards[id] 已存在则跳过(幂等);
    • 否则通过 ctx.addCard(createCard({ id, type: 'standard', suit, rank, value, tags, data })) 创建并 ctx.addCardToZone(id, 'deck') 加入牌堆。
  3. 卡牌字段映射:
    • type: 'standard'
    • suit: 花色名(如 'spade')
    • rank: 点数字符串(如 'A'、'10')
    • value: A=1, 2~10=2~10, J=11, Q=12, K=13
    • tags: [color, suit](如 ['black', 'spade'])
    • data: { symbol, color, rank, suit }(symbol 如 'S' / 'H' / 'D' / 'C')

注意:value 字段中 A=1,与 StandardPokerRankResolver 把 A 解析为 14 不同 —— 后者会优先使用 rank 字段而非 value,因此 A 在牌型系统中仍视为 14。

actions — SHUFFLE handler ​

ts
{
  type: SHUFFLE_ACTION,
  execute(action, ctx): { state, events }
}

行为 ​

  1. 从 action.payload(或空对象)中读出 zoneId,缺省取 STANDARD_DECK_ZONE_ID(即 'deck')。
  2. 查找 zone;不存在则抛 Error,消息为 Cannot shuffle: zone ${zoneId} not found。
  3. 通过 ctx.random.shuffle(zone.cards) 洗牌(Fisher–Yates,返回新数组)。
  4. 返回新 state(用洗牌后的 cards 替换该 zone 的 cards 字段)与事件列表:
    • DECK_SHUFFLED,payload 为 { zoneId, count: shuffledCards.length }。

示例 ​

ts
import { createAction } from 'decklet'
import {
  SHUFFLE_ACTION,
  type ShufflePayload
} from 'decklet/plugins'

const shuffleAction = createAction({
  type: SHUFFLE_ACTION,
  payload: { zoneId: 'deck' } as ShufflePayload
})
// engine.dispatch(shuffleAction) -> 触发 SHUFFLE handler

events — GAME_STARTED handler ​

ts
{
  type: 'GAME_STARTED',
  handle(_event, ctx): void
}

行为 ​

在 GAME_STARTED 事件触发时,通过 ctx.dispatch(createAction({ type: SHUFFLE_ACTION, payload: { zoneId: 'deck' } })) 派发一个洗牌 Action,使牌堆在任何抽牌前已被随机洗牌。

示例 ​

ts
import { GameEngine } from 'decklet'
import {
  StandardDeckPlugin,
  STANDARD_DECK_ZONE_ID,
  DrawPlugin,
  TurnPlugin
} from 'decklet/plugins'

const engine = new GameEngine({ seed: 42, gameId: 'g1' })
engine.use(StandardDeckPlugin)
engine.use(DrawPlugin)
engine.use(TurnPlugin)

engine.createGame({ players: [{ id: 'p1', name: 'Alice', seat: 0, status: 'active', data: {} }] })
// setup 阶段创建 52 张牌 + deck zone
engine.start()
// GAME_STARTED -> 派发 SHUFFLE -> 牌堆被打乱
const deckZone = engine.getState().zones[STANDARD_DECK_ZONE_ID]
console.log(deckZone.cards.length) // -> 52

注意事项 ​

  • setup 严格幂等:所有写入均先检查存在性,回放模式下 setup 重复执行不会产生重复卡牌。
  • value 字段 A=1,这是为计分或比较而设;如需在牌型系统中将 A 视为 14,应使用 rank 字段(由 StandardPokerRankResolver 解析)。
  • GAME_STARTED 派发的 SHUFFLE action 走 ActionQueue 链路,受 maxActionsPerTick 约束。
  • 该插件不处理抽牌逻辑;要支持抽牌,请配合 DrawPlugin 使用。