Skip to content

Commit 5ee8e24

Browse files
committed
chore: add astar variation of pathfinding
1 parent dfbc0c0 commit 5ee8e24

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package astar
2+
3+
import (
4+
"container/heap"
5+
"math"
6+
7+
"github.com/believer/aoc-2025/utils/grid"
8+
)
9+
10+
type Node struct {
11+
Point grid.Point
12+
Direction grid.Point
13+
}
14+
15+
type Item struct {
16+
Node Node
17+
Cost int
18+
Heuristic int
19+
Path []grid.Point
20+
}
21+
22+
type Queue []*Item
23+
24+
func (pq Queue) Len() int { return len(pq) }
25+
func (pq Queue) Less(i, j int) bool {
26+
return pq[i].Cost+pq[i].Heuristic < pq[j].Cost+pq[j].Heuristic
27+
}
28+
func (pq Queue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
29+
func (pq *Queue) Push(x any) { *pq = append(*pq, x.(*Item)) }
30+
31+
func (pq *Queue) Pop() any {
32+
old := *pq
33+
n := len(old)
34+
item := old[n-1]
35+
*pq = old[0 : n-1]
36+
37+
return item
38+
}
39+
40+
func heuristic(a, b grid.Point) int {
41+
dx := a.X - b.X
42+
if dx < 0 {
43+
dx = -dx
44+
}
45+
dy := a.Y - b.Y
46+
if dy < 0 {
47+
dy = -dy
48+
}
49+
return dx + dy
50+
}
51+
52+
type Astar struct {
53+
Current *Item
54+
Seen map[Node]int
55+
Score int
56+
Queue *Queue
57+
}
58+
59+
func New(start, direction grid.Point) Astar {
60+
queue := &Queue{}
61+
startNode := Node{
62+
Point: start,
63+
Direction: direction,
64+
}
65+
66+
item := Item{
67+
Node: startNode,
68+
Cost: 0,
69+
Path: []grid.Point{start},
70+
}
71+
72+
heap.Init(queue)
73+
heap.Push(queue, &item)
74+
75+
return Astar{
76+
Current: &item,
77+
Seen: map[Node]int{},
78+
Score: math.MaxInt,
79+
Queue: queue,
80+
}
81+
}
82+
83+
// Length of queue
84+
func (a *Astar) Len() int {
85+
return a.Queue.Len()
86+
}
87+
88+
// Grab the first item from the queue
89+
func (a *Astar) Pop() *Item {
90+
a.Current = heap.Pop(a.Queue).(*Item)
91+
return a.Current
92+
}
93+
94+
// Report whether the current item is more expensive
95+
// than the lowest score
96+
func (a *Astar) IsExpensive() bool {
97+
return a.Current.Cost > a.Score
98+
}
99+
100+
// Report whether the current item has been seen
101+
func (a *Astar) HasSeen() bool {
102+
if v, ok := a.Seen[a.Current.Node]; ok && v < a.Current.Cost {
103+
return true
104+
}
105+
106+
a.Seen[a.Current.Node] = a.Current.Cost
107+
108+
return false
109+
}
110+
111+
// Report whether we've reach a given point (usually the end)
112+
// and if the cost is lower or equal to the lowest score
113+
func (a *Astar) At(point grid.Point) bool {
114+
return a.Current.Node.Point == point
115+
}
116+
117+
// Add a new item to the queue
118+
func (a *Astar) Push(cost int, path []grid.Point, node Node, goal grid.Point) {
119+
h := heuristic(node.Point, goal)
120+
121+
heap.Push(a.Queue, &Item{
122+
Cost: cost,
123+
Path: path,
124+
Heuristic: h,
125+
Node: node,
126+
})
127+
}

go/2025/utils/pathfinding/dijkstra/dijkstra.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ type Queue []*Item
2323
func (pq Queue) Len() int { return len(pq) }
2424
func (pq Queue) Less(i, j int) bool { return pq[i].Cost < pq[j].Cost }
2525
func (pq Queue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
26-
27-
func (pq *Queue) Push(x any) {
28-
*pq = append(*pq, x.(*Item))
29-
}
26+
func (pq *Queue) Push(x any) { *pq = append(*pq, x.(*Item)) }
3027

3128
func (pq *Queue) Pop() any {
3229
old := *pq

0 commit comments

Comments
 (0)