fixed lint errors in d2astar (#533)
* fixed lint errors in d2astar * Update astar.go
This commit is contained in:
parent
62b8a610c0
commit
18003a8543
|
@ -76,15 +76,16 @@ func (nm nodeMap) get(p Pather) *node {
|
||||||
n.pather = p
|
n.pather = p
|
||||||
nm[p] = n
|
nm[p] = n
|
||||||
}
|
}
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path calculates a short path and the distance between the two Pather nodes.
|
// Path calculates a short path and the distance between the two Pather nodes.
|
||||||
//
|
|
||||||
// If no path is found, found will be false and path will be the closest node to the target with a valid path.
|
// If no path is found, found will be false and path will be the closest node to the target with a valid path.
|
||||||
func Path(from, to Pather, maxCost float64) (path []Pather, distance float64, found bool) {
|
func Path(from, to Pather, maxCost float64) (path []Pather, distance float64, found bool) {
|
||||||
nm := nodeMapPool.Get().(nodeMap)
|
nm := nodeMapPool.Get().(nodeMap)
|
||||||
nq := priorityQueuePool.Get().(priorityQueue)
|
nq := priorityQueuePool.Get().(priorityQueue)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
for k, v := range nm {
|
for k, v := range nm {
|
||||||
v.reset()
|
v.reset()
|
||||||
|
@ -93,7 +94,9 @@ func Path(from, to Pather, maxCost float64) (path []Pather, distance float64, fo
|
||||||
}
|
}
|
||||||
|
|
||||||
nq = nq[0:0]
|
nq = nq[0:0]
|
||||||
|
|
||||||
nodeMapPool.Put(nm)
|
nodeMapPool.Put(nm)
|
||||||
|
|
||||||
priorityQueuePool.Put(nq)
|
priorityQueuePool.Put(nq)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -106,7 +109,9 @@ func Path(from, to Pather, maxCost float64) (path []Pather, distance float64, fo
|
||||||
// There's no path, fallback to closest.
|
// There's no path, fallback to closest.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
current := heap.Pop(&nq).(*node)
|
current := heap.Pop(&nq).(*node)
|
||||||
|
|
||||||
current.open = false
|
current.open = false
|
||||||
current.closed = true
|
current.closed = true
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Package d2astar provides an a* pathing algorithm
|
||||||
|
package d2astar
|
|
@ -15,18 +15,22 @@ package d2astar
|
||||||
// The key differences between this example and the Tile world:
|
// The key differences between this example and the Tile world:
|
||||||
// 1) There is no grid. Trucks have arbitrary coordinates.
|
// 1) There is no grid. Trucks have arbitrary coordinates.
|
||||||
// 2) Edges are not implied by the grid positions. Instead edges are explicitly
|
// 2) Edges are not implied by the grid positions. Instead edges are explicitly
|
||||||
// modelled as Tubes.
|
// modeled as Tubes.
|
||||||
//
|
//
|
||||||
// The key similarities between this example and the Tile world:
|
// The key similarities between this example and the Tile world:
|
||||||
// 1) They both use Manhattan distance as their heuristic
|
// 1) They both use Manhattan distance as their heuristic
|
||||||
// 2) Both implement Pather
|
// 2) Both implement Pather
|
||||||
|
|
||||||
//Goreland represents a world of trucks and tubes.
|
// Goreland represents a world of trucks and tubes.
|
||||||
type Goreland struct {
|
type Goreland struct {
|
||||||
// trucks map[int]*Truck // not needed really
|
// trucks map[int]*Truck // not needed really
|
||||||
}
|
}
|
||||||
|
|
||||||
//Tube is an edge. They connect Trucks, and have a cost.
|
const (
|
||||||
|
_tenFuckingMillion = 10000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tube is an edge. They connect Trucks, and have a cost.
|
||||||
type Tube struct {
|
type Tube struct {
|
||||||
from *Truck
|
from *Truck
|
||||||
to *Truck
|
to *Truck
|
||||||
|
@ -47,39 +51,41 @@ type Truck struct {
|
||||||
|
|
||||||
// PathNeighbors returns the neighbors of the Truck
|
// PathNeighbors returns the neighbors of the Truck
|
||||||
func (t *Truck) PathNeighbors() []Pather {
|
func (t *Truck) PathNeighbors() []Pather {
|
||||||
|
|
||||||
neighbors := []Pather{}
|
neighbors := []Pather{}
|
||||||
|
|
||||||
for _, tubeElement := range t.outTo {
|
for _, tubeElement := range t.outTo {
|
||||||
neighbors = append(neighbors, Pather(tubeElement.to))
|
neighbors = append(neighbors, Pather(tubeElement.to))
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbors
|
return neighbors
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathNeighborCost returns the cost of the tube leading to Truck.
|
// PathNeighborCost returns the cost of the tube leading to Truck.
|
||||||
func (t *Truck) PathNeighborCost(to Pather) float64 {
|
func (t *Truck) PathNeighborCost(to Pather) float64 {
|
||||||
|
|
||||||
for _, tubeElement := range (t).outTo {
|
for _, tubeElement := range (t).outTo {
|
||||||
if Pather((tubeElement.to)) == to {
|
if Pather((tubeElement.to)) == to {
|
||||||
return tubeElement.Cost
|
return tubeElement.Cost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 10000000
|
|
||||||
|
return _tenFuckingMillion
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathEstimatedCost uses Manhattan distance to estimate orthogonal distance
|
// PathEstimatedCost uses Manhattan distance to estimate orthogonal distance
|
||||||
// between non-adjacent nodes.
|
// between non-adjacent nodes.
|
||||||
func (t *Truck) PathEstimatedCost(to Pather) float64 {
|
func (t *Truck) PathEstimatedCost(to Pather) float64 {
|
||||||
|
|
||||||
toT := to.(*Truck)
|
toT := to.(*Truck)
|
||||||
absX := toT.X - t.X
|
absX := toT.X - t.X
|
||||||
|
|
||||||
if absX < 0 {
|
if absX < 0 {
|
||||||
absX = -absX
|
absX = -absX
|
||||||
}
|
}
|
||||||
|
|
||||||
absY := toT.Y - t.Y
|
absY := toT.Y - t.Y
|
||||||
if absY < 0 {
|
if absY < 0 {
|
||||||
absY = -absY
|
absY = -absY
|
||||||
}
|
}
|
||||||
|
|
||||||
r := float64(absX + absY)
|
r := float64(absX + absY)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
@ -87,11 +93,12 @@ func (t *Truck) PathEstimatedCost(to Pather) float64 {
|
||||||
|
|
||||||
// RenderPath renders a path on top of a Goreland world.
|
// RenderPath renders a path on top of a Goreland world.
|
||||||
func (w Goreland) RenderPath(path []Pather) string {
|
func (w Goreland) RenderPath(path []Pather) string {
|
||||||
|
|
||||||
s := ""
|
s := ""
|
||||||
|
|
||||||
for _, p := range path {
|
for _, p := range path {
|
||||||
pT := p.(*Truck)
|
pT := p.(*Truck)
|
||||||
s = pT.label + " " + s
|
s = pT.label + " " + s
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,16 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddTruck(x int, y int, label string) *Truck {
|
func addTruck(x, y int, label string) *Truck {
|
||||||
t1 := new(Truck)
|
t1 := new(Truck)
|
||||||
t1.X = x
|
t1.X = x
|
||||||
t1.Y = y
|
t1.Y = y
|
||||||
t1.label = label
|
t1.label = label
|
||||||
|
|
||||||
return t1
|
return t1
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddTube(t1, t2 *Truck, cost float64) *Tube {
|
func addTube(t1, t2 *Truck, cost float64) *Tube {
|
||||||
tube1 := new(Tube)
|
tube1 := new(Tube)
|
||||||
tube1.Cost = cost
|
tube1.Cost = cost
|
||||||
tube1.from = t1
|
tube1.from = t1
|
||||||
|
@ -49,17 +50,16 @@ func AddTube(t1, t2 *Truck, cost float64) *Tube {
|
||||||
// Solver should avoid the plugged tube.
|
// Solver should avoid the plugged tube.
|
||||||
// Expect solution Start,Middle,End Total cost: 2.0
|
// Expect solution Start,Middle,End Total cost: 2.0
|
||||||
|
|
||||||
func createGorelandGraphPathDiagonal(t *testing.T, diagonalCost float64, expectedDist float64) {
|
func createGorelandGraphPathDiagonal(t *testing.T, diagonalCost, expectedDist float64) {
|
||||||
|
|
||||||
world := new(Goreland)
|
world := new(Goreland)
|
||||||
|
|
||||||
trStart := AddTruck(0, 0, "Start")
|
trStart := addTruck(0, 0, "Start")
|
||||||
trMid := AddTruck(0, 1, "Middle")
|
trMid := addTruck(0, 1, "Middle")
|
||||||
trEnd := AddTruck(1, 1, "End")
|
trEnd := addTruck(1, 1, "End")
|
||||||
|
|
||||||
AddTube(trStart, trEnd, diagonalCost)
|
addTube(trStart, trEnd, diagonalCost)
|
||||||
AddTube(trStart, trMid, 1)
|
addTube(trStart, trMid, 1)
|
||||||
AddTube(trMid, trEnd, 1)
|
addTube(trMid, trEnd, 1)
|
||||||
|
|
||||||
t.Logf("Goreland. Diagonal cost: %v\n\n", diagonalCost)
|
t.Logf("Goreland. Diagonal cost: %v\n\n", diagonalCost)
|
||||||
|
|
||||||
|
@ -70,9 +70,11 @@ func createGorelandGraphPathDiagonal(t *testing.T, diagonalCost float64, expecte
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Resulting path\n%s", world.RenderPath(p))
|
t.Logf("Resulting path\n%s", world.RenderPath(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found && expectedDist >= 0 {
|
if !found && expectedDist >= 0 {
|
||||||
t.Fatal("Could not find a path")
|
t.Fatal("Could not find a path")
|
||||||
}
|
}
|
||||||
|
|
||||||
if found && dist != expectedDist {
|
if found && dist != expectedDist {
|
||||||
t.Fatalf("Expected dist to be %v but got %v", expectedDist, dist)
|
t.Fatalf("Expected dist to be %v but got %v", expectedDist, dist)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,17 @@ func testPath(worldInput string, t *testing.T, expectedDist float64) {
|
||||||
world := ParseWorld(worldInput)
|
world := ParseWorld(worldInput)
|
||||||
t.Logf("Input world\n%s", world.RenderPath([]Pather{}))
|
t.Logf("Input world\n%s", world.RenderPath([]Pather{}))
|
||||||
p, dist, found := Path(world.From(), world.To(), math.MaxFloat64)
|
p, dist, found := Path(world.From(), world.To(), math.MaxFloat64)
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
t.Log("Could not find a path")
|
t.Log("Could not find a path")
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Resulting path\n%s", world.RenderPath(p))
|
t.Logf("Resulting path\n%s", world.RenderPath(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found && expectedDist >= 0 {
|
if !found && expectedDist >= 0 {
|
||||||
t.Fatal("Could not find a path")
|
t.Fatal("Could not find a path")
|
||||||
}
|
}
|
||||||
|
|
||||||
if found && dist != expectedDist {
|
if found && dist != expectedDist {
|
||||||
t.Fatalf("Expected dist to be %v but got %v", expectedDist, dist)
|
t.Fatalf("Expected dist to be %v but got %v", expectedDist, dist)
|
||||||
}
|
}
|
||||||
|
@ -126,6 +129,7 @@ F............................~.................................................
|
||||||
..............................................X....~.......MM......X.X.X.X.X.X.
|
..............................................X....~.......MM......X.X.X.X.X.X.
|
||||||
...............................................X...~.......M.........X...X...XT
|
...............................................X...~.......M.........X...X...XT
|
||||||
`)
|
`)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
Path(world.From(), world.To(), math.MaxFloat64)
|
Path(world.From(), world.To(), math.MaxFloat64)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// KindRunes map tile kinds to output runes.
|
// KindRunes map tile kinds to output runes.
|
||||||
var KindRunes = map[int]rune{
|
var KindRunes = map[int]rune{ // nolint:gochecknoglobals // it's a test
|
||||||
KindPlain: '.',
|
KindPlain: '.',
|
||||||
KindRiver: '~',
|
KindRiver: '~',
|
||||||
KindMountain: 'M',
|
KindMountain: 'M',
|
||||||
|
@ -40,7 +40,7 @@ var KindRunes = map[int]rune{
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuneKinds map input runes to tile kinds.
|
// RuneKinds map input runes to tile kinds.
|
||||||
var RuneKinds = map[rune]int{
|
var RuneKinds = map[rune]int{ // nolint:gochecknoglobals // it's a test
|
||||||
'.': KindPlain,
|
'.': KindPlain,
|
||||||
'~': KindRiver,
|
'~': KindRiver,
|
||||||
'M': KindMountain,
|
'M': KindMountain,
|
||||||
|
@ -50,7 +50,7 @@ var RuneKinds = map[rune]int{
|
||||||
}
|
}
|
||||||
|
|
||||||
// KindCosts map tile kinds to movement costs.
|
// KindCosts map tile kinds to movement costs.
|
||||||
var KindCosts = map[int]float64{
|
var KindCosts = map[int]float64{ // nolint:gochecknoglobals // it's a test
|
||||||
KindPlain: 1.0,
|
KindPlain: 1.0,
|
||||||
KindFrom: 1.0,
|
KindFrom: 1.0,
|
||||||
KindTo: 1.0,
|
KindTo: 1.0,
|
||||||
|
@ -71,7 +71,8 @@ type Tile struct {
|
||||||
// PathNeighbors returns the neighbors of the tile, excluding blockers and
|
// PathNeighbors returns the neighbors of the tile, excluding blockers and
|
||||||
// tiles off the edge of the board.
|
// tiles off the edge of the board.
|
||||||
func (t *Tile) PathNeighbors() []Pather {
|
func (t *Tile) PathNeighbors() []Pather {
|
||||||
neighbors := []Pather{}
|
neighbors := make([]Pather, 0)
|
||||||
|
|
||||||
for _, offset := range [][]int{
|
for _, offset := range [][]int{
|
||||||
{-1, 0},
|
{-1, 0},
|
||||||
{1, 0},
|
{1, 0},
|
||||||
|
@ -83,6 +84,7 @@ func (t *Tile) PathNeighbors() []Pather {
|
||||||
neighbors = append(neighbors, n)
|
neighbors = append(neighbors, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbors
|
return neighbors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,13 +99,16 @@ func (t *Tile) PathNeighborCost(to Pather) float64 {
|
||||||
func (t *Tile) PathEstimatedCost(to Pather) float64 {
|
func (t *Tile) PathEstimatedCost(to Pather) float64 {
|
||||||
toT := to.(*Tile)
|
toT := to.(*Tile)
|
||||||
absX := toT.X - t.X
|
absX := toT.X - t.X
|
||||||
|
|
||||||
if absX < 0 {
|
if absX < 0 {
|
||||||
absX = -absX
|
absX = -absX
|
||||||
}
|
}
|
||||||
|
|
||||||
absY := toT.Y - t.Y
|
absY := toT.Y - t.Y
|
||||||
if absY < 0 {
|
if absY < 0 {
|
||||||
absY = -absY
|
absY = -absY
|
||||||
}
|
}
|
||||||
|
|
||||||
return float64(absX + absY)
|
return float64(absX + absY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +120,7 @@ func (w World) Tile(x, y int) *Tile {
|
||||||
if w[x] == nil {
|
if w[x] == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return w[x][y]
|
return w[x][y]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +129,7 @@ func (w World) SetTile(t *Tile, x, y int) {
|
||||||
if w[x] == nil {
|
if w[x] == nil {
|
||||||
w[x] = map[int]*Tile{}
|
w[x] = map[int]*Tile{}
|
||||||
}
|
}
|
||||||
|
|
||||||
w[x][y] = t
|
w[x][y] = t
|
||||||
t.X = x
|
t.X = x
|
||||||
t.Y = y
|
t.Y = y
|
||||||
|
@ -139,6 +146,7 @@ func (w World) FirstOfKind(kind int) *Tile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,41 +166,50 @@ func (w World) RenderPath(path []Pather) string {
|
||||||
if width == 0 {
|
if width == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
height := len(w[0])
|
height := len(w[0])
|
||||||
pathLocs := map[string]bool{}
|
pathLocs := map[string]bool{}
|
||||||
|
|
||||||
for _, p := range path {
|
for _, p := range path {
|
||||||
pT := p.(*Tile)
|
pT := p.(*Tile)
|
||||||
pathLocs[fmt.Sprintf("%d,%d", pT.X, pT.Y)] = true
|
pathLocs[fmt.Sprintf("%d,%d", pT.X, pT.Y)] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := make([]string, height)
|
rows := make([]string, height)
|
||||||
|
|
||||||
for x := 0; x < width; x++ {
|
for x := 0; x < width; x++ {
|
||||||
for y := 0; y < height; y++ {
|
for y := 0; y < height; y++ {
|
||||||
t := w.Tile(x, y)
|
t := w.Tile(x, y)
|
||||||
r := ' '
|
r := ' '
|
||||||
|
|
||||||
if pathLocs[fmt.Sprintf("%d,%d", x, y)] {
|
if pathLocs[fmt.Sprintf("%d,%d", x, y)] {
|
||||||
r = KindRunes[KindPath]
|
r = KindRunes[KindPath]
|
||||||
} else if t != nil {
|
} else if t != nil {
|
||||||
r = KindRunes[t.Kind]
|
r = KindRunes[t.Kind]
|
||||||
}
|
}
|
||||||
|
|
||||||
rows[y] += string(r)
|
rows[y] += string(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(rows, "\n")
|
return strings.Join(rows, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseWorld parses a textual representation of a world into a world map.
|
// ParseWorld parses a textual representation of a world into a world map.
|
||||||
func ParseWorld(input string) World {
|
func ParseWorld(input string) World {
|
||||||
w := World{}
|
w := World{}
|
||||||
|
|
||||||
for y, row := range strings.Split(strings.TrimSpace(input), "\n") {
|
for y, row := range strings.Split(strings.TrimSpace(input), "\n") {
|
||||||
for x, raw := range row {
|
for x, raw := range row {
|
||||||
kind, ok := RuneKinds[raw]
|
kind, ok := RuneKinds[raw]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
kind = KindBlocker
|
kind = KindBlocker
|
||||||
}
|
}
|
||||||
w.SetTile(&Tile{
|
|
||||||
Kind: kind,
|
w.SetTile(&Tile{Kind: kind}, x, y)
|
||||||
}, x, y)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,5 +31,6 @@ func (pq *priorityQueue) Pop() interface{} {
|
||||||
no := old[n-1]
|
no := old[n-1]
|
||||||
no.index = -1
|
no.index = -1
|
||||||
*pq = old[0 : n-1]
|
*pq = old[0 : n-1]
|
||||||
|
|
||||||
return no
|
return no
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue