From d1b7699cb4790098e3eb7bf093b1c6d1a1f0242e Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Fri, 30 Jun 2023 12:50:10 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20poker=20?= =?UTF-8?q?=E5=8C=85=E8=AE=BE=E8=AE=A1=EF=BC=8C=E7=A7=BB=E9=99=A4=20Poker?= =?UTF-8?q?=20=E7=BB=93=E6=9E=84=E4=BD=93=EF=BC=8C=E4=BB=A5=20Rule=20?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=BD=93=E8=BF=9B=E8=A1=8C=E5=8F=96=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除了 poker.Poker 的设计,采用了 poker.Rule 管理规则。 - 将牌堆和规则进行分离,可单独使用。 - 增加大量辅助函数及内置牌型检测函数。 --- game/poker/card.go | 101 +++++++++- game/poker/card_pile.go | 2 +- game/poker/color.go | 16 +- game/poker/func.go | 19 -- game/poker/hand.go | 420 +++++++++++++++++++++++++++++++++++++--- game/poker/options.go | 82 ++++++-- game/poker/poker.go | 116 +++++------ game/poker/rule.go | 178 +++++++++++++++++ utils/maths/math.go | 17 ++ 9 files changed, 823 insertions(+), 128 deletions(-) delete mode 100644 game/poker/func.go create mode 100644 game/poker/rule.go diff --git a/game/poker/card.go b/game/poker/card.go index 8dad8ae..4aa0536 100644 --- a/game/poker/card.go +++ b/game/poker/card.go @@ -1,6 +1,9 @@ package poker -import "fmt" +import ( + "fmt" + "math" +) // NewCard 创建一张扑克牌 // - 当 point 为 PointBlackJoker 或 PointRedJoker 时,color 将没有效果 @@ -54,6 +57,102 @@ func (slf Card) Equal(card Card) bool { return slf.GetPoint() == card.GetPoint() && slf.GetColor() == card.GetColor() } +// MaxPoint 返回两张扑克牌中点数较大的一张 +func (slf Card) MaxPoint(card Card) Card { + if slf.GetPoint() > card.GetPoint() { + return slf + } + return card +} + +// MinPoint 返回两张扑克牌中点数较小的一张 +func (slf Card) MinPoint(card Card) Card { + if slf.GetPoint() < card.GetPoint() { + return slf + } + return card +} + +// MaxColor 返回两张扑克牌中花色较大的一张 +func (slf Card) MaxColor(card Card) Card { + if slf.GetColor() > card.GetColor() { + return slf + } + return card +} + +// MinColor 返回两张扑克牌中花色较小的一张 +func (slf Card) MinColor(card Card) Card { + if slf.GetColor() < card.GetColor() { + return slf + } + return card +} + +// Max 返回两张扑克牌中点数和花色较大的一张 +func (slf Card) Max(card Card) Card { + if slf.GetPoint() > card.GetPoint() { + return slf + } else if slf.GetPoint() < card.GetPoint() { + return card + } else { + if slf.GetColor() > card.GetColor() { + return slf + } + return card + } +} + +// Min 返回两张扑克牌中点数和花色较小的一张 +func (slf Card) Min(card Card) Card { + if slf.GetPoint() < card.GetPoint() { + return slf + } else if slf.GetPoint() > card.GetPoint() { + return card + } else { + if slf.GetColor() < card.GetColor() { + return slf + } + return card + } +} + +// IsJoker 判断是否为大小王 +func (slf Card) IsJoker() bool { + point := slf.GetPoint() + return point == PointRedJoker || point == PointBlackJoker +} + +// CalcPointDifference 计算两张扑克牌的点数差 +func (slf Card) CalcPointDifference(card Card) int { + return int(slf.GetPoint()) - int(card.GetPoint()) +} + +// CalcPointDifferenceAbs 计算两张扑克牌的点数差的绝对值 +func (slf Card) CalcPointDifferenceAbs(card Card) int { + return int(math.Abs(float64(slf.CalcPointDifference(card)))) +} + +// CalcColorDifference 计算两张扑克牌的花色差 +func (slf Card) CalcColorDifference(card Card) int { + return int(slf.GetColor()) - int(card.GetColor()) +} + +// CalcColorDifferenceAbs 计算两张扑克牌的花色差的绝对值 +func (slf Card) CalcColorDifferenceAbs(card Card) int { + return int(math.Abs(float64(slf.CalcColorDifference(card)))) +} + +// IsNeighborPoint 判断两张扑克牌是否为相邻的点数 +func (slf Card) IsNeighborPoint(card Card) bool { + return slf.CalcPointDifferenceAbs(card) == 1 +} + +// IsNeighborColor 判断两张扑克牌是否为相邻的花色 +func (slf Card) IsNeighborColor(card Card) bool { + return slf.CalcColorDifferenceAbs(card) == 1 +} + // String 将扑克牌转换为字符串形式 func (slf Card) String() string { return fmt.Sprintf("(%s %s)", slf.point, slf.color) diff --git a/game/poker/card_pile.go b/game/poker/card_pile.go index 87cf981..ee37abb 100644 --- a/game/poker/card_pile.go +++ b/game/poker/card_pile.go @@ -48,7 +48,7 @@ func (slf *CardPile) Reset() { cards = append(cards, NewCard(PointBlackJoker, ColorNone)) } for point := PointK; point >= PointA; point-- { - for color := ColorSpade; color <= ColorDiamond; color++ { + for color := ColorDiamond; color <= ColorSpade; color++ { if !slf.IsExclude(point, color) { cards = append(cards, NewCard(point, color)) } diff --git a/game/poker/color.go b/game/poker/color.go index 21b84f2..6cbc652 100644 --- a/game/poker/color.go +++ b/game/poker/color.go @@ -2,12 +2,20 @@ package poker const ( ColorNone Color = 0 // 无花色,通常为大小王 - ColorSpade Color = 1 // 黑桃 - ColorHeart Color = 2 // 红桃 - ColorClub Color = 3 // 梅花 - ColorDiamond Color = 4 // 方片 + ColorSpade Color = 4 // 黑桃 + ColorHeart Color = 3 // 红桃 + ColorClub Color = 2 // 梅花 + ColorDiamond Color = 1 // 方片 ) +var defaultColorSort = map[Color]int{ + ColorSpade: int(ColorSpade), + ColorHeart: int(ColorHeart), + ColorClub: int(ColorClub), + ColorDiamond: int(ColorDiamond), + ColorNone: int(ColorDiamond + 1), +} + // Color 扑克牌花色 type Color int diff --git a/game/poker/func.go b/game/poker/func.go deleted file mode 100644 index 9731484..0000000 --- a/game/poker/func.go +++ /dev/null @@ -1,19 +0,0 @@ -package poker - -// GetCardsPoint 获取一组扑克牌的点数 -func GetCardsPoint(cards ...Card) []Point { - var points = make([]Point, len(cards)) - for i, card := range cards { - points[i] = card.GetPoint() - } - return points -} - -// GetCardsColor 获取一组扑克牌的花色 -func GetCardsColor(cards ...Card) []Color { - var colors = make([]Color, len(cards)) - for i, card := range cards { - colors[i] = card.GetColor() - } - return colors -} diff --git a/game/poker/hand.go b/game/poker/hand.go index 4ff4b42..4fe1ab4 100644 --- a/game/poker/hand.go +++ b/game/poker/hand.go @@ -1,36 +1,412 @@ package poker +const ( + HandNone = "None" // 无牌型 +) + // HandHandle 扑克牌型验证函数 -type HandHandle func(poker *Poker, cards []Card) bool - -// HandPairs 对子 -func HandPairs() HandHandle { - return func(poker *Poker, cards []Card) bool { - return len(cards) == 2 && cards[0].EqualPoint(cards[1]) - } -} - -// HandFlushPairs 同花对子 -func HandFlushPairs() HandHandle { - return func(poker *Poker, cards []Card) bool { - if len(cards) != 2 { - return false - } - card1, card2 := cards[0], cards[1] - return card1.Equal(card2) - } -} +type HandHandle func(rule *Rule, cards []Card) bool // HandSingle 单牌 func HandSingle() HandHandle { - return func(poker *Poker, cards []Card) bool { + return func(rule *Rule, cards []Card) bool { return len(cards) == 1 } } +// HandPairs 对子 +func HandPairs() HandHandle { + return func(rule *Rule, cards []Card) bool { + return len(cards) == 2 && rule.IsPointContinuity(2, cards...) + } +} + // HandThreeOfKind 三张 func HandThreeOfKind() HandHandle { - return func(poker *Poker, cards []Card) bool { - return len(cards) == 3 && cards[0].EqualPoint(cards[1]) && cards[1].EqualPoint(cards[2]) + return func(rule *Rule, cards []Card) bool { + return len(cards) == 3 && rule.IsPointContinuity(3, cards...) + } +} + +// HandThreeOfKindWithOne 三带一 +func HandThreeOfKindWithOne() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + if len(group) != 2 { + return false + } + var hasThree bool + var count int + for _, cards := range group { + if len(cards) == 3 { + hasThree = true + } else { + count = len(cards) + } + } + return hasThree && count == 1 + } +} + +// HandThreeOfKindWithTwo 三带二 +func HandThreeOfKindWithTwo() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + if len(group) != 2 { + return false + } + var hasThree bool + var count int + for _, cards := range group { + if len(cards) == 3 { + hasThree = true + } else { + count = len(cards) + } + } + return hasThree && count == 2 + } +} + +// HandOrderSingle 顺子 +func HandOrderSingle(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + return len(cards) >= count && rule.IsPointContinuity(1, cards...) + } +} + +// HandOrderPairs 对子顺子 +func HandOrderPairs(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) < count*2 || len(cards)%2 != 0 { + return false + } + return rule.IsPointContinuity(2, cards...) + } +} + +// HandOrderSingleThree 三张顺子 +func HandOrderSingleThree(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) < count*3 || len(cards)%3 != 0 { + return false + } + return rule.IsPointContinuity(3, cards...) + } +} + +// HandOrderSingleFour 四张顺子 +func HandOrderSingleFour(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) < count*4 || len(cards)%4 != 0 { + return false + } + return rule.IsPointContinuity(4, cards...) + } +} + +// HandOrderThreeWithOne 三带一顺子 +func HandOrderThreeWithOne(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var continuous []Card + var other int + for _, cards := range group { + if len(cards) == 3 { + continuous = append(continuous, cards...) + } else { + other += len(cards) + } + } + if !rule.IsPointContinuity(3, continuous...) { + return false + } + return other == len(continuous)/3 + } +} + +// HandOrderThreeWithTwo 三带二顺子 +func HandOrderThreeWithTwo(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var continuous []Card + var other int + for _, cards := range group { + if len(cards) == 3 { + continuous = append(continuous, cards...) + } else if len(cards)%2 == 0 { + other += len(cards) / 2 + } else { + return false + } + } + if !rule.IsPointContinuity(3, continuous...) { + return false + } + return other == len(continuous)/3 + } +} + +// HandOrderFourWithOne 四带一顺子 +func HandOrderFourWithOne(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var continuous []Card + var other int + for _, cards := range group { + if len(cards) == 4 { + continuous = append(continuous, cards...) + } else { + other += len(cards) + } + } + if !rule.IsPointContinuity(4, continuous...) { + return false + } + return other == len(continuous)/4 + } +} + +// HandOrderFourWithTwo 四带二顺子 +func HandOrderFourWithTwo(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var continuous []Card + var other int + for _, cards := range group { + if len(cards) == 4 { + continuous = append(continuous, cards...) + } else if len(cards)%2 == 0 { + other += len(cards) / 2 + } else { + return false + } + } + if !rule.IsPointContinuity(4, continuous...) { + return false + } + return other == len(continuous)/4 + } +} + +// HandOrderFourWithThree 四带三顺子 +func HandOrderFourWithThree(count int) HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var continuous []Card + var other int + for _, cards := range group { + if len(cards) == 4 { + continuous = append(continuous, cards...) + } else if len(cards)%3 == 0 { + other += len(cards) / 3 + } else { + return false + } + } + if !rule.IsPointContinuity(4, continuous...) { + return false + } + return other == len(continuous)/4 + } +} + +// HandFourWithOne 四带一 +func HandFourWithOne() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var hasFour bool + var count int + for _, cards := range group { + if len(cards) == 4 { + hasFour = true + } else { + count = len(cards) + } + } + return hasFour && count == 1 + } +} + +// HandFourWithTwo 四带二 +func HandFourWithTwo() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var hasFour bool + var count int + for _, cards := range group { + if len(cards) == 4 { + hasFour = true + } else { + count = len(cards) + } + } + return hasFour && count == 2 + } +} + +// HandFourWithThree 四带三 +func HandFourWithThree() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var hasFour bool + var count int + for _, cards := range group { + if len(cards) == 4 { + hasFour = true + } else { + count = len(cards) + } + } + return hasFour && count == 3 + } +} + +// HandFourWithTwoPairs 四带两对 +func HandFourWithTwoPairs() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var hasFour bool + var count int + for _, cards := range group { + length := len(cards) + if length == 4 && !hasFour { + hasFour = true + } else if length%2 == 0 { + count += len(cards) / 2 + if count > 2 { + return false + } + } else { + return false + } + } + return hasFour && count == 2 + } +} + +// HandBomb 炸弹 +func HandBomb() HandHandle { + return func(rule *Rule, cards []Card) bool { + return len(cards) == 4 && rule.IsPointContinuity(4, cards...) + } +} + +// HandStraightPairs 连对 +func HandStraightPairs() HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) < 6 || len(cards)%2 != 0 { + return false + } + return rule.IsPointContinuity(2, cards...) + } +} + +// HandPlane 飞机 +// - 表示三张点数相同的牌组成的连续的牌 +func HandPlane() HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) < 6 || len(cards)%3 != 0 { + return false + } + return rule.IsPointContinuity(3, cards...) + } +} + +// HandPlaneWithOne 飞机带单 +func HandPlaneWithOne() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + if len(group) < 2 { + return false + } + var hasThree bool + var count int + for _, cards := range group { + if len(cards) == 3 { + hasThree = true + } else { + count = len(cards) + } + } + return hasThree && count == 1 + } +} + +// HandRocket 王炸 +// - 表示一对王牌,即大王和小王 +func HandRocket() HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) != 2 { + return false + } + return IsRocket(cards[0], cards[1]) + } +} + +// HandFlush 同花 +// - 表示所有牌的花色都相同 +func HandFlush() HandHandle { + return func(rule *Rule, cards []Card) bool { + return IsFlush(cards...) + } +} + +// HandFlushStraight 同花顺 +// - count: 顺子的对子数量,例如当 count = 2 时,可以是 334455、445566、556677、667788、778899 +// - lower: 顺子的最小连续数量 +// - limit: 顺子的最大连续数量 +func HandFlushStraight(count, lower, limit int) HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) < lower*count || len(cards) > limit*count || len(cards)%count != 0 { + return false + } + if !IsFlush(cards...) { + return false + } + return rule.IsPointContinuity(count, cards...) + } +} + +// HandLeopard 豹子 +// - 表示三张点数相同的牌 +// - 例如:333、444、555、666、777、888、999、JJJ、QQQ、KKK、AAA +// - 大小王不能用于豹子,因为他们没有点数 +func HandLeopard() HandHandle { + return func(rule *Rule, cards []Card) bool { + if len(cards) == 0 { + return false + } + if len(cards) == 1 { + return true + } + var card = cards[0] + for i := 1; i < len(cards); i++ { + if !card.Equal(cards[1]) { + return false + } + } + return true + } +} + +// HandTwoWithOne 二带一 +// - 表示两张点数相同的牌,加上一张其他点数的牌 +// - 例如:334、445、556、667、778、889、99J、TTQ、JJK、QQA、AA2 +// - 大小王不能用于二带一,因为他们没有点数 +// - 通常用于炸金花玩法中检查对子 +func HandTwoWithOne() HandHandle { + return func(rule *Rule, cards []Card) bool { + group := GroupByPoint(cards...) + var hasTwo bool + var count int + for _, cards := range group { + if len(cards) == 2 { + hasTwo = true + } else { + count = len(cards) + } + } + return hasTwo && count == 1 } } diff --git a/game/poker/options.go b/game/poker/options.go index 4627a5d..ed6ade5 100644 --- a/game/poker/options.go +++ b/game/poker/options.go @@ -2,38 +2,94 @@ package poker import "fmt" -type Option func(poker *Poker) +type Option func(rule *Rule) // WithHand 通过绑定特定牌型的方式创建扑克玩法 -func WithHand(pokerHand string, handle HandHandle) Option { - return func(poker *Poker) { - if _, exist := poker.pokerHand[pokerHand]; exist { +// - 牌型顺序决定了牌型的优先级 +func WithHand(pokerHand string, value int, handle HandHandle) Option { + return func(rule *Rule) { + if _, exist := rule.pokerHand[pokerHand]; exist { panic(fmt.Errorf("same poker hand name: %s", pokerHand)) } - poker.pokerHand[pokerHand] = handle - poker.pokerHandPriority = append(poker.pokerHandPriority, pokerHand) + rule.pokerHand[pokerHand] = handle + rule.pokerHandValue[pokerHand] = value + + restraint, exist := rule.restraint[pokerHand] + if !exist { + restraint = map[string]struct{}{} + rule.restraint[pokerHand] = restraint + } + restraint[pokerHand] = struct{}{} + } +} + +// WithHandRestraint 通过绑定特定克制牌型的方式创建扑克玩法 +func WithHandRestraint(pokerHand, restraint string) Option { + return func(rule *Rule) { + r, exist := rule.restraint[pokerHand] + if !exist { + r = map[string]struct{}{} + rule.restraint[pokerHand] = r + } + r[restraint] = struct{}{} + } +} + +// WithHandRestraintFull 通过绑定所有克制牌型的方式创建扑克玩法 +// - 需要确保在牌型声明之后调用 +func WithHandRestraintFull(pokerHand string) Option { + return func(rule *Rule) { + for hand := range rule.pokerHand { + r, exist := rule.restraint[pokerHand] + if !exist { + r = map[string]struct{}{} + rule.restraint[pokerHand] = r + } + r[hand] = struct{}{} + } } } // WithPointValue 通过特定的扑克点数牌值创建扑克玩法 func WithPointValue(pointValues map[Point]int) Option { - return func(poker *Poker) { - poker.pointValue = pointValues + return func(rule *Rule) { + rule.pointValue = pointValues } } // WithColorValue 通过特定的扑克花色牌值创建扑克玩法 func WithColorValue(colorValues map[Color]int) Option { - return func(poker *Poker) { - poker.colorValue = colorValues + return func(rule *Rule) { + rule.colorValue = colorValues } } -// WithPointSort 通过特定的扑克点数顺序创建扑克玩法 +// WithPointSort 通过特定的扑克点数顺序创建扑克玩法,顺序必须为连续的 func WithPointSort(pointSort map[Point]int) Option { - return func(poker *Poker) { + return func(rule *Rule) { for k, v := range pointSort { - poker.pointSort[k] = v + rule.pointSort[k] = v + } + } +} + +// WithColorSort 通过特定的扑克花色顺序创建扑克玩法,顺序必须为连续的 +func WithColorSort(colorSort map[Color]int) Option { + return func(rule *Rule) { + for k, v := range colorSort { + rule.colorSort[k] = v + } + } +} + +// WithExcludeContinuityPoint 排除连续的点数 +func WithExcludeContinuityPoint(points ...Point) Option { + return func(rule *Rule) { + if rule.excludeContinuityPoint == nil { + rule.excludeContinuityPoint = make(map[Point]struct{}) + } + for _, point := range points { + rule.excludeContinuityPoint[point] = struct{}{} } } } diff --git a/game/poker/poker.go b/game/poker/poker.go index 898098e..c575bb2 100644 --- a/game/poker/poker.go +++ b/game/poker/poker.go @@ -1,90 +1,70 @@ package poker -import ( - "github.com/kercylan98/minotaur/utils/hash" - "github.com/kercylan98/minotaur/utils/maths" - "sort" -) - -func New(pile *CardPile, options ...Option) *Poker { - poker := &Poker{ - pile: pile, - pokerHand: map[string]HandHandle{}, - pointSort: hash.Copy(defaultPointSort), +// IsContainJoker 检查扑克牌是否包含大小王 +func IsContainJoker(cards ...Card) bool { + for _, card := range cards { + if card.IsJoker() { + return true + } } - for _, option := range options { - option(poker) - } - if poker.pointValue == nil { - poker.pointValue = poker.pointSort - } - return poker + return false } -type Poker struct { - pile *CardPile - pokerHand map[string]HandHandle - pokerHandPriority []string - pointValue map[Point]int - colorValue map[Color]int - pointSort map[Point]int +// GroupByPoint 将扑克牌按照点数分组 +func GroupByPoint(cards ...Card) map[Point][]Card { + group := map[Point][]Card{} + for _, card := range cards { + group[card.GetPoint()] = append(group[card.GetPoint()], card) + } + return group } -// IsContinuity 检查一组扑克牌是否连续 -func (slf *Poker) IsContinuity(cards ...Card) bool { - length := len(cards) - if length == 0 { +// GroupByColor 将扑克牌按照花色分组 +func GroupByColor(cards ...Card) map[Color][]Card { + group := map[Color][]Card{} + for _, card := range cards { + group[card.GetColor()] = append(group[card.GetColor()], card) + } + return group +} + +// IsRocket 两张牌能否组成红黑 Joker +func IsRocket(cardA, cardB Card) bool { + return cardA.GetPoint() == PointRedJoker && cardB.GetPoint() == PointBlackJoker || cardA.GetPoint() == PointBlackJoker && cardB.GetPoint() == PointRedJoker +} + +// IsFlush 判断是否是同花 +func IsFlush(cards ...Card) bool { + if len(cards) == 0 { return false } - if length == 1 { + if len(cards) == 1 { return true } - var points = make([]int, length) - for i, card := range cards { - points[i] = slf.pointSort[card.GetPoint()] - } - sort.Slice(points, func(i, j int) bool { return points[i] < points[j] }) - for i := 0; i < length-1; i++ { - if points[i+1]-points[i] != 1 { + + color := cards[0].GetColor() + for i := 1; i < len(cards); i++ { + if cards[i].GetColor() != color { return false } } return true } -// CardValue 获取扑克牌的牌值 -func (slf *Poker) CardValue(cards ...Card) int { - var value int - for _, card := range cards { - value += slf.pointValue[card.GetPoint()] - value += slf.colorValue[card.GetColor()] +// GetCardsPoint 获取一组扑克牌的点数 +func GetCardsPoint(cards ...Card) []Point { + var points = make([]Point, len(cards)) + for i, card := range cards { + points[i] = card.GetPoint() } - return value + return points } -// Compare 根据特定的条件表达式比较两组扑克牌的牌值 -func (slf *Poker) Compare(cards1 []Card, expression maths.CompareExpression, cards2 []Card) bool { - return maths.Compare(slf.CardValue(cards1...), expression, slf.CardValue(cards2...)) -} - -// PokerHand 获取一组扑克的牌型 -// -// 参数: -// - cards: 扑克牌切片,类型为 []builtin.Card,表示一组扑克牌。 -// -// 返回值: -// - string: 命中的牌型名称。 -// - bool: 是否命中牌型。 -func (slf *Poker) PokerHand(cards ...Card) (cardType string, hit bool) { - for _, phn := range slf.pokerHandPriority { - if slf.pokerHand[phn](slf, cards) { - return phn, true - } +// GetCardsColor 获取一组扑克牌的花色 +func GetCardsColor(cards ...Card) []Color { + var colors = make([]Color, len(cards)) + for i, card := range cards { + colors[i] = card.GetColor() } - return "", false -} - -// GetPile 获取牌堆 -func (slf *Poker) GetPile() *CardPile { - return slf.pile + return colors } diff --git a/game/poker/rule.go b/game/poker/rule.go new file mode 100644 index 0000000..05601c4 --- /dev/null +++ b/game/poker/rule.go @@ -0,0 +1,178 @@ +package poker + +import ( + "github.com/kercylan98/minotaur/utils/hash" + "github.com/kercylan98/minotaur/utils/maths" + "sort" +) + +func NewRule(options ...Option) *Rule { + poker := &Rule{ + pokerHand: map[string]HandHandle{}, + pointSort: hash.Copy(defaultPointSort), + colorSort: hash.Copy(defaultColorSort), + pokerHandValue: map[string]int{}, + restraint: map[string]map[string]struct{}{}, + } + for _, option := range options { + option(poker) + } + if poker.pointValue == nil { + poker.pointValue = poker.pointSort + } + return poker +} + +type Rule struct { + pokerHand map[string]HandHandle + pokerHandValue map[string]int + pointValue map[Point]int + colorValue map[Color]int + pointSort map[Point]int + colorSort map[Color]int + excludeContinuityPoint map[Point]struct{} + restraint map[string]map[string]struct{} +} + +// PokerHandIsMatch 检查两组扑克牌牌型是否匹配 +func (slf *Rule) PokerHandIsMatch(cardsA, cardsB []Card) bool { + handA, exist := slf.PokerHand(cardsA...) + if !exist { + return false + } + handB, exist := slf.PokerHand(cardsB...) + if !exist { + return false + } + if hash.Exist(slf.restraint[handA], handB) || hash.Exist(slf.restraint[handB], handA) { + return false + } + return len(cardsA) == len(cardsB) +} + +// PokerHand 获取一组扑克的牌型 +func (slf *Rule) PokerHand(cards ...Card) (pokerHand string, hit bool) { + for phn := range slf.pokerHandValue { + if slf.pokerHand[phn](slf, cards) { + return phn, true + } + } + return HandNone, false +} + +// IsPointContinuity 检查一组扑克牌点数是否连续,count 表示了每个点数的数量 +func (slf *Rule) IsPointContinuity(count int, cards ...Card) bool { + if len(cards) == 0 { + return false + } + group := GroupByPoint(cards...) + var values []int + for point, cards := range group { + if _, exist := slf.excludeContinuityPoint[point]; exist { + return false + } + if count != len(cards) { + return false + } + values = append(values, slf.GetValueWithPoint(point)) + } + return maths.IsContinuityWithSort(values) +} + +// IsSameColor 检查一组扑克牌是否同花 +func (slf *Rule) IsSameColor(cards ...Card) bool { + length := len(cards) + if length == 0 { + return false + } + if length == 1 { + return true + } + var color = cards[0].GetColor() + for i := 1; i < length; i++ { + if cards[i].GetColor() != color { + return false + } + } + return true +} + +// IsSamePoint 检查一组扑克牌是否同点 +func (slf *Rule) IsSamePoint(cards ...Card) bool { + length := len(cards) + if length == 0 { + return false + } + if length == 1 { + return true + } + var point = cards[0].GetPoint() + for i := 1; i < length; i++ { + if cards[i].GetPoint() != point { + return false + } + } + return true +} + +// SortByPointDesc 将扑克牌按照点数从大到小排序 +func (slf *Rule) SortByPointDesc(cards []Card) []Card { + sort.Slice(cards, func(i, j int) bool { + return slf.pointSort[cards[i].GetPoint()] > slf.pointSort[cards[j].GetPoint()] + }) + return cards +} + +// SortByPointAsc 将扑克牌按照点数从小到大排序 +func (slf *Rule) SortByPointAsc(cards []Card) []Card { + sort.Slice(cards, func(i, j int) bool { + return slf.pointSort[cards[i].GetPoint()] < slf.pointSort[cards[j].GetPoint()] + }) + return cards +} + +// SortByColorDesc 将扑克牌按照花色从大到小排序 +func (slf *Rule) SortByColorDesc(cards []Card) []Card { + sort.Slice(cards, func(i, j int) bool { + return slf.colorSort[cards[i].GetColor()] > slf.colorSort[cards[j].GetColor()] + }) + return cards +} + +// SortByColorAsc 将扑克牌按照花色从小到大排序 +func (slf *Rule) SortByColorAsc(cards []Card) []Card { + sort.Slice(cards, func(i, j int) bool { + return slf.colorSort[cards[i].GetColor()] < slf.colorSort[cards[j].GetColor()] + }) + return cards +} + +// GetValueWithPokerHand 获取扑克牌的牌型牌值 +func (slf *Rule) GetValueWithPokerHand(hand string) int { + return slf.pokerHandValue[hand] +} + +// GetValueWithCards 获取扑克牌的牌值 +func (slf *Rule) GetValueWithCards(cards ...Card) int { + var value int + for _, card := range cards { + value += slf.pointValue[card.GetPoint()] + value += slf.colorValue[card.GetColor()] + } + return value +} + +// GetValueWithPoint 获取扑克牌的点数牌值 +func (slf *Rule) GetValueWithPoint(point Point) int { + return slf.pointValue[point] +} + +// GetValueWithColor 获取扑克牌的花色牌值 +func (slf *Rule) GetValueWithColor(color Color) int { + return slf.colorValue[color] +} + +// CompareValueWithCards 根据特定的条件表达式比较两组扑克牌的牌值 +func (slf *Rule) CompareValueWithCards(cards1 []Card, expression maths.CompareExpression, cards2 []Card) bool { + return maths.Compare(slf.GetValueWithCards(cards1...), expression, slf.GetValueWithCards(cards2...)) +} diff --git a/utils/maths/math.go b/utils/maths/math.go index 0434643..6fb118e 100644 --- a/utils/maths/math.go +++ b/utils/maths/math.go @@ -3,6 +3,7 @@ package maths import ( "github.com/kercylan98/minotaur/utils/generic" "math" + "sort" ) const ( @@ -121,3 +122,19 @@ func UnMerge[V generic.SignedNumber](refer, num V) (a, b V) { b = num / refer return a, b } + +// ToContinuous 将一组非连续的数字转换为从1开始的连续数字 +// - 返回值是一个 map,key 是从 1 开始的连续数字,value 是原始数字 +func ToContinuous[V generic.Integer](nums []V) map[V]V { + if len(nums) == 0 { + return nil + } + sort.Slice(nums, func(i, j int) bool { + return nums[i] < nums[j] + }) + var result = make(map[V]V) + for i, num := range nums { + result[V(i+1)] = num + } + return result +}