✨ 扑克牌堆实现
This commit is contained in:
parent
f414ffe28c
commit
52d707486a
|
@ -0,0 +1,12 @@
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
func NewPoker() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type Poker struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Poker) Generate() {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
|
"github.com/kercylan98/minotaur/utils/slice"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPokerCardPile 返回一个新的牌堆,其中 size 表示了该牌堆由多少副牌组成
|
||||||
|
// - 在不洗牌的情况下,默认牌堆顶部到底部为从大到小排列
|
||||||
|
func NewPokerCardPile(size int, options ...PokerCardPileOption) *PokerCardPile {
|
||||||
|
pile := &PokerCardPile{
|
||||||
|
size: size,
|
||||||
|
pile: make([]PokerCard, 0, size*54),
|
||||||
|
}
|
||||||
|
pile.shuffleHandle = func(cards []PokerCard) []PokerCard {
|
||||||
|
sort.Slice(cards, func(i, j int) bool {
|
||||||
|
return random.Float64() >= 0.5
|
||||||
|
})
|
||||||
|
return cards
|
||||||
|
}
|
||||||
|
for _, option := range options {
|
||||||
|
option(pile)
|
||||||
|
}
|
||||||
|
pile.Reset()
|
||||||
|
return pile
|
||||||
|
}
|
||||||
|
|
||||||
|
// PokerCardPile 扑克牌堆
|
||||||
|
type PokerCardPile struct {
|
||||||
|
pile []PokerCard
|
||||||
|
size int
|
||||||
|
shuffleHandle func(cards []PokerCard) []PokerCard
|
||||||
|
excludeColor map[PokerColor]struct{}
|
||||||
|
excludePoint map[PokerPoint]struct{}
|
||||||
|
excludeCard map[PokerPoint]map[PokerColor]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset 重置牌堆的扑克牌数量及顺序
|
||||||
|
func (slf *PokerCardPile) Reset() {
|
||||||
|
var cards = make([]PokerCard, 0, 54)
|
||||||
|
if !slf.IsExclude(PokerPointRedJoker, PokerColorNone) {
|
||||||
|
cards = append(cards, NewPokerCard(PokerPointRedJoker, PokerColorNone))
|
||||||
|
}
|
||||||
|
if !slf.IsExclude(PokerPointBlackJoker, PokerColorNone) {
|
||||||
|
cards = append(cards, NewPokerCard(PokerPointBlackJoker, PokerColorNone))
|
||||||
|
}
|
||||||
|
for point := PokerPointK; point >= PokerPointA; point-- {
|
||||||
|
for color := PokerColorSpade; color <= PokerColorDiamond; color++ {
|
||||||
|
if !slf.IsExclude(point, color) {
|
||||||
|
cards = append(cards, NewPokerCard(point, color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slf.pile = slf.pile[0:0]
|
||||||
|
for i := 0; i < slf.size; i++ {
|
||||||
|
slf.pile = append(slf.pile, cards...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExclude 检查特定点数和花色是否被排除在外
|
||||||
|
func (slf *PokerCardPile) IsExclude(point PokerPoint, color PokerColor) bool {
|
||||||
|
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 {
|
||||||
|
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() {
|
||||||
|
before := slf.Count()
|
||||||
|
cards := slf.shuffleHandle(slf.Cards())
|
||||||
|
if len(cards) != before {
|
||||||
|
panic("the count after shuffling does not match the count before shuffling")
|
||||||
|
}
|
||||||
|
slf.pile = cards
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cards 获取当前牌堆的所有扑克牌
|
||||||
|
func (slf *PokerCardPile) Cards() []PokerCard {
|
||||||
|
return slf.pile
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFree 返回牌堆是否没有扑克牌了
|
||||||
|
func (slf *PokerCardPile) IsFree() bool {
|
||||||
|
return len(slf.pile) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count 获取牌堆剩余牌量
|
||||||
|
func (slf *PokerCardPile) Count() int {
|
||||||
|
return len(slf.pile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull 从牌堆特定位置抽出一张牌
|
||||||
|
func (slf *PokerCardPile) Pull(index int) PokerCard {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
pc := slf.pile[index]
|
||||||
|
slice.Del(&slf.pile, index)
|
||||||
|
return pc
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullTop 从牌堆顶部抽出一张牌
|
||||||
|
func (slf *PokerCardPile) PullTop() PokerCard {
|
||||||
|
if slf.IsFree() {
|
||||||
|
panic("empty poker cards pile")
|
||||||
|
}
|
||||||
|
pc := slf.pile[0]
|
||||||
|
slice.Del(&slf.pile, 0)
|
||||||
|
return pc
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullBottom 从牌堆底部抽出一张牌
|
||||||
|
func (slf *PokerCardPile) PullBottom() PokerCard {
|
||||||
|
if slf.IsFree() {
|
||||||
|
panic("empty poker cards pile")
|
||||||
|
}
|
||||||
|
i := len(slf.pile) - 1
|
||||||
|
pc := slf.pile[i]
|
||||||
|
slice.Del(&slf.pile, i)
|
||||||
|
return pc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push 将扑克牌插入到牌堆特定位置
|
||||||
|
func (slf *PokerCardPile) Push(index int, card PokerCard) {
|
||||||
|
slice.Insert(&slf.pile, index, card)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushTop 将扑克牌插入到牌堆顶部
|
||||||
|
func (slf *PokerCardPile) PushTop(card PokerCard) {
|
||||||
|
slf.pile = append([]PokerCard{card}, slf.pile...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBottom 将扑克牌插入到牌堆底部
|
||||||
|
func (slf *PokerCardPile) PushBottom(card PokerCard) {
|
||||||
|
slf.pile = append(slf.pile, card)
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
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{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package builtin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewPokerCardPile(t *testing.T) {
|
||||||
|
pile := NewPokerCardPile(2)
|
||||||
|
_ = pile.PullTop()
|
||||||
|
pile.Reset()
|
||||||
|
fmt.Println(pile)
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
// Poker 扑克牌游戏通用玩法接口定义
|
||||||
|
type Poker interface {
|
||||||
|
}
|
|
@ -137,7 +137,6 @@ func PointToPos[V generic.SignedNumber](width V, xy Point[V]) V {
|
||||||
|
|
||||||
// PosToCoordinate 通过宽度将一个二维数组的顺序位置转换为xy坐标
|
// PosToCoordinate 通过宽度将一个二维数组的顺序位置转换为xy坐标
|
||||||
func PosToCoordinate[V generic.SignedNumber](width, pos V) (x, y V) {
|
func PosToCoordinate[V generic.SignedNumber](width, pos V) (x, y V) {
|
||||||
|
|
||||||
x = V(math.Mod(float64(pos), float64(width)))
|
x = V(math.Mod(float64(pos), float64(width)))
|
||||||
y = pos / width
|
y = pos / width
|
||||||
return x, y
|
return x, y
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package hash
|
||||||
|
|
||||||
|
// Exist 检查特定 key 是否存在
|
||||||
|
func Exist[K comparable, V any](m map[K]V, key K) bool {
|
||||||
|
_, exist := m[key]
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllExist 检查多个 key 是否存在
|
||||||
|
func AllExist[K comparable, V any](m map[K]V, keys ...K) bool {
|
||||||
|
for key := range m {
|
||||||
|
if _, exist := m[key]; !exist {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -109,3 +109,15 @@ func Clamp[V generic.Number](value, min, max V) V {
|
||||||
func Tolerance[V generic.Number](value1, value2, tolerance V) bool {
|
func Tolerance[V generic.Number](value1, value2, tolerance V) bool {
|
||||||
return V(math.Abs(float64(value1-value2))) <= tolerance
|
return V(math.Abs(float64(value1-value2))) <= tolerance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge 通过一个参考值合并两个数字
|
||||||
|
func Merge[V generic.SignedNumber](refer, a, b V) V {
|
||||||
|
return b*refer + a
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnMerge 通过一个参考值取消合并的两个数字
|
||||||
|
func UnMerge[V generic.SignedNumber](refer, num V) (a, b V) {
|
||||||
|
a = V(math.Mod(float64(num), float64(refer)))
|
||||||
|
b = num / refer
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue