mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-11-02 09:17:19 -04:00
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
|
||||
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
2
d2common/d2astar/doc.go
Normal file
@ -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:
|
||||
// 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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -31,5 +31,6 @@ func (pq *priorityQueue) Pop() interface{} {
|
||||
no := old[n-1]
|
||||
no.index = -1
|
||||
*pq = old[0 : n-1]
|
||||
|
||||
return no
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user