Skip to content

Commit

Permalink
feat: arrangement.Engine 新增更多的辅助函数
Browse files Browse the repository at this point in the history
  • Loading branch information
kercylan98 committed Aug 3, 2023
1 parent 84f36ea commit 822ffc7
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 13 deletions.
32 changes: 23 additions & 9 deletions utils/arrangement/arrangement.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion utils/arrangement/arrangement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
111 changes: 110 additions & 1 deletion utils/arrangement/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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
}
12 changes: 10 additions & 2 deletions utils/arrangement/item_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 设置成员的固定编排区域
Expand All @@ -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)
}
}

0 comments on commit 822ffc7

Please sign in to comment.