| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- package astar6
- import (
- "f1-game/internal/types"
- "fmt"
- "testing"
- "time"
- )
- var (
- config1 = Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(9, 29),
- MapData: [][]int32{
- // > Column or Height
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, // v Row ro Width
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1},
- {1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1},
- {1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1},
- {1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1},
- {1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1},
- {1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1},
- },
- IsDebug: true,
- }
- config2 = Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(9, 4),
- MapData: [][]int32{
- // > Column or Height
- {1, 1, 1, 1, 1}, // v Row ro Width
- {1, 0, 1, 0, 0},
- {1, 1, 0, 1, 1},
- {1, 1, 1, 0, 1},
- {1, 1, 1, 0, 1},
- {1, 1, 0, 0, 1},
- {1, 1, 0, 1, 1},
- {1, 1, 0, 1, 1},
- {1, 0, 1, 1, 1},
- {1, 1, 1, 1, 1},
- },
- IsDebug: true,
- }
- )
- func TestAstar(t *testing.T) {
- astar := New(config1)
- // astar := New(config2)
- startTime := time.Now().UnixNano()
- fmt.Printf("开始:%d\n", startTime)
- path, ok := astar.FindPath(DefaultFindContext(0, 0, 9, 29))
- // path, ok := astar.FindPath(0, 0, 9, 4)
- // path, ok := astar.FindPath(0, 0, 3, 8)
- endTime := time.Now().UnixNano()
- fmt.Printf("结束:%d\n", endTime)
- fmt.Printf("耗时:%v(ns)\n", endTime-startTime)
- if !ok {
- t.Error("find path failed")
- } else {
- astar.Print(path)
- }
- }
- // TestAstarVisualStraightPath 测试视觉上更直的路径
- // 验证从(1,1)到(7,2)的路径是否更接近视觉直线
- func TestAstarVisualStraightPath(t *testing.T) {
- config := Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(9, 9),
- MapData: [][]int32{
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- },
- IsDebug: true,
- }
- astar := New(config)
- // 测试从(1,1)到(7,2)的路径
- path, ok := astar.FindPath(DefaultFindContext(1, 1, 7, 2))
- if !ok {
- t.Error("find path failed")
- return
- }
- fmt.Println("\n=== 测试视觉直线路径 (1,1) -> (7,2) ===")
- fmt.Printf("路径: %v\n", path)
- // 打印路径节点
- for i, node := range path {
- fmt.Printf("步骤%d: (%d, %d)\n", i, node.X(), node.Y())
- }
- // 验证路径是否包含(5,3)点,这是视觉上更直的路径标志
- has53 := false
- for _, node := range path {
- if node.X() == 5 && node.Y() == 3 {
- has53 = true
- break
- }
- }
- if has53 {
- fmt.Println("\n✓ 路径包含(5,3)点,这是视觉上更直的路径!")
- } else {
- fmt.Println("\n路径不包含(5,3)点")
- }
- }
- // BenchmarkFindPath 性能基准测试
- func BenchmarkFindPath(b *testing.B) {
- astar := New(config1)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- astar.FindPath(DefaultFindContext(0, 0, 9, 29))
- }
- }
- // BenchmarkFindPathParallel 并行性能测试
- func BenchmarkFindPathParallel(b *testing.B) {
- astar := New(config1)
- b.ResetTimer()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- astar.FindPath(DefaultFindContext(0, 0, 9, 29))
- }
- })
- }
- // TestConcurrentFindPath 并发安全测试
- func TestConcurrentFindPath(t *testing.T) {
- astar := New(config1)
- const goroutines = 100
- const iterations = 50
- done := make(chan bool, goroutines)
- for g := 0; g < goroutines; g++ {
- go func() {
- for i := 0; i < iterations; i++ {
- path, ok := astar.FindPath(DefaultFindContext(0, 0, 9, 29))
- if !ok {
- t.Error("concurrent find path failed")
- return
- }
- if len(path) == 0 {
- t.Error("concurrent find path returned empty")
- return
- }
- }
- done <- true
- }()
- }
- // 等待所有goroutine完成
- for g := 0; g < goroutines; g++ {
- <-done
- }
- fmt.Printf("並发测试完成: %d 个goroutine x %d 次迭代 = %d 次寻路\n", goroutines, iterations, goroutines*iterations)
- }
- // TestDynamicWalkableNode 测试动态设置节点为不可行走
- // 验证了 bug 修复:在寻路过程中,应诠节点的可行走状态
- func TestDynamicWalkableNode(t *testing.T) {
- config := Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(9, 9),
- MapData: [][]int32{
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- },
- IsDebug: false,
- }
- astar := New(config)
- // 第一次寻路:正常的直线路径
- path1, ok1 := astar.FindPath(DefaultFindContext(0, 0, 9, 9))
- if !ok1 {
- t.Error("First path finding failed")
- return
- }
- fmt.Printf("第一次寻路长度: %d\n", len(path1))
- // 动态设置中间节点(5,5)为不可行走
- middleNode := astar.GetNode(5, 5)
- if middleNode != nil {
- middleNode.SetWalkable(false)
- fmt.Println("已设置节点(5,5)为不可行走")
- }
- // 第二次寻路:与上一次不同(应诠中间节点)
- path2, ok2 := astar.FindPath(DefaultFindContext(0, 0, 9, 9))
- if !ok2 {
- t.Error("Second path finding failed after setting node as unwalkable")
- return
- }
- fmt.Printf("第二次寻路长度: %d\n", len(path2))
- // 验证路径有效:设置障碍后的路径应诠(5,5)节点
- for _, node := range path2 {
- if node.X() == 5 && node.Y() == 5 {
- t.Error("Path contains the unwalkable node (5,5), bug not fixed!")
- return
- }
- }
- fmt.Println("✓ 路径不包含不可行走节点(5,5),bug 修复正确!")
- // 恢复节点可行走状态
- if middleNode != nil {
- middleNode.SetWalkable(true)
- fmt.Println("已恢复节点(5,5)为可行走")
- }
- // 第三次寻路:应诠恢复为path1的类似正常路径
- path3, ok3 := astar.FindPath(DefaultFindContext(0, 0, 9, 9))
- if !ok3 {
- t.Error("Third path finding failed after restoring node as walkable")
- return
- }
- fmt.Printf("第三次寻路长度: %d\n", len(path3))
- }
- // BenchmarkFindPathWithObstacles 测试优化效果
- // 模拟实际游戏场景:1000x1000地图、从(2,1)到(98,97)、有障碍点
- func BenchmarkFindPathWithObstacles(b *testing.B) {
- // 创建1000x1000地图
- mapSize := int32(1000)
- mapData := make([][]int32, mapSize)
- for i := range mapData {
- mapData[i] = make([]int32, mapSize)
- for j := range mapData[i] {
- mapData[i][j] = 1
- }
- }
- // 随机放置1000个障碍点
- for i := 0; i < 1000; i++ {
- x := int32(i % 1000)
- y := int32(i / 1000)
- if x > 0 && x < mapSize && y > 0 && y < mapSize {
- mapData[x][y] = 0
- }
- }
- config := Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(mapSize-1, mapSize-1),
- MapData: mapData,
- }
- astar := New(config)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- // 从(2,1)到(98,97)的寻路
- astar.FindPath(DefaultFindContext(2, 1, 98, 97))
- }
- }
- // BenchmarkMemoryUsage 内存使用基准测试
- // 测试每次寻路的内存分配情况
- func BenchmarkMemoryUsage(b *testing.B) {
- astar := New(config1)
- b.ReportAllocs() // 报告内存分配
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- astar.FindPath(DefaultFindContext(0, 0, 9, 29))
- }
- }
- // BenchmarkShortPath 短距离寻路基准测试
- func BenchmarkShortPath(b *testing.B) {
- config := Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(20, 20),
- MapData: [][]int32{
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
- },
- }
- astar := New(config)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- astar.FindPath(DefaultFindContext(0, 0, 19, 19))
- }
- }
- // BenchmarkMediumPath 中等距离寻路基准测试
- func BenchmarkMediumPath(b *testing.B) {
- config := Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(100, 100),
- MapData: make([][]int32, 101),
- }
- for i := range config.MapData {
- config.MapData[i] = make([]int32, 101)
- for j := range config.MapData[i] {
- config.MapData[i][j] = 1
- }
- }
- astar := New(config)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- astar.FindPath(DefaultFindContext(0, 0, 99, 99))
- }
- }
- // BenchmarkLargePath 大型地图寻路基准测试
- func BenchmarkLargePath(b *testing.B) {
- config := Config{
- Min: types.NewPoint(0, 0),
- Max: types.NewPoint(500, 500),
- MapData: make([][]int32, 501),
- }
- for i := range config.MapData {
- config.MapData[i] = make([]int32, 501)
- for j := range config.MapData[i] {
- config.MapData[i][j] = 1
- }
- }
- astar := New(config)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- astar.FindPath(DefaultFindContext(0, 0, 499, 499))
- }
- }
|