feat: arrangement.Engine 新增更多的辅助函数
This commit is contained in:
parent
84f36eaaba
commit
822ffc7041
|
@ -8,9 +8,11 @@ import (
|
|||
// NewArrangement 创建一个新的编排
|
||||
func NewArrangement[ID comparable, AreaInfo any](options ...Option[ID, AreaInfo]) *Arrangement[ID, AreaInfo] {
|
||||
arrangement := &Arrangement[ID, AreaInfo]{
|
||||
items: map[ID]Item[ID]{},
|
||||
fixed: map[ID]ItemFixedAreaHandle[AreaInfo]{},
|
||||
priority: map[ID][]ItemPriorityHandle[ID, AreaInfo]{},
|
||||
items: map[ID]Item[ID]{},
|
||||
fixed: map[ID]ItemFixedAreaHandle[AreaInfo]{},
|
||||
priority: map[ID][]ItemPriorityHandle[ID, AreaInfo]{},
|
||||
itemNotAllow: map[ID][]ItemNotAllowVerifyHandle[ID, AreaInfo]{},
|
||||
threshold: 10,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(arrangement)
|
||||
|
@ -23,11 +25,12 @@ func NewArrangement[ID comparable, AreaInfo any](options ...Option[ID, AreaInfo]
|
|||
// - 目前我能想到的用途只有我的过往经历:排课
|
||||
// - 如果是在游戏领域,或许适用于多人小队匹配编排等类似情况
|
||||
type Arrangement[ID comparable, AreaInfo any] struct {
|
||||
areas []*Area[ID, AreaInfo] // 所有的编排区域
|
||||
items map[ID]Item[ID] // 所有的成员
|
||||
fixed map[ID]ItemFixedAreaHandle[AreaInfo] // 固定编排区域的成员
|
||||
priority map[ID][]ItemPriorityHandle[ID, AreaInfo] // 成员的优先级函数
|
||||
threshold int // 重试次数阈值
|
||||
areas []*Area[ID, AreaInfo] // 所有的编排区域
|
||||
items map[ID]Item[ID] // 所有的成员
|
||||
fixed map[ID]ItemFixedAreaHandle[AreaInfo] // 固定编排区域的成员
|
||||
priority map[ID][]ItemPriorityHandle[ID, AreaInfo] // 成员的优先级函数
|
||||
itemNotAllow map[ID][]ItemNotAllowVerifyHandle[ID, AreaInfo] // 成员的不允的编排区域检测函数
|
||||
threshold int // 重试次数阈值
|
||||
|
||||
constraintHandles []ConstraintHandle[ID, AreaInfo]
|
||||
conflictHandles []ConflictHandle[ID, AreaInfo]
|
||||
|
@ -116,7 +119,7 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
|||
}
|
||||
|
||||
if area == nil { // 无法通过优先级找到合适的编排区域
|
||||
for i, a := range slf.areas {
|
||||
for i, a := range editor.GetAreasWithScoreDesc(current) {
|
||||
if _, exist := itemAreaPriority[current.GetID()][i]; exist {
|
||||
continue
|
||||
}
|
||||
|
@ -158,6 +161,17 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
|||
|
||||
// try 尝试将 current 编排到 a 中
|
||||
func (slf *Arrangement[ID, AreaInfo]) try(editor *Editor[ID, AreaInfo], a *Area[ID, AreaInfo], current Item[ID]) bool {
|
||||
allow := true
|
||||
for _, verify := range slf.itemNotAllow[current.GetID()] {
|
||||
if verify(a.GetAreaInfo(), current) {
|
||||
allow = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allow {
|
||||
return false
|
||||
}
|
||||
|
||||
err, conflictItems, allow := a.IsAllow(current)
|
||||
if !allow {
|
||||
if err != nil {
|
||||
|
|
|
@ -48,7 +48,7 @@ func TestArrangement_Arrange(t *testing.T) {
|
|||
a.AddItem(&Player{ID: i + 1})
|
||||
}
|
||||
|
||||
res, no := a.Arrange(50)
|
||||
res, no := a.Arrange()
|
||||
for _, area := range res {
|
||||
var str = fmt.Sprintf("area %d: ", area.GetAreaInfo().ID)
|
||||
for id := range area.GetItems() {
|
||||
|
|
|
@ -3,9 +3,10 @@ package arrangement
|
|||
import (
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Editor 编排器
|
||||
// Editor 提供了大量辅助函数的编辑器
|
||||
type Editor[ID comparable, AreaInfo any] struct {
|
||||
a *Arrangement[ID, AreaInfo]
|
||||
pending []Item[ID]
|
||||
|
@ -44,6 +45,24 @@ func (slf *Editor[ID, AreaInfo]) GetAreas() []*Area[ID, AreaInfo] {
|
|||
return slice.Copy(slf.a.areas)
|
||||
}
|
||||
|
||||
// GetAreasWithScoreAsc 获取所有的编排区域,并按照分数升序排序
|
||||
func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area[ID, AreaInfo] {
|
||||
areas := slice.Copy(slf.a.areas)
|
||||
sort.Slice(areas, func(i, j int) bool {
|
||||
return areas[i].GetScore(extra...) < areas[j].GetScore(extra...)
|
||||
})
|
||||
return areas
|
||||
}
|
||||
|
||||
// GetAreasWithScoreDesc 获取所有的编排区域,并按照分数降序排序
|
||||
func (slf *Editor[ID, AreaInfo]) GetAreasWithScoreDesc(extra ...Item[ID]) []*Area[ID, AreaInfo] {
|
||||
areas := slice.Copy(slf.a.areas)
|
||||
sort.Slice(areas, func(i, j int) bool {
|
||||
return areas[i].GetScore(extra...) > areas[j].GetScore(extra...)
|
||||
})
|
||||
return areas
|
||||
}
|
||||
|
||||
// GetRetryCount 获取重试次数
|
||||
func (slf *Editor[ID, AreaInfo]) GetRetryCount() int {
|
||||
return slf.retryCount
|
||||
|
@ -53,3 +72,93 @@ func (slf *Editor[ID, AreaInfo]) GetRetryCount() int {
|
|||
func (slf *Editor[ID, AreaInfo]) GetThresholdProgressRate() float64 {
|
||||
return float64(slf.retryCount) / float64(slf.a.threshold)
|
||||
}
|
||||
|
||||
// GetAllowAreas 获取允许的编排区域
|
||||
func (slf *Editor[ID, AreaInfo]) GetAllowAreas(item Item[ID]) []*Area[ID, AreaInfo] {
|
||||
var areas []*Area[ID, AreaInfo]
|
||||
for _, area := range slf.a.areas {
|
||||
if _, _, allow := area.IsAllow(item); allow {
|
||||
areas = append(areas, area)
|
||||
}
|
||||
}
|
||||
return areas
|
||||
}
|
||||
|
||||
// GetNoAllowAreas 获取不允许的编排区域
|
||||
func (slf *Editor[ID, AreaInfo]) GetNoAllowAreas(item Item[ID]) []*Area[ID, AreaInfo] {
|
||||
var areas []*Area[ID, AreaInfo]
|
||||
for _, area := range slf.a.areas {
|
||||
if _, _, allow := area.IsAllow(item); !allow {
|
||||
areas = append(areas, area)
|
||||
}
|
||||
}
|
||||
return areas
|
||||
}
|
||||
|
||||
// GetBestAllowArea 获取最佳的允许的编排区域,如果不存在,则返回 nil
|
||||
func (slf *Editor[ID, AreaInfo]) GetBestAllowArea(item Item[ID]) *Area[ID, AreaInfo] {
|
||||
var areas = slf.GetAllowAreas(item)
|
||||
if len(areas) == 0 {
|
||||
return nil
|
||||
}
|
||||
var bestArea = areas[0]
|
||||
var score = bestArea.GetScore(item)
|
||||
for _, area := range areas {
|
||||
if area.GetScore(item) > score {
|
||||
bestArea = area
|
||||
score = area.GetScore(item)
|
||||
}
|
||||
}
|
||||
return bestArea
|
||||
}
|
||||
|
||||
// GetBestNoAllowArea 获取最佳的不允许的编排区域,如果不存在,则返回 nil
|
||||
func (slf *Editor[ID, AreaInfo]) GetBestNoAllowArea(item Item[ID]) *Area[ID, AreaInfo] {
|
||||
var areas = slf.GetNoAllowAreas(item)
|
||||
if len(areas) == 0 {
|
||||
return nil
|
||||
}
|
||||
var bestArea = areas[0]
|
||||
var score = bestArea.GetScore(item)
|
||||
for _, area := range areas {
|
||||
if area.GetScore(item) > score {
|
||||
bestArea = area
|
||||
score = area.GetScore(item)
|
||||
}
|
||||
}
|
||||
return bestArea
|
||||
}
|
||||
|
||||
// GetWorstAllowArea 获取最差的允许的编排区域,如果不存在,则返回 nil
|
||||
func (slf *Editor[ID, AreaInfo]) GetWorstAllowArea(item Item[ID]) *Area[ID, AreaInfo] {
|
||||
var areas = slf.GetAllowAreas(item)
|
||||
if len(areas) == 0 {
|
||||
return nil
|
||||
}
|
||||
var worstArea = areas[0]
|
||||
var score = worstArea.GetScore(item)
|
||||
for _, area := range areas {
|
||||
if area.GetScore(item) < score {
|
||||
worstArea = area
|
||||
score = area.GetScore(item)
|
||||
}
|
||||
}
|
||||
return worstArea
|
||||
}
|
||||
|
||||
// GetWorstNoAllowArea 获取最差的不允许的编排区域,如果不存在,则返回 nil
|
||||
func (slf *Editor[ID, AreaInfo]) GetWorstNoAllowArea(item Item[ID]) *Area[ID, AreaInfo] {
|
||||
var areas = slf.GetNoAllowAreas(item)
|
||||
if len(areas) == 0 {
|
||||
return nil
|
||||
}
|
||||
var worstArea = areas[0]
|
||||
var score = worstArea.GetScore(item)
|
||||
for _, area := range areas {
|
||||
if area.GetScore(item) < score {
|
||||
worstArea = area
|
||||
score = area.GetScore(item)
|
||||
}
|
||||
}
|
||||
return worstArea
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ package arrangement
|
|||
type ItemOption[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, AreaInfo], item Item[ID])
|
||||
|
||||
type (
|
||||
ItemFixedAreaHandle[AreaInfo any] func(areaInfo AreaInfo) bool
|
||||
ItemPriorityHandle[ID comparable, AreaInfo any] func(areaInfo AreaInfo, item Item[ID]) float64
|
||||
ItemFixedAreaHandle[AreaInfo any] func(areaInfo AreaInfo) bool
|
||||
ItemPriorityHandle[ID comparable, AreaInfo any] func(areaInfo AreaInfo, item Item[ID]) float64
|
||||
ItemNotAllowVerifyHandle[ID comparable, AreaInfo any] func(areaInfo AreaInfo, item Item[ID]) bool
|
||||
)
|
||||
|
||||
// WithItemFixed 设置成员的固定编排区域
|
||||
|
@ -21,3 +22,10 @@ func WithItemPriority[ID comparable, AreaInfo any](priority ItemPriorityHandle[I
|
|||
arrangement.priority[item.GetID()] = append(arrangement.priority[item.GetID()], priority)
|
||||
}
|
||||
}
|
||||
|
||||
// WithItemNotAllow 设置成员不允许的编排区域
|
||||
func WithItemNotAllow[ID comparable, AreaInfo any](verify ItemNotAllowVerifyHandle[ID, AreaInfo]) ItemOption[ID, AreaInfo] {
|
||||
return func(arrangement *Arrangement[ID, AreaInfo], item Item[ID]) {
|
||||
arrangement.itemNotAllow[item.GetID()] = append(arrangement.itemNotAllow[item.GetID()], verify)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue