revert: 移除 poker 包的 matcher,改为使用 combination 包

This commit is contained in:
kercylan98 2023-07-31 10:01:10 +08:00
parent de76411726
commit 8b92921230
4 changed files with 0 additions and 482 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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
})
}
}

View File

@ -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()))
}
}