revert: 移除 poker 包的 matcher,改为使用 combination 包
This commit is contained in:
parent
de76411726
commit
8b92921230
|
@ -1,52 +0,0 @@
|
||||||
package poker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewMatcher 创建一个新的匹配器
|
|
||||||
// - evaluate: 用于评估一组扑克牌的分值,分值最高的组合将被选中
|
|
||||||
func NewMatcher[P, C generic.Number, T Card[P, C]]() *Matcher[P, C, T] {
|
|
||||||
matcher := &Matcher[P, C, T]{
|
|
||||||
filter: map[string]*MatcherFilter[P, C, T]{},
|
|
||||||
}
|
|
||||||
return matcher
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matcher 匹配器
|
|
||||||
// - 用于匹配扑克牌型,筛选分组等
|
|
||||||
type Matcher[P, C generic.Number, T Card[P, C]] struct {
|
|
||||||
filter map[string]*MatcherFilter[P, C, T]
|
|
||||||
sort []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegType 注册一个新的牌型
|
|
||||||
// - name: 牌型名称
|
|
||||||
// - evaluate: 用于评估一组扑克牌的分值,分值最高的组合将被选中
|
|
||||||
// - options: 牌型选项
|
|
||||||
func (slf *Matcher[P, C, T]) RegType(name string, evaluate func([]T) int64, options ...MatcherOption[P, C, T]) *Matcher[P, C, T] {
|
|
||||||
if hash.Exist(slf.filter, name) {
|
|
||||||
panic("exist of the same type")
|
|
||||||
}
|
|
||||||
filter := &MatcherFilter[P, C, T]{
|
|
||||||
evaluate: evaluate,
|
|
||||||
}
|
|
||||||
for _, option := range options {
|
|
||||||
option(filter)
|
|
||||||
}
|
|
||||||
slf.filter[name] = filter
|
|
||||||
slf.sort = append(slf.sort, name)
|
|
||||||
return slf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group 将一组扑克牌按照匹配器的规则分组,并返回最佳组合及其牌型名称
|
|
||||||
func (slf *Matcher[P, C, T]) Group(cards []T) (name string, result []T) {
|
|
||||||
for _, n := range slf.sort {
|
|
||||||
result = slf.filter[n].group(cards)
|
|
||||||
if len(result) > 0 {
|
|
||||||
return n, result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package poker
|
|
||||||
|
|
||||||
import "github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
|
|
||||||
type MatcherFilter[P, C generic.Number, T Card[P, C]] struct {
|
|
||||||
evaluate func([]T) int64
|
|
||||||
handles []func(cards []T) [][]T
|
|
||||||
asc bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *MatcherFilter[P, C, T]) AddHandle(handle func(cards []T) [][]T) {
|
|
||||||
slf.handles = append(slf.handles, handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *MatcherFilter[P, C, T]) group(cards []T) []T {
|
|
||||||
var bestCombination = cards
|
|
||||||
|
|
||||||
for _, handle := range slf.handles {
|
|
||||||
var bestScore int64
|
|
||||||
filteredCombinations := handle(bestCombination)
|
|
||||||
if len(filteredCombinations) == 0 {
|
|
||||||
return []T{}
|
|
||||||
}
|
|
||||||
for _, combination := range filteredCombinations {
|
|
||||||
score := slf.evaluate(combination)
|
|
||||||
if slf.asc {
|
|
||||||
if score < bestScore || bestCombination == nil {
|
|
||||||
bestCombination = combination
|
|
||||||
bestScore = score
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if score > bestScore || bestCombination == nil {
|
|
||||||
bestCombination = combination
|
|
||||||
bestScore = score
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestCombination
|
|
||||||
}
|
|
|
@ -1,271 +0,0 @@
|
||||||
package poker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MatcherOption 匹配器选项
|
|
||||||
type MatcherOption[P, C generic.Number, T Card[P, C]] func(matcher *MatcherFilter[P, C, T])
|
|
||||||
|
|
||||||
// WithMatcherScoreAsc 通过升序评估分数创建匹配器
|
|
||||||
// - 用于评估一组扑克牌的分值,分值最低的组合将被选中
|
|
||||||
// - 默认为分数最高的组合将被选中
|
|
||||||
func WithMatcherScoreAsc[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.asc = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherLeastLength 通过匹配最小长度的扑克牌创建匹配器
|
|
||||||
// - length: 牌型的长度,表示需要匹配的扑克牌最小数量
|
|
||||||
func WithMatcherLeastLength[P, C generic.Number, T Card[P, C]](length int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
combinations = slice.LimitedCombinations(cards, length, len(cards))
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherLength 通过匹配长度的扑克牌创建匹配器
|
|
||||||
// - length: 牌型的长度,表示需要匹配的扑克牌数量
|
|
||||||
func WithMatcherLength[P, C generic.Number, T Card[P, C]](length int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
combinations = slice.LimitedCombinations(cards, length, length)
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherContinuity 通过匹配连续的扑克牌创建匹配器
|
|
||||||
func WithMatcherContinuity[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
n := len(cards)
|
|
||||||
|
|
||||||
if n <= 0 {
|
|
||||||
return combinations
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对扑克牌按点数进行排序
|
|
||||||
sort.Slice(cards, func(i, j int) bool {
|
|
||||||
return cards[i].GetPoint() < cards[j].GetPoint()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 查找连续的牌型组合
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
combination := []T{cards[i]}
|
|
||||||
for j := i + 1; j < n; j++ {
|
|
||||||
if cards[j].GetPoint()-combination[len(combination)-1].GetPoint() == 1 {
|
|
||||||
combination = append(combination, cards[j])
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(combination) >= 2 {
|
|
||||||
combinations = append(combinations, combination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherContinuityPointOrder 通过匹配连续的扑克牌创建匹配器,与 WithMatcherContinuity 不同的是,该选项将按照自定义的点数顺序进行匹配
|
|
||||||
func WithMatcherContinuityPointOrder[P, C generic.Number, T Card[P, C]](order map[P]int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
var getOrder = func(card T) P {
|
|
||||||
if v, ok := order[card.GetPoint()]; ok {
|
|
||||||
return P(v)
|
|
||||||
}
|
|
||||||
return card.GetPoint()
|
|
||||||
}
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
n := len(cards)
|
|
||||||
|
|
||||||
if n <= 0 {
|
|
||||||
return combinations
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对扑克牌按点数进行排序
|
|
||||||
sort.Slice(cards, func(i, j int) bool {
|
|
||||||
return getOrder(cards[i]) < getOrder(cards[j])
|
|
||||||
})
|
|
||||||
|
|
||||||
// 查找连续的牌型组合
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
combination := []T{cards[i]}
|
|
||||||
for j := i + 1; j < n; j++ {
|
|
||||||
if getOrder(cards[j])-getOrder(combination[len(combination)-1]) == 1 {
|
|
||||||
combination = append(combination, cards[j])
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(combination) >= 2 {
|
|
||||||
combinations = append(combinations, combination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherFlush 通过匹配同花的扑克牌创建匹配器
|
|
||||||
func WithMatcherFlush[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
|
|
||||||
groups := GroupByColor[P, C, T](cards...)
|
|
||||||
for _, group := range groups {
|
|
||||||
combinations = append(combinations, slice.Combinations(group)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherTie 通过匹配相同点数的扑克牌创建匹配器
|
|
||||||
func WithMatcherTie[P, C generic.Number, T Card[P, C]]() MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
groups := GroupByPoint[P, C, T](cards...)
|
|
||||||
for _, group := range groups {
|
|
||||||
for _, ts := range slice.Combinations(group) {
|
|
||||||
combinations = append(combinations, ts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherTieCount 通过匹配相同点数的特定数量的扑克牌创建匹配器
|
|
||||||
// - count: 牌型中相同点数的牌的数量
|
|
||||||
func WithMatcherTieCount[P, C generic.Number, T Card[P, C]](count int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
groups := GroupByPoint[P, C, T](cards...)
|
|
||||||
for _, group := range groups {
|
|
||||||
if len(group) < count {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, ts := range slice.Combinations(group) {
|
|
||||||
if len(ts) == count {
|
|
||||||
combinations = append(combinations, ts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherTieCountNum 通过匹配相同点数的特定数量的扑克牌创建匹配器
|
|
||||||
// - count: 牌型中相同点数的牌的数量
|
|
||||||
// - num: 牌型中相同点数的牌的数量
|
|
||||||
func WithMatcherTieCountNum[P, C generic.Number, T Card[P, C]](count, num int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
cs := slice.LimitedCombinations(cards, count*num, count*num)
|
|
||||||
var pointCount = make(map[P]int)
|
|
||||||
for _, group := range cs {
|
|
||||||
var ok = false
|
|
||||||
for _, t := range group {
|
|
||||||
pointCount[t.GetPoint()]++
|
|
||||||
if len(pointCount) == 2 {
|
|
||||||
var matchCount = true
|
|
||||||
for _, n := range pointCount {
|
|
||||||
if n != num {
|
|
||||||
matchCount = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if matchCount {
|
|
||||||
ok = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
combinations = append(combinations, group)
|
|
||||||
}
|
|
||||||
for point := range pointCount {
|
|
||||||
delete(pointCount, point)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherNCarryM 通过匹配N带相同点数M的扑克牌创建匹配器
|
|
||||||
// - n: 需要匹配的主牌数量
|
|
||||||
// - m: 需要匹配的附加牌数量
|
|
||||||
func WithMatcherNCarryM[P, C generic.Number, T Card[P, C]](n, m int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
groups := GroupByPoint[P, C, T](cards...)
|
|
||||||
for _, group := range groups {
|
|
||||||
if len(group) != n {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ms := slice.Combinations(slice.SubWithCheck(cards, group, func(a, b T) bool { return reflect.DeepEqual(a, b) }))
|
|
||||||
for i := 0; i < len(ms); i++ {
|
|
||||||
ts := GroupByPoint[P, C, T](ms[i]...)
|
|
||||||
for _, cs := range ts {
|
|
||||||
if len(cs) == m {
|
|
||||||
combinations = append(combinations, slice.Merge(group, cs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMatcherNCarryMSingle 通过匹配N带M的扑克牌创建匹配器
|
|
||||||
// - n: 需要匹配的主牌数量
|
|
||||||
// - m: 需要匹配的附加牌数量
|
|
||||||
func WithMatcherNCarryMSingle[P, C generic.Number, T Card[P, C]](n, m int) MatcherOption[P, C, T] {
|
|
||||||
return func(matcher *MatcherFilter[P, C, T]) {
|
|
||||||
matcher.AddHandle(func(cards []T) [][]T {
|
|
||||||
var combinations [][]T
|
|
||||||
groups := GroupByPoint[P, C, T](cards...)
|
|
||||||
for _, group := range groups {
|
|
||||||
if len(group) != n {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ms := slice.Combinations(slice.SubWithCheck(cards, group, func(a, b T) bool { return reflect.DeepEqual(a, b) }))
|
|
||||||
for i := 0; i < len(ms); i++ {
|
|
||||||
ts := ms[i]
|
|
||||||
if len(ts) == m {
|
|
||||||
combinations = append(combinations, slice.Merge(group, ts))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(combinations) > 0 {
|
|
||||||
fmt.Println(len(combinations))
|
|
||||||
}
|
|
||||||
return combinations
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
package poker_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/kercylan98/minotaur/game/poker"
|
|
||||||
"github.com/kercylan98/minotaur/utils/generic"
|
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Card[P, C generic.Number] struct {
|
|
||||||
guid int64
|
|
||||||
point P
|
|
||||||
color C
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Card[P, C]) GetGuid() int64 {
|
|
||||||
return slf.guid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Card[P, C]) GetPoint() P {
|
|
||||||
return slf.point
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *Card[P, C]) GetColor() C {
|
|
||||||
return slf.color
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatcher_Group(t *testing.T) {
|
|
||||||
|
|
||||||
evaluate := func(cards []*Card[int, int]) int64 {
|
|
||||||
score := int64(0)
|
|
||||||
for _, card := range cards {
|
|
||||||
if card.point == 1 {
|
|
||||||
score += 14
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
score += int64(card.GetPoint())
|
|
||||||
}
|
|
||||||
return score
|
|
||||||
}
|
|
||||||
|
|
||||||
matcher := poker.NewMatcher[int, int, *Card[int, int]]()
|
|
||||||
//matcher.RegType("三条", evaluate,
|
|
||||||
// poker.WithMatcherNCarryMSingle[*Card](3, 2))
|
|
||||||
matcher.RegType("皇家同花顺", evaluate,
|
|
||||||
poker.WithMatcherFlush[int, int, *Card[int, int]](),
|
|
||||||
poker.WithMatcherContinuityPointOrder[int, int, *Card[int, int]](map[int]int{1: 14}),
|
|
||||||
poker.WithMatcherLength[int, int, *Card[int, int]](5),
|
|
||||||
).RegType("同花顺", evaluate,
|
|
||||||
poker.WithMatcherFlush[int, int, *Card[int, int]](),
|
|
||||||
poker.WithMatcherContinuityPointOrder[int, int, *Card[int, int]](map[int]int{1: 14}),
|
|
||||||
poker.WithMatcherLeastLength[int, int, *Card[int, int]](3),
|
|
||||||
).RegType("四条", evaluate,
|
|
||||||
poker.WithMatcherTieCount[int, int, *Card[int, int]](4),
|
|
||||||
).RegType("葫芦", evaluate,
|
|
||||||
poker.WithMatcherNCarryM[int, int, *Card[int, int]](3, 2),
|
|
||||||
).RegType("顺子", evaluate,
|
|
||||||
poker.WithMatcherContinuityPointOrder[int, int, *Card[int, int]](map[int]int{1: 14}),
|
|
||||||
poker.WithMatcherLength[int, int, *Card[int, int]](5),
|
|
||||||
).RegType("三条", evaluate,
|
|
||||||
poker.WithMatcherNCarryMSingle[int, int, *Card[int, int]](3, 2),
|
|
||||||
).RegType("两对", evaluate,
|
|
||||||
poker.WithMatcherTieCountNum[int, int, *Card[int, int]](2, 2),
|
|
||||||
).RegType("一对", evaluate,
|
|
||||||
poker.WithMatcherTieCount[int, int, *Card[int, int]](2),
|
|
||||||
).RegType("高牌", evaluate,
|
|
||||||
poker.WithMatcherTieCount[int, int, *Card[int, int]](1),
|
|
||||||
)
|
|
||||||
|
|
||||||
var pub = []*Card[int, int]{
|
|
||||||
{point: 4, color: 3},
|
|
||||||
{point: 5, color: 2},
|
|
||||||
{point: 6, color: 1},
|
|
||||||
{point: 6, color: 2},
|
|
||||||
{point: 13, color: 2},
|
|
||||||
}
|
|
||||||
|
|
||||||
var pri = []*Card[int, int]{
|
|
||||||
{point: 1, color: 1},
|
|
||||||
{point: 1, color: 2},
|
|
||||||
{point: 4, color: 3},
|
|
||||||
{point: 5, color: 4},
|
|
||||||
}
|
|
||||||
|
|
||||||
var start = time.Now()
|
|
||||||
var usePub, usePri = slice.LimitedCombinations(pub, 3, 3), slice.LimitedCombinations(pri, 2, 2)
|
|
||||||
|
|
||||||
var topResult []*Card[int, int]
|
|
||||||
var topScore int64
|
|
||||||
var topName string
|
|
||||||
var source []*Card[int, int]
|
|
||||||
for _, handCards := range usePri {
|
|
||||||
for _, pubCards := range usePub {
|
|
||||||
cards := append(handCards, pubCards...)
|
|
||||||
name, result := matcher.Group(cards)
|
|
||||||
score := evaluate(result)
|
|
||||||
if score > topScore || topResult == nil {
|
|
||||||
topScore = score
|
|
||||||
topResult = result
|
|
||||||
topName = name
|
|
||||||
source = cards
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("time:", time.Since(start))
|
|
||||||
fmt.Println("result:", topName)
|
|
||||||
for _, card := range topResult {
|
|
||||||
fmt.Println(fmt.Sprintf("Point: %d Color: %d", card.GetPoint(), card.GetColor()))
|
|
||||||
}
|
|
||||||
fmt.Println("source:", topScore)
|
|
||||||
for _, card := range source {
|
|
||||||
fmt.Println(fmt.Sprintf("Point: %d Color: %d", card.GetPoint(), card.GetColor()))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue