fixed lint errors in d2astar (#533)

* fixed lint errors in d2astar

* Update astar.go
This commit is contained in:
dk 2020-07-03 15:33:14 -07:00 committed by GitHub
parent 62b8a610c0
commit 18003a8543
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 26 deletions

View File

@ -76,15 +76,16 @@ func (nm nodeMap) get(p Pather) *node {
n.pather = p
nm[p] = n
}
return n
}
// 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.
func Path(from, to Pather, maxCost float64) (path []Pather, distance float64, found bool) {
nm := nodeMapPool.Get().(nodeMap)
nq := priorityQueuePool.Get().(priorityQueue)
defer func() {
for k, v := range nm {
v.reset()
@ -93,7 +94,9 @@ func Path(from, to Pather, maxCost float64) (path []Pather, distance float64, fo
}
nq = nq[0:0]
nodeMapPool.Put(nm)
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.
break
}
current := heap.Pop(&nq).(*node)
current.open = false
current.closed = true

2
d2common/d2astar/doc.go Normal file
View File

@ -0,0 +1,2 @@
// Package d2astar provides an a* pathing algorithm
package d2astar

View File

@ -15,18 +15,22 @@ package d2astar
// The key differences between this example and the Tile world:
// 1) There is no grid. Trucks have arbitrary coordinates.
// 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:
// 1) They both use Manhattan distance as their heuristic
// 2) Both implement Pather
//Goreland represents a world of trucks and tubes.
// Goreland represents a world of trucks and tubes.
type Goreland struct {
// 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 {
from *Truck
to *Truck
@ -47,39 +51,41 @@ type Truck struct {
// PathNeighbors returns the neighbors of the Truck
func (t *Truck) PathNeighbors() []Pather {
neighbors := []Pather{}
for _, tubeElement := range t.outTo {
neighbors = append(neighbors, Pather(tubeElement.to))
}
return neighbors
}
// PathNeighborCost returns the cost of the tube leading to Truck.
func (t *Truck) PathNeighborCost(to Pather) float64 {
for _, tubeElement := range (t).outTo {
if Pather((tubeElement.to)) == to {
return tubeElement.Cost
}
}
return 10000000
return _tenFuckingMillion
}
// PathEstimatedCost uses Manhattan distance to estimate orthogonal distance
// between non-adjacent nodes.
func (t *Truck) PathEstimatedCost(to Pather) float64 {
toT := to.(*Truck)
absX := toT.X - t.X
if absX < 0 {
absX = -absX
}
absY := toT.Y - t.Y
if absY < 0 {
absY = -absY
}
r := float64(absX + absY)
return r
@ -87,11 +93,12 @@ func (t *Truck) PathEstimatedCost(to Pather) float64 {
// RenderPath renders a path on top of a Goreland world.
func (w Goreland) RenderPath(path []Pather) string {
s := ""
for _, p := range path {
pT := p.(*Truck)
s = pT.label + " " + s
}
return s
}

View File

@ -5,15 +5,16 @@ import (
"testing"
)
func AddTruck(x int, y int, label string) *Truck {
func addTruck(x, y int, label string) *Truck {
t1 := new(Truck)
t1.X = x
t1.Y = y
t1.label = label
return t1
}
func AddTube(t1, t2 *Truck, cost float64) *Tube {
func addTube(t1, t2 *Truck, cost float64) *Tube {
tube1 := new(Tube)
tube1.Cost = cost
tube1.from = t1
@ -49,17 +50,16 @@ func AddTube(t1, t2 *Truck, cost float64) *Tube {
// Solver should avoid the plugged tube.
// 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)
trStart := AddTruck(0, 0, "Start")
trMid := AddTruck(0, 1, "Middle")
trEnd := AddTruck(1, 1, "End")
trStart := addTruck(0, 0, "Start")
trMid := addTruck(0, 1, "Middle")
trEnd := addTruck(1, 1, "End")
AddTube(trStart, trEnd, diagonalCost)
AddTube(trStart, trMid, 1)
AddTube(trMid, trEnd, 1)
addTube(trStart, trEnd, diagonalCost)
addTube(trStart, trMid, 1)
addTube(trMid, trEnd, 1)
t.Logf("Goreland. Diagonal cost: %v\n\n", diagonalCost)
@ -70,9 +70,11 @@ func createGorelandGraphPathDiagonal(t *testing.T, diagonalCost float64, expecte
} else {
t.Logf("Resulting path\n%s", world.RenderPath(p))
}
if !found && expectedDist >= 0 {
t.Fatal("Could not find a path")
}
if found && dist != expectedDist {
t.Fatalf("Expected dist to be %v but got %v", expectedDist, dist)
}

View File

@ -16,14 +16,17 @@ func testPath(worldInput string, t *testing.T, expectedDist float64) {
world := ParseWorld(worldInput)
t.Logf("Input world\n%s", world.RenderPath([]Pather{}))
p, dist, found := Path(world.From(), world.To(), math.MaxFloat64)
if !found {
t.Log("Could not find a path")
} else {
t.Logf("Resulting path\n%s", world.RenderPath(p))
}
if !found && expectedDist >= 0 {
t.Fatal("Could not find a path")
}
if found && dist != expectedDist {
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...~.......M.........X...X...XT
`)
for i := 0; i < b.N; i++ {
Path(world.From(), world.To(), math.MaxFloat64)
}

View File

@ -29,7 +29,7 @@ const (
)
// KindRunes map tile kinds to output runes.
var KindRunes = map[int]rune{
var KindRunes = map[int]rune{ // nolint:gochecknoglobals // it's a test
KindPlain: '.',
KindRiver: '~',
KindMountain: 'M',
@ -40,7 +40,7 @@ var KindRunes = map[int]rune{
}
// RuneKinds map input runes to tile kinds.
var RuneKinds = map[rune]int{
var RuneKinds = map[rune]int{ // nolint:gochecknoglobals // it's a test
'.': KindPlain,
'~': KindRiver,
'M': KindMountain,
@ -50,7 +50,7 @@ var RuneKinds = map[rune]int{
}
// 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,
KindFrom: 1.0,
KindTo: 1.0,
@ -71,7 +71,8 @@ type Tile struct {
// PathNeighbors returns the neighbors of the tile, excluding blockers and
// tiles off the edge of the board.
func (t *Tile) PathNeighbors() []Pather {
neighbors := []Pather{}
neighbors := make([]Pather, 0)
for _, offset := range [][]int{
{-1, 0},
{1, 0},
@ -83,6 +84,7 @@ func (t *Tile) PathNeighbors() []Pather {
neighbors = append(neighbors, n)
}
}
return neighbors
}
@ -97,13 +99,16 @@ func (t *Tile) PathNeighborCost(to Pather) float64 {
func (t *Tile) PathEstimatedCost(to Pather) float64 {
toT := to.(*Tile)
absX := toT.X - t.X
if absX < 0 {
absX = -absX
}
absY := toT.Y - t.Y
if absY < 0 {
absY = -absY
}
return float64(absX + absY)
}
@ -115,6 +120,7 @@ func (w World) Tile(x, y int) *Tile {
if w[x] == nil {
return nil
}
return w[x][y]
}
@ -123,6 +129,7 @@ func (w World) SetTile(t *Tile, x, y int) {
if w[x] == nil {
w[x] = map[int]*Tile{}
}
w[x][y] = t
t.X = x
t.Y = y
@ -139,6 +146,7 @@ func (w World) FirstOfKind(kind int) *Tile {
}
}
}
return nil
}
@ -158,41 +166,50 @@ func (w World) RenderPath(path []Pather) string {
if width == 0 {
return ""
}
height := len(w[0])
pathLocs := map[string]bool{}
for _, p := range path {
pT := p.(*Tile)
pathLocs[fmt.Sprintf("%d,%d", pT.X, pT.Y)] = true
}
rows := make([]string, height)
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
t := w.Tile(x, y)
r := ' '
if pathLocs[fmt.Sprintf("%d,%d", x, y)] {
r = KindRunes[KindPath]
} else if t != nil {
r = KindRunes[t.Kind]
}
rows[y] += string(r)
}
}
return strings.Join(rows, "\n")
}
// ParseWorld parses a textual representation of a world into a world map.
func ParseWorld(input string) World {
w := World{}
for y, row := range strings.Split(strings.TrimSpace(input), "\n") {
for x, raw := range row {
kind, ok := RuneKinds[raw]
if !ok {
kind = KindBlocker
}
w.SetTile(&Tile{
Kind: kind,
}, x, y)
w.SetTile(&Tile{Kind: kind}, x, y)
}
}
return w
}

View File

@ -31,5 +31,6 @@ func (pq *priorityQueue) Pop() interface{} {
no := old[n-1]
no.index = -1
*pq = old[0 : n-1]
return no
}