移除辐射图,更改为分布图,优化分布图实现
This commit is contained in:
parent
98fde32fb5
commit
f3af67ff66
|
@ -0,0 +1,108 @@
|
||||||
|
package dp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/g2d"
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDistributionPattern 构建一个分布图实例
|
||||||
|
func NewDistributionPattern[Item any](sameKindVerifyHandle func(itemA, itemB Item) bool) *DistributionPattern[Item] {
|
||||||
|
return &DistributionPattern[Item]{
|
||||||
|
links: map[int]map[int]Item{},
|
||||||
|
sameKindVerifyHandle: sameKindVerifyHandle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DistributionPattern 分布图
|
||||||
|
type DistributionPattern[Item any] struct {
|
||||||
|
matrix []Item
|
||||||
|
links map[int]map[int]Item
|
||||||
|
sameKindVerifyHandle func(itemA, itemB Item) bool
|
||||||
|
width int
|
||||||
|
usePos bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadMatrix 通过二维矩阵加载分布图
|
||||||
|
// - 通过该函数加载的分布图使用的矩阵是复制后的矩阵,因此无法直接通过刷新(Refresh)来更新分布关系
|
||||||
|
// - 需要通过直接刷新的方式请使用 LoadMatrixWithPos
|
||||||
|
func (slf *DistributionPattern[Item]) LoadMatrix(matrix [][]Item) {
|
||||||
|
slf.LoadMatrixWithPos(g2d.MatrixToPosMatrix(matrix))
|
||||||
|
slf.usePos = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadMatrixWithPos 通过二维矩阵加载分布图
|
||||||
|
func (slf *DistributionPattern[Item]) LoadMatrixWithPos(width int, matrix []Item) {
|
||||||
|
slf.width = width
|
||||||
|
slf.matrix = matrix
|
||||||
|
slf.usePos = true
|
||||||
|
for k := range slf.links {
|
||||||
|
delete(slf.links, k)
|
||||||
|
}
|
||||||
|
for pos, item := range slf.matrix {
|
||||||
|
slf.buildRelationships(pos, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh 刷新特定位置的分布关系
|
||||||
|
// - 由于 LoadMatrix 的矩阵是复制后的矩阵,所以任何外部的改动都不会影响到分布图的变化,在这种情况下,刷新将没有任何意义
|
||||||
|
// - 需要通过直接刷新的方式请使用 LoadMatrixWithPos 加载矩阵,或者通过 RefreshWithItem 函数进行刷新
|
||||||
|
func (slf *DistributionPattern[Item]) Refresh(pos int) {
|
||||||
|
if !slf.usePos {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
links, exist := slf.links[pos]
|
||||||
|
if !exist {
|
||||||
|
slf.buildRelationships(pos, slf.matrix[pos])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
temp := hash.Copy(links)
|
||||||
|
for tp := range links {
|
||||||
|
delete(slf.links, tp)
|
||||||
|
}
|
||||||
|
for tp, target := range temp {
|
||||||
|
slf.buildRelationships(tp, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshWithItem 通过特定的成员刷新特定位置的分布关系
|
||||||
|
// - 如果矩阵通过 LoadMatrixWithPos 加载,将会重定向至 Refresh
|
||||||
|
func (slf *DistributionPattern[Item]) RefreshWithItem(pos int, item Item) {
|
||||||
|
if slf.usePos {
|
||||||
|
slf.Refresh(pos)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.matrix[pos] = item
|
||||||
|
links, exist := slf.links[pos]
|
||||||
|
if !exist {
|
||||||
|
slf.buildRelationships(pos, slf.matrix[pos])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
temp := hash.Copy(links)
|
||||||
|
for tp := range links {
|
||||||
|
delete(slf.links, tp)
|
||||||
|
}
|
||||||
|
for tp, target := range temp {
|
||||||
|
slf.buildRelationships(tp, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建关系
|
||||||
|
func (slf *DistributionPattern[Item]) buildRelationships(pos int, item Item) {
|
||||||
|
links, exist := slf.links[pos]
|
||||||
|
if !exist {
|
||||||
|
links = map[int]Item{pos: item}
|
||||||
|
slf.links[pos] = links
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tp := range g2d.GetAdjacentCoordinatesWithPos(slf.matrix, slf.width, pos) {
|
||||||
|
target := slf.matrix[tp]
|
||||||
|
if _, exist := links[tp]; exist || !slf.sameKindVerifyHandle(item, target) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.links[tp] = links
|
||||||
|
links[tp] = target
|
||||||
|
slf.buildRelationships(tp, target)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package dp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewDistributionPattern(t *testing.T) {
|
||||||
|
|
||||||
|
dp := NewDistributionPattern[int](func(itemA, itemB int) bool {
|
||||||
|
return itemA == itemB
|
||||||
|
})
|
||||||
|
|
||||||
|
matrix := []int{1, 1, 2, 2, 2, 2, 1, 2, 2}
|
||||||
|
dp.LoadMatrixWithPos(3, matrix)
|
||||||
|
|
||||||
|
for pos, link := range dp.links {
|
||||||
|
fmt.Println(pos, link, fmt.Sprintf("%p", link))
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,3 +90,16 @@ func PosToCoordinateX(width, pos int) int {
|
||||||
func PosToCoordinateY(width, pos int) int {
|
func PosToCoordinateY(width, pos int) int {
|
||||||
return pos / width
|
return pos / width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatrixToPosMatrix 将二维矩阵转换为顺序的二维矩阵
|
||||||
|
func MatrixToPosMatrix[V any](matrix [][]V) (width int, posMatrix []V) {
|
||||||
|
width = len(matrix)
|
||||||
|
height := len(matrix[0])
|
||||||
|
posMatrix = make([]V, width*height)
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
posMatrix[CoordinateToPos(width, x, y)] = matrix[x][y]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -1,186 +0,0 @@
|
||||||
package g2d
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
|
||||||
"github.com/kercylan98/minotaur/utils/synchronization"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewRadiationPattern[ItemType comparable, Item RadiationPatternItem[ItemType]](matrix [][]Item, options ...RadiationPatternOption[ItemType, Item]) *RadiationPattern[ItemType, Item] {
|
|
||||||
var clone = make([][]Item, len(matrix))
|
|
||||||
for x := 0; x < len(matrix); x++ {
|
|
||||||
ys := make([]Item, len(matrix[0]))
|
|
||||||
for y := 0; y < len(matrix[0]); y++ {
|
|
||||||
ys[y] = matrix[x][y]
|
|
||||||
}
|
|
||||||
clone[x] = ys
|
|
||||||
}
|
|
||||||
rp := &RadiationPattern[ItemType, Item]{
|
|
||||||
matrix: clone,
|
|
||||||
links: synchronization.NewMap[int64, map[int64]bool](),
|
|
||||||
positions: map[int64][2]int{},
|
|
||||||
nils: map[int]map[int]bool{},
|
|
||||||
}
|
|
||||||
for _, option := range options {
|
|
||||||
option(rp)
|
|
||||||
}
|
|
||||||
for x := 0; x < len(matrix); x++ {
|
|
||||||
rp.nils[x] = map[int]bool{}
|
|
||||||
}
|
|
||||||
for x := 0; x < len(matrix); x++ {
|
|
||||||
for y := 0; y < len(matrix[0]); y++ {
|
|
||||||
item := matrix[x][y]
|
|
||||||
if rp.excludes[item.GetType()] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rp.positions[item.GetGuid()] = CoordinateToCoordinateArray(x, y)
|
|
||||||
rp.searchNeighbour(x, y, synchronization.NewMap[int64, bool](), synchronization.NewMap[int64, bool]())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rp
|
|
||||||
}
|
|
||||||
|
|
||||||
// RadiationPattern 辐射图数据结构
|
|
||||||
// - 辐射图用于将一个二维数组里相邻的所有类型相同的成员进行标注
|
|
||||||
type RadiationPattern[ItemType comparable, Item RadiationPatternItem[ItemType]] struct {
|
|
||||||
matrix [][]Item
|
|
||||||
links *synchronization.Map[int64, map[int64]bool] // 成员类型相同且相连的链接
|
|
||||||
positions map[int64][2]int // 根据成员guid记录的成员位置
|
|
||||||
nils map[int]map[int]bool // 空位置
|
|
||||||
excludes map[ItemType]bool // 排除建立关系的类型
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLinks 获取特定成员能够辐射到的所有成员
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) GetLinks(guid int64) []int64 {
|
|
||||||
return hash.KeyToSlice(slf.links.Get(guid))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLinkPositions 获取特定成员能够辐射到的所有成员位置
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) GetLinkPositions(guid int64) [][2]int {
|
|
||||||
links := slf.links.Get(guid)
|
|
||||||
var result = make([][2]int, 0, len(links))
|
|
||||||
for g := range links {
|
|
||||||
result = append(result, slf.positions[g])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPosition 获取特定成员的位置
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) GetPosition(guid int64) [2]int {
|
|
||||||
return slf.positions[guid]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove 移除特定位置的辐射信息
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) Remove(x, y int) {
|
|
||||||
old := slf.matrix[x][y]
|
|
||||||
oldGuid := old.GetGuid()
|
|
||||||
for linkGuid := range slf.links.Get(oldGuid) {
|
|
||||||
xy := slf.positions[linkGuid]
|
|
||||||
slf.searchNeighbour(xy[0], xy[1], synchronization.NewMap[int64, bool](), synchronization.NewMap[int64, bool]())
|
|
||||||
}
|
|
||||||
slf.links.Delete(oldGuid)
|
|
||||||
delete(slf.positions, oldGuid)
|
|
||||||
slf.nils[x][y] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh 刷新特定位置成员并且更新其辐射信息
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) Refresh(x, y int, item Item) {
|
|
||||||
if slf.excludes[item.GetType()] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slf.Remove(x, y)
|
|
||||||
|
|
||||||
slf.nils[x][y] = false
|
|
||||||
slf.matrix[x][y] = item
|
|
||||||
slf.positions[item.GetGuid()] = CoordinateToCoordinateArray(x, y)
|
|
||||||
slf.searchNeighbour(x, y, synchronization.NewMap[int64, bool](), synchronization.NewMap[int64, bool]())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RefreshBySwap 通过交换的方式刷新两个成员的辐射信息
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) RefreshBySwap(x1, y1, x2, y2 int, item1, item2 Item) {
|
|
||||||
var xys = [][2]int{CoordinateToCoordinateArray(x1, y1), CoordinateToCoordinateArray(x2, y2)}
|
|
||||||
for _, xy := range xys {
|
|
||||||
x, y := CoordinateArrayToCoordinate(xy)
|
|
||||||
slf.Remove(x, y)
|
|
||||||
}
|
|
||||||
for i, item := range []Item{item1, item2} {
|
|
||||||
if slf.excludes[item.GetType()] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
x, y := CoordinateArrayToCoordinate(xys[i])
|
|
||||||
slf.nils[x][y] = false
|
|
||||||
slf.matrix[x][y] = item
|
|
||||||
slf.positions[item.GetGuid()] = CoordinateToCoordinateArray(x, y)
|
|
||||||
slf.searchNeighbour(x, y, synchronization.NewMap[int64, bool](), synchronization.NewMap[int64, bool]())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slf *RadiationPattern[ItemType, Item]) searchNeighbour(x, y int, filter *synchronization.Map[int64, bool], childrenLinks *synchronization.Map[int64, bool]) {
|
|
||||||
var item = slf.matrix[x][y]
|
|
||||||
if slf.excludes[item.GetType()] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
neighboursLock sync.Mutex
|
|
||||||
neighbours = map[int64]bool{}
|
|
||||||
itemType = item.GetType()
|
|
||||||
wait sync.WaitGroup
|
|
||||||
itemGuid = item.GetGuid()
|
|
||||||
handle = func(x, y int) bool {
|
|
||||||
neighbour := slf.matrix[x][y]
|
|
||||||
nt := neighbour.GetType()
|
|
||||||
if slf.excludes[nt] || nt != itemType || slf.nils[x][y] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
neighbourGuid := neighbour.GetGuid()
|
|
||||||
neighboursLock.Lock()
|
|
||||||
neighbours[neighbourGuid] = true
|
|
||||||
neighboursLock.Unlock()
|
|
||||||
childrenLinks.Set(neighbourGuid, true)
|
|
||||||
slf.searchNeighbour(x, y, filter, childrenLinks)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if filter.Get(itemGuid) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filter.Set(itemGuid, true)
|
|
||||||
wait.Add(4)
|
|
||||||
go func() {
|
|
||||||
for sy := y - 1; sy >= 0; sy-- {
|
|
||||||
if !handle(x, sy) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
for sy := y + 1; sy < len(slf.matrix[0]); sy++ {
|
|
||||||
if !handle(x, sy) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
for sx := x - 1; sx >= 0; sx-- {
|
|
||||||
if !handle(sx, y) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait.Done()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
for sx := x + 1; sx < len(slf.matrix); sx++ {
|
|
||||||
if !handle(sx, y) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait.Done()
|
|
||||||
}()
|
|
||||||
wait.Wait()
|
|
||||||
childrenLinks.Range(func(key int64, value bool) {
|
|
||||||
neighbours[key] = value
|
|
||||||
})
|
|
||||||
slf.links.Set(itemGuid, neighbours)
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package g2d
|
|
||||||
|
|
||||||
type RadiationPatternItem[Type comparable] interface {
|
|
||||||
GetGuid() int64
|
|
||||||
GetType() Type
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package g2d
|
|
||||||
|
|
||||||
type RadiationPatternOption[ItemType comparable, Item RadiationPatternItem[ItemType]] func(rp *RadiationPattern[ItemType, Item])
|
|
||||||
|
|
||||||
func WithRadiationPatternExclude[ItemType comparable, Item RadiationPatternItem[ItemType]](itemType ...ItemType) RadiationPatternOption[ItemType, Item] {
|
|
||||||
return func(rp *RadiationPattern[ItemType, Item]) {
|
|
||||||
if rp.excludes == nil {
|
|
||||||
rp.excludes = map[ItemType]bool{}
|
|
||||||
}
|
|
||||||
for _, t := range itemType {
|
|
||||||
rp.excludes[t] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue