Skip to content

CombinationDetector ​

通用牌型检测器,将一组卡牌识别为 CardCombination。

概述 ​

CombinationDetector 是 combination 系统的识别组件,位于 src/combination/CombinationDetector.ts。它仅覆盖与具体游戏无关的常见牌型:

  • 单张(SINGLE)
  • 对子(PAIR)
  • 三张(TRIPLE)
  • 四张(FOUR_OF_A_KIND)
  • 顺子(STRAIGHT,长度由 minStraightLength 决定)

不可识别的组合统一返回 CombinationType.UNKNOWN,并在 metadata.reason 中给出原因。

设计上为纯函数式:依赖注入 RankResolver,不持有可变状态,可在多个游戏间共享实例。

各游戏的专属牌型(如斗地主的飞机、火箭)应在 src/plugins/<game>/combinations/ 中独立扩展,不应在此处添加。

构造函数 ​

new CombinationDetector(options?) ​

ts
new CombinationDetector(options?: CombinationDetectorOptions)

参数 ​

参数类型必填默认值说明
optionsCombinationDetectorOptions否{}配置选项

CombinationDetectorOptions ​

ts
interface CombinationDetectorOptions {
  resolver?: RankResolver
  minStraightLength?: number
}
字段类型必填默认值说明
resolverRankResolver否new StandardPokerRankResolver()点数解析器
minStraightLengthnumber否5顺子所需的最短长度

属性 ​

无公开属性。

方法 ​

detect() ​

检测卡牌集合的牌型。

ts
detect(cards: readonly Card[]): CardCombination

参数 ​

参数类型必填默认值说明
cardsreadonly Card[]是—待检测的卡牌集合(不会被修改)

返回值 ​

CardCombination:包含类型、排序后卡牌与点数信息的描述对象。

行为 ​

识别流程按以下顺序执行,命中即返回:

  1. 空集:返回 { type: UNKNOWN, cards: [], metadata: { reason: 'empty' } }。
  2. 单张(cards.length === 1):返回 SINGLE,rank 与 value 均设为该牌点数。
  3. 单点数多张(uniqueRankCount === 1):
    • 2 张 → PAIR
    • 3 张 → TRIPLE
    • 4 张 → FOUR_OF_A_KIND
    • 其他张数 → UNKNOWN,metadata.reason = 'too many cards of same rank'
  4. 顺子(uniqueRankCount === cards.length && cards.length >= minStraightLength):
    • 对去重后的点数序列升序排序,调用内部 isConsecutive 判定是否每相邻两项差 1;
    • 若连续,返回 STRAIGHT,rank 与 value 设为最高点数 high,metadata 包含 { length, low, high }。
  5. 其他:返回 UNKNOWN,metadata.reason = 'no matching pattern'。

isConsecutive 内部函数 ​

判定给定的(已排序、去重的)点数序列是否连续递增 1。用于识别 STRAIGHT 牌型;不足 2 个元素的序列视为非连续。该函数为模块私有,不对外导出。

示例 ​

ts
import { CombinationDetector } from '../combination/CombinationDetector.js'
import { CombinationType } from '../combination/CombinationType.js'
import type { Card } from '../card/Card.js'

const detector = new CombinationDetector()

const single: Card[] = [
  { id: 'c1', type: 'standard', suit: 'spade', rank: '5', data: {} }
]
detector.detect(single)
// -> { type: SINGLE, cards: [...], rank: 5, value: 5 }

const pair: Card[] = [
  { id: 'c1', type: 'standard', suit: 'spade', rank: '5', data: {} },
  { id: 'c2', type: 'standard', suit: 'heart', rank: '5', data: {} }
]
detector.detect(pair)
// -> { type: PAIR, cards: [...], rank: 5, value: 5 }

const straight: Card[] = [
  { id: 's5', type: 'standard', suit: 'spade', rank: '5', data: {} },
  { id: 's6', type: 'standard', suit: 'spade', rank: '6', data: {} },
  { id: 's7', type: 'standard', suit: 'spade', rank: '7', data: {} },
  { id: 's8', type: 'standard', suit: 'spade', rank: '8', data: {} },
  { id: 's9', type: 'standard', suit: 'spade', rank: '9', data: {} }
]
detector.detect(straight)
// -> { type: STRAIGHT, cards: [...], rank: 9, value: 9, metadata: { length: 5, low: 5, high: 9 } }

detector.detect([])
// -> { type: UNKNOWN, cards: [], metadata: { reason: 'empty' } }

自定义顺子长度 ​

通过 minStraightLength 可调整识别顺子所需的最短长度:

ts
import { CombinationDetector } from '../combination/CombinationDetector.js'

// 三张连续即视为顺子
const detector = new CombinationDetector({ minStraightLength: 3 })

自定义点数语义 ​

注入 LowAceRankResolver 可让 A 视为最低牌(1),支持 A-2-3-4-5 形态的低 A 顺子:

ts
import { CombinationDetector } from '../combination/CombinationDetector.js'
import { LowAceRankResolver } from '../combination/CombinationValue.js'

const detector = new CombinationDetector({ resolver: new LowAceRankResolver() })

注意事项 ​

  • 该检测器不识别同花、葫芦、同花顺等高级牌型。源码 CombinationType 枚举中亦未定义 FLUSH / FULL_HOUSE / STRAIGHT_FLUSH,仅在任务文档中提及 —— 实际源码以 CombinationType 为准。
  • 同一实例可被多个游戏共享;构造一次后多次调用 detect() 之间无副作用。
  • 返回的 cards 字段是经 sortByRank 排序后的新数组,不会持有对入参数组的引用。