495 lines
13 KiB
Go
495 lines
13 KiB
Go
![]() |
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
|
|||
|
}
|