From 18003a854391bb614424880d34c4422221df8472 Mon Sep 17 00:00:00 2001 From: dk Date: Fri, 3 Jul 2020 15:33:14 -0700 Subject: [PATCH] fixed lint errors in d2astar (#533) * fixed lint errors in d2astar * Update astar.go --- d2common/d2astar/astar.go | 7 ++++++- d2common/d2astar/doc.go | 2 ++ d2common/d2astar/goreland_example.go | 23 ++++++++++++++------- d2common/d2astar/goreland_test.go | 22 +++++++++++--------- d2common/d2astar/path_test.go | 4 ++++ d2common/d2astar/pather_test.go | 31 +++++++++++++++++++++------- d2common/d2astar/priority_queue.go | 1 + 7 files changed, 64 insertions(+), 26 deletions(-) create mode 100644 d2common/d2astar/doc.go diff --git a/d2common/d2astar/astar.go b/d2common/d2astar/astar.go index 779dbbc2..90642e8b 100644 --- a/d2common/d2astar/astar.go +++ b/d2common/d2astar/astar.go @@ -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 diff --git a/d2common/d2astar/doc.go b/d2common/d2astar/doc.go new file mode 100644 index 00000000..9c799931 --- /dev/null +++ b/d2common/d2astar/doc.go @@ -0,0 +1,2 @@ +// Package d2astar provides an a* pathing algorithm +package d2astar diff --git a/d2common/d2astar/goreland_example.go b/d2common/d2astar/goreland_example.go index 786edc02..a9d48703 100644 --- a/d2common/d2astar/goreland_example.go +++ b/d2common/d2astar/goreland_example.go @@ -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 } diff --git a/d2common/d2astar/goreland_test.go b/d2common/d2astar/goreland_test.go index 0d6de057..2af18ed2 100644 --- a/d2common/d2astar/goreland_test.go +++ b/d2common/d2astar/goreland_test.go @@ -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) } diff --git a/d2common/d2astar/path_test.go b/d2common/d2astar/path_test.go index bacf37e0..3aa29e22 100644 --- a/d2common/d2astar/path_test.go +++ b/d2common/d2astar/path_test.go @@ -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) } diff --git a/d2common/d2astar/pather_test.go b/d2common/d2astar/pather_test.go index 25649009..4686072c 100644 --- a/d2common/d2astar/pather_test.go +++ b/d2common/d2astar/pather_test.go @@ -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 } diff --git a/d2common/d2astar/priority_queue.go b/d2common/d2astar/priority_queue.go index acb762a4..bcb7d174 100644 --- a/d2common/d2astar/priority_queue.go +++ b/d2common/d2astar/priority_queue.go @@ -31,5 +31,6 @@ func (pq *priorityQueue) Pop() interface{} { no := old[n-1] no.index = -1 *pq = old[0 : n-1] + return no }