三维A*寻路实现
This commit is contained in:
parent
09f9a2c8bf
commit
100268c10e
|
@ -0,0 +1,99 @@
|
|||
package astar
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Pathfinding3D 寻路
|
||||
// - grid: X、Y、Z
|
||||
func Pathfinding3D(grid [][][]int, start, target Point3D, collisionRange int) ([]Point3D, error) {
|
||||
path, err := aStar3D(grid, start, target, collisionRange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func aStar3D(grid [][][]int, start, goal Point3D, collisionRange int) ([]Point3D, error) {
|
||||
startNode := &Node3D{point: start, g: 0, h: heuristic(start, goal)}
|
||||
startNode.f = startNode.g + startNode.h
|
||||
openList := make(Node3DList, 0)
|
||||
heap.Init(&openList)
|
||||
heap.Push(&openList, startNode)
|
||||
closedList := make(map[Point3D]*Node3D)
|
||||
|
||||
for openList.Len() > 0 {
|
||||
current := heap.Pop(&openList).(*Node3D)
|
||||
if current.point == goal {
|
||||
path := make([]Point3D, 0)
|
||||
for current != nil {
|
||||
path = append([]Point3D{current.point}, path...)
|
||||
current = current.parent
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
closedList[current.point] = current
|
||||
for _, neighborPoint := range neighbors(current.point, grid, collisionRange) {
|
||||
if _, ok := closedList[neighborPoint]; ok {
|
||||
continue
|
||||
}
|
||||
tentativeG := current.g + 1
|
||||
neighborNode := &Node3D{point: neighborPoint, parent: current, g: tentativeG, h: heuristic(neighborPoint, goal)}
|
||||
neighborNode.f = neighborNode.g + neighborNode.h
|
||||
for _, openNode := range openList {
|
||||
if openNode.point == neighborPoint && tentativeG >= openNode.g {
|
||||
continue
|
||||
}
|
||||
}
|
||||
heap.Push(&openList, neighborNode)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no path found")
|
||||
}
|
||||
|
||||
func heuristic(a, b Point3D) float64 {
|
||||
return math.Abs(float64(a.X-b.X)) + math.Abs(float64(a.Y-b.Y)) + math.Abs(float64(a.Z-b.Z))
|
||||
}
|
||||
|
||||
func inBounds(point Point3D, grid [][][]int) bool {
|
||||
return point.X >= 0 && point.X < len(grid) && point.Y >= 0 && point.Y < len(grid[0]) && point.Z >= 0 && point.Z < len(grid[0][0])
|
||||
}
|
||||
|
||||
func passable(point Point3D, grid [][][]int, collisionRange int) bool {
|
||||
for x := -collisionRange; x <= collisionRange; x++ {
|
||||
for y := -collisionRange; y <= collisionRange; y++ {
|
||||
for z := -collisionRange; z <= collisionRange; z++ {
|
||||
newPoint := Point3D{point.X + x, point.Y + y, point.Z + z}
|
||||
if inBounds(newPoint, grid) && grid[newPoint.X][newPoint.Y][newPoint.Z] == 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func neighbors(point Point3D, grid [][][]int, collisionRange int) []Point3D {
|
||||
neighborPoints := []Point3D{
|
||||
{point.X - 1, point.Y, point.Z}, {point.X + 1, point.Y, point.Z},
|
||||
{point.X, point.Y - 1, point.Z}, {point.X, point.Y + 1, point.Z},
|
||||
{point.X, point.Y, point.Z - 1}, {point.X, point.Y, point.Z + 1},
|
||||
}
|
||||
result := make([]Point3D, 0)
|
||||
for _, neighbor := range neighborPoints {
|
||||
if inBounds(neighbor, grid) && passable(neighbor, grid, collisionRange) {
|
||||
result = append(result, neighbor)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package astar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPathfinding3D(t *testing.T) {
|
||||
grid := [][][]int{
|
||||
{
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 1, 1, 1, 0},
|
||||
{0, 1, 0, 1, 0},
|
||||
{0, 1, 1, 1, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{1, 0, 0, 0, 0},
|
||||
{0, 1, 0, 1, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 1, 0, 1, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 1, 1, 1, 0},
|
||||
{0, 1, 0, 1, 0},
|
||||
{0, 1, 1, 1, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0},
|
||||
},
|
||||
}
|
||||
|
||||
start := Point3D{2, 1, 0}
|
||||
target := Point3D{2, 2, 2}
|
||||
collisionRange := 0
|
||||
|
||||
path, err := Pathfinding3D(grid, start, target, collisionRange)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(path)
|
||||
printGrid(grid, start)
|
||||
}
|
||||
|
||||
func printGrid(grid [][][]int, start Point3D) {
|
||||
for z := 0; z < len(grid); z++ {
|
||||
fmt.Printf("z = %d\n", z)
|
||||
for y := 0; y < len(grid[z]); y++ {
|
||||
for x := 0; x < len(grid[z][y]); x++ {
|
||||
if x == start.X && y == start.Y && z == start.Z {
|
||||
fmt.Print("S ")
|
||||
} else if grid[z][y][x] == 0 {
|
||||
fmt.Print(". ")
|
||||
} else {
|
||||
fmt.Print("# ")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package astar
|
||||
|
||||
type Node3D struct {
|
||||
point Point3D
|
||||
parent *Node3D
|
||||
g, h, f float64
|
||||
index int
|
||||
}
|
||||
|
||||
type Node3DList []*Node3D
|
||||
|
||||
func (slf *Node3DList) Len() int {
|
||||
return len(*slf)
|
||||
}
|
||||
|
||||
func (slf *Node3DList) Less(i, j int) bool {
|
||||
nl := *slf
|
||||
return nl[i].f < nl[j].f
|
||||
}
|
||||
|
||||
func (slf *Node3DList) Swap(i, j int) {
|
||||
nl := *slf
|
||||
nl[i], nl[j] = nl[j], nl[i]
|
||||
nl[i].index = i
|
||||
nl[j].index = j
|
||||
}
|
||||
|
||||
func (slf *Node3DList) Push(x interface{}) {
|
||||
n := len(*slf)
|
||||
node := x.(*Node3D)
|
||||
node.index = n
|
||||
*slf = append(*slf, node)
|
||||
}
|
||||
|
||||
func (slf *Node3DList) Pop() interface{} {
|
||||
old := *slf
|
||||
n := len(old)
|
||||
node := old[n-1]
|
||||
node.index = -1
|
||||
*slf = old[0 : n-1]
|
||||
return node
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package astar
|
||||
|
||||
type Point3D struct {
|
||||
X, Y, Z int
|
||||
}
|
Loading…
Reference in New Issue