From 9accaa5868b1ff0ea4daa07ab97525ccd5f739ca Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Sun, 25 Jun 2023 11:15:50 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=89=91=E5=85=8B=E7=89=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/builtin/poker.go | 62 ------------- game/builtin/poker_card.go | 42 --------- game/builtin/poker_card_pile_options.go | 54 ----------- game/builtin/poker_card_pile_test.go | 13 --- game/builtin/poker_color.go | 35 -------- game/builtin/poker_options.go | 33 ------- game/builtin/poker_point.go | 45 ---------- game/doc.go | 12 +-- game/poker.go | 5 -- game/poker/card.go | 60 +++++++++++++ game/poker/card_example_test.go | 14 +++ .../poker_card_pile.go => poker/card_pile.go} | 73 +++++++-------- game/poker/card_pile_example_test.go | 17 ++++ game/poker/card_pile_options.go | 60 +++++++++++++ game/poker/card_test.go | 60 +++++++++++++ game/poker/color.go | 35 ++++++++ game/poker/doc.go | 8 ++ game/poker/func.go | 19 ++++ game/poker/hand.go | 36 ++++++++ game/poker/options.go | 39 ++++++++ game/poker/point.go | 63 +++++++++++++ game/poker/poker.go | 90 +++++++++++++++++++ 22 files changed, 543 insertions(+), 332 deletions(-) delete mode 100644 game/builtin/poker.go delete mode 100644 game/builtin/poker_card.go delete mode 100644 game/builtin/poker_card_pile_options.go delete mode 100644 game/builtin/poker_card_pile_test.go delete mode 100644 game/builtin/poker_color.go delete mode 100644 game/builtin/poker_options.go delete mode 100644 game/builtin/poker_point.go delete mode 100644 game/poker.go create mode 100644 game/poker/card.go create mode 100644 game/poker/card_example_test.go rename game/{builtin/poker_card_pile.go => poker/card_pile.go} (57%) create mode 100644 game/poker/card_pile_example_test.go create mode 100644 game/poker/card_pile_options.go create mode 100644 game/poker/card_test.go create mode 100644 game/poker/color.go create mode 100644 game/poker/doc.go create mode 100644 game/poker/func.go create mode 100644 game/poker/hand.go create mode 100644 game/poker/options.go create mode 100644 game/poker/point.go create mode 100644 game/poker/poker.go diff --git a/game/builtin/poker.go b/game/builtin/poker.go deleted file mode 100644 index c264b46..0000000 --- a/game/builtin/poker.go +++ /dev/null @@ -1,62 +0,0 @@ -package builtin - -import "github.com/kercylan98/minotaur/utils/maths" - -func NewPoker[PlayerID comparable](pile *PokerCardPile, options ...PokerOption[PlayerID]) *Poker[PlayerID] { - poker := &Poker[PlayerID]{ - pile: pile, - handCards: map[PlayerID][][]PokerCard{}, - } - for _, option := range options { - option(poker) - } - return poker -} - -type Poker[PlayerID comparable] struct { - pile *PokerCardPile - comparePoint map[PokerPoint]int - compareColor map[PokerColor]int - handCards map[PlayerID][][]PokerCard -} - -// HandCard 获取玩家特定索引的手牌组 -func (slf *Poker[PlayerID]) HandCard(playerId PlayerID, index int) []PokerCard { - return slf.handCards[playerId][index] -} - -// HandCards 获取玩家所有手牌 -// - 获取结果为多份手牌 -func (slf *Poker[PlayerID]) HandCards(playerId PlayerID) [][]PokerCard { - return slf.handCards[playerId] -} - -// HandCardGroupCount 获取玩家共有多少副手牌 -func (slf *Poker[PlayerID]) HandCardGroupCount(playerId PlayerID) int { - return len(slf.handCards[playerId]) -} - -// GetPile 获取牌堆 -func (slf *Poker[PlayerID]) GetPile() *PokerCardPile { - return slf.pile -} - -// Compare 比较两张扑克牌大小 -func (slf *Poker[PlayerID]) Compare(card1 PokerCard, expression maths.CompareExpression, card2 PokerCard) bool { - var point1, point2 int - if slf.comparePoint == nil { - point1, point2 = int(card1.GetPoint()), int(card2.GetPoint()) - } else { - point1, point2 = slf.comparePoint[card1.GetPoint()], slf.comparePoint[card2.GetPoint()] - } - if maths.Compare(point1, expression, point2) { - return true - } - var color1, color2 int - if slf.comparePoint == nil { - color1, color2 = int(card1.GetColor()), int(card2.GetColor()) - } else { - color1, color2 = slf.compareColor[card1.GetColor()], slf.compareColor[card2.GetColor()] - } - return maths.Compare(color1, expression, color2) -} diff --git a/game/builtin/poker_card.go b/game/builtin/poker_card.go deleted file mode 100644 index 0380820..0000000 --- a/game/builtin/poker_card.go +++ /dev/null @@ -1,42 +0,0 @@ -package builtin - -import "fmt" - -// NewPokerCard 创建一张扑克牌 -// - 当 point 为 PokerPointBlackJoker 或 PokerPointRedJoker 时,color 将没有效果 -func NewPokerCard(point PokerPoint, color PokerColor) PokerCard { - if point == PokerPointRedJoker || point == PokerPointBlackJoker { - color = PokerColorNone - } - card := PokerCard{ - point: point, - color: color, - } - return card -} - -// PokerCard 扑克牌 -type PokerCard struct { - point PokerPoint - color PokerColor -} - -// GetPoint 返回扑克牌的点数 -func (slf PokerCard) GetPoint() PokerPoint { - return slf.point -} - -// GetColor 返回扑克牌的花色 -func (slf PokerCard) GetColor() PokerColor { - return slf.color -} - -// GetPointAndColor 返回扑克牌的点数和花色 -func (slf PokerCard) GetPointAndColor() (PokerPoint, PokerColor) { - return slf.point, slf.color -} - -// String 将扑克牌转换为字符串形式 -func (slf PokerCard) String() string { - return fmt.Sprintf("(%s %s)", slf.point, slf.color) -} diff --git a/game/builtin/poker_card_pile_options.go b/game/builtin/poker_card_pile_options.go deleted file mode 100644 index 124e5d6..0000000 --- a/game/builtin/poker_card_pile_options.go +++ /dev/null @@ -1,54 +0,0 @@ -package builtin - -type PokerCardPileOption func(pile *PokerCardPile) - -// WithPokerCardPileShuffle 通过特定的洗牌算法创建牌堆 -// - 需要保证洗牌后的牌堆剩余扑克数量与之前相同,否则将会引发 panic -func WithPokerCardPileShuffle(shuffleHandle func(pile []PokerCard) []PokerCard) PokerCardPileOption { - return func(pile *PokerCardPile) { - if shuffleHandle == nil { - return - } - pile.shuffleHandle = shuffleHandle - } -} - -// WithPokerCardPileExcludeColor 通过排除特定花色的方式创建牌堆 -func WithPokerCardPileExcludeColor(colors ...PokerColor) PokerCardPileOption { - return func(pile *PokerCardPile) { - if pile.excludeColor == nil { - pile.excludeColor = map[PokerColor]struct{}{} - } - for _, color := range colors { - pile.excludeColor[color] = struct{}{} - } - } -} - -// WithPokerCardPileExcludePoint 通过排除特定点数的方式创建牌堆 -func WithPokerCardPileExcludePoint(points ...PokerPoint) PokerCardPileOption { - return func(pile *PokerCardPile) { - if pile.excludePoint == nil { - pile.excludePoint = map[PokerPoint]struct{}{} - } - for _, point := range points { - pile.excludePoint[point] = struct{}{} - } - } -} - -// WithPokerCardPileExcludeCard 通过排除特定扑克牌的方式创建牌堆 -func WithPokerCardPileExcludeCard(cards ...PokerCard) PokerCardPileOption { - return func(pile *PokerCardPile) { - if pile.excludeCard == nil { - pile.excludeCard = map[PokerPoint]map[PokerColor]struct{}{} - } - for _, card := range cards { - cs, exist := pile.excludeCard[card.GetPoint()] - if !exist { - cs = map[PokerColor]struct{}{} - } - cs[card.GetColor()] = struct{}{} - } - } -} diff --git a/game/builtin/poker_card_pile_test.go b/game/builtin/poker_card_pile_test.go deleted file mode 100644 index a9210b6..0000000 --- a/game/builtin/poker_card_pile_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package builtin - -import ( - "fmt" - "testing" -) - -func TestNewPokerCardPile(t *testing.T) { - pile := NewPokerCardPile(2) - _ = pile.PullTop() - pile.Reset() - fmt.Println(pile) -} diff --git a/game/builtin/poker_color.go b/game/builtin/poker_color.go deleted file mode 100644 index 3e30c51..0000000 --- a/game/builtin/poker_color.go +++ /dev/null @@ -1,35 +0,0 @@ -package builtin - -const ( - PokerColorNone PokerColor = 0 // 无花色,通常为大小王 - PokerColorSpade PokerColor = 1 // 黑桃 - PokerColorHeart PokerColor = 2 // 红桃 - PokerColorClub PokerColor = 3 // 梅花 - PokerColorDiamond PokerColor = 4 // 方片 -) - -// PokerColor 扑克牌花色 -type PokerColor int - -// InBounds 扑克牌花色是否在界限内 -// - 将检查花色是否在黑桃、红桃、梅花、方片之间 -func (slf PokerColor) InBounds() bool { - return slf <= PokerColorSpade && slf >= PokerColorDiamond -} - -func (slf PokerColor) String() string { - var str string - switch slf { - case PokerColorSpade: - str = "Spade" - case PokerColorHeart: - str = "Heart" - case PokerColorClub: - str = "Club" - case PokerColorDiamond: - str = "Diamond" - default: - str = "None" - } - return str -} diff --git a/game/builtin/poker_options.go b/game/builtin/poker_options.go deleted file mode 100644 index b87608c..0000000 --- a/game/builtin/poker_options.go +++ /dev/null @@ -1,33 +0,0 @@ -package builtin - -type PokerOption[PlayerID comparable] func(poker *Poker[PlayerID]) - -// WithPokerPointOrderOfSize 通过特定的点数大小顺序创建扑克玩法 -// - 顺序由大到小,points 数组必须包含每一个点数 -func WithPokerPointOrderOfSize[PlayerID comparable](points [15]PokerPoint) PokerOption[PlayerID] { - return func(poker *Poker[PlayerID]) { - var compare = make(map[PokerPoint]int) - for i, point := range points { - compare[point] = len(points) - i - } - if len(compare) != len(points) { - panic("not every point included") - } - poker.comparePoint = compare - } -} - -// WithPokerColorOrderOfSize 通过特定的花色大小顺序创建扑克玩法 -// - 顺序由大到小,colors 数组必须包含每一种花色 -func WithPokerColorOrderOfSize[PlayerID comparable](colors [4]PokerColor) PokerOption[PlayerID] { - return func(poker *Poker[PlayerID]) { - var compare = make(map[PokerColor]int) - for i, color := range colors { - compare[color] = len(colors) - i - } - if len(compare) != len(colors) { - panic("not every color included") - } - poker.compareColor = compare - } -} diff --git a/game/builtin/poker_point.go b/game/builtin/poker_point.go deleted file mode 100644 index a3096f0..0000000 --- a/game/builtin/poker_point.go +++ /dev/null @@ -1,45 +0,0 @@ -package builtin - -import "strconv" - -const ( - PokerPointA PokerPoint = 1 - PokerPoint2 PokerPoint = 2 - PokerPoint3 PokerPoint = 3 - PokerPoint4 PokerPoint = 4 - PokerPoint5 PokerPoint = 5 - PokerPoint6 PokerPoint = 6 - PokerPoint7 PokerPoint = 7 - PokerPoint8 PokerPoint = 8 - PokerPoint9 PokerPoint = 9 - PokerPoint10 PokerPoint = 10 - PokerPointJ PokerPoint = 11 - PokerPointQ PokerPoint = 12 - PokerPointK PokerPoint = 13 - PokerPointBlackJoker PokerPoint = 14 - PokerPointRedJoker PokerPoint = 15 -) - -// PokerPoint 扑克点数 -type PokerPoint int - -func (slf PokerPoint) String() string { - var str string - switch slf { - case PokerPointA: - str = "A" - case PokerPointJ: - str = "J" - case PokerPointQ: - str = "Q" - case PokerPointK: - str = "K" - case PokerPointBlackJoker: - str = "B" - case PokerPointRedJoker: - str = "R" - default: - str = strconv.Itoa(int(slf)) - } - return str -} diff --git a/game/doc.go b/game/doc.go index cd0c1df..f487979 100644 --- a/game/doc.go +++ b/game/doc.go @@ -1,10 +1,6 @@ -// Package game 定义了通用游戏相关的接口 -// - actor.go:游戏通用对象接口定义 -// - aoi2d.go:基于2D的感兴趣领域(Area Of Interest)接口定义 -// - aoi2d_entity.go:基于2D的感兴趣领域(Area Of Interest)对象接口定义 -// - attrs.go:游戏属性接口定义,属性通常为直接读取配置,是否合理暂不清晰,目前不推荐使用 -// - fsm.go:有限状态机接口定义 -// - fsm_state.go:有限状态机状态接口定义 +// Package game 提供了通用游戏相关的接口和一组内置的实现。 // -// 其中 builtin 包内包含了内置的实现 +// 主要特性: +// - 包 builtin 内提供了大量的常用游戏功能的内置实现,包括房间、状态机等。 +// - 包 poker 内提供了用于处理扑克牌游戏的函数和数据结构。 package game diff --git a/game/poker.go b/game/poker.go deleted file mode 100644 index 727d33f..0000000 --- a/game/poker.go +++ /dev/null @@ -1,5 +0,0 @@ -package game - -// Poker 扑克牌游戏通用玩法接口定义 -type Poker interface { -} diff --git a/game/poker/card.go b/game/poker/card.go new file mode 100644 index 0000000..8dad8ae --- /dev/null +++ b/game/poker/card.go @@ -0,0 +1,60 @@ +package poker + +import "fmt" + +// 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 +} + +// 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() +} + +// String 将扑克牌转换为字符串形式 +func (slf Card) String() string { + return fmt.Sprintf("(%s %s)", slf.point, slf.color) +} diff --git a/game/poker/card_example_test.go b/game/poker/card_example_test.go new file mode 100644 index 0000000..8984c30 --- /dev/null +++ b/game/poker/card_example_test.go @@ -0,0 +1,14 @@ +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) +} diff --git a/game/builtin/poker_card_pile.go b/game/poker/card_pile.go similarity index 57% rename from game/builtin/poker_card_pile.go rename to game/poker/card_pile.go index a44a9ee..87cf981 100644 --- a/game/builtin/poker_card_pile.go +++ b/game/poker/card_pile.go @@ -1,4 +1,4 @@ -package builtin +package poker import ( "fmt" @@ -8,14 +8,14 @@ import ( "sort" ) -// NewPokerCardPile 返回一个新的牌堆,其中 size 表示了该牌堆由多少副牌组成 +// NewCardPile 返回一个新的牌堆,其中 size 表示了该牌堆由多少副牌组成 // - 在不洗牌的情况下,默认牌堆顶部到底部为从大到小排列 -func NewPokerCardPile(size int, options ...PokerCardPileOption) *PokerCardPile { - pile := &PokerCardPile{ +func NewCardPile(size int, options ...CardPileOption) *CardPile { + pile := &CardPile{ size: size, - pile: make([]PokerCard, 0, size*54), + pile: make([]Card, 0, size*54), } - pile.shuffleHandle = func(cards []PokerCard) []PokerCard { + pile.shuffleHandle = func(cards []Card) []Card { sort.Slice(cards, func(i, j int) bool { return random.Float64() >= 0.5 }) @@ -28,29 +28,29 @@ func NewPokerCardPile(size int, options ...PokerCardPileOption) *PokerCardPile { return pile } -// PokerCardPile 扑克牌堆 -type PokerCardPile struct { - pile []PokerCard +// CardPile 扑克牌堆 +type CardPile struct { + pile []Card size int - shuffleHandle func(cards []PokerCard) []PokerCard - excludeColor map[PokerColor]struct{} - excludePoint map[PokerPoint]struct{} - excludeCard map[PokerPoint]map[PokerColor]struct{} + shuffleHandle func(cards []Card) []Card + excludeColor map[Color]struct{} + excludePoint map[Point]struct{} + excludeCard map[Point]map[Color]struct{} } // Reset 重置牌堆的扑克牌数量及顺序 -func (slf *PokerCardPile) Reset() { - var cards = make([]PokerCard, 0, 54) - if !slf.IsExclude(PokerPointRedJoker, PokerColorNone) { - cards = append(cards, NewPokerCard(PokerPointRedJoker, PokerColorNone)) +func (slf *CardPile) Reset() { + var cards = make([]Card, 0, 54) + if !slf.IsExclude(PointRedJoker, ColorNone) { + cards = append(cards, NewCard(PointRedJoker, ColorNone)) } - if !slf.IsExclude(PokerPointBlackJoker, PokerColorNone) { - cards = append(cards, NewPokerCard(PokerPointBlackJoker, PokerColorNone)) + if !slf.IsExclude(PointBlackJoker, ColorNone) { + cards = append(cards, NewCard(PointBlackJoker, ColorNone)) } - for point := PokerPointK; point >= PokerPointA; point-- { - for color := PokerColorSpade; color <= PokerColorDiamond; color++ { + for point := PointK; point >= PointA; point-- { + for color := ColorSpade; color <= ColorDiamond; color++ { if !slf.IsExclude(point, color) { - cards = append(cards, NewPokerCard(point, color)) + cards = append(cards, NewCard(point, color)) } } } @@ -61,18 +61,21 @@ func (slf *PokerCardPile) Reset() { } // IsExclude 检查特定点数和花色是否被排除在外 -func (slf *PokerCardPile) IsExclude(point PokerPoint, color PokerColor) bool { +func (slf *CardPile) IsExclude(point Point, color Color) bool { + if point == PointRedJoker || point == PointBlackJoker { + color = ColorNone + } return hash.Exist(slf.excludePoint, point) || hash.Exist(slf.excludeColor, color) || hash.Exist(slf.excludeCard[point], color) } // IsExcludeWithCard 检查特定扑克牌是否被排除在外 -func (slf *PokerCardPile) IsExcludeWithCard(card PokerCard) bool { +func (slf *CardPile) IsExcludeWithCard(card Card) bool { point, color := card.GetPointAndColor() return hash.Exist(slf.excludePoint, point) || hash.Exist(slf.excludeColor, color) || hash.Exist(slf.excludeCard[point], color) } // Shuffle 洗牌 -func (slf *PokerCardPile) Shuffle() { +func (slf *CardPile) Shuffle() { before := slf.Count() cards := slf.shuffleHandle(slf.Cards()) if len(cards) != before { @@ -82,22 +85,22 @@ func (slf *PokerCardPile) Shuffle() { } // Cards 获取当前牌堆的所有扑克牌 -func (slf *PokerCardPile) Cards() []PokerCard { +func (slf *CardPile) Cards() []Card { return slf.pile } // IsFree 返回牌堆是否没有扑克牌了 -func (slf *PokerCardPile) IsFree() bool { +func (slf *CardPile) IsFree() bool { return len(slf.pile) == 0 } // Count 获取牌堆剩余牌量 -func (slf *PokerCardPile) Count() int { +func (slf *CardPile) Count() int { return len(slf.pile) } // Pull 从牌堆特定位置抽出一张牌 -func (slf *PokerCardPile) Pull(index int) PokerCard { +func (slf *CardPile) Pull(index int) Card { 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)) } @@ -107,7 +110,7 @@ func (slf *PokerCardPile) Pull(index int) PokerCard { } // PullTop 从牌堆顶部抽出一张牌 -func (slf *PokerCardPile) PullTop() PokerCard { +func (slf *CardPile) PullTop() Card { if slf.IsFree() { panic("empty poker cards pile") } @@ -117,7 +120,7 @@ func (slf *PokerCardPile) PullTop() PokerCard { } // PullBottom 从牌堆底部抽出一张牌 -func (slf *PokerCardPile) PullBottom() PokerCard { +func (slf *CardPile) PullBottom() Card { if slf.IsFree() { panic("empty poker cards pile") } @@ -128,17 +131,17 @@ func (slf *PokerCardPile) PullBottom() PokerCard { } // Push 将扑克牌插入到牌堆特定位置 -func (slf *PokerCardPile) Push(index int, card PokerCard) { +func (slf *CardPile) Push(index int, card Card) { slice.Insert(&slf.pile, index, card) return } // PushTop 将扑克牌插入到牌堆顶部 -func (slf *PokerCardPile) PushTop(card PokerCard) { - slf.pile = append([]PokerCard{card}, slf.pile...) +func (slf *CardPile) PushTop(card Card) { + slf.pile = append([]Card{card}, slf.pile...) } // PushBottom 将扑克牌插入到牌堆底部 -func (slf *PokerCardPile) PushBottom(card PokerCard) { +func (slf *CardPile) PushBottom(card Card) { slf.pile = append(slf.pile, card) } diff --git a/game/poker/card_pile_example_test.go b/game/poker/card_pile_example_test.go new file mode 100644 index 0000000..e62ec97 --- /dev/null +++ b/game/poker/card_pile_example_test.go @@ -0,0 +1,17 @@ +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 Spade) (K Heart) (K Club) (K Diamond) (Q Spade) (Q Heart) (Q Club) (Q Diamond) (J Spade) (J Heart) (J Club) (J Diamond) (10 Spade) (10 Heart) (10 Club) (10 Diamond) (9 Spade) (9 Heart) (9 Club) (9 Diamond) (8 Spade) (8 Heart) (8 Club) (8 Diamond) (7 Spade) (7 Heart) (7 Club) (7 Diamond) (6 Spade) (6 Heart) (6 Club) (6 Diamond) (5 Spade) (5 Heart) (5 Club) (5 Diamond) (4 Spade) (4 Heart) (4 Club) (4 Diamond) (3 Spade) (3 Heart) (3 Club) (3 Diamond) (2 Spade) (2 Heart) (2 Club) (2 Diamond) (A Spade) (A Heart) (A Club) (A Diamond)] +} diff --git a/game/poker/card_pile_options.go b/game/poker/card_pile_options.go new file mode 100644 index 0000000..5b66428 --- /dev/null +++ b/game/poker/card_pile_options.go @@ -0,0 +1,60 @@ +package poker + +type CardPileOption func(pile *CardPile) + +// WithCardPileShuffle 通过特定的洗牌算法创建牌堆 +// - 需要保证洗牌后的牌堆剩余扑克数量与之前相同,否则将会引发 panic +func WithCardPileShuffle(shuffleHandle func(pile []Card) []Card) CardPileOption { + return func(pile *CardPile) { + if shuffleHandle == nil { + return + } + pile.shuffleHandle = shuffleHandle + } +} + +// WithCardPileExcludeColor 通过排除特定花色的方式创建牌堆 +func WithCardPileExcludeColor(colors ...Color) CardPileOption { + return func(pile *CardPile) { + if pile.excludeColor == nil { + pile.excludeColor = map[Color]struct{}{} + } + for _, color := range colors { + pile.excludeColor[color] = struct{}{} + } + } +} + +// WithCardPileExcludePoint 通过排除特定点数的方式创建牌堆 +func WithCardPileExcludePoint(points ...Point) CardPileOption { + return func(pile *CardPile) { + if pile.excludePoint == nil { + pile.excludePoint = map[Point]struct{}{} + } + for _, point := range points { + pile.excludePoint[point] = struct{}{} + } + } +} + +// WithCardPileExcludeCard 通过排除特定扑克牌的方式创建牌堆 +func WithCardPileExcludeCard(cards ...Card) CardPileOption { + return func(pile *CardPile) { + if pile.excludeCard == nil { + pile.excludeCard = map[Point]map[Color]struct{}{} + } + for _, card := range cards { + point := card.GetPoint() + cs, exist := pile.excludeCard[point] + if !exist { + cs = map[Color]struct{}{} + pile.excludeCard[point] = cs + } + if point == PointRedJoker || point == PointBlackJoker { + cs[ColorNone] = struct{}{} + } else { + cs[card.GetColor()] = struct{}{} + } + } + } +} diff --git a/game/poker/card_test.go b/game/poker/card_test.go new file mode 100644 index 0000000..124c7e5 --- /dev/null +++ b/game/poker/card_test.go @@ -0,0 +1,60 @@ +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) + }) +} diff --git a/game/poker/color.go b/game/poker/color.go new file mode 100644 index 0000000..21b84f2 --- /dev/null +++ b/game/poker/color.go @@ -0,0 +1,35 @@ +package poker + +const ( + ColorNone Color = 0 // 无花色,通常为大小王 + ColorSpade Color = 1 // 黑桃 + ColorHeart Color = 2 // 红桃 + ColorClub Color = 3 // 梅花 + ColorDiamond Color = 4 // 方片 +) + +// 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 +} diff --git a/game/poker/doc.go b/game/poker/doc.go new file mode 100644 index 0000000..0f2b731 --- /dev/null +++ b/game/poker/doc.go @@ -0,0 +1,8 @@ +// Package poker 提供了一组用于处理扑克牌游戏的函数和数据结构。该包旨在简化扑克牌游戏的开发过程,并提供一致的接口和易于使用的功能。 +// +// 主要特性: +// - 扑克牌操作:"poker"包支持处理扑克牌的各种操作,如洗牌、发牌、比较牌面大小等。您可以使用这些操作来模拟和实现各种扑克牌游戏。 +// - 扑克牌规则:该包提供了一系列函数,用于执行常见的扑克牌规则,如判断是否是同花顺、计算牌面点数等。这些函数旨在提供准确和可靠的规则判断和计算结果。 +// - 扑克牌算法:"poker"包还提供了一些算法,用于解决扑克牌游戏中的问题,如计算最佳牌型、判断是否存在顺子等。这些算法旨在提供高效和优化的解决方案。 +// - 简化接口:该包的设计目标之一是提供简化的接口,使扑克牌游戏的开发变得更加直观和易于使用。您可以轻松地创建和操作扑克牌对象,而无需处理繁琐的底层细节。 +package poker diff --git a/game/poker/func.go b/game/poker/func.go new file mode 100644 index 0000000..9731484 --- /dev/null +++ b/game/poker/func.go @@ -0,0 +1,19 @@ +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 new file mode 100644 index 0000000..4ff4b42 --- /dev/null +++ b/game/poker/hand.go @@ -0,0 +1,36 @@ +package poker + +// 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) + } +} + +// HandSingle 单牌 +func HandSingle() HandHandle { + return func(poker *Poker, cards []Card) bool { + return len(cards) == 1 + } +} + +// 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]) + } +} diff --git a/game/poker/options.go b/game/poker/options.go new file mode 100644 index 0000000..4627a5d --- /dev/null +++ b/game/poker/options.go @@ -0,0 +1,39 @@ +package poker + +import "fmt" + +type Option func(poker *Poker) + +// WithHand 通过绑定特定牌型的方式创建扑克玩法 +func WithHand(pokerHand string, handle HandHandle) Option { + return func(poker *Poker) { + if _, exist := poker.pokerHand[pokerHand]; exist { + panic(fmt.Errorf("same poker hand name: %s", pokerHand)) + } + poker.pokerHand[pokerHand] = handle + poker.pokerHandPriority = append(poker.pokerHandPriority, pokerHand) + } +} + +// WithPointValue 通过特定的扑克点数牌值创建扑克玩法 +func WithPointValue(pointValues map[Point]int) Option { + return func(poker *Poker) { + poker.pointValue = pointValues + } +} + +// WithColorValue 通过特定的扑克花色牌值创建扑克玩法 +func WithColorValue(colorValues map[Color]int) Option { + return func(poker *Poker) { + poker.colorValue = colorValues + } +} + +// WithPointSort 通过特定的扑克点数顺序创建扑克玩法 +func WithPointSort(pointSort map[Point]int) Option { + return func(poker *Poker) { + for k, v := range pointSort { + poker.pointSort[k] = v + } + } +} diff --git a/game/poker/point.go b/game/poker/point.go new file mode 100644 index 0000000..dcaa2cf --- /dev/null +++ b/game/poker/point.go @@ -0,0 +1,63 @@ +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 +} diff --git a/game/poker/poker.go b/game/poker/poker.go new file mode 100644 index 0000000..898098e --- /dev/null +++ b/game/poker/poker.go @@ -0,0 +1,90 @@ +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), + } + for _, option := range options { + option(poker) + } + if poker.pointValue == nil { + poker.pointValue = poker.pointSort + } + return poker +} + +type Poker struct { + pile *CardPile + pokerHand map[string]HandHandle + pokerHandPriority []string + pointValue map[Point]int + colorValue map[Color]int + pointSort map[Point]int +} + +// IsContinuity 检查一组扑克牌是否连续 +func (slf *Poker) IsContinuity(cards ...Card) bool { + length := len(cards) + if length == 0 { + return false + } + if length == 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 { + 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()] + } + return value +} + +// 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 + } + } + return "", false +} + +// GetPile 获取牌堆 +func (slf *Poker) GetPile() *CardPile { + return slf.pile +}