扑克牌支持

This commit is contained in:
kercylan98 2023-06-25 11:15:50 +08:00
parent 789abc065a
commit 9accaa5868
22 changed files with 543 additions and 332 deletions

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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{}{}
}
}
}

View File

@ -1,13 +0,0 @@
package builtin
import (
"fmt"
"testing"
)
func TestNewPokerCardPile(t *testing.T) {
pile := NewPokerCardPile(2)
_ = pile.PullTop()
pile.Reset()
fmt.Println(pile)
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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

View File

@ -1,5 +0,0 @@
package game
// Poker 扑克牌游戏通用玩法接口定义
type Poker interface {
}

60
game/poker/card.go Normal file
View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)]
}

View File

@ -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{}{}
}
}
}
}

60
game/poker/card_test.go Normal file
View File

@ -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)
})
}

35
game/poker/color.go Normal file
View File

@ -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
}

8
game/poker/doc.go Normal file
View File

@ -0,0 +1,8 @@
// Package poker 提供了一组用于处理扑克牌游戏的函数和数据结构。该包旨在简化扑克牌游戏的开发过程,并提供一致的接口和易于使用的功能。
//
// 主要特性:
// - 扑克牌操作:"poker"包支持处理扑克牌的各种操作,如洗牌、发牌、比较牌面大小等。您可以使用这些操作来模拟和实现各种扑克牌游戏。
// - 扑克牌规则:该包提供了一系列函数,用于执行常见的扑克牌规则,如判断是否是同花顺、计算牌面点数等。这些函数旨在提供准确和可靠的规则判断和计算结果。
// - 扑克牌算法:"poker"包还提供了一些算法,用于解决扑克牌游戏中的问题,如计算最佳牌型、判断是否存在顺子等。这些算法旨在提供高效和优化的解决方案。
// - 简化接口:该包的设计目标之一是提供简化的接口,使扑克牌游戏的开发变得更加直观和易于使用。您可以轻松地创建和操作扑克牌对象,而无需处理繁琐的底层细节。
package poker

19
game/poker/func.go Normal file
View File

@ -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
}

36
game/poker/hand.go Normal file
View File

@ -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])
}
}

39
game/poker/options.go Normal file
View File

@ -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
}
}
}

63
game/poker/point.go Normal file
View File

@ -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
}

90
game/poker/poker.go Normal file
View File

@ -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
}