100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package soko
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"opslag.de/schobers/geom"
|
|
)
|
|
|
|
type PathFinder struct {
|
|
state State
|
|
}
|
|
|
|
func NewPathFinder(s State) PathFinder {
|
|
return PathFinder{s}
|
|
}
|
|
|
|
type betterNeighbourFn func(geom.Point, int)
|
|
|
|
func (p PathFinder) findBetterNeighbours(distances map[geom.Point]int, curr geom.Point, better betterNeighbourFn) {
|
|
currDistance := distances[curr]
|
|
newDistance := currDistance + 1
|
|
|
|
neighbours := Neighbours(curr)
|
|
for _, next := range neighbours {
|
|
if !p.state.IsWalkable(next) || p.state.Bricks.Has(next) { // filter neighbours
|
|
continue
|
|
}
|
|
if distance, ok := distances[next]; ok && distance <= newDistance { // skip when shorter path exists
|
|
continue
|
|
}
|
|
distances[next] = newDistance
|
|
better(next, newDistance)
|
|
}
|
|
}
|
|
|
|
func (p PathFinder) Find(target geom.Point) []geom.Point {
|
|
source := p.state.Player.Pos
|
|
frontier := []geom.Point{source}
|
|
distances := map[geom.Point]int{source: 0}
|
|
moves := map[geom.Point]geom.Point{source: source}
|
|
heuristic := func(i int) int {
|
|
pos := frontier[i]
|
|
return distances[pos] + pos.DistInt(target)
|
|
}
|
|
for {
|
|
curr := frontier[0]
|
|
if curr == target {
|
|
break
|
|
}
|
|
frontier = frontier[1:]
|
|
|
|
p.findBetterNeighbours(distances, curr, func(next geom.Point, newDistance int) {
|
|
moves[next] = curr
|
|
frontier = append(frontier, next)
|
|
})
|
|
|
|
if len(frontier) == 0 {
|
|
return nil // no path
|
|
}
|
|
|
|
// apply heuristic to frontier (favor points closer to target)
|
|
sort.Slice(frontier, func(i, j int) bool { return heuristic(i) < heuristic(j) })
|
|
}
|
|
|
|
// build reverse path
|
|
curr := target
|
|
path := []geom.Point{curr}
|
|
for {
|
|
curr = moves[curr]
|
|
if curr == source {
|
|
break
|
|
}
|
|
path = append(path, curr)
|
|
}
|
|
// reverse path
|
|
n := len(path)
|
|
for i := 0; i < n/2; i++ {
|
|
path[i], path[n-i-1] = path[n-i-1], path[i]
|
|
}
|
|
return path
|
|
}
|
|
|
|
func (p PathFinder) FindDistances() map[geom.Point]int {
|
|
source := p.state.Player.Pos
|
|
frontier := []geom.Point{source}
|
|
distances := map[geom.Point]int{source: 0}
|
|
for {
|
|
curr := frontier[0]
|
|
frontier = frontier[1:]
|
|
|
|
p.findBetterNeighbours(distances, curr, func(next geom.Point, newDistance int) {
|
|
frontier = append(frontier, next)
|
|
})
|
|
|
|
if len(frontier) == 0 {
|
|
return distances
|
|
}
|
|
}
|
|
}
|