✨ 扑克牌支持
This commit is contained in:
parent
789abc065a
commit
9accaa5868
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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{}{}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewPokerCardPile(t *testing.T) {
|
||||
pile := NewPokerCardPile(2)
|
||||
_ = pile.PullTop()
|
||||
pile.Reset()
|
||||
fmt.Println(pile)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
12
game/doc.go
12
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
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package game
|
||||
|
||||
// Poker 扑克牌游戏通用玩法接口定义
|
||||
type Poker interface {
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)]
|
||||
}
|
|
@ -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{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Package poker 提供了一组用于处理扑克牌游戏的函数和数据结构。该包旨在简化扑克牌游戏的开发过程,并提供一致的接口和易于使用的功能。
|
||||
//
|
||||
// 主要特性:
|
||||
// - 扑克牌操作:"poker"包支持处理扑克牌的各种操作,如洗牌、发牌、比较牌面大小等。您可以使用这些操作来模拟和实现各种扑克牌游戏。
|
||||
// - 扑克牌规则:该包提供了一系列函数,用于执行常见的扑克牌规则,如判断是否是同花顺、计算牌面点数等。这些函数旨在提供准确和可靠的规则判断和计算结果。
|
||||
// - 扑克牌算法:"poker"包还提供了一些算法,用于解决扑克牌游戏中的问题,如计算最佳牌型、判断是否存在顺子等。这些算法旨在提供高效和优化的解决方案。
|
||||
// - 简化接口:该包的设计目标之一是提供简化的接口,使扑克牌游戏的开发变得更加直观和易于使用。您可以轻松地创建和操作扑克牌对象,而无需处理繁琐的底层细节。
|
||||
package poker
|
|
@ -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
|
||||
}
|
|
@ -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])
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue