feat: arrangement.Engine 新增更多的辅助函数

This commit is contained in:
kercylan98 2023-08-03 17:36:51 +08:00
parent 84f36eaaba
commit 822ffc7041
4 changed files with 144 additions and 13 deletions

View File

@ -11,6 +11,8 @@ func NewArrangement[ID comparable, AreaInfo any](options ...Option[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)
@ -27,6 +29,7 @@ type Arrangement[ID comparable, AreaInfo any] struct {
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]
@ -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 {

View File

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

View File

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

View File

@ -6,6 +6,7 @@ type ItemOption[ID comparable, AreaInfo any] func(arrangement *Arrangement[ID, A
type (
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)
}
}