feat: arrangement.Engine 新增更多的辅助函数
This commit is contained in:
parent
84f36eaaba
commit
822ffc7041
|
@ -11,6 +11,8 @@ func NewArrangement[ID comparable, AreaInfo any](options ...Option[ID, AreaInfo]
|
||||||
items: map[ID]Item[ID]{},
|
items: map[ID]Item[ID]{},
|
||||||
fixed: map[ID]ItemFixedAreaHandle[AreaInfo]{},
|
fixed: map[ID]ItemFixedAreaHandle[AreaInfo]{},
|
||||||
priority: map[ID][]ItemPriorityHandle[ID, AreaInfo]{},
|
priority: map[ID][]ItemPriorityHandle[ID, AreaInfo]{},
|
||||||
|
itemNotAllow: map[ID][]ItemNotAllowVerifyHandle[ID, AreaInfo]{},
|
||||||
|
threshold: 10,
|
||||||
}
|
}
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
option(arrangement)
|
option(arrangement)
|
||||||
|
@ -27,6 +29,7 @@ type Arrangement[ID comparable, AreaInfo any] struct {
|
||||||
items map[ID]Item[ID] // 所有的成员
|
items map[ID]Item[ID] // 所有的成员
|
||||||
fixed map[ID]ItemFixedAreaHandle[AreaInfo] // 固定编排区域的成员
|
fixed map[ID]ItemFixedAreaHandle[AreaInfo] // 固定编排区域的成员
|
||||||
priority map[ID][]ItemPriorityHandle[ID, AreaInfo] // 成员的优先级函数
|
priority map[ID][]ItemPriorityHandle[ID, AreaInfo] // 成员的优先级函数
|
||||||
|
itemNotAllow map[ID][]ItemNotAllowVerifyHandle[ID, AreaInfo] // 成员的不允的编排区域检测函数
|
||||||
threshold int // 重试次数阈值
|
threshold int // 重试次数阈值
|
||||||
|
|
||||||
constraintHandles []ConstraintHandle[ID, AreaInfo]
|
constraintHandles []ConstraintHandle[ID, AreaInfo]
|
||||||
|
@ -116,7 +119,7 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
||||||
}
|
}
|
||||||
|
|
||||||
if area == nil { // 无法通过优先级找到合适的编排区域
|
if area == nil { // 无法通过优先级找到合适的编排区域
|
||||||
for i, a := range slf.areas {
|
for i, a := range editor.GetAreasWithScoreDesc(current) {
|
||||||
if _, exist := itemAreaPriority[current.GetID()][i]; exist {
|
if _, exist := itemAreaPriority[current.GetID()][i]; exist {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -158,6 +161,17 @@ func (slf *Arrangement[ID, AreaInfo]) Arrange() (areas []*Area[ID, AreaInfo], no
|
||||||
|
|
||||||
// try 尝试将 current 编排到 a 中
|
// try 尝试将 current 编排到 a 中
|
||||||
func (slf *Arrangement[ID, AreaInfo]) try(editor *Editor[ID, AreaInfo], a *Area[ID, AreaInfo], current Item[ID]) bool {
|
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)
|
err, conflictItems, allow := a.IsAllow(current)
|
||||||
if !allow {
|
if !allow {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -48,7 +48,7 @@ func TestArrangement_Arrange(t *testing.T) {
|
||||||
a.AddItem(&Player{ID: i + 1})
|
a.AddItem(&Player{ID: i + 1})
|
||||||
}
|
}
|
||||||
|
|
||||||
res, no := a.Arrange(50)
|
res, no := a.Arrange()
|
||||||
for _, area := range res {
|
for _, area := range res {
|
||||||
var str = fmt.Sprintf("area %d: ", area.GetAreaInfo().ID)
|
var str = fmt.Sprintf("area %d: ", area.GetAreaInfo().ID)
|
||||||
for id := range area.GetItems() {
|
for id := range area.GetItems() {
|
||||||
|
|
|
@ -3,9 +3,10 @@ package arrangement
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
"github.com/kercylan98/minotaur/utils/slice"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Editor 编排器
|
// Editor 提供了大量辅助函数的编辑器
|
||||||
type Editor[ID comparable, AreaInfo any] struct {
|
type Editor[ID comparable, AreaInfo any] struct {
|
||||||
a *Arrangement[ID, AreaInfo]
|
a *Arrangement[ID, AreaInfo]
|
||||||
pending []Item[ID]
|
pending []Item[ID]
|
||||||
|
@ -44,6 +45,24 @@ func (slf *Editor[ID, AreaInfo]) GetAreas() []*Area[ID, AreaInfo] {
|
||||||
return slice.Copy(slf.a.areas)
|
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 获取重试次数
|
// GetRetryCount 获取重试次数
|
||||||
func (slf *Editor[ID, AreaInfo]) GetRetryCount() int {
|
func (slf *Editor[ID, AreaInfo]) GetRetryCount() int {
|
||||||
return slf.retryCount
|
return slf.retryCount
|
||||||
|
@ -53,3 +72,93 @@ func (slf *Editor[ID, AreaInfo]) GetRetryCount() int {
|
||||||
func (slf *Editor[ID, AreaInfo]) GetThresholdProgressRate() float64 {
|
func (slf *Editor[ID, AreaInfo]) GetThresholdProgressRate() float64 {
|
||||||
return float64(slf.retryCount) / float64(slf.a.threshold)
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ type ItemOption[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, A
|
||||||
type (
|
type (
|
||||||
ItemFixedAreaHandle[AreaInfo any] func(areaInfo AreaInfo) bool
|
ItemFixedAreaHandle[AreaInfo any] func(areaInfo AreaInfo) bool
|
||||||
ItemPriorityHandle[ID comparable, AreaInfo any] func(areaInfo AreaInfo, item Item[ID]) float64
|
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 设置成员的固定编排区域
|
// WithItemFixed 设置成员的固定编排区域
|
||||||
|
@ -21,3 +22,10 @@ func WithItemPriority[ID comparable, AreaInfo any](priority ItemPriorityHandle[I
|
||||||
arrangement.priority[item.GetID()] = append(arrangement.priority[item.GetID()], priority)
|
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