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