114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
// Package ddz 实现斗地主的完整规则:发牌、牌型判定、比较、候选出牌生成与对局状态机
|
||
package ddz
|
||
|
||
import (
|
||
"math/rand"
|
||
"sort"
|
||
)
|
||
|
||
// 牌的编码约定:
|
||
// 0-51 普通牌:点数 rank = 编号/4 + 3(3,4,...,10,J=11,Q=12,K=13,A=14,2=15),花色 = 编号%4
|
||
// 52 小王(rank=16)
|
||
// 53 大王(rank=17)
|
||
const (
|
||
SmallJoker = 52 // 小王编号
|
||
BigJoker = 53 // 大王编号
|
||
)
|
||
|
||
// Rank 返回牌的点数(3..15,小王16,大王17)
|
||
func Rank(card int) int {
|
||
if card == SmallJoker {
|
||
return 16
|
||
}
|
||
if card == BigJoker {
|
||
return 17
|
||
}
|
||
return card/4 + 3
|
||
}
|
||
|
||
// rankNames 点数到显示名的映射(下标 = rank)
|
||
var rankNames = map[int]string{
|
||
3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9", 10: "10",
|
||
11: "J", 12: "Q", 13: "K", 14: "A", 15: "2", 16: "小王", 17: "大王",
|
||
}
|
||
|
||
// RankName 点数的显示名称
|
||
func RankName(rank int) string { return rankNames[rank] }
|
||
|
||
// CardName 牌的显示名称(忽略花色,斗地主只关心点数)
|
||
func CardName(card int) string { return RankName(Rank(card)) }
|
||
|
||
// CardsName 一组牌的显示名称(如 "33 55",按点数降序)
|
||
func CardsName(cards []int) string {
|
||
sorted := append([]int{}, cards...)
|
||
sort.Slice(sorted, func(i, j int) bool { return Rank(sorted[i]) > Rank(sorted[j]) })
|
||
s := ""
|
||
for i, c := range sorted {
|
||
if i > 0 {
|
||
s += " "
|
||
}
|
||
s += CardName(c)
|
||
}
|
||
return s
|
||
}
|
||
|
||
// SortDesc 手牌按点数降序排序(前端展示与出牌习惯)
|
||
func SortDesc(cards []int) {
|
||
sort.Slice(cards, func(i, j int) bool {
|
||
ri, rj := Rank(cards[i]), Rank(cards[j])
|
||
if ri != rj {
|
||
return ri > rj
|
||
}
|
||
return cards[i] > cards[j]
|
||
})
|
||
}
|
||
|
||
// Deal 洗牌并发牌:返回三家手牌(各17张)与3张底牌
|
||
func Deal() (hands [3][]int, bottom []int) {
|
||
deck := make([]int, 54)
|
||
for i := range deck {
|
||
deck[i] = i
|
||
}
|
||
rand.Shuffle(54, func(i, j int) { deck[i], deck[j] = deck[j], deck[i] })
|
||
for i := 0; i < 3; i++ {
|
||
hands[i] = append([]int{}, deck[i*17:(i+1)*17]...)
|
||
SortDesc(hands[i])
|
||
}
|
||
bottom = append([]int{}, deck[51:]...)
|
||
return hands, bottom
|
||
}
|
||
|
||
// rankCount 统计一组牌各点数的张数
|
||
func rankCount(cards []int) map[int]int {
|
||
m := map[int]int{}
|
||
for _, c := range cards {
|
||
m[Rank(c)]++
|
||
}
|
||
return m
|
||
}
|
||
|
||
// removeCards 从手牌中移除指定牌,返回新手牌(不修改原切片);牌不存在返回 nil
|
||
func removeCards(hand, cards []int) []int {
|
||
used := map[int]bool{}
|
||
for _, c := range cards {
|
||
used[c] = true
|
||
}
|
||
// 校验 cards 都在手牌中
|
||
inHand := map[int]bool{}
|
||
for _, c := range hand {
|
||
inHand[c] = true
|
||
}
|
||
for _, c := range cards {
|
||
if !inHand[c] {
|
||
return nil
|
||
}
|
||
}
|
||
rest := make([]int, 0, len(hand)-len(cards))
|
||
for _, c := range hand {
|
||
if !used[c] {
|
||
rest = append(rest, c)
|
||
}
|
||
}
|
||
return rest
|
||
}
|