samba/server/cacheta/poker/sortPoker.go
2025-06-04 09:51:39 +08:00

495 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package poker
import (
"fmt"
"sort"
)
type GroupType int
const (
GtUnknown GroupType = 0 // 不是组合
GtKind GroupType = 1 // 同花
GtStraight GroupType = 2 // 顺子
)
type GroupPokers struct {
Pokers []*Poker
GroupType GroupType
}
// 按顺子排序
func sortByStraight(pks []*Poker) []*Poker {
var tmpPks []*Poker
tmpPks = append(tmpPks, pks...)
sort.Slice(tmpPks, func(i, j int) bool {
if tmpPks[i].Color == tmpPks[j].Color {
return tmpPks[i].Point < tmpPks[j].Point
}
return tmpPks[i].Color < tmpPks[j].Color
})
return tmpPks
}
// 按条子排序
func sortByKind(pks []*Poker) []*Poker {
var tmpPks []*Poker
tmpPks = append(tmpPks, pks...)
sort.Slice(tmpPks, func(i, j int) bool {
if tmpPks[i].Point == tmpPks[j].Point {
return tmpPks[i].Color < tmpPks[j].Color
}
return tmpPks[i].Point < tmpPks[j].Point
})
return tmpPks
}
// 是否万能牌
func isWildPoker(wildPoker, pk *Poker) bool {
return wildPoker.ToValue() == pk.ToValue()
}
// 手牌分为万能牌和正常牌
func divideWildPokers(wildPoker *Poker, pks []*Poker) (wildPokers []*Poker, normalPokers []*Poker) {
for _, pk := range pks {
if isWildPoker(wildPoker, pk) {
wildPokers = append(wildPokers, pk)
wildPokers = sortByKind(wildPokers)
} else {
normalPokers = append(normalPokers, pk)
}
}
return
}
// 两牌是否递增, 不考虑万能牌
func isIncrement(c Color, p Point, pk *Poker) bool {
if c == pk.Color {
if p == PointK && pk.Point == PointA {
return true
}
return pk.Point-p == 1
}
return false
}
// 两牌点数是不是相同,花色不同, 不考虑万能牌
func isSamePoint(pk1, pk2 *Poker) bool {
if pk1.Color == pk2.Color || pk1.Point != pk2.Point {
return false
}
return true
}
// 是否条子,不考虑万能牌
func isKind3(pk1, pk2, pk3 *Poker) bool {
if pk1.Point != pk2.Point || pk2.Point != pk3.Point {
return false
}
if pk1.Color == pk2.Color || pk2.Color == pk3.Color || pk1.Color == pk3.Color {
return false
}
return true
}
// 查找出一个顺子组合
func findAStraightGroup(wildPokers, pks []*Poker) (group *GroupPokers, highPokes, wildPokers2 []*Poker) {
pks = sortByStraight(pks)
wildPokers2 = wildPokers
highPokes = pks
if len(pks) < 2 {
return
}
var groupPokers []*Poker
groupPokers = append(groupPokers, pks[0])
// 找顺子,不考虑万能牌
for i := 0; i < len(pks)-1; i++ {
groupPokers = groupPokers[0:0]
groupPokers = append(groupPokers, pks[i])
// pks[i]与pks[i+1] 成顺子关系
if isIncrement(pks[i].Color, pks[i].Point, pks[i+1]) {
groupPokers = append(groupPokers, pks[i+1])
if i+2 < len(pks) {
// pks[i],pks[i+1],pks[i+2] 成顺子关系
if isIncrement(pks[i+1].Color, pks[i+1].Point, pks[i+2]) {
groupPokers = append(groupPokers, pks[i+2])
group = &GroupPokers{Pokers: groupPokers, GroupType: GtStraight}
highPokes = append(pks[:i], pks[i+3:]...)
return
}
}
// 最后两个为Q K且第一个为A
if i+2 == len(pks) && isIncrement(pks[i+1].Color, pks[i+1].Point, pks[0]) {
groupPokers = append(groupPokers, pks[0])
group = &GroupPokers{Pokers: groupPokers, GroupType: GtStraight}
highPokes = pks[1:i]
return
}
}
}
// 没有万能牌,直接返回
if len(wildPokers) == 0 {
return
}
// 加上万能牌找顺子
for i := 0; i < len(pks)-1; i++ {
groupPokers = groupPokers[0:0]
groupPokers = append(groupPokers, pks[i])
// 两个顺,加一个万能牌
if isIncrement(pks[i].Color, pks[i].Point, pks[i+1]) {
groupPokers = append(groupPokers, pks[i+1])
groupPokers = append(groupPokers, wildPokers[len(wildPokers)-1])
wildPokers2 = wildPokers[:len(wildPokers2)-1]
group = &GroupPokers{Pokers: groupPokers, GroupType: GtStraight}
highPokes = append(pks[:i], pks[i+2:]...)
return
}
// 跳牌,中间放一个万能牌
if isIncrement(pks[i].Color, pks[i].Point, pks[i+1]) || isIncrement(pks[i].Color, pks[i].Point+1, pks[i+1]) {
groupPokers = append(groupPokers, wildPokers[len(wildPokers)-1])
groupPokers = append(groupPokers, pks[i+1])
wildPokers2 = wildPokers[:len(wildPokers2)-1]
group = &GroupPokers{Pokers: groupPokers, GroupType: GtStraight}
highPokes = append(pks[:i], pks[i+2:]...)
return
}
}
return
}
// 查找出一个条子组合
// 同花 1:必须是三种花色 2:除万能牌之外,不能出现点数不同 3.万能牌只能有一张或全是万能牌 4.所有牌都是万能牌,花色至少有两种
func findAKindGroup(wildPokers, pks []*Poker) (group *GroupPokers, highPokes, wildPokers2 []*Poker) {
pks = sortByKind(pks)
wildPokers2 = wildPokers
highPokes = pks
if len(pks)+len(wildPokers) < 2 {
return
}
var groupPokers []*Poker
// 找条子,不考虑万能牌
for i := 0; i < len(pks)-2; i++ {
// pks[i], pks[i+1], pks[i+2] 成条子关系
if isKind3(pks[i], pks[i+1], pks[i+2]) {
groupPokers = groupPokers[0:0]
groupPokers = append(groupPokers, pks[i])
groupPokers = append(groupPokers, pks[i+1])
groupPokers = append(groupPokers, pks[i+2])
group = &GroupPokers{Pokers: groupPokers, GroupType: GtKind}
highPokes = append(pks[:i], pks[i+3:]...)
return
}
}
// 没有万能牌,直接返回
if len(wildPokers) == 0 {
return
}
// 加上万能牌找条子
for i := 0; i < len(pks)-1; i++ {
// 两条,加一个万能牌
if isSamePoint(pks[i], pks[i+1]) {
groupPokers = groupPokers[0:0]
groupPokers = append(groupPokers, pks[i])
groupPokers = append(groupPokers, pks[i+1])
groupPokers = append(groupPokers, wildPokers[len(wildPokers)-1])
wildPokers2 = wildPokers[:len(wildPokers)-1]
group = &GroupPokers{Pokers: groupPokers, GroupType: GtKind}
highPokes = append(pks[:i], pks[i+2:]...)
return
}
}
// 组合不能出现多张万能+一张其它牌,只能全是万能牌或只有一张万能牌
//// 两张万能牌加一张散牌点数与万能牌点数相同,比如:散牌:♣5 ♣9 万能牌:♥9 ♥9 形成:♣9 ♥9 ♥9 散牌:♣5
//if len(wildPokers) > 1 && len(pks) > 0 {
// for i := 0; i < len(pks); i++ {
// if isSamePoint(wildPokers[0], pks[i]) {
// groupPokers = groupPokers[0:0]
// groupPokers = append(groupPokers, pks[i])
// groupPokers = append(groupPokers, wildPokers[len(wildPokers)-1])
// groupPokers = append(groupPokers, wildPokers[len(wildPokers)-2])
// wildPokers2 = wildPokers[:len(wildPokers)-2]
// group = &GroupPokers{Pokers: groupPokers, GroupType: GtKind}
// highPokes = append(pks[:i], pks[i+1:]...)
// return
// }
// }
//}
// 三张万能牌组成条子
if len(wildPokers) > 2 {
for i := 0; i < len(wildPokers)-2; i++ {
// 两条,加一个万能牌
if isSamePoint(wildPokers[i], wildPokers[i+1]) {
groupPokers = groupPokers[0:0]
groupPokers = append(groupPokers, wildPokers[i])
groupPokers = append(groupPokers, wildPokers[i+1])
groupPokers = append(groupPokers, wildPokers[i+2])
wildPokers2 = append(wildPokers[:i], wildPokers[i+3:]...)
group = &GroupPokers{Pokers: groupPokers, GroupType: GtKind}
return
}
}
}
return
}
// 顺子 1:万能牌最多只能有一个 2:只能同花色
func isStraight(wildPoker *Poker, pks ...*Poker) bool {
straightPks := make([]*Poker, 0)
wildCnt := 0 // 万能牌数量
color := ColorUnknown
point := PointUnknown
for _, pk := range pks {
// 万能牌只能有一个
if isWildPoker(wildPoker, pk) {
wildCnt++
if wildCnt > 1 {
return false
}
} else {
if color == ColorUnknown {
color = pk.Color
}
if point == PointUnknown {
point = pk.Point
if pk.Color != color {
return false
}
} else {
// 花色必须相同,点数必须不同
if pk.Point == point || pk.Color != color {
return false
}
}
straightPks = append(straightPks, pk)
}
}
sort.Slice(straightPks, func(i, j int) bool {
return straightPks[i].Point < straightPks[j].Point
})
if straightPks[0].Point == PointA && straightPks[len(straightPks)-1].Point == PointK {
straightPks = append(straightPks, straightPks[0])
straightPks = straightPks[1:]
}
value := straightPks[0].Point
step := 1
for {
if step == len(straightPks) {
break
}
if value+1 == straightPks[step].Point {
value = straightPks[step].Point
step++
continue
}
if value == PointK && straightPks[step].Point == PointA {
value = straightPks[step].Point
step++
continue
}
if wildCnt > 0 {
value++
wildCnt--
continue
} else {
return false
}
}
return true
}
func addPokerToStraightGroup(wildPoker, pk *Poker, group *GroupPokers) (bool, *GroupPokers) {
var tmpPokers []*Poker
tmpPokers = append(tmpPokers, pk)
tmpPokers = append(tmpPokers, group.Pokers...)
if ok := isStraight(wildPoker, tmpPokers...); ok {
group.Pokers = append(group.Pokers, pk)
return true, group
}
return false, nil
}
func addPokerToKindGroup(wildPoker, pk *Poker, group *GroupPokers) (bool, *GroupPokers) {
// 组合要么只有一个万能牌,要么全是万能牌
wildNum := 0
if isWildPoker(wildPoker, pk) {
for _, p := range group.Pokers {
if isWildPoker(wildPoker, p) {
wildNum++
}
}
if wildNum == 0 {
group.Pokers = append(group.Pokers, pk)
return true, group
}
if wildNum != len(group.Pokers) {
return false, nil
}
// 全是万能牌的组合,可以一直添加万能牌
group.Pokers = append(group.Pokers, pk)
return true, group
}
// 与组牌中不是万能牌的牌比点数,必须相同
cmp := false
for _, gp := range group.Pokers {
if !isWildPoker(wildPoker, gp) {
cmp = isSamePoint(pk, group.Pokers[0])
break
}
}
if !cmp {
return false, nil
}
colors := make(map[Color]int)
colors[pk.Color] = 1
// 组牌中除万能牌外,其它的牌的颜色最多只能出现三种
for _, p := range group.Pokers {
if !isWildPoker(wildPoker, p) {
colors[p.Color] = 1
}
}
if len(colors) > 3 {
return false, nil
}
group.Pokers = append(group.Pokers, pk)
return true, group
}
type GroupHigh struct {
groups []*GroupPokers
high []*Poker
wildPokers []*Poker
}
func PopCopy(groups []*GroupPokers, high []*Poker, wildPokers []*Poker) *GroupHigh {
gh := &GroupHigh{}
for _, g := range groups {
var gp GroupPokers
gp.GroupType = g.GroupType
gp.Pokers = append(gp.Pokers, g.Pokers...)
gh.groups = append(gh.groups, &gp)
}
gh.high = append(gh.high, high...)
gh.wildPokers = append(gh.wildPokers, wildPokers...)
return gh
}
// 回溯函数
func backtrack(wildPoker *Poker, ghs, best *GroupHigh, deep int, id int) {
id = id*10 + 1
//if id == 21 {
// fmt.Println("")
//}
//fmt.Println(fmt.Sprintf("id:%v %s", id, DebugGroupsHigh(ghs.groups, ghs.high, ghs.wildPokers)))
group, high, wild := findAStraightGroup(ghs.wildPokers, ghs.high)
if group != nil {
groups := append(ghs.groups, group)
gh1 := PopCopy(groups, high, wild)
backtrack(wildPoker, gh1, best, deep+1, id)
}
id++
group, high, wild = findAKindGroup(ghs.wildPokers, ghs.high)
if group != nil {
groups := append(ghs.groups, group)
gh1 := PopCopy(groups, high, wild)
backtrack(wildPoker, gh1, best, deep+1, id)
}
var newHigh []*Poker
for i := 0; i < len(high); i++ {
add := false
// 尝试将当前牌加入到一个现有组合中
for k, gp := range ghs.groups {
var aGroup *GroupPokers
if gp.GroupType == GtKind {
_, aGroup = addPokerToKindGroup(wildPoker, high[i], gp)
}
if gp.GroupType == GtStraight {
_, aGroup = addPokerToStraightGroup(wildPoker, high[i], gp)
}
if aGroup != nil {
ghs.groups[k] = aGroup
tmpHigh := append(high[:i], high[i+1:]...)
gh1 := PopCopy(ghs.groups, tmpHigh, wild)
id++
backtrack(wildPoker, gh1, best, deep+1, id)
add = true
break
}
}
if !add {
newHigh = append(newHigh, high[i])
}
}
ghs.high = newHigh
ghs.wildPokers = wild
var newWild []*Poker
for i := 0; i < len(wild); i++ {
add := false
// 尝试将万能牌加入到一个现有组合中
for k, gp := range ghs.groups {
var aGroup *GroupPokers
if gp.GroupType == GtKind {
_, aGroup = addPokerToKindGroup(wildPoker, wild[i], gp)
}
if gp.GroupType == GtStraight {
_, aGroup = addPokerToStraightGroup(wildPoker, wild[i], gp)
}
if aGroup != nil {
ghs.groups[k] = aGroup
tmpWild := append(wild[:i], wild[i+1:]...)
gh1 := PopCopy(ghs.groups, high, tmpWild)
id++
backtrack(wildPoker, gh1, best, deep+1, id)
add = true
break
}
}
if !add {
newWild = append(newWild, wild[i])
}
}
//ghs.wildPokers = newWild
// 如果所有牌都已处理完毕,则评估当前解的质量
if len(ghs.high)+len(ghs.wildPokers) < len(best.high)+len(best.wildPokers) {
best.high = ghs.high
best.groups = ghs.groups
best.wildPokers = ghs.wildPokers
//fmt.Println(debugGroupsHigh(best))
}
}
func DebugGroupsHigh(gs []*GroupPokers, highs []*Poker, wilds []*Poker) string {
str := ""
for _, g := range gs {
str += fmt.Sprintf("组合:%s ", PokersToString(g.Pokers))
}
str += fmt.Sprintf("散牌:%s ", PokersToString(highs))
if len(wilds) > 0 {
str += fmt.Sprintf("万能牌:%s ", PokersToString(wilds))
}
return str
}
func FindBestGroupsAndRemains(wildPoker *Poker, pks []*Poker) ([]*GroupPokers, []*Poker) {
wildPokers, normalPokers := divideWildPokers(wildPoker, pks)
ghs := PopCopy(nil, normalPokers, wildPokers)
best := PopCopy(nil, normalPokers, wildPokers)
//fmt.Println(debugGroupsHigh(ghs))
// 开始回溯
backtrack(wildPoker, ghs, best, 0, 0)
//fmt.Println(debugGroupsHigh(ghs))
best.high = append(best.high, best.wildPokers...)
best.wildPokers = best.wildPokers[:0]
return best.groups, best.high
}