refactor: 重构 poker 包为全泛型包,支持通过 poker.Matcher 根据一组扑克牌选出最佳组合
This commit is contained in:
parent
d446ff18b9
commit
d71d8434b6
|
@ -1,159 +1,13 @@
|
|||
package poker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// NewCard 创建一张扑克牌
|
||||
// - 当 point 为 PointBlackJoker 或 PointRedJoker 时,color 将没有效果
|
||||
func NewCard(point Point, color Color) Card {
|
||||
if point == PointRedJoker || point == PointBlackJoker {
|
||||
color = ColorNone
|
||||
}
|
||||
card := Card{
|
||||
point: point,
|
||||
color: color,
|
||||
}
|
||||
return card
|
||||
}
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
// Card 扑克牌
|
||||
type Card struct {
|
||||
point Point
|
||||
color Color
|
||||
}
|
||||
|
||||
// GetPoint 返回扑克牌的点数
|
||||
func (slf Card) GetPoint() Point {
|
||||
return slf.point
|
||||
}
|
||||
|
||||
// GetColor 返回扑克牌的花色
|
||||
func (slf Card) GetColor() Color {
|
||||
if slf.point == PointRedJoker || slf.point == PointBlackJoker {
|
||||
return ColorNone
|
||||
}
|
||||
return slf.color
|
||||
}
|
||||
|
||||
// GetPointAndColor 返回扑克牌的点数和花色
|
||||
func (slf Card) GetPointAndColor() (Point, Color) {
|
||||
return slf.GetPoint(), slf.GetColor()
|
||||
}
|
||||
|
||||
// EqualPoint 比较与另一张扑克牌的点数是否相同
|
||||
func (slf Card) EqualPoint(card Card) bool {
|
||||
return slf.GetPoint() == card.GetPoint()
|
||||
}
|
||||
|
||||
// EqualColor 比较与另一张扑克牌的花色是否相同
|
||||
func (slf Card) EqualColor(card Card) bool {
|
||||
return slf.GetColor() == card.GetColor()
|
||||
}
|
||||
|
||||
// Equal 比较与另一张扑克牌的点数和花色是否相同
|
||||
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)
|
||||
type Card[P, C generic.Number] interface {
|
||||
// GetGuid 获取扑克牌的唯一标识
|
||||
GetGuid() int64
|
||||
// GetPoint 获取扑克牌的点数
|
||||
GetPoint() P
|
||||
// GetColor 获取扑克牌的花色
|
||||
GetColor() C
|
||||
}
|
||||
|
|
|
@ -1,189 +0,0 @@
|
|||
package poker_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/game/poker"
|
||||
)
|
||||
|
||||
func ExampleNewCard() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card)
|
||||
|
||||
// Output:
|
||||
// (A Spade)
|
||||
}
|
||||
|
||||
func ExampleCard_Equal() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card1.Equal(card2))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleCard_EqualColor() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.EqualColor(card2))
|
||||
|
||||
// Output:
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleCard_EqualPoint() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.EqualPoint(card2))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleCard_GetColor() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card.GetColor())
|
||||
|
||||
// Output:
|
||||
// Spade
|
||||
}
|
||||
|
||||
func ExampleCard_GetPoint() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card.GetPoint())
|
||||
|
||||
// Output:
|
||||
// A
|
||||
}
|
||||
|
||||
func ExampleCard_GetPointAndColor() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card.GetPointAndColor())
|
||||
|
||||
// Output:
|
||||
// A Spade
|
||||
}
|
||||
|
||||
func ExampleCard_MaxPoint() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.Point2, poker.ColorSpade)
|
||||
fmt.Println(card1.MaxPoint(card2))
|
||||
|
||||
// Output:
|
||||
// (2 Spade)
|
||||
}
|
||||
|
||||
func ExampleCard_MinPoint() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.Point2, poker.ColorSpade)
|
||||
fmt.Println(card1.MinPoint(card2))
|
||||
|
||||
// Output:
|
||||
// (A Spade)
|
||||
}
|
||||
|
||||
func ExampleCard_String() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card.String())
|
||||
|
||||
// Output:
|
||||
// (A Spade)
|
||||
}
|
||||
|
||||
func ExampleCard_MaxColor() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.MaxColor(card2))
|
||||
|
||||
// Output:
|
||||
// (A Spade)
|
||||
}
|
||||
|
||||
func ExampleCard_MinColor() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.MinColor(card2))
|
||||
|
||||
// Output:
|
||||
// (A Heart)
|
||||
}
|
||||
|
||||
func ExampleCard_Max() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.Max(card2))
|
||||
|
||||
// Output:
|
||||
// (A Spade)
|
||||
}
|
||||
|
||||
func ExampleCard_Min() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.Min(card2))
|
||||
|
||||
// Output:
|
||||
// (A Heart)
|
||||
}
|
||||
|
||||
func ExampleCard_IsJoker() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
fmt.Println(card.IsJoker())
|
||||
|
||||
// Output:
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleCard_CalcPointDifference() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.Point2, poker.ColorSpade)
|
||||
fmt.Println(card1.CalcPointDifference(card2))
|
||||
|
||||
// Output:
|
||||
// -1
|
||||
}
|
||||
|
||||
func ExampleCard_CalcColorDifference() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.CalcColorDifference(card2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleCard_CalcPointDifferenceAbs() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.Point2, poker.ColorSpade)
|
||||
fmt.Println(card1.CalcPointDifferenceAbs(card2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleCard_CalcColorDifferenceAbs() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.CalcColorDifferenceAbs(card2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleCard_IsNeighborPoint() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.Point2, poker.ColorSpade)
|
||||
fmt.Println(card1.IsNeighborPoint(card2))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleCard_IsNeighborColor() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
fmt.Println(card1.IsNeighborColor(card2))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
|
@ -2,6 +2,7 @@ package poker
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/random"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
|
@ -10,12 +11,17 @@ import (
|
|||
|
||||
// NewCardPile 返回一个新的牌堆,其中 size 表示了该牌堆由多少副牌组成
|
||||
// - 在不洗牌的情况下,默认牌堆顶部到底部为从大到小排列
|
||||
func NewCardPile(size int, options ...CardPileOption) *CardPile {
|
||||
pile := &CardPile{
|
||||
size: size,
|
||||
pile: make([]Card, 0, size*54),
|
||||
func NewCardPile[P, C generic.Number, T Card[P, C]](size int, jokers [2]P, points [13]P, colors [4]C, generateCard func(guid int64, point P, color C) T, options ...CardPileOption[P, C, T]) *CardPile[P, C, T] {
|
||||
pile := &CardPile[P, C, T]{
|
||||
size: size,
|
||||
pile: make([]T, 0, size*54),
|
||||
generateCard: generateCard,
|
||||
cards: map[int64]T{},
|
||||
jokers: jokers,
|
||||
points: points,
|
||||
colors: colors,
|
||||
}
|
||||
pile.shuffleHandle = func(cards []Card) []Card {
|
||||
pile.shuffleHandle = func(cards []T) []T {
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return random.Float64() >= 0.5
|
||||
})
|
||||
|
@ -29,28 +35,47 @@ func NewCardPile(size int, options ...CardPileOption) *CardPile {
|
|||
}
|
||||
|
||||
// CardPile 扑克牌堆
|
||||
type CardPile struct {
|
||||
pile []Card
|
||||
type CardPile[P, C generic.Number, T Card[P, C]] struct {
|
||||
pile []T
|
||||
size int
|
||||
shuffleHandle func(cards []Card) []Card
|
||||
excludeColor map[Color]struct{}
|
||||
excludePoint map[Point]struct{}
|
||||
excludeCard map[Point]map[Color]struct{}
|
||||
shuffleHandle func(cards []T) []T
|
||||
excludeColor map[C]struct{}
|
||||
excludePoint map[P]struct{}
|
||||
excludeCard map[P]map[C]struct{}
|
||||
generateCard func(guid int64, point P, color C) T
|
||||
guid int64
|
||||
cards map[int64]T
|
||||
jokers [2]P
|
||||
points [13]P
|
||||
colors [4]C
|
||||
}
|
||||
|
||||
// GetCard 通过 guid 获取一张牌
|
||||
func (slf *CardPile[P, C, T]) GetCard(guid int64) T {
|
||||
return slf.cards[guid]
|
||||
}
|
||||
|
||||
// Reset 重置牌堆的扑克牌数量及顺序
|
||||
func (slf *CardPile) Reset() {
|
||||
var cards = make([]Card, 0, 54)
|
||||
if !slf.IsExclude(PointRedJoker, ColorNone) {
|
||||
cards = append(cards, NewCard(PointRedJoker, ColorNone))
|
||||
}
|
||||
if !slf.IsExclude(PointBlackJoker, ColorNone) {
|
||||
cards = append(cards, NewCard(PointBlackJoker, ColorNone))
|
||||
}
|
||||
for point := PointK; point >= PointA; point-- {
|
||||
for color := ColorDiamond; color <= ColorSpade; color++ {
|
||||
if !slf.IsExclude(point, color) {
|
||||
cards = append(cards, NewCard(point, color))
|
||||
func (slf *CardPile[P, C, T]) Reset() {
|
||||
var cards = make([]T, 0, 54*slf.size)
|
||||
for i := 0; i < slf.size; i++ {
|
||||
for _, joker := range slf.jokers {
|
||||
if !slf.IsExclude(joker, C(0)) {
|
||||
slf.guid++
|
||||
card := slf.generateCard(slf.guid, joker, C(0))
|
||||
slf.cards[slf.guid] = card
|
||||
cards = append(cards, card)
|
||||
}
|
||||
}
|
||||
|
||||
for p := 0; p < len(slf.points); p++ {
|
||||
for c := 0; c < len(slf.colors); c++ {
|
||||
point, color := slf.points[p], slf.colors[c]
|
||||
if !slf.IsExclude(point, color) {
|
||||
slf.guid++
|
||||
card := slf.generateCard(slf.guid, point, color)
|
||||
slf.cards[slf.guid] = card
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,21 +86,18 @@ func (slf *CardPile) Reset() {
|
|||
}
|
||||
|
||||
// IsExclude 检查特定点数和花色是否被排除在外
|
||||
func (slf *CardPile) IsExclude(point Point, color Color) bool {
|
||||
if point == PointRedJoker || point == PointBlackJoker {
|
||||
color = ColorNone
|
||||
}
|
||||
func (slf *CardPile[P, C, T]) IsExclude(point P, color C) bool {
|
||||
return hash.Exist(slf.excludePoint, point) || hash.Exist(slf.excludeColor, color) || hash.Exist(slf.excludeCard[point], color)
|
||||
}
|
||||
|
||||
// IsExcludeWithCard 检查特定扑克牌是否被排除在外
|
||||
func (slf *CardPile) IsExcludeWithCard(card Card) bool {
|
||||
point, color := card.GetPointAndColor()
|
||||
func (slf *CardPile[P, C, T]) IsExcludeWithCard(card T) bool {
|
||||
point, color := GetPointAndColor[P, C, T](card)
|
||||
return hash.Exist(slf.excludePoint, point) || hash.Exist(slf.excludeColor, color) || hash.Exist(slf.excludeCard[point], color)
|
||||
}
|
||||
|
||||
// Shuffle 洗牌
|
||||
func (slf *CardPile) Shuffle() {
|
||||
func (slf *CardPile[P, C, T]) Shuffle() {
|
||||
before := slf.Count()
|
||||
cards := slf.shuffleHandle(slf.Cards())
|
||||
if len(cards) != before {
|
||||
|
@ -85,22 +107,22 @@ func (slf *CardPile) Shuffle() {
|
|||
}
|
||||
|
||||
// Cards 获取当前牌堆的所有扑克牌
|
||||
func (slf *CardPile) Cards() []Card {
|
||||
func (slf *CardPile[P, C, T]) Cards() []T {
|
||||
return slf.pile
|
||||
}
|
||||
|
||||
// IsFree 返回牌堆是否没有扑克牌了
|
||||
func (slf *CardPile) IsFree() bool {
|
||||
func (slf *CardPile[P, C, T]) IsFree() bool {
|
||||
return len(slf.pile) == 0
|
||||
}
|
||||
|
||||
// Count 获取牌堆剩余牌量
|
||||
func (slf *CardPile) Count() int {
|
||||
func (slf *CardPile[P, C, T]) Count() int {
|
||||
return len(slf.pile)
|
||||
}
|
||||
|
||||
// Pull 从牌堆特定位置抽出一张牌
|
||||
func (slf *CardPile) Pull(index int) Card {
|
||||
func (slf *CardPile[P, C, T]) Pull(index int) T {
|
||||
if index >= slf.Count() || index < 0 {
|
||||
panic(fmt.Errorf("failed to pull a poker card from the pile, the index is less than 0 or exceeds the remaining number of cards in the pile. count: %d, index: %d", slf.Count(), index))
|
||||
}
|
||||
|
@ -110,7 +132,7 @@ func (slf *CardPile) Pull(index int) Card {
|
|||
}
|
||||
|
||||
// PullTop 从牌堆顶部抽出一张牌
|
||||
func (slf *CardPile) PullTop() Card {
|
||||
func (slf *CardPile[P, C, T]) PullTop() T {
|
||||
if slf.IsFree() {
|
||||
panic("empty poker cards pile")
|
||||
}
|
||||
|
@ -120,7 +142,7 @@ func (slf *CardPile) PullTop() Card {
|
|||
}
|
||||
|
||||
// PullBottom 从牌堆底部抽出一张牌
|
||||
func (slf *CardPile) PullBottom() Card {
|
||||
func (slf *CardPile[P, C, T]) PullBottom() T {
|
||||
if slf.IsFree() {
|
||||
panic("empty poker cards pile")
|
||||
}
|
||||
|
@ -131,17 +153,17 @@ func (slf *CardPile) PullBottom() Card {
|
|||
}
|
||||
|
||||
// Push 将扑克牌插入到牌堆特定位置
|
||||
func (slf *CardPile) Push(index int, card Card) {
|
||||
func (slf *CardPile[P, C, T]) Push(index int, card T) {
|
||||
slice.Insert(&slf.pile, index, card)
|
||||
return
|
||||
}
|
||||
|
||||
// PushTop 将扑克牌插入到牌堆顶部
|
||||
func (slf *CardPile) PushTop(card Card) {
|
||||
slf.pile = append([]Card{card}, slf.pile...)
|
||||
func (slf *CardPile[P, C, T]) PushTop(card T) {
|
||||
slf.pile = append([]T{card}, slf.pile...)
|
||||
}
|
||||
|
||||
// PushBottom 将扑克牌插入到牌堆底部
|
||||
func (slf *CardPile) PushBottom(card Card) {
|
||||
func (slf *CardPile[P, C, T]) PushBottom(card T) {
|
||||
slf.pile = append(slf.pile, card)
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package poker_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/game/poker"
|
||||
)
|
||||
|
||||
func ExampleNewCardPile() {
|
||||
var pile = poker.NewCardPile(1,
|
||||
poker.WithCardPileExcludeCard(poker.NewCard(poker.PointBlackJoker, poker.ColorNone)),
|
||||
)
|
||||
|
||||
fmt.Println(pile.Cards())
|
||||
|
||||
// Output:
|
||||
// [(R None) (K Diamond) (K Club) (K Heart) (K Spade) (Q Diamond) (Q Club) (Q Heart) (Q Spade) (J Diamond) (J Club) (J Heart) (J Spade) (10 Diamond) (10 Club) (10 Heart) (10 Spade) (9 Diamond) (9 Club) (9 Heart) (9 Spade) (8 Diamond) (8 Club) (8 Heart) (8 Spade) (7 Diamond) (7 Club) (7 Heart) (7 Spade) (6 Diamond) (6 Club) (6 Heart) (6 Spade) (5 Diamond) (5 Club) (5 Heart) (5 Spade) (4 Diamond) (4 Club) (4 Heart) (4 Spade) (3 Diamond) (3 Club) (3 Heart) (3 Spade) (2 Diamond) (2 Club) (2 Heart) (2 Spade) (A Diamond) (A Club) (A Heart) (A Spade)]
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package poker
|
||||
|
||||
type CardPileOption func(pile *CardPile)
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
type CardPileOption[P, C generic.Number, T Card[P, C]] func(pile *CardPile[P, C, T])
|
||||
|
||||
// WithCardPileShuffle 通过特定的洗牌算法创建牌堆
|
||||
// - 需要保证洗牌后的牌堆剩余扑克数量与之前相同,否则将会引发 panic
|
||||
func WithCardPileShuffle(shuffleHandle func(pile []Card) []Card) CardPileOption {
|
||||
return func(pile *CardPile) {
|
||||
func WithCardPileShuffle[P, C generic.Number, T Card[P, C]](shuffleHandle func(pile []T) []T) CardPileOption[P, C, T] {
|
||||
return func(pile *CardPile[P, C, T]) {
|
||||
if shuffleHandle == nil {
|
||||
return
|
||||
}
|
||||
|
@ -14,10 +16,10 @@ func WithCardPileShuffle(shuffleHandle func(pile []Card) []Card) CardPileOption
|
|||
}
|
||||
|
||||
// WithCardPileExcludeColor 通过排除特定花色的方式创建牌堆
|
||||
func WithCardPileExcludeColor(colors ...Color) CardPileOption {
|
||||
return func(pile *CardPile) {
|
||||
func WithCardPileExcludeColor[P, C generic.Number, T Card[P, C]](colors ...C) CardPileOption[P, C, T] {
|
||||
return func(pile *CardPile[P, C, T]) {
|
||||
if pile.excludeColor == nil {
|
||||
pile.excludeColor = map[Color]struct{}{}
|
||||
pile.excludeColor = map[C]struct{}{}
|
||||
}
|
||||
for _, color := range colors {
|
||||
pile.excludeColor[color] = struct{}{}
|
||||
|
@ -26,10 +28,10 @@ func WithCardPileExcludeColor(colors ...Color) CardPileOption {
|
|||
}
|
||||
|
||||
// WithCardPileExcludePoint 通过排除特定点数的方式创建牌堆
|
||||
func WithCardPileExcludePoint(points ...Point) CardPileOption {
|
||||
return func(pile *CardPile) {
|
||||
func WithCardPileExcludePoint[P, C generic.Number, T Card[P, C]](points ...P) CardPileOption[P, C, T] {
|
||||
return func(pile *CardPile[P, C, T]) {
|
||||
if pile.excludePoint == nil {
|
||||
pile.excludePoint = map[Point]struct{}{}
|
||||
pile.excludePoint = map[P]struct{}{}
|
||||
}
|
||||
for _, point := range points {
|
||||
pile.excludePoint[point] = struct{}{}
|
||||
|
@ -38,22 +40,22 @@ func WithCardPileExcludePoint(points ...Point) CardPileOption {
|
|||
}
|
||||
|
||||
// WithCardPileExcludeCard 通过排除特定扑克牌的方式创建牌堆
|
||||
func WithCardPileExcludeCard(cards ...Card) CardPileOption {
|
||||
return func(pile *CardPile) {
|
||||
func WithCardPileExcludeCard[P, C generic.Number, T Card[P, C]](cards ...Card[P, C]) CardPileOption[P, C, T] {
|
||||
return func(pile *CardPile[P, C, T]) {
|
||||
if pile.excludeCard == nil {
|
||||
pile.excludeCard = map[Point]map[Color]struct{}{}
|
||||
pile.excludeCard = map[P]map[C]struct{}{}
|
||||
}
|
||||
for _, card := range cards {
|
||||
point := card.GetPoint()
|
||||
cs, exist := pile.excludeCard[point]
|
||||
if !exist {
|
||||
cs = map[Color]struct{}{}
|
||||
cs = map[C]struct{}{}
|
||||
pile.excludeCard[point] = cs
|
||||
}
|
||||
if point == PointRedJoker || point == PointBlackJoker {
|
||||
cs[ColorNone] = struct{}{}
|
||||
} else {
|
||||
cs[card.GetColor()] = struct{}{}
|
||||
for _, joker := range pile.jokers {
|
||||
if point != joker {
|
||||
cs[card.GetColor()] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package poker_test
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/game/poker"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCard_GetPoint(t *testing.T) {
|
||||
Convey("TestCard_GetPoint", t, func() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
So(card.GetPoint(), ShouldEqual, poker.PointA)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCard_GetColor(t *testing.T) {
|
||||
Convey("TestCard_GetColor", t, func() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
So(card.GetColor(), ShouldEqual, poker.ColorSpade)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCard_GetPointAndColor(t *testing.T) {
|
||||
Convey("TestCard_GetPointAndColor", t, func() {
|
||||
card := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
point, color := card.GetPointAndColor()
|
||||
So(point, ShouldEqual, poker.PointA)
|
||||
So(color, ShouldEqual, poker.ColorSpade)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCard_EqualPoint(t *testing.T) {
|
||||
Convey("TestCard_EqualPoint", t, func() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card3 := poker.NewCard(poker.Point2, poker.ColorSpade)
|
||||
So(card1.EqualPoint(card2), ShouldEqual, true)
|
||||
So(card2.EqualPoint(card3), ShouldEqual, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCard_EqualColor(t *testing.T) {
|
||||
Convey("TestCard_EqualColor", t, func() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card3 := poker.NewCard(poker.PointA, poker.ColorHeart)
|
||||
So(card1.EqualColor(card2), ShouldEqual, true)
|
||||
So(card2.EqualColor(card3), ShouldEqual, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCard_Equal(t *testing.T) {
|
||||
Convey("TestCard_Equal", t, func() {
|
||||
card1 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card2 := poker.NewCard(poker.PointA, poker.ColorSpade)
|
||||
card3 := poker.NewCard(poker.Point2, poker.ColorHeart)
|
||||
So(card1.Equal(card2), ShouldEqual, true)
|
||||
So(card2.Equal(card3), ShouldEqual, false)
|
||||
})
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package poker
|
||||
|
||||
const (
|
||||
ColorNone Color = 0 // 无花色,通常为大小王
|
||||
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
|
||||
|
||||
// InBounds 扑克牌花色是否在界限内
|
||||
// - 将检查花色是否在黑桃、红桃、梅花、方片之间
|
||||
func (slf Color) InBounds() bool {
|
||||
return slf <= ColorSpade && slf >= ColorDiamond
|
||||
}
|
||||
|
||||
func (slf Color) String() string {
|
||||
var str string
|
||||
switch slf {
|
||||
case ColorSpade:
|
||||
str = "Spade"
|
||||
case ColorHeart:
|
||||
str = "Heart"
|
||||
case ColorClub:
|
||||
str = "Club"
|
||||
case ColorDiamond:
|
||||
str = "Diamond"
|
||||
default:
|
||||
str = "None"
|
||||
}
|
||||
return str
|
||||
}
|
|
@ -1,37 +1,39 @@
|
|||
package poker
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
const (
|
||||
HandNone = "None" // 无牌型
|
||||
)
|
||||
|
||||
// HandHandle 扑克牌型验证函数
|
||||
type HandHandle func(rule *Rule, cards []Card) bool
|
||||
type HandHandle[P, C generic.Number, T Card[P, C]] func(rule *Rule[P, C, T], cards []T) bool
|
||||
|
||||
// HandSingle 单牌
|
||||
func HandSingle() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandSingle[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
return len(cards) == 1
|
||||
}
|
||||
}
|
||||
|
||||
// HandPairs 对子
|
||||
func HandPairs() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandPairs[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
return len(cards) == 2 && rule.IsPointContinuity(2, cards...)
|
||||
}
|
||||
}
|
||||
|
||||
// HandThreeOfKind 三张
|
||||
func HandThreeOfKind() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandThreeOfKind[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
return len(cards) == 3 && rule.IsPointContinuity(3, cards...)
|
||||
}
|
||||
}
|
||||
|
||||
// HandThreeOfKindWithOne 三带一
|
||||
func HandThreeOfKindWithOne() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandThreeOfKindWithOne[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
if len(group) != 2 {
|
||||
return false
|
||||
}
|
||||
|
@ -49,9 +51,9 @@ func HandThreeOfKindWithOne() HandHandle {
|
|||
}
|
||||
|
||||
// HandThreeOfKindWithTwo 三带二
|
||||
func HandThreeOfKindWithTwo() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandThreeOfKindWithTwo[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
if len(group) != 2 {
|
||||
return false
|
||||
}
|
||||
|
@ -69,15 +71,15 @@ func HandThreeOfKindWithTwo() HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderSingle 顺子
|
||||
func HandOrderSingle(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandOrderSingle[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
return len(cards) >= count && rule.IsPointContinuity(1, cards...)
|
||||
}
|
||||
}
|
||||
|
||||
// HandOrderPairs 对子顺子
|
||||
func HandOrderPairs(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandOrderPairs[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) < count*2 || len(cards)%2 != 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -86,8 +88,8 @@ func HandOrderPairs(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderSingleThree 三张顺子
|
||||
func HandOrderSingleThree(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandOrderSingleThree[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) < count*3 || len(cards)%3 != 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -96,8 +98,8 @@ func HandOrderSingleThree(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderSingleFour 四张顺子
|
||||
func HandOrderSingleFour(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandOrderSingleFour[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) < count*4 || len(cards)%4 != 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -106,10 +108,10 @@ func HandOrderSingleFour(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderThreeWithOne 三带一顺子
|
||||
func HandOrderThreeWithOne(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
var continuous []Card
|
||||
func HandOrderThreeWithOne[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var continuous []T
|
||||
var other int
|
||||
for _, cards := range group {
|
||||
if len(cards) == 3 {
|
||||
|
@ -126,10 +128,10 @@ func HandOrderThreeWithOne(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderThreeWithTwo 三带二顺子
|
||||
func HandOrderThreeWithTwo(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
var continuous []Card
|
||||
func HandOrderThreeWithTwo[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var continuous []T
|
||||
var other int
|
||||
for _, cards := range group {
|
||||
if len(cards) == 3 {
|
||||
|
@ -148,10 +150,10 @@ func HandOrderThreeWithTwo(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderFourWithOne 四带一顺子
|
||||
func HandOrderFourWithOne(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
var continuous []Card
|
||||
func HandOrderFourWithOne[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var continuous []T
|
||||
var other int
|
||||
for _, cards := range group {
|
||||
if len(cards) == 4 {
|
||||
|
@ -168,10 +170,10 @@ func HandOrderFourWithOne(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderFourWithTwo 四带二顺子
|
||||
func HandOrderFourWithTwo(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
var continuous []Card
|
||||
func HandOrderFourWithTwo[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var continuous []T
|
||||
var other int
|
||||
for _, cards := range group {
|
||||
if len(cards) == 4 {
|
||||
|
@ -190,10 +192,10 @@ func HandOrderFourWithTwo(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandOrderFourWithThree 四带三顺子
|
||||
func HandOrderFourWithThree(count int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
var continuous []Card
|
||||
func HandOrderFourWithThree[P, C generic.Number, T Card[P, C]](count int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var continuous []T
|
||||
var other int
|
||||
for _, cards := range group {
|
||||
if len(cards) == 4 {
|
||||
|
@ -212,9 +214,9 @@ func HandOrderFourWithThree(count int) HandHandle {
|
|||
}
|
||||
|
||||
// HandFourWithOne 四带一
|
||||
func HandFourWithOne() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandFourWithOne[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var hasFour bool
|
||||
var count int
|
||||
for _, cards := range group {
|
||||
|
@ -229,9 +231,9 @@ func HandFourWithOne() HandHandle {
|
|||
}
|
||||
|
||||
// HandFourWithTwo 四带二
|
||||
func HandFourWithTwo() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandFourWithTwo[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var hasFour bool
|
||||
var count int
|
||||
for _, cards := range group {
|
||||
|
@ -246,9 +248,9 @@ func HandFourWithTwo() HandHandle {
|
|||
}
|
||||
|
||||
// HandFourWithThree 四带三
|
||||
func HandFourWithThree() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandFourWithThree[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var hasFour bool
|
||||
var count int
|
||||
for _, cards := range group {
|
||||
|
@ -263,9 +265,9 @@ func HandFourWithThree() HandHandle {
|
|||
}
|
||||
|
||||
// HandFourWithTwoPairs 四带两对
|
||||
func HandFourWithTwoPairs() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandFourWithTwoPairs[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var hasFour bool
|
||||
var count int
|
||||
for _, cards := range group {
|
||||
|
@ -286,15 +288,15 @@ func HandFourWithTwoPairs() HandHandle {
|
|||
}
|
||||
|
||||
// HandBomb 炸弹
|
||||
func HandBomb() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandBomb[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
return len(cards) == 4 && rule.IsPointContinuity(4, cards...)
|
||||
}
|
||||
}
|
||||
|
||||
// HandStraightPairs 连对
|
||||
func HandStraightPairs() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandStraightPairs[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) < 6 || len(cards)%2 != 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -304,8 +306,8 @@ func HandStraightPairs() HandHandle {
|
|||
|
||||
// HandPlane 飞机
|
||||
// - 表示三张点数相同的牌组成的连续的牌
|
||||
func HandPlane() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandPlane[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) < 6 || len(cards)%3 != 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -314,9 +316,9 @@ func HandPlane() HandHandle {
|
|||
}
|
||||
|
||||
// HandPlaneWithOne 飞机带单
|
||||
func HandPlaneWithOne() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandPlaneWithOne[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
if len(group) < 2 {
|
||||
return false
|
||||
}
|
||||
|
@ -335,20 +337,20 @@ func HandPlaneWithOne() HandHandle {
|
|||
|
||||
// HandRocket 王炸
|
||||
// - 表示一对王牌,即大王和小王
|
||||
func HandRocket() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandRocket[P, C generic.Number, T Card[P, C]](pile *CardPile[P, C, T]) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) != 2 {
|
||||
return false
|
||||
}
|
||||
return IsRocket(cards[0], cards[1])
|
||||
return IsRocket[P, C, T](pile, cards[0], cards[1])
|
||||
}
|
||||
}
|
||||
|
||||
// HandFlush 同花
|
||||
// - 表示所有牌的花色都相同
|
||||
func HandFlush() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
return IsFlush(cards...)
|
||||
func HandFlush[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
return IsFlush[P, C, T](cards...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,12 +358,12 @@ func HandFlush() HandHandle {
|
|||
// - count: 顺子的对子数量,例如当 count = 2 时,可以是 334455、445566、556677、667788、778899
|
||||
// - lower: 顺子的最小连续数量
|
||||
// - limit: 顺子的最大连续数量
|
||||
func HandFlushStraight(count, lower, limit int) HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandFlushStraight[P, C generic.Number, T Card[P, C]](count, lower, limit int) HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) < lower*count || len(cards) > limit*count || len(cards)%count != 0 {
|
||||
return false
|
||||
}
|
||||
if !IsFlush(cards...) {
|
||||
if !IsFlush[P, C, T](cards...) {
|
||||
return false
|
||||
}
|
||||
return rule.IsPointContinuity(count, cards...)
|
||||
|
@ -372,8 +374,8 @@ func HandFlushStraight(count, lower, limit int) HandHandle {
|
|||
// - 表示三张点数相同的牌
|
||||
// - 例如:333、444、555、666、777、888、999、JJJ、QQQ、KKK、AAA
|
||||
// - 大小王不能用于豹子,因为他们没有点数
|
||||
func HandLeopard() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
func HandLeopard[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
if len(cards) == 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -382,7 +384,7 @@ func HandLeopard() HandHandle {
|
|||
}
|
||||
var card = cards[0]
|
||||
for i := 1; i < len(cards); i++ {
|
||||
if !card.Equal(cards[1]) {
|
||||
if !Equal[P, C, T](card, cards[1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -395,9 +397,9 @@ func HandLeopard() HandHandle {
|
|||
// - 例如:334、445、556、667、778、889、99J、TTQ、JJK、QQA、AA2
|
||||
// - 大小王不能用于二带一,因为他们没有点数
|
||||
// - 通常用于炸金花玩法中检查对子
|
||||
func HandTwoWithOne() HandHandle {
|
||||
return func(rule *Rule, cards []Card) bool {
|
||||
group := GroupByPoint(cards...)
|
||||
func HandTwoWithOne[P, C generic.Number, T Card[P, C]]() HandHandle[P, C, T] {
|
||||
return func(rule *Rule[P, C, T], cards []T) bool {
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var hasTwo bool
|
||||
var count int
|
||||
for _, cards := range group {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package poker
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
)
|
||||
|
||||
// NewMatcher 创建一个新的匹配器
|
||||
// - evaluate: 用于评估一组扑克牌的分值,分值最高的组合将被选中
|
||||
func NewMatcher[P, C generic.Number, T Card[P, C]]() *Matcher[P, C, T] {
|
||||
matcher := &Matcher[P, C, T]{
|
||||
filter: map[string]*MatcherFilter[P, C, T]{},
|
||||
}
|
||||
return matcher
|
||||
}
|
||||
|
||||
// Matcher 匹配器
|
||||
// - 用于匹配扑克牌型,筛选分组等
|
||||
type Matcher[P, C generic.Number, T Card[P, C]] struct {
|
||||
filter map[string]*MatcherFilter[P, C, T]
|
||||
sort []string
|
||||
}
|
||||
|
||||
// RegType 注册一个新的牌型
|
||||
// - name: 牌型名称
|
||||
// - evaluate: 用于评估一组扑克牌的分值,分值最高的组合将被选中
|
||||
// - options: 牌型选项
|
||||
func (slf *Matcher[P, C, T]) RegType(name string, evaluate func([]T) int64, options ...MatcherOption[P, C, T]) *Matcher[P, C, T] {
|
||||
if hash.Exist(slf.filter, name) {
|
||||
panic("exist of the same type")
|
||||
}
|
||||
filter := &MatcherFilter[P, C, T]{
|
||||
evaluate: evaluate,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(filter)
|
||||
}
|
||||
slf.filter[name] = filter
|
||||
slf.sort = append(slf.sort, name)
|
||||
return slf
|
||||
}
|
||||
|
||||
// Group 将一组扑克牌按照匹配器的规则分组,并返回最佳组合及其牌型名称
|
||||
func (slf *Matcher[P, C, T]) Group(cards []T) (name string, result []T) {
|
||||
for _, n := range slf.sort {
|
||||
result = slf.filter[n].group(cards)
|
||||
if len(result) > 0 {
|
||||
return n, result
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package poker
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
type MatcherFilter[P, C generic.Number, T Card[P, C]] struct {
|
||||
evaluate func([]T) int64
|
||||
handles []func(cards []T) [][]T
|
||||
asc bool
|
||||
}
|
||||
|
||||
func (slf *MatcherFilter[P, C, T]) AddHandle(handle func(cards []T) [][]T) {
|
||||
slf.handles = append(slf.handles, handle)
|
||||
}
|
||||
|
||||
func (slf *MatcherFilter[P, C, T]) group(cards []T) []T {
|
||||
var bestCombination = cards
|
||||
|
||||
for _, handle := range slf.handles {
|
||||
var bestScore int64
|
||||
filteredCombinations := handle(bestCombination)
|
||||
if len(filteredCombinations) == 0 {
|
||||
return []T{}
|
||||
}
|
||||
for _, combination := range filteredCombinations {
|
||||
score := slf.evaluate(combination)
|
||||
if slf.asc {
|
||||
if score < bestScore || bestCombination == nil {
|
||||
bestCombination = combination
|
||||
bestScore = score
|
||||
}
|
||||
} else {
|
||||
if score > bestScore || bestCombination == nil {
|
||||
bestCombination = combination
|
||||
bestScore = score
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return bestCombination
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
package poker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// MatcherOption 匹配器选项
|
||||
type MatcherOption[P, C generic.Number, T Card[P, C]] func(matcher *MatcherFilter[P, C, T])
|
||||
|
||||
// WithMatcherScoreAsc 通过升序评估分数创建匹配器
|
||||
// - 用于评估一组扑克牌的分值,分值最低的组合将被选中
|
||||
// - 默认为分数最高的组合将被选中
|
||||
func WithMatcherScoreAsc[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.asc = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherLeastLength 通过匹配最小长度的扑克牌创建匹配器
|
||||
// - length: 牌型的长度,表示需要匹配的扑克牌最小数量
|
||||
func WithMatcherLeastLength[P, C generic.Number, T Card[P, C]](length int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
combinations = slice.LimitedCombinations(cards, length, len(cards))
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherLength 通过匹配长度的扑克牌创建匹配器
|
||||
// - length: 牌型的长度,表示需要匹配的扑克牌数量
|
||||
func WithMatcherLength[P, C generic.Number, T Card[P, C]](length int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
combinations = slice.LimitedCombinations(cards, length, length)
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherContinuity 通过匹配连续的扑克牌创建匹配器
|
||||
func WithMatcherContinuity[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
n := len(cards)
|
||||
|
||||
if n <= 0 {
|
||||
return combinations
|
||||
}
|
||||
|
||||
// 对扑克牌按点数进行排序
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return cards[i].GetPoint() < cards[j].GetPoint()
|
||||
})
|
||||
|
||||
// 查找连续的牌型组合
|
||||
for i := 0; i < n; i++ {
|
||||
combination := []T{cards[i]}
|
||||
for j := i + 1; j < n; j++ {
|
||||
if cards[j].GetPoint()-combination[len(combination)-1].GetPoint() == 1 {
|
||||
combination = append(combination, cards[j])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(combination) >= 2 {
|
||||
combinations = append(combinations, combination)
|
||||
}
|
||||
}
|
||||
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherContinuityPointOrder 通过匹配连续的扑克牌创建匹配器,与 WithMatcherContinuity 不同的是,该选项将按照自定义的点数顺序进行匹配
|
||||
func WithMatcherContinuityPointOrder[P, C generic.Number, T Card[P, C]](order map[P]int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
var getOrder = func(card T) P {
|
||||
if v, ok := order[card.GetPoint()]; ok {
|
||||
return P(v)
|
||||
}
|
||||
return card.GetPoint()
|
||||
}
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
n := len(cards)
|
||||
|
||||
if n <= 0 {
|
||||
return combinations
|
||||
}
|
||||
|
||||
// 对扑克牌按点数进行排序
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return getOrder(cards[i]) < getOrder(cards[j])
|
||||
})
|
||||
|
||||
// 查找连续的牌型组合
|
||||
for i := 0; i < n; i++ {
|
||||
combination := []T{cards[i]}
|
||||
for j := i + 1; j < n; j++ {
|
||||
if getOrder(cards[j])-getOrder(combination[len(combination)-1]) == 1 {
|
||||
combination = append(combination, cards[j])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(combination) >= 2 {
|
||||
combinations = append(combinations, combination)
|
||||
}
|
||||
}
|
||||
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherFlush 通过匹配同花的扑克牌创建匹配器
|
||||
func WithMatcherFlush[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
|
||||
groups := GroupByColor[P, C, T](cards...)
|
||||
for _, group := range groups {
|
||||
combinations = append(combinations, slice.Combinations(group)...)
|
||||
}
|
||||
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherTie 通过匹配相同点数的扑克牌创建匹配器
|
||||
func WithMatcherTie[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
groups := GroupByPoint[P, C, T](cards...)
|
||||
for _, group := range groups {
|
||||
for _, ts := range slice.Combinations(group) {
|
||||
combinations = append(combinations, ts)
|
||||
}
|
||||
}
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherTieCount 通过匹配相同点数的特定数量的扑克牌创建匹配器
|
||||
// - count: 牌型中相同点数的牌的数量
|
||||
func WithMatcherTieCount[P, C generic.Number, T Card[P, C]](count int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
groups := GroupByPoint[P, C, T](cards...)
|
||||
for _, group := range groups {
|
||||
if len(group) < count {
|
||||
continue
|
||||
}
|
||||
for _, ts := range slice.Combinations(group) {
|
||||
if len(ts) == count {
|
||||
combinations = append(combinations, ts)
|
||||
}
|
||||
}
|
||||
}
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherTieCountNum 通过匹配相同点数的特定数量的扑克牌创建匹配器
|
||||
// - count: 牌型中相同点数的牌的数量
|
||||
// - num: 牌型中相同点数的牌的数量
|
||||
func WithMatcherTieCountNum[P, C generic.Number, T Card[P, C]](count, num int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
cs := slice.LimitedCombinations(cards, count*num, count*num)
|
||||
var pointCount = make(map[P]int)
|
||||
for _, group := range cs {
|
||||
var ok = false
|
||||
for _, t := range group {
|
||||
pointCount[t.GetPoint()]++
|
||||
if len(pointCount) == 2 {
|
||||
var matchCount = true
|
||||
for _, n := range pointCount {
|
||||
if n != num {
|
||||
matchCount = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if matchCount {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
combinations = append(combinations, group)
|
||||
}
|
||||
for point := range pointCount {
|
||||
delete(pointCount, point)
|
||||
}
|
||||
}
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherNCarryM 通过匹配N带相同点数M的扑克牌创建匹配器
|
||||
// - n: 需要匹配的主牌数量
|
||||
// - m: 需要匹配的附加牌数量
|
||||
func WithMatcherNCarryM[P, C generic.Number, T Card[P, C]](n, m int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
groups := GroupByPoint[P, C, T](cards...)
|
||||
for _, group := range groups {
|
||||
if len(group) != n {
|
||||
continue
|
||||
}
|
||||
ms := slice.Combinations(slice.SubWithCheck(cards, group, func(a, b T) bool { return reflect.DeepEqual(a, b) }))
|
||||
for i := 0; i < len(ms); i++ {
|
||||
ts := GroupByPoint[P, C, T](ms[i]...)
|
||||
for _, cs := range ts {
|
||||
if len(cs) == m {
|
||||
combinations = append(combinations, slice.Merge(group, cs))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithMatcherNCarryMSingle 通过匹配N带M的扑克牌创建匹配器
|
||||
// - n: 需要匹配的主牌数量
|
||||
// - m: 需要匹配的附加牌数量
|
||||
func WithMatcherNCarryMSingle[P, C generic.Number, T Card[P, C]](n, m int) MatcherOption[P, C, T] {
|
||||
return func(matcher *MatcherFilter[P, C, T]) {
|
||||
matcher.AddHandle(func(cards []T) [][]T {
|
||||
var combinations [][]T
|
||||
groups := GroupByPoint[P, C, T](cards...)
|
||||
for _, group := range groups {
|
||||
if len(group) != n {
|
||||
continue
|
||||
}
|
||||
ms := slice.Combinations(slice.SubWithCheck(cards, group, func(a, b T) bool { return reflect.DeepEqual(a, b) }))
|
||||
for i := 0; i < len(ms); i++ {
|
||||
ts := ms[i]
|
||||
if len(ts) == m {
|
||||
combinations = append(combinations, slice.Merge(group, ts))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(combinations) > 0 {
|
||||
fmt.Println(len(combinations))
|
||||
}
|
||||
return combinations
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package poker_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/game/poker"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Card[P, C generic.Number] struct {
|
||||
guid int64
|
||||
point P
|
||||
color C
|
||||
}
|
||||
|
||||
func (slf *Card[P, C]) GetGuid() int64 {
|
||||
return slf.guid
|
||||
}
|
||||
|
||||
func (slf *Card[P, C]) GetPoint() P {
|
||||
return slf.point
|
||||
}
|
||||
|
||||
func (slf *Card[P, C]) GetColor() C {
|
||||
return slf.color
|
||||
}
|
||||
|
||||
func TestMatcher_Group(t *testing.T) {
|
||||
|
||||
evaluate := func(cards []*Card[int, int]) int64 {
|
||||
score := int64(0)
|
||||
for _, card := range cards {
|
||||
if card.point == 1 {
|
||||
score += 14
|
||||
continue
|
||||
}
|
||||
score += int64(card.GetPoint())
|
||||
}
|
||||
return score
|
||||
}
|
||||
|
||||
matcher := poker.NewMatcher[int, int, *Card[int, int]]()
|
||||
//matcher.RegType("三条", evaluate,
|
||||
// poker.WithMatcherNCarryMSingle[*Card](3, 2))
|
||||
matcher.RegType("皇家同花顺", evaluate,
|
||||
poker.WithMatcherFlush[int, int, *Card[int, int]](),
|
||||
poker.WithMatcherContinuityPointOrder[int, int, *Card[int, int]](map[int]int{1: 14}),
|
||||
poker.WithMatcherLength[int, int, *Card[int, int]](5),
|
||||
).RegType("同花顺", evaluate,
|
||||
poker.WithMatcherFlush[int, int, *Card[int, int]](),
|
||||
poker.WithMatcherContinuityPointOrder[int, int, *Card[int, int]](map[int]int{1: 14}),
|
||||
poker.WithMatcherLeastLength[int, int, *Card[int, int]](3),
|
||||
).RegType("四条", evaluate,
|
||||
poker.WithMatcherTieCount[int, int, *Card[int, int]](4),
|
||||
).RegType("葫芦", evaluate,
|
||||
poker.WithMatcherNCarryM[int, int, *Card[int, int]](3, 2),
|
||||
).RegType("顺子", evaluate,
|
||||
poker.WithMatcherContinuityPointOrder[int, int, *Card[int, int]](map[int]int{1: 14}),
|
||||
poker.WithMatcherLength[int, int, *Card[int, int]](5),
|
||||
).RegType("三条", evaluate,
|
||||
poker.WithMatcherNCarryMSingle[int, int, *Card[int, int]](3, 2),
|
||||
).RegType("两对", evaluate,
|
||||
poker.WithMatcherTieCountNum[int, int, *Card[int, int]](2, 2),
|
||||
).RegType("一对", evaluate,
|
||||
poker.WithMatcherTieCount[int, int, *Card[int, int]](2),
|
||||
).RegType("高牌", evaluate,
|
||||
poker.WithMatcherTieCount[int, int, *Card[int, int]](1),
|
||||
)
|
||||
|
||||
var pub = []*Card[int, int]{
|
||||
{point: 4, color: 3},
|
||||
{point: 5, color: 2},
|
||||
{point: 6, color: 1},
|
||||
{point: 6, color: 2},
|
||||
{point: 13, color: 2},
|
||||
}
|
||||
|
||||
var pri = []*Card[int, int]{
|
||||
{point: 1, color: 1},
|
||||
{point: 1, color: 2},
|
||||
{point: 4, color: 3},
|
||||
{point: 5, color: 4},
|
||||
}
|
||||
|
||||
var start = time.Now()
|
||||
var usePub, usePri = slice.LimitedCombinations(pub, 3, 3), slice.LimitedCombinations(pri, 2, 2)
|
||||
|
||||
var topResult []*Card[int, int]
|
||||
var topScore int64
|
||||
var topName string
|
||||
var source []*Card[int, int]
|
||||
for _, handCards := range usePri {
|
||||
for _, pubCards := range usePub {
|
||||
cards := append(handCards, pubCards...)
|
||||
name, result := matcher.Group(cards)
|
||||
score := evaluate(result)
|
||||
if score > topScore || topResult == nil {
|
||||
topScore = score
|
||||
topResult = result
|
||||
topName = name
|
||||
source = cards
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("time:", time.Since(start))
|
||||
fmt.Println("result:", topName)
|
||||
for _, card := range topResult {
|
||||
fmt.Println(fmt.Sprintf("Point: %d Color: %d", card.GetPoint(), card.GetColor()))
|
||||
}
|
||||
fmt.Println("source:", topScore)
|
||||
for _, card := range source {
|
||||
fmt.Println(fmt.Sprintf("Point: %d Color: %d", card.GetPoint(), card.GetColor()))
|
||||
}
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
package poker
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
)
|
||||
|
||||
type Option func(rule *Rule)
|
||||
type Option[P, C generic.Number, T Card[P, C]] func(rule *Rule[P, C, T])
|
||||
|
||||
// WithHand 通过绑定特定牌型的方式创建扑克玩法
|
||||
// - 牌型顺序决定了牌型的优先级
|
||||
func WithHand(pokerHand string, value int, handle HandHandle) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithHand[P, C generic.Number, T Card[P, C]](pokerHand string, value int, handle HandHandle[P, C, T]) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
if _, exist := rule.pokerHand[pokerHand]; exist {
|
||||
panic(fmt.Errorf("same poker hand name: %s", pokerHand))
|
||||
}
|
||||
|
@ -24,8 +27,8 @@ func WithHand(pokerHand string, value int, handle HandHandle) Option {
|
|||
}
|
||||
|
||||
// WithHandRestraint 通过绑定特定克制牌型的方式创建扑克玩法
|
||||
func WithHandRestraint(pokerHand, restraint string) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithHandRestraint[P, C generic.Number, T Card[P, C]](pokerHand, restraint string) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
r, exist := rule.restraint[pokerHand]
|
||||
if !exist {
|
||||
r = map[string]struct{}{}
|
||||
|
@ -37,8 +40,8 @@ func WithHandRestraint(pokerHand, restraint string) Option {
|
|||
|
||||
// WithHandRestraintFull 通过绑定所有克制牌型的方式创建扑克玩法
|
||||
// - 需要确保在牌型声明之后调用
|
||||
func WithHandRestraintFull(pokerHand string) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithHandRestraintFull[P, C generic.Number, T Card[P, C]](pokerHand string) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
for hand := range rule.pokerHand {
|
||||
r, exist := rule.restraint[pokerHand]
|
||||
if !exist {
|
||||
|
@ -51,22 +54,22 @@ func WithHandRestraintFull(pokerHand string) Option {
|
|||
}
|
||||
|
||||
// WithPointValue 通过特定的扑克点数牌值创建扑克玩法
|
||||
func WithPointValue(pointValues map[Point]int) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithPointValue[P, C generic.Number, T Card[P, C]](pointValues map[P]int) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
rule.pointValue = pointValues
|
||||
}
|
||||
}
|
||||
|
||||
// WithColorValue 通过特定的扑克花色牌值创建扑克玩法
|
||||
func WithColorValue(colorValues map[Color]int) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithColorValue[P, C generic.Number, T Card[P, C]](colorValues map[C]int) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
rule.colorValue = colorValues
|
||||
}
|
||||
}
|
||||
|
||||
// WithPointSort 通过特定的扑克点数顺序创建扑克玩法,顺序必须为连续的
|
||||
func WithPointSort(pointSort map[Point]int) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithPointSort[P, C generic.Number, T Card[P, C]](pointSort map[P]int) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
for k, v := range pointSort {
|
||||
rule.pointSort[k] = v
|
||||
}
|
||||
|
@ -74,8 +77,8 @@ func WithPointSort(pointSort map[Point]int) Option {
|
|||
}
|
||||
|
||||
// WithColorSort 通过特定的扑克花色顺序创建扑克玩法,顺序必须为连续的
|
||||
func WithColorSort(colorSort map[Color]int) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithColorSort[P, C generic.Number, T Card[P, C]](colorSort map[C]int) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
for k, v := range colorSort {
|
||||
rule.colorSort[k] = v
|
||||
}
|
||||
|
@ -83,10 +86,10 @@ func WithColorSort(colorSort map[Color]int) Option {
|
|||
}
|
||||
|
||||
// WithExcludeContinuityPoint 排除连续的点数
|
||||
func WithExcludeContinuityPoint(points ...Point) Option {
|
||||
return func(rule *Rule) {
|
||||
func WithExcludeContinuityPoint[P, C generic.Number, T Card[P, C]](points ...P) Option[P, C, T] {
|
||||
return func(rule *Rule[P, C, T]) {
|
||||
if rule.excludeContinuityPoint == nil {
|
||||
rule.excludeContinuityPoint = make(map[Point]struct{})
|
||||
rule.excludeContinuityPoint = make(map[P]struct{})
|
||||
}
|
||||
for _, point := range points {
|
||||
rule.excludeContinuityPoint[point] = struct{}{}
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
package poker
|
||||
|
||||
import "strconv"
|
||||
|
||||
const (
|
||||
PointA Point = 1
|
||||
Point2 Point = 2
|
||||
Point3 Point = 3
|
||||
Point4 Point = 4
|
||||
Point5 Point = 5
|
||||
Point6 Point = 6
|
||||
Point7 Point = 7
|
||||
Point8 Point = 8
|
||||
Point9 Point = 9
|
||||
Point10 Point = 10
|
||||
PointJ Point = 11
|
||||
PointQ Point = 12
|
||||
PointK Point = 13
|
||||
PointBlackJoker Point = 14
|
||||
PointRedJoker Point = 15
|
||||
)
|
||||
|
||||
var defaultPointSort = map[Point]int{
|
||||
PointA: int(PointA),
|
||||
Point2: int(Point2),
|
||||
Point3: int(Point3),
|
||||
Point4: int(Point4),
|
||||
Point5: int(Point5),
|
||||
Point6: int(Point6),
|
||||
Point7: int(Point7),
|
||||
Point8: int(Point8),
|
||||
Point9: int(Point9),
|
||||
Point10: int(Point10),
|
||||
PointJ: int(PointJ),
|
||||
PointQ: int(PointQ),
|
||||
PointK: int(PointK),
|
||||
PointBlackJoker: int(PointBlackJoker),
|
||||
PointRedJoker: int(PointRedJoker),
|
||||
}
|
||||
|
||||
// Point 扑克点数
|
||||
type Point int
|
||||
|
||||
func (slf Point) String() string {
|
||||
var str string
|
||||
switch slf {
|
||||
case PointA:
|
||||
str = "A"
|
||||
case PointJ:
|
||||
str = "J"
|
||||
case PointQ:
|
||||
str = "Q"
|
||||
case PointK:
|
||||
str = "K"
|
||||
case PointBlackJoker:
|
||||
str = "B"
|
||||
case PointRedJoker:
|
||||
str = "R"
|
||||
default:
|
||||
str = strconv.Itoa(int(slf))
|
||||
}
|
||||
return str
|
||||
}
|
|
@ -1,9 +1,14 @@
|
|||
package poker
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"math"
|
||||
)
|
||||
|
||||
// IsContainJoker 检查扑克牌是否包含大小王
|
||||
func IsContainJoker(cards ...Card) bool {
|
||||
func IsContainJoker[P, C generic.Number, T Card[P, C]](pile *CardPile[P, C, T], cards ...T) bool {
|
||||
for _, card := range cards {
|
||||
if card.IsJoker() {
|
||||
if IsJoker[P, C, T](pile, card) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +16,8 @@ func IsContainJoker(cards ...Card) bool {
|
|||
}
|
||||
|
||||
// GroupByPoint 将扑克牌按照点数分组
|
||||
func GroupByPoint(cards ...Card) map[Point][]Card {
|
||||
group := map[Point][]Card{}
|
||||
func GroupByPoint[P, C generic.Number, T Card[P, C]](cards ...T) map[P][]T {
|
||||
group := map[P][]T{}
|
||||
for _, card := range cards {
|
||||
group[card.GetPoint()] = append(group[card.GetPoint()], card)
|
||||
}
|
||||
|
@ -20,8 +25,8 @@ func GroupByPoint(cards ...Card) map[Point][]Card {
|
|||
}
|
||||
|
||||
// GroupByColor 将扑克牌按照花色分组
|
||||
func GroupByColor(cards ...Card) map[Color][]Card {
|
||||
group := map[Color][]Card{}
|
||||
func GroupByColor[P, C generic.Number, T Card[P, C]](cards ...T) map[C][]T {
|
||||
group := map[C][]T{}
|
||||
for _, card := range cards {
|
||||
group[card.GetColor()] = append(group[card.GetColor()], card)
|
||||
}
|
||||
|
@ -29,12 +34,18 @@ func GroupByColor(cards ...Card) map[Color][]Card {
|
|||
}
|
||||
|
||||
// IsRocket 两张牌能否组成红黑 Joker
|
||||
func IsRocket(cardA, cardB Card) bool {
|
||||
return cardA.GetPoint() == PointRedJoker && cardB.GetPoint() == PointBlackJoker || cardA.GetPoint() == PointBlackJoker && cardB.GetPoint() == PointRedJoker
|
||||
func IsRocket[P, C generic.Number, T Card[P, C]](pile *CardPile[P, C, T], cardA, cardB T) bool {
|
||||
var num int
|
||||
for _, joker := range pile.jokers {
|
||||
if cardA.GetPoint() == joker || cardB.GetPoint() == joker {
|
||||
num++
|
||||
}
|
||||
}
|
||||
return num == 2
|
||||
}
|
||||
|
||||
// IsFlush 判断是否是同花
|
||||
func IsFlush(cards ...Card) bool {
|
||||
func IsFlush[P, C generic.Number, T Card[P, C]](cards ...T) bool {
|
||||
if len(cards) == 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -52,8 +63,8 @@ func IsFlush(cards ...Card) bool {
|
|||
}
|
||||
|
||||
// GetCardsPoint 获取一组扑克牌的点数
|
||||
func GetCardsPoint(cards ...Card) []Point {
|
||||
var points = make([]Point, len(cards))
|
||||
func GetCardsPoint[P, C generic.Number, T Card[P, C]](cards ...T) []P {
|
||||
var points = make([]P, len(cards))
|
||||
for i, card := range cards {
|
||||
points[i] = card.GetPoint()
|
||||
}
|
||||
|
@ -61,8 +72,8 @@ func GetCardsPoint(cards ...Card) []Point {
|
|||
}
|
||||
|
||||
// GetCardsColor 获取一组扑克牌的花色
|
||||
func GetCardsColor(cards ...Card) []Color {
|
||||
var colors = make([]Color, len(cards))
|
||||
func GetCardsColor[P, C generic.Number, T Card[P, C]](cards ...T) []C {
|
||||
var colors = make([]C, len(cards))
|
||||
for i, card := range cards {
|
||||
colors[i] = card.GetColor()
|
||||
}
|
||||
|
@ -70,9 +81,9 @@ func GetCardsColor(cards ...Card) []Color {
|
|||
}
|
||||
|
||||
// IsContain 一组扑克牌是否包含某张牌
|
||||
func IsContain(cards []Card, card Card) bool {
|
||||
func IsContain[P, C generic.Number, T Card[P, C]](cards []T, card T) bool {
|
||||
for _, c := range cards {
|
||||
if c.Equal(card) {
|
||||
if Equal[P, C, T](c, card) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -80,11 +91,117 @@ func IsContain(cards []Card, card Card) bool {
|
|||
}
|
||||
|
||||
// IsContainAll 一组扑克牌是否包含另一组扑克牌
|
||||
func IsContainAll(cards []Card, cards2 []Card) bool {
|
||||
func IsContainAll[P, C generic.Number, T Card[P, C]](cards []T, cards2 []T) bool {
|
||||
for _, card := range cards2 {
|
||||
if !IsContain(cards, card) {
|
||||
if !IsContain[P, C, T](cards, card) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetPointAndColor 返回扑克牌的点数和花色
|
||||
func GetPointAndColor[P, C generic.Number, T Card[P, C]](card T) (P, C) {
|
||||
return card.GetPoint(), card.GetColor()
|
||||
}
|
||||
|
||||
// EqualPoint 比较两张扑克牌的点数是否相同
|
||||
func EqualPoint[P, C generic.Number, T Card[P, C]](card1 T, card2 T) bool {
|
||||
return card1.GetPoint() == card2.GetPoint()
|
||||
}
|
||||
|
||||
// EqualColor 比较两张扑克牌的花色是否相同
|
||||
func EqualColor[P, C generic.Number, T Card[P, C]](card1 T, card2 T) bool {
|
||||
return card1.GetColor() == card2.GetColor()
|
||||
}
|
||||
|
||||
// Equal 比较两张扑克牌的点数和花色是否相同
|
||||
func Equal[P, C generic.Number, T Card[P, C]](card1 T, card2 T) bool {
|
||||
return EqualPoint[P, C, T](card1, card2) && EqualColor[P, C, T](card1, card2)
|
||||
}
|
||||
|
||||
// MaxPoint 返回两张扑克牌中点数较大的一张
|
||||
func MaxPoint[P, C generic.Number, T Card[P, C]](card1 T, card2 T) T {
|
||||
if card1.GetPoint() > card2.GetPoint() {
|
||||
return card1
|
||||
}
|
||||
return card2
|
||||
}
|
||||
|
||||
// MinPoint 返回两张扑克牌中点数较小的一张
|
||||
func MinPoint[P, C generic.Number, T Card[P, C]](card1 T, card2 T) T {
|
||||
if card1.GetPoint() < card2.GetPoint() {
|
||||
return card1
|
||||
}
|
||||
return card2
|
||||
}
|
||||
|
||||
// MaxColor 返回两张扑克牌中花色较大的一张
|
||||
func MaxColor[P, C generic.Number, T Card[P, C]](card1 T, card2 T) T {
|
||||
if card1.GetColor() > card2.GetColor() {
|
||||
return card1
|
||||
}
|
||||
return card2
|
||||
}
|
||||
|
||||
// MinColor 返回两张扑克牌中花色较小的一张
|
||||
func MinColor[P, C generic.Number, T Card[P, C]](card1 T, card2 T) T {
|
||||
if card1.GetColor() < card2.GetColor() {
|
||||
return card1
|
||||
}
|
||||
return card2
|
||||
}
|
||||
|
||||
// Max 返回两张扑克牌中点数和花色较大的一张
|
||||
func Max[P, C generic.Number, T Card[P, C]](card1 T, card2 T) T {
|
||||
if card1.GetPoint() > card2.GetPoint() {
|
||||
return card1
|
||||
} else if card1.GetPoint() < card2.GetPoint() {
|
||||
return card2
|
||||
} else if card1.GetColor() > card2.GetColor() {
|
||||
return card1
|
||||
}
|
||||
return card2
|
||||
}
|
||||
|
||||
// Min 返回两张扑克牌中点数和花色较小的一张
|
||||
func Min[P, C generic.Number, T Card[P, C]](card1 T, card2 T) T {
|
||||
if card1.GetPoint() < card2.GetPoint() {
|
||||
return card1
|
||||
} else if card1.GetPoint() > card2.GetPoint() {
|
||||
return card2
|
||||
} else if card1.GetColor() < card2.GetColor() {
|
||||
return card1
|
||||
}
|
||||
return card2
|
||||
}
|
||||
|
||||
// PointDifference 计算两张扑克牌的点数差
|
||||
func PointDifference[P, C generic.Number, T Card[P, C]](card1 T, card2 T) int {
|
||||
return int(math.Abs(float64(card1.GetPoint()) - float64(card2.GetPoint())))
|
||||
}
|
||||
|
||||
// ColorDifference 计算两张扑克牌的花色差
|
||||
func ColorDifference[P, C generic.Number, T Card[P, C]](card1 T, card2 T) int {
|
||||
return int(math.Abs(float64(card1.GetColor()) - float64(card2.GetColor())))
|
||||
}
|
||||
|
||||
// IsNeighborColor 判断两张扑克牌是否为相邻的花色
|
||||
func IsNeighborColor[P, C generic.Number, T Card[P, C]](card1 T, card2 T) bool {
|
||||
return ColorDifference[P, C, T](card1, card2) == 1
|
||||
}
|
||||
|
||||
// IsNeighborPoint 判断两张扑克牌是否为相邻的点数
|
||||
func IsNeighborPoint[P, C generic.Number, T Card[P, C]](card1 T, card2 T) bool {
|
||||
return PointDifference[P, C, T](card1, card2) == 1
|
||||
}
|
||||
|
||||
// IsJoker 判断扑克牌是否为大小王
|
||||
func IsJoker[P, C generic.Number, T Card[P, C]](pile *CardPile[P, C, T], card T) bool {
|
||||
for _, joker := range pile.jokers {
|
||||
if card.GetPoint() == joker {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package poker
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"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),
|
||||
func NewRule[P, C generic.Number, T Card[P, C]](options ...Option[P, C, T]) *Rule[P, C, T] {
|
||||
poker := &Rule[P, C, T]{
|
||||
pokerHand: map[string]HandHandle[P, C, T]{},
|
||||
//pointSort: hash.Copy(defaultPointSort),
|
||||
//colorSort: hash.Copy(defaultColorSort),
|
||||
pokerHandValue: map[string]int{},
|
||||
restraint: map[string]map[string]struct{}{},
|
||||
}
|
||||
|
@ -23,19 +24,19 @@ func NewRule(options ...Option) *Rule {
|
|||
return poker
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
pokerHand map[string]HandHandle
|
||||
type Rule[P, C generic.Number, T Card[P, C]] struct {
|
||||
pokerHand map[string]HandHandle[P, C, T]
|
||||
pokerHandValue map[string]int
|
||||
pointValue map[Point]int
|
||||
colorValue map[Color]int
|
||||
pointSort map[Point]int
|
||||
colorSort map[Color]int
|
||||
excludeContinuityPoint map[Point]struct{}
|
||||
pointValue map[P]int
|
||||
colorValue map[C]int
|
||||
pointSort map[P]int
|
||||
colorSort map[C]int
|
||||
excludeContinuityPoint map[P]struct{}
|
||||
restraint map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
// GetCardCountWithPointMaximumNumber 获取指定点数的牌的数量
|
||||
func (slf *Rule) GetCardCountWithPointMaximumNumber(cards []Card, point Point, maximumNumber int) int {
|
||||
func (slf *Rule[P, C, T]) GetCardCountWithPointMaximumNumber(cards []T, point P, maximumNumber int) int {
|
||||
count := 0
|
||||
for _, card := range cards {
|
||||
if card.GetPoint() == point {
|
||||
|
@ -49,7 +50,7 @@ func (slf *Rule) GetCardCountWithPointMaximumNumber(cards []Card, point Point, m
|
|||
}
|
||||
|
||||
// GetCardCountWithColorMaximumNumber 获取指定花色的牌的数量
|
||||
func (slf *Rule) GetCardCountWithColorMaximumNumber(cards []Card, color Color, maximumNumber int) int {
|
||||
func (slf *Rule[P, C, T]) GetCardCountWithColorMaximumNumber(cards []T, color C, maximumNumber int) int {
|
||||
count := 0
|
||||
for _, card := range cards {
|
||||
if card.GetColor() == color {
|
||||
|
@ -63,10 +64,10 @@ func (slf *Rule) GetCardCountWithColorMaximumNumber(cards []Card, color Color, m
|
|||
}
|
||||
|
||||
// GetCardCountWithMaximumNumber 获取指定牌的数量
|
||||
func (slf *Rule) GetCardCountWithMaximumNumber(cards []Card, card Card, maximumNumber int) int {
|
||||
func (slf *Rule[P, C, T]) GetCardCountWithMaximumNumber(cards []T, card T, maximumNumber int) int {
|
||||
count := 0
|
||||
for _, c := range cards {
|
||||
if c.Equal(card) {
|
||||
if Equal[P, C, T](c, card) {
|
||||
count++
|
||||
if count >= maximumNumber {
|
||||
return count
|
||||
|
@ -77,7 +78,7 @@ func (slf *Rule) GetCardCountWithMaximumNumber(cards []Card, card Card, maximumN
|
|||
}
|
||||
|
||||
// GetCardCountWithPoint 获取指定点数的牌的数量
|
||||
func (slf *Rule) GetCardCountWithPoint(cards []Card, point Point) int {
|
||||
func (slf *Rule[P, C, T]) GetCardCountWithPoint(cards []T, point P) int {
|
||||
count := 0
|
||||
for _, card := range cards {
|
||||
if card.GetPoint() == point {
|
||||
|
@ -88,7 +89,7 @@ func (slf *Rule) GetCardCountWithPoint(cards []Card, point Point) int {
|
|||
}
|
||||
|
||||
// GetCardCountWithColor 获取指定花色的牌的数量
|
||||
func (slf *Rule) GetCardCountWithColor(cards []Card, color Color) int {
|
||||
func (slf *Rule[P, C, T]) GetCardCountWithColor(cards []T, color C) int {
|
||||
count := 0
|
||||
for _, card := range cards {
|
||||
if card.GetColor() == color {
|
||||
|
@ -99,10 +100,10 @@ func (slf *Rule) GetCardCountWithColor(cards []Card, color Color) int {
|
|||
}
|
||||
|
||||
// GetCardCount 获取指定牌的数量
|
||||
func (slf *Rule) GetCardCount(cards []Card, card Card) int {
|
||||
func (slf *Rule[P, C, T]) GetCardCount(cards []T, card T) int {
|
||||
count := 0
|
||||
for _, c := range cards {
|
||||
if c.Equal(card) {
|
||||
if Equal[P, C, T](c, card) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +111,7 @@ func (slf *Rule) GetCardCount(cards []Card, card Card) int {
|
|||
}
|
||||
|
||||
// PokerHandIsMatch 检查两组扑克牌牌型是否匹配
|
||||
func (slf *Rule) PokerHandIsMatch(cardsA, cardsB []Card) bool {
|
||||
func (slf *Rule[P, C, T]) PokerHandIsMatch(cardsA, cardsB []T) bool {
|
||||
handA, exist := slf.PokerHand(cardsA...)
|
||||
if !exist {
|
||||
return false
|
||||
|
@ -126,7 +127,7 @@ func (slf *Rule) PokerHandIsMatch(cardsA, cardsB []Card) bool {
|
|||
}
|
||||
|
||||
// PokerHand 获取一组扑克的牌型
|
||||
func (slf *Rule) PokerHand(cards ...Card) (pokerHand string, hit bool) {
|
||||
func (slf *Rule[P, C, T]) PokerHand(cards ...T) (pokerHand string, hit bool) {
|
||||
for phn := range slf.pokerHandValue {
|
||||
if slf.pokerHand[phn](slf, cards) {
|
||||
return phn, true
|
||||
|
@ -136,11 +137,11 @@ func (slf *Rule) PokerHand(cards ...Card) (pokerHand string, hit bool) {
|
|||
}
|
||||
|
||||
// IsPointContinuity 检查一组扑克牌点数是否连续,count 表示了每个点数的数量
|
||||
func (slf *Rule) IsPointContinuity(count int, cards ...Card) bool {
|
||||
func (slf *Rule[P, C, T]) IsPointContinuity(count int, cards ...T) bool {
|
||||
if len(cards) == 0 {
|
||||
return false
|
||||
}
|
||||
group := GroupByPoint(cards...)
|
||||
group := GroupByPoint[P, C, T](cards...)
|
||||
var values []int
|
||||
for point, cards := range group {
|
||||
if _, exist := slf.excludeContinuityPoint[point]; exist {
|
||||
|
@ -155,7 +156,7 @@ func (slf *Rule) IsPointContinuity(count int, cards ...Card) bool {
|
|||
}
|
||||
|
||||
// IsSameColor 检查一组扑克牌是否同花
|
||||
func (slf *Rule) IsSameColor(cards ...Card) bool {
|
||||
func (slf *Rule[P, C, T]) IsSameColor(cards ...T) bool {
|
||||
length := len(cards)
|
||||
if length == 0 {
|
||||
return false
|
||||
|
@ -173,7 +174,7 @@ func (slf *Rule) IsSameColor(cards ...Card) bool {
|
|||
}
|
||||
|
||||
// IsSamePoint 检查一组扑克牌是否同点
|
||||
func (slf *Rule) IsSamePoint(cards ...Card) bool {
|
||||
func (slf *Rule[P, C, T]) IsSamePoint(cards ...T) bool {
|
||||
length := len(cards)
|
||||
if length == 0 {
|
||||
return false
|
||||
|
@ -191,7 +192,7 @@ func (slf *Rule) IsSamePoint(cards ...Card) bool {
|
|||
}
|
||||
|
||||
// SortByPointDesc 将扑克牌按照点数从大到小排序
|
||||
func (slf *Rule) SortByPointDesc(cards []Card) []Card {
|
||||
func (slf *Rule[P, C, T]) SortByPointDesc(cards []T) []T {
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return slf.pointSort[cards[i].GetPoint()] > slf.pointSort[cards[j].GetPoint()]
|
||||
})
|
||||
|
@ -199,7 +200,7 @@ func (slf *Rule) SortByPointDesc(cards []Card) []Card {
|
|||
}
|
||||
|
||||
// SortByPointAsc 将扑克牌按照点数从小到大排序
|
||||
func (slf *Rule) SortByPointAsc(cards []Card) []Card {
|
||||
func (slf *Rule[P, C, T]) SortByPointAsc(cards []T) []T {
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return slf.pointSort[cards[i].GetPoint()] < slf.pointSort[cards[j].GetPoint()]
|
||||
})
|
||||
|
@ -207,7 +208,7 @@ func (slf *Rule) SortByPointAsc(cards []Card) []Card {
|
|||
}
|
||||
|
||||
// SortByColorDesc 将扑克牌按照花色从大到小排序
|
||||
func (slf *Rule) SortByColorDesc(cards []Card) []Card {
|
||||
func (slf *Rule[P, C, T]) SortByColorDesc(cards []T) []T {
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return slf.colorSort[cards[i].GetColor()] > slf.colorSort[cards[j].GetColor()]
|
||||
})
|
||||
|
@ -215,7 +216,7 @@ func (slf *Rule) SortByColorDesc(cards []Card) []Card {
|
|||
}
|
||||
|
||||
// SortByColorAsc 将扑克牌按照花色从小到大排序
|
||||
func (slf *Rule) SortByColorAsc(cards []Card) []Card {
|
||||
func (slf *Rule[P, C, T]) SortByColorAsc(cards []T) []T {
|
||||
sort.Slice(cards, func(i, j int) bool {
|
||||
return slf.colorSort[cards[i].GetColor()] < slf.colorSort[cards[j].GetColor()]
|
||||
})
|
||||
|
@ -223,13 +224,13 @@ func (slf *Rule) SortByColorAsc(cards []Card) []Card {
|
|||
}
|
||||
|
||||
// GetValueWithPokerHand 获取扑克牌的牌型牌值
|
||||
func (slf *Rule) GetValueWithPokerHand(hand string, cards ...Card) int {
|
||||
func (slf *Rule[P, C, T]) GetValueWithPokerHand(hand string, cards ...T) int {
|
||||
hv := slf.pokerHandValue[hand]
|
||||
return hv * slf.GetValueWithCards(cards...)
|
||||
}
|
||||
|
||||
// GetValueWithCards 获取扑克牌的牌值
|
||||
func (slf *Rule) GetValueWithCards(cards ...Card) int {
|
||||
func (slf *Rule[P, C, T]) GetValueWithCards(cards ...T) int {
|
||||
var value int
|
||||
for _, card := range cards {
|
||||
value += slf.GetValueWithPoint(card.GetPoint())
|
||||
|
@ -239,16 +240,16 @@ func (slf *Rule) GetValueWithCards(cards ...Card) int {
|
|||
}
|
||||
|
||||
// GetValueWithPoint 获取扑克牌的点数牌值
|
||||
func (slf *Rule) GetValueWithPoint(point Point) int {
|
||||
func (slf *Rule[P, C, T]) GetValueWithPoint(point P) int {
|
||||
return slf.pointValue[point]
|
||||
}
|
||||
|
||||
// GetValueWithColor 获取扑克牌的花色牌值
|
||||
func (slf *Rule) GetValueWithColor(color Color) int {
|
||||
func (slf *Rule[P, C, T]) GetValueWithColor(color C) int {
|
||||
return slf.colorValue[color]
|
||||
}
|
||||
|
||||
// CompareValueWithCards 根据特定的条件表达式比较两组扑克牌的牌值
|
||||
func (slf *Rule) CompareValueWithCards(cards1 []Card, expression maths.CompareExpression, cards2 []Card) bool {
|
||||
func (slf *Rule[P, C, T]) CompareValueWithCards(cards1 []T, expression maths.CompareExpression, cards2 []T) bool {
|
||||
return maths.Compare(slf.GetValueWithCards(cards1...), expression, slf.GetValueWithCards(cards2...))
|
||||
}
|
||||
|
|
|
@ -197,3 +197,125 @@ func ContainsAny[V any](slice []V, values V) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetIndex 判断数组是否包含某个元素,如果包含则返回索引
|
||||
func GetIndex[V comparable](slice []V, value V) int {
|
||||
for i, v := range slice {
|
||||
if v == value {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetIndexAny 判断数组是否包含某个元素,如果包含则返回索引
|
||||
func GetIndexAny[V any](slice []V, values V) int {
|
||||
for i, v := range slice {
|
||||
if reflect.DeepEqual(v, values) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Combinations 获取给定数组的所有组合,包括重复元素的组合
|
||||
func Combinations[T any](a []T) [][]T {
|
||||
n := len(a)
|
||||
|
||||
// 去除重复元素,保留唯一元素
|
||||
uniqueSet := make(map[uintptr]bool)
|
||||
uniqueSlice := make([]T, 0, n)
|
||||
for _, val := range a {
|
||||
ptr := reflect.ValueOf(val).Pointer()
|
||||
if !uniqueSet[ptr] {
|
||||
uniqueSet[ptr] = true
|
||||
uniqueSlice = append(uniqueSlice, val)
|
||||
}
|
||||
}
|
||||
|
||||
n = len(uniqueSlice) // 去重后的数组长度
|
||||
totalCombinations := 1 << n // 2的n次方
|
||||
var result [][]T
|
||||
for i := 0; i < totalCombinations; i++ {
|
||||
var currentCombination []T
|
||||
for j := 0; j < n; j++ {
|
||||
if (i & (1 << j)) != 0 {
|
||||
currentCombination = append(currentCombination, uniqueSlice[j])
|
||||
}
|
||||
}
|
||||
result = append(result, currentCombination)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// LimitedCombinations 获取给定数组的所有组合,且每个组合的成员数量限制在指定范围内
|
||||
func LimitedCombinations[T any](a []T, minSize, maxSize int) [][]T {
|
||||
n := len(a)
|
||||
if n == 0 || minSize <= 0 || maxSize <= 0 || minSize > maxSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result [][]T
|
||||
var currentCombination []T
|
||||
|
||||
var backtrack func(startIndex int, currentSize int)
|
||||
backtrack = func(startIndex int, currentSize int) {
|
||||
if currentSize >= minSize && currentSize <= maxSize {
|
||||
combination := make([]T, len(currentCombination))
|
||||
copy(combination, currentCombination)
|
||||
result = append(result, combination)
|
||||
}
|
||||
|
||||
for i := startIndex; i < n; i++ {
|
||||
currentCombination = append(currentCombination, a[i])
|
||||
backtrack(i+1, currentSize+1)
|
||||
currentCombination = currentCombination[:len(currentCombination)-1]
|
||||
}
|
||||
}
|
||||
|
||||
backtrack(0, 0)
|
||||
return result
|
||||
}
|
||||
|
||||
// IsIntersectWithCheck 判断两个切片是否有交集
|
||||
func IsIntersectWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) bool {
|
||||
for _, a := range a {
|
||||
for _, b := range b {
|
||||
if checkHandle(a, b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIntersect 判断两个切片是否有交集
|
||||
func IsIntersect[T any](a, b []T) bool {
|
||||
for _, a := range a {
|
||||
for _, b := range b {
|
||||
if reflect.DeepEqual(a, b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SubWithCheck 获取移除指定元素后的切片
|
||||
// - checkHandle 返回 true 表示需要移除
|
||||
func SubWithCheck[T any](a, b []T, checkHandle func(a, b T) bool) []T {
|
||||
var result []T
|
||||
for _, a := range a {
|
||||
flag := false
|
||||
for _, b := range b {
|
||||
if checkHandle(a, b) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package slice_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLimitedCombinations(t *testing.T) {
|
||||
c := slice.LimitedCombinations([]int{1, 2, 3, 4, 5}, 3, 3)
|
||||
for _, v := range c {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue