diff --git a/utils/g2d/matrix/match3_test.go b/utils/g2d/matrix/match3_test.go index 96d255a..d58df0f 100644 --- a/utils/g2d/matrix/match3_test.go +++ b/utils/g2d/matrix/match3_test.go @@ -33,7 +33,7 @@ func TestMatch3(t *testing.T) { return &Item[int]{t: t} })) } - var match3 = NewMatch3[int, *Item[int]](9, 9, + var match3 = NewMatch3[int, *Item[int]](3, 3, options..., ) @@ -50,9 +50,9 @@ func TestMatch3(t *testing.T) { fmt.Println() } fmt.Println() - links := match3.links.Get(40) - linkItem := match3.matrix.m[match3.positions[40][0]][match3.positions[40][1]] - fmt.Println("LINK", linkItem.t, match3.positions[40]) + 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++ { @@ -72,7 +72,7 @@ func TestMatch3(t *testing.T) { xys = append(xys, match3.positions[guid]) } - for _, rect := range g2d.SearchNotRepeatFullRectangle(xys...) { + 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)) @@ -86,4 +86,14 @@ func TestMatch3(t *testing.T) { 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)) } diff --git a/utils/g2d/shape.go b/utils/g2d/shape.go index 4205faa..1d682c8 100644 --- a/utils/g2d/shape.go +++ b/utils/g2d/shape.go @@ -2,6 +2,7 @@ package g2d import ( "github.com/kercylan98/minotaur/utils/g2d/shape" + "sort" ) type MatrixShapeSearchResult[Mark any] struct { @@ -141,75 +142,183 @@ func SearchNotRepeatCross(xys ...[2]int) (result [][][2]int) { record := map[int]map[int]bool{} for x := 0; x < len(rectangleShape); x++ { for y := 0; y < len(rectangleShape[0]); y++ { - record[x] = map[int]bool{y: true} + record[x] = map[int]bool{} } } for _, xy := range xys { var points [][2]int - var records [][2]int var find = map[int]bool{} x, y := PositionArrayToXY(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 for sx := x - 1; sx >= 0; sx-- { - if record[sx][y] || !rectangleShape[sx][y] { + if !rectangleShape[sx][y] { break } find[1] = true points = append(points, [2]int{sx + left, y + top}) - records = append(records, [2]int{sx, y}) } if !find[1] { continue } for sx := x + 1; sx < len(rectangleShape); sx++ { - if record[sx][y] || !rectangleShape[sx][y] { + if !rectangleShape[sx][y] { break } find[2] = true points = append(points, [2]int{sx + left, y + top}) - records = append(records, [2]int{sx, y}) } if !find[2] { continue } for sy := y - 1; sy >= 0; sy-- { - if record[x][sy] || !rectangleShape[x][sy] { + if !rectangleShape[x][sy] { break } find[3] = true points = append(points, [2]int{x + left, sy + top}) - records = append(records, [2]int{x, sy}) } if !find[3] { continue } - for sy := y + 1; sy <= len(rectangleShape[0]); sy++ { - if record[x][sy] || !rectangleShape[x][sy] { + for sy := y + 1; sy < len(rectangleShape[0]); sy++ { + if !rectangleShape[x][sy] { break } find[4] = true points = append(points, [2]int{x + left, sy + top}) - records = append(records, [2]int{x, sy}) } if !find[4] { continue } - for _, point := range records { - record[point[0]][point[1]] = true - } result = append(result, append(points, [2]int{x + left, y + top})) } - return + sort.Slice(result, func(i, j int) bool { + return len(result[i]) > len(result[j]) + }) + + var notRepeat [][][2]int + for _, points := range result { + var match = true + for _, point := range points { + x, y := PositionArrayToXY(point) + x = x + (0 - left) + y = y + (0 - top) + if record[x][y] { + match = false + break + } + record[x][y] = true + } + if match { + notRepeat = append(notRepeat, points) + } + } + + return notRepeat +} + +// SearchNotRepeatRightAngle 在一组二维坐标中从大到小搜索不重复的直角(L)线 +func SearchNotRepeatRightAngle(minLength int, xys ...[2]int) (result [][][2]int) { + if minLength < 3 { + return nil + } + left, _, top, _ := GetShapeCoverageArea(xys...) + rectangleShape := GenerateShape(xys...) + record := map[int]map[int]bool{} + for x := 0; x < len(rectangleShape); x++ { + for y := 0; y < len(rectangleShape[0]); y++ { + record[x] = map[int]bool{} + } + } + + for _, xy := range xys { + var points [][2]int + var find = map[int]bool{} + x, y := PositionArrayToXY(xy) + x = x + (0 - left) + y = y + (0 - top) + // 搜索四个方向 + for sx := x - 1; sx >= 0; sx-- { + if !rectangleShape[sx][y] { + break + } + find[1] = true + points = append(points, [2]int{sx + left, y + top}) + } + if find[1] { + goto up + } + for sx := x + 1; sx < len(rectangleShape); sx++ { + if !rectangleShape[sx][y] { + break + } + find[2] = true + points = append(points, [2]int{sx + left, y + top}) + } + up: + for sy := y - 1; sy >= 0; sy-- { + if !rectangleShape[x][sy] { + break + } + find[3] = true + points = append(points, [2]int{x + left, sy + top}) + } + if find[3] { + goto end + } + // down + for sy := y + 1; sy < len(rectangleShape[0]); sy++ { + if !rectangleShape[x][sy] { + break + } + find[4] = true + points = append(points, [2]int{x + left, sy + top}) + } + if !find[4] { + continue + } + end: + { + result = append(result, append(points, [2]int{x + left, y + top})) + } + } + + sort.Slice(result, func(i, j int) bool { + return len(result[i]) > len(result[j]) + }) + + var notRepeat [][][2]int + for _, points := range result { + if len(points) < minLength { + continue + } + var match = true + for _, point := range points { + x, y := PositionArrayToXY(point) + x = x + (0 - left) + y = y + (0 - top) + if record[x][y] { + match = false + break + } + record[x][y] = true + } + if match { + notRepeat = append(notRepeat, points) + } + } + + return notRepeat } // SearchNotRepeatFullRectangle 在一组二维坐标中从大到小搜索不重复的填充满的矩形 // - 不重复指一个位置被使用后将不会被其他矩形使用 // - 返回值表示了匹配的形状的左上角和右下角的点坐标 -func SearchNotRepeatFullRectangle(xys ...[2]int) (result [][2][2]int) { +func SearchNotRepeatFullRectangle(minWidth, minHeight int, xys ...[2]int) (result [][2][2]int) { left, _, top, _ := GetShapeCoverageArea(xys...) rectangleShape := GenerateShape(xys...) record := map[int]map[int]bool{} @@ -221,7 +330,7 @@ func SearchNotRepeatFullRectangle(xys ...[2]int) (result [][2][2]int) { } } - shapes := GetExpressibleRectangleBySize(width, height, 2, 2) + shapes := GetExpressibleRectangleBySize(width, height, minWidth, minHeight) for _, s := range shapes { x, y := 0, 0 for { @@ -284,6 +393,9 @@ func GetExpressibleRectangleBySize(width, height, minWidth, minHeight int) (resu if width == 0 || height == 0 { return nil } + if width < minWidth || height < minHeight { + return nil + } width-- height-- for { diff --git a/utils/g2d/shape_test.go b/utils/g2d/shape_test.go index a4e28f1..ce3a0bf 100644 --- a/utils/g2d/shape_test.go +++ b/utils/g2d/shape_test.go @@ -16,3 +16,15 @@ func TestGetShapeCoverageArea(t *testing.T) { fmt.Println() } } + +func TestGetExpressibleRectangleBySize(t *testing.T) { + for _, xy := range GetExpressibleRectangleBySize(3, 3, 2, 2) { + for y := 0; y < xy[1]+1; y++ { + for x := 0; x < xy[0]+1; x++ { + fmt.Print("0", " ") + } + fmt.Println() + } + fmt.Println() + } +}