package astar import ( "container/heap" "fmt" "github.com/perlw/advent_of_code/toolkit/grid" "github.com/perlw/advent_of_code/toolkit/pq" ) type Node struct { x int y int px int py int } func getNeighbours(node Node, width, height int) []Node { result := make([]Node, 0, 4) if node.x > 0 { result = append(result, Node{ x: node.x - 1, y: node.y, }) } if node.x < width-1 { result = append(result, Node{ x: node.x + 1, y: node.y, }) } if node.y > 0 { result = append(result, Node{ x: node.x, y: node.y - 1, }) } if node.y < height-1 { result = append(result, Node{ x: node.x, y: node.y + 1, }) } return result } func abs(x int) int { if x < 0 { return -x } return x } func heuristic(a, b Node) int { return (abs(a.x-b.x) + abs(a.y-a.y)) } // FindPath finds the path between start and goal (inclusively). func FindPath( g grid.Grid, startx, starty, goalx, goaly int, genPathGrid bool, ) ([]Node, *grid.Grid) { open := make(pq.PriorityQueue, 0, g.Width*g.Height) heap.Init(&open) heap.Push(&open, &pq.Item{ Value: Node{ x: startx, y: starty, px: startx, py: starty, }, Priority: 0, }) closed := make(pq.PriorityQueue, 0, g.Width*g.Height) heap.Init(&closed) var outGrid *grid.Grid if genPathGrid { outGrid = &grid.Grid{ Cells: make([]grid.Cell, len(g.Cells)), Width: g.Width, Height: g.Height, Label: g.Label, } copy(outGrid.Cells, g.Cells) outGrid.Set(startx, starty, grid.Cell{ R: 'S', }) outGrid.Set(goalx, goaly, grid.Cell{ R: 'G', }) } goal := Node{ x: goalx, y: goaly, } for len(open) > 0 { item := heap.Pop(&open).(*pq.Item) current := item.Value.(Node) if current.x == goal.x && current.y == goal.y { break } heap.Push(&closed, &pq.Item{ Value: current, Priority: item.Priority, }) neighbours := getNeighbours(current, g.Width, g.Height) for _, next := range neighbours { if closed.Has(next, func(a, b interface{}) bool { aa := a.(Node) bb := b.(Node) return aa.x == bb.x && aa.y == bb.y }) { continue } if open.Has(next, func(a, b interface{}) bool { aa := a.(Node) bb := b.(Node) return aa.x == bb.x && aa.y == bb.y }) { continue } heap.Push(&open, &pq.Item{ Value: Node{ x: next.x, y: next.y, px: current.x, py: current.y, }, Priority: item.Priority + heuristic(next, goal), }) } } fmt.Printf("open: [\n") for _, item := range open { fmt.Printf("%d %+v,\n", item.Priority, item.Value.(Node)) } fmt.Printf("]\n") fmt.Printf("closed: [\n") for _, item := range closed { fmt.Printf("%d %+v,\n", item.Priority, item.Value.(Node)) } fmt.Printf("]\n") if genPathGrid { for _, item := range open { node := item.Value.(Node) dir := Node{ x: node.x - node.px, y: node.y - node.py, } var r rune if dir.x > 0 { r = '←' } else if dir.x < 0 { r = '→' } else if dir.y > 0 { r = '↑' } else if dir.y < 0 { r = '↓' } if outGrid.Get(node.x, node.y).R == ' ' { outGrid.Set(node.x, node.y, grid.Cell{ R: r, }) } } } return []Node{}, outGrid }