🎨 优化单元测试及示例

This commit is contained in:
kercylan98
2023-06-19 10:52:53 +08:00
parent 1059f9c016
commit ea7ed7ea4e
4 changed files with 303 additions and 63 deletions

View File

@@ -1,16 +1,49 @@
package geometry
import (
"bytes"
"fmt"
"github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/slice"
"math"
"sort"
"strings"
)
var (
ShapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
)
// NewShape 通过多个点生成一个形状进行返回
func NewShape[V generic.SignedNumber](points ...Point[V]) Shape[V] {
return points
}
// NewShapeWithString 通过字符串将指定 rune 转换为点位置生成形状进行返回
// - 每个点的顺序从上到下,从左到右
func NewShapeWithString[V generic.SignedNumber](rows []string, point rune) (shape Shape[V]) {
var width int
for _, row := range rows {
length := len(row)
if length > width {
width = length
}
}
for y := 0; y < len(rows); y++ {
runes := bytes.Runes([]byte(rows[y]))
for x := 0; x < width; x++ {
if x >= len(runes) {
break
}
if point == runes[x] {
shape = append(shape, NewPoint(V(x), V(y)))
}
}
}
return shape
}
// Shape 通过多个点表示了一个形状
type Shape[V generic.SignedNumber] []Point[V]
@@ -19,6 +52,11 @@ func (slf Shape[V]) Points() []Point[V] {
return slf
}
// PointCount 获取这个形状的点数量
func (slf Shape[V]) PointCount() int {
return len(slf)
}
// String 将该形状转换为可视化的字符串进行返回
func (slf Shape[V]) String() string {
var result string
@@ -41,9 +79,18 @@ func (slf Shape[V]) String() string {
result += "# "
}
}
result += "\r\n"
result = result[:len(result)-1]
result += "\n"
}
} else {
if left < 0 {
left += -left
right += -left
}
if top < 0 {
top += -top
bottom += -top
}
for y := V(0); y < top+height; y++ {
for x := V(0); x < left+width; x++ {
exist := false
@@ -59,10 +106,11 @@ func (slf Shape[V]) String() string {
result += "# "
}
}
result += "\r\n"
result = result[:len(result)-1]
result += "\n"
}
}
return result
return fmt.Sprintf("%v\n%s", slf.Points(), strings.TrimSuffix(result, "\n"))
}
// ShapeSearch 获取该形状中包含的所有图形组合及其位置
@@ -72,7 +120,7 @@ func (slf Shape[V]) String() string {
//
// 可通过可选项对搜索结果进行过滤
func (slf Shape[V]) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) {
opt := &shapeSearchOptions{upperLimit: math.MaxInt, rectangleMaxWidth: math.MaxInt, rectangleMaxHeight: math.MaxInt}
opt := newShapeSearchOptions()
opt.directionCountUpper = map[Direction]int{}
for _, d := range DirectionUDLR {
opt.directionCountUpper[d] = math.MaxInt
@@ -143,7 +191,7 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
height := bottom - top + 1
areaWidth := width + left
areaHeight := height + top
rectangleShape := GenerateShapeOnRectangle(slf.Points()...)
rectangleShape := GenerateShapeOnRectangleWithCoordinate(slf.Points()...)
records := make(map[V]struct{})
var match = func(links Shape[V], directionCount map[Direction]int, count int) bool {
@@ -229,53 +277,55 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
// 搜索四个方向
var next = -1
var directionPoint = point
var links = Shape[V]{point}
var links = Shape[V]{}
var linkRecord = map[V]struct{}{}
var directionCount = map[Direction]int{}
var count = 0
for i, directions := range [][]Direction{DirectionUDLR, DirectionLRUD} {
var direction Direction
next, direction = slice.NextLoop(directions, next)
for {
directionPoint = GetDirectionNextWithCoordinateArray(direction, directionPoint)
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
break
}
offset := directionPoint.GetOffset(-left, -top)
if offset.Negative() {
break
}
offsetPos := int(offset.GetPos(width))
if offsetPos < 0 || offsetPos >= len(rectangleShape) || !rectangleShape[offsetPos].Data {
break
}
links = append(links, directionPoint)
directionCount[direction]++
count++
match(links, directionCount, count)
next, direction = slice.NextLoop(directions, next)
for {
directionPoint = GetDirectionNextWithCoordinateArray(direction, directionPoint)
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
break
}
offset := directionPoint.GetOffset(-left, -top)
if offset.OutOf(V(0), V(0), width, height) || !rectangleShape[int(offset.GetX())][int(offset.GetY())] {
break
}
recordPos := directionPoint.GetPos(areaWidth)
if _, exist := linkRecord[recordPos]; !exist {
linkRecord[recordPos] = struct{}{}
links = append(links, directionPoint)
directionCount[direction]++
count++
match(links, directionCount, count)
}
pos := directionPoint.GetPos(areaWidth)
if _, exist := records[pos]; !exist {
result = append(result, Shape[V]{directionPoint})
records[pos] = struct{}{}
pos := directionPoint.GetPos(areaWidth)
if _, exist := records[pos]; !exist && opt.directionCount < 1 {
result = append(result, Shape[V]{directionPoint})
records[pos] = struct{}{}
}
}
}
finish := false
switch i {
case 0:
if direction == DirectionRight {
finish = true
finish := false
switch i {
case 0:
if direction == DirectionRight {
finish = true
}
case 1:
if direction == DirectionDown {
finish = true
}
}
case 1:
if direction == DirectionDown {
finish = true
if finish {
break
}
directionPoint = point
}
if finish {
break
}
directionPoint = point
}
}