diff --git a/utils/g2d/matrix/match3.go b/utils/g2d/matrix/match3.go deleted file mode 100644 index f497f3d..0000000 --- a/utils/g2d/matrix/match3.go +++ /dev/null @@ -1,224 +0,0 @@ -package matrix - -import ( - "github.com/kercylan98/minotaur/utils/g2d" - "github.com/kercylan98/minotaur/utils/synchronization" - "sync" -) - -func NewMatch3[ItemType comparable, Item Match3Item[ItemType]](width, height int, options ...Match3Option[ItemType, Item]) *Match3[ItemType, Item] { - match3 := &Match3[ItemType, Item]{ - matrix: NewMatrix[Item](width, height), - generators: map[ItemType]func() Item{}, - links: synchronization.NewMap[int64, map[int64]bool](), - positions: map[int64][2]int{}, - notNil: map[int]map[int]bool{}, - matchStrategy: map[int]func(matrix [][]Item) [][]Item{}, - generateNotMatchRetryCount: 3, - } - for x := 0; x < width; x++ { - match3.notNil[x] = map[int]bool{} - } - for _, option := range options { - option(match3) - } - if len(match3.generators) == 0 { - panic("please use WithMatch3Generator set at least one generation strategy") - } - if len(match3.matchStrategy) == 0 { - panic("please use WithMatch3Strategy set at least one match strategy") - } - return match3 -} - -// Match3 基于三消类游戏的二维矩阵 -// - 提供了适合三消类游戏的功能 -type Match3[ItemType comparable, Item Match3Item[ItemType]] struct { - matrix *Matrix[Item] - - guid int64 // 成员guid - links *synchronization.Map[int64, map[int64]bool] // 成员类型相同且相连的链接 - positions map[int64][2]int // 根据成员guid记录的成员位置 - notNil map[int]map[int]bool // 特定位置是否不为空 - - generators map[ItemType]func() Item // 成员生成器 - matchStrategy map[int]func(matrix [][]Item) [][]Item // 匹配策略 - generateNotMatchRetryCount int // 生成不匹配类型重试次数 -} - -// GetHeight 获取高度 -func (slf *Match3[ItemType, Item]) GetHeight() int { - return slf.matrix.h -} - -// GetWidth 获取宽度 -func (slf *Match3[ItemType, Item]) GetWidth() int { - return slf.matrix.w -} - -// GenerateItem 在特定位置生成特定类型的成员 -func (slf *Match3[ItemType, Item]) GenerateItem(x, y int, itemType ItemType) Item { - item := slf.generators[itemType]() - item.SetGuid(slf.getNextGuid()) - slf.set(x, y, item) - return item -} - -// Predict 预言 -func (slf *Match3[ItemType, Item]) Predict() { - // TODO -} - -// GenerateItemsByNotMatch 生成一批在特定位置不会触发任何匹配规则的成员类型 -// - 这一批成员不会被加入到矩阵中,索引与位置索引相对应 -// - 无解的策略下会导致死循环 -func (slf *Match3[ItemType, Item]) GenerateItemsByNotMatch(xys ...[2]int) (result []ItemType) { - result = make([]ItemType, 0, len(xys)) - lastIndex := len(xys) - 1 - retry := 0 - backup := NewBackup(slf) -start: - { - for i, xy := range xys { - x, y := g2d.PositionArrayToXY(xy) - var match bool - for _, f := range slf.generators { - slf.set(x, y, f()) - for i := 1; i <= len(slf.matchStrategy); i++ { - if len(slf.matchStrategy[i](slf.matrix.m)) > 0 { - match = true - break - } - } - if !match { - break - } - } - if match { - if i == lastIndex { - if retry < slf.generateNotMatchRetryCount { - retry++ - result = result[:0] - backup.Restore() - goto start - } else { - panic("no solution, the matrix rule is wrong or there are matching members.") - } - } else { - result = result[:0] - backup.Restore() - goto start - } - } - result = append(result, slf.matrix.m[x][y].GetType()) - } - } - return -} - -// GetMatch 获取二维矩阵 -// - 该矩阵为克隆的,意味着任何修改都不会影响原有内容 -func (slf *Match3[ItemType, Item]) GetMatch() [][]Item { - var ( - width = slf.GetWidth() - height = slf.GetHeight() - clone = make([][]Item[ItemType], width) - ) - for x := 0; x < width; x++ { - ys := make([]Item, height) - for y := 0; y < height; y++ { - ys[y] = slf.matrix.m[x][y].Clone().(Item) - } - clone[x] = ys - } - return clone -} - -// 设置特定位置的成员 -func (slf *Match3[ItemType, Item]) set(x, y int, item Item) { - if old := slf.matrix.m[x][y]; slf.notNil[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) - } - - slf.notNil[x][y] = true - slf.matrix.Set(x, y, item) - slf.positions[item.GetGuid()] = [2]int{x, y} - slf.searchNeighbour(x, y, synchronization.NewMap[int64, bool](), synchronization.NewMap[int64, bool]()) -} - -func (slf *Match3[ItemType, Item]) searchNeighbour(x, y int, filter *synchronization.Map[int64, bool], childrenLinks *synchronization.Map[int64, bool]) { - var ( - item = slf.matrix.m[x][y] - 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.m[x][y] - if !slf.notNil[x][y] || neighbour.GetType() != itemType { - 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 < slf.matrix.h; 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 < slf.matrix.w; 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) -} - -// 获取下一个guid -func (slf *Match3[ItemType, Item]) getNextGuid() int64 { - slf.guid++ - return slf.guid -} diff --git a/utils/g2d/matrix/match3_backup.go b/utils/g2d/matrix/match3_backup.go deleted file mode 100644 index 7d009c8..0000000 --- a/utils/g2d/matrix/match3_backup.go +++ /dev/null @@ -1,60 +0,0 @@ -package matrix - -import ( - "github.com/kercylan98/minotaur/utils/g2d" - "github.com/kercylan98/minotaur/utils/hash" - "github.com/kercylan98/minotaur/utils/synchronization" -) - -func NewBackup[ItemType comparable, Item Match3Item[ItemType]](match3 *Match3[ItemType, Item]) *Match3Backup[ItemType, Item] { - backup := &Match3Backup[ItemType, Item]{match3: match3} - - backup.guid = match3.guid - backup.links = synchronization.NewMap[int64, map[int64]bool]() - match3.links.Range(func(key int64, value map[int64]bool) { - backup.links.Set(key, hash.Copy(value)) - }) - backup.positions = map[int64][2]int{} - for key, value := range match3.positions { - backup.positions[key] = g2d.PositionClone(value) - } - backup.notNil = map[int]map[int]bool{} - for key, values := range match3.notNil { - var notNil = map[int]bool{} - for key, value := range values { - notNil[key] = value - } - backup.notNil[key] = notNil - } - return backup -} - -type Match3Backup[ItemType comparable, Item Match3Item[ItemType]] struct { - match3 *Match3[ItemType, Item] - - guid int64 // 成员guid - links *synchronization.Map[int64, map[int64]bool] // 成员类型相同且相连的链接 - positions map[int64][2]int // 根据成员guid记录的成员位置 - notNil map[int]map[int]bool // 特定位置是否不为空 -} - -// Restore 还原备份 -func (slf *Match3Backup[ItemType, Item]) Restore() { - slf.match3.guid = slf.guid - slf.match3.links = synchronization.NewMap[int64, map[int64]bool]() - slf.links.Range(func(key int64, value map[int64]bool) { - slf.match3.links.Set(key, hash.Copy(value)) - }) - slf.match3.positions = map[int64][2]int{} - for key, value := range slf.positions { - slf.match3.positions[key] = g2d.PositionClone(value) - } - slf.match3.notNil = map[int]map[int]bool{} - for key, values := range slf.notNil { - var notNil = map[int]bool{} - for key, value := range values { - notNil[key] = value - } - slf.match3.notNil[key] = notNil - } -} diff --git a/utils/g2d/matrix/match3_item.go b/utils/g2d/matrix/match3_item.go deleted file mode 100644 index 99f3d78..0000000 --- a/utils/g2d/matrix/match3_item.go +++ /dev/null @@ -1,13 +0,0 @@ -package matrix - -// Match3Item 三消成员接口定义 -type Match3Item[Type comparable] interface { - // SetGuid 设置guid - SetGuid(guid int64) - // GetGuid 获取guid - GetGuid() int64 - // GetType 获取成员类型 - GetType() Type - // Clone 克隆 - Clone() Match3Item[Type] -} diff --git a/utils/g2d/matrix/match3_options.go b/utils/g2d/matrix/match3_options.go deleted file mode 100644 index 22414a2..0000000 --- a/utils/g2d/matrix/match3_options.go +++ /dev/null @@ -1,17 +0,0 @@ -package matrix - -type Match3Option[ItemType comparable, Item Match3Item[ItemType]] func(match3 *Match3[ItemType, Item]) - -func WithMatch3Generator[ItemType comparable, Item Match3Item[ItemType]](itemType ItemType, generator func() Item) Match3Option[ItemType, Item] { - return func(match3 *Match3[ItemType, Item]) { - match3.generators[itemType] = generator - } -} - -// WithMatch3Tactics 设置匹配策略 -// - 匹配策略用于匹配出对应成员 -func WithMatch3Tactics[ItemType comparable, Item Match3Item[ItemType]](tactics func(matrix [][]Item) [][]Item) Match3Option[ItemType, Item] { - return func(match3 *Match3[ItemType, Item]) { - match3.matchStrategy[len(match3.matchStrategy)+1] = tactics - } -} diff --git a/utils/g2d/matrix/match3_test.go b/utils/g2d/matrix/match3_test.go deleted file mode 100644 index dff579e..0000000 --- a/utils/g2d/matrix/match3_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package matrix - -import ( - "fmt" - "github.com/kercylan98/minotaur/utils/g2d" - "github.com/kercylan98/minotaur/utils/random" - "testing" - "time" -) - -type Item[Type comparable] struct { - guid int64 - t Type -} - -func (slf *Item[Type]) SetGuid(guid int64) { - slf.guid = guid -} - -func (slf *Item[Type]) GetGuid() int64 { - return slf.guid -} - -func (slf *Item[Type]) GetType() Type { - return slf.t -} - -func (slf *Item[Type]) Clone() Match3Item[Type] { - return &Item[Type]{ - guid: slf.guid, - t: slf.t, - } -} - -func TestMatch3(t *testing.T) { - var options []Match3Option[int, *Item[int]] - for i := 0; i < 7; i++ { - t := i + 1 - options = append(options, WithMatch3Generator[int, *Item[int]](t, func() *Item[int] { - return &Item[int]{t: t} - })) - } - var match3 = NewMatch3[int, *Item[int]](3, 3, - options..., - ) - - for x := 0; x < match3.GetWidth(); x++ { - for y := 0; y < match3.GetHeight(); y++ { - match3.GenerateItem(x, y, random.Int(1, 2)) - } - } - - for y := 0; y < match3.GetHeight(); y++ { - for x := 0; x < match3.GetWidth(); x++ { - fmt.Print(match3.matrix.m[x][y].t, " ") - } - fmt.Println() - } - fmt.Println() - links := match3.links.Get(4) - linkItem := match3.matrix.m[match3.positions[4][0]][match3.positions[4][1]] - fmt.Println("LINK", linkItem.t, match3.positions[4]) - - for y := 0; y < match3.GetHeight(); y++ { - for x := 0; x < match3.GetWidth(); x++ { - item := match3.matrix.m[x][y] - if links[item.guid] { - fmt.Print("*", " ") - } else { - fmt.Print(match3.matrix.m[x][y].t, " ") - } - } - fmt.Println() - } - - var now = time.Now() - var xys [][2]int - for guid := range links { - xys = append(xys, match3.positions[guid]) - } - - for _, rect := range g2d.SearchNotRepeatFullRectangle(2, 2, xys...) { - fmt.Println(fmt.Sprintf("找到矩形: TopLeft: (%d, %d), BottomRight: (%d, %d)", rect[0][0], rect[0][1], rect[1][0], rect[1][1])) - } - fmt.Println("耗时", time.Since(now)) - - now = time.Now() - for _, rect := range g2d.SearchNotRepeatCross(xys...) { - fmt.Print("找到十字:") - for _, points := range rect { - fmt.Print(fmt.Sprintf("{%d, %d}", points[0], points[1])) - } - fmt.Println() - } - fmt.Println("耗时", time.Since(now)) - - now = time.Now() - for _, rect := range g2d.SearchNotRepeatRightAngle(4, xys...) { - fmt.Print("找到L形:") - for _, points := range rect { - fmt.Print(fmt.Sprintf("{%d, %d}", points[0], points[1])) - } - fmt.Println() - } - fmt.Println("耗时", time.Since(now)) - - now = time.Now() - for _, rect := range g2d.SearchNotRepeatT(4, xys...) { - fmt.Print("找到T形:") - for _, points := range rect { - fmt.Print(fmt.Sprintf("{%d, %d}", points[0], points[1])) - } - fmt.Println() - } - fmt.Println("耗时", time.Since(now)) - - now = time.Now() - for _, rect := range g2d.SearchNotRepeatStraightLine(3, xys...) { - fmt.Print("找到直线:") - for _, points := range rect { - fmt.Print(fmt.Sprintf("{%d, %d}", points[0], points[1])) - } - fmt.Println() - } - fmt.Println("耗时", time.Since(now)) -} diff --git a/utils/g2d/radiation_pattern.go b/utils/g2d/radiation_pattern.go new file mode 100644 index 0000000..24fbf84 --- /dev/null +++ b/utils/g2d/radiation_pattern.go @@ -0,0 +1,125 @@ +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) *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{}, + } + for x := 0; x < len(matrix); x++ { + for y := 0; y < len(matrix[0]); y++ { + item := matrix[x][y] + rp.positions[item.GetGuid()] = PositionToArray(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记录的成员位置 +} + +// GetLinks 获取特定成员能够辐射到的所有成员 +func (slf *RadiationPattern[ItemType, Item]) GetLinks(guid int64) []int64 { + return hash.KeyToSlice(slf.links.Get(guid)) +} + +// Refresh 刷新特定位置成员并且更新其辐射信息 +func (slf *RadiationPattern[ItemType, Item]) Refresh(x, y int, item Item) { + 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.positions[item.GetGuid()] = PositionToArray(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] + 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] + if neighbour.GetType() != itemType { + 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) +} diff --git a/utils/g2d/radiation_pattern_item.go b/utils/g2d/radiation_pattern_item.go new file mode 100644 index 0000000..7949067 --- /dev/null +++ b/utils/g2d/radiation_pattern_item.go @@ -0,0 +1,6 @@ +package g2d + +type RadiationPatternItem[Type comparable] interface { + GetGuid() int64 + GetType() Type +} diff --git a/utils/slice/slice.go b/utils/slice/slice.go index c805468..5f53385 100644 --- a/utils/slice/slice.go +++ b/utils/slice/slice.go @@ -15,6 +15,19 @@ func Copy[V any](slice []V) []V { return s } +// CopyMatrix 复制二维数组 +func CopyMatrix[V any](slice [][]V) [][]V { + var s = make([][]V, len(slice), len(slice)) + for i := 0; i < len(slice); i++ { + is := make([]V, len(slice[0])) + for j := 0; j < len(slice[0]); j++ { + is[j] = slice[i][j] + } + s[i] = is + } + return s +} + // Insert 在特定索引插入元素 func Insert[V any](slice *[]V, index int, value V) { s := *slice