diff --git a/d2common/d2astar/README.md b/d2common/d2astar/README.md deleted file mode 100644 index 4294d281..00000000 --- a/d2common/d2astar/README.md +++ /dev/null @@ -1,153 +0,0 @@ -D2 A* -======== - -**A\* pathfinding implementation for OpenDiablo2** - -***Forked from [go-astar](https://github.com/beefsack/go-astar)*** - -Changes -------- -* Used [sync.Pool](https://golang.org/pkg/sync/#Pool) to reuse objects created during path-finding. This improves performance by roughly 30% by reducing allocations. -* Added a max cost to prevent searching the entire region for a path. -* If there is no path the target within the max cost, the path found that gets closest to target will be returned. This allows the player to click in inaccessible areas causing the character to run along the edge. - -TODO ------- -* Evaluate bi-directional A*, specifically if it would more quickly identify if the user clicked an in inaccessible area (such as an island). - - -The [A\* pathfinding algorithm](http://en.wikipedia.org/wiki/A*_search_algorithm) is a pathfinding algorithm noted for its performance and accuracy and is commonly used in game development. It can be used to find short paths for any weighted graph. - -A fantastic overview of A\* can be found at [Amit Patel's Stanford website](http://theory.stanford.edu/~amitp/GameProgramming/AStarComparison.html). - -Examples --------- - -The following crude examples were taken directly from the automated tests. Please see `path_test.go` for more examples. - -### Key - -* `.` - Plain (movement cost 1) -* `~` - River (movement cost 2) -* `M` - Mountain (movement cost 3) -* `X` - Blocker, unable to move through -* `F` - From / start position -* `T` - To / goal position -* `●` - Calculated path - -### Straight line - -``` -.....~...... .....~...... -.....MM..... .....MM..... -.F........T. -> .●●●●●●●●●●. -....MMM..... ....MMM..... -............ ............ -``` - -### Around a mountain - -``` -.....~...... .....~...... -.....MM..... .....MM..... -.F..MMMM..T. -> .●●●MMMM●●●. -....MMM..... ...●MMM●●... -............ ...●●●●●.... -``` - -### Blocked path - -``` -............ -.........XXX -.F.......XTX -> No path -.........XXX -............ -``` - -### Maze - -``` -FX.X........ ●X.X●●●●●●.. -.X...XXXX.X. ●X●●●XXXX●X. -.X.X.X....X. -> ●X●X.X●●●●X. -...X.X.XXXXX ●●●X.X●XXXXX -.XX..X.....T .XX..X●●●●●● -``` - -### Mountain climber - -``` -..F..M...... ..●●●●●●●●●. -.....MM..... .....MM...●. -....MMMM..T. -> ....MMMM..●. -....MMM..... ....MMM..... -............ ............ -``` - -### River swimmer - -``` -.....~...... .....~...... -.....~...... ....●●●..... -.F...X...T.. -> .●●●●X●●●●.. -.....M...... .....M...... -.....M...... .....M...... -``` - -Usage ------ - -### Import the package - -```go -import "github.com/beefsack/go-astar" -``` - -### Implement Pather interface - -An example implementation is done for the tests in `path_test.go` for the Tile type. - -The `PathNeighbors` method should return a slice of the direct neighbors. - -The `PathNeighborCost` method should calculate an exact movement cost for direct neighbors. - -The `PathEstimatedCost` is a heuristic method for estimating the distance between arbitrary tiles. The examples in the test files use [Manhattan distance](http://en.wikipedia.org/wiki/Taxicab_geometry) to estimate orthogonal distance between tiles. - -```go -type Tile struct{} - -func (t *Tile) PathNeighbors() []astar.Pather { - return []astar.Pather{ - t.Up(), - t.Right(), - t.Down(), - t.Left(), - } -} - -func (t *Tile) PathNeighborCost(to astar.Pather) float64 { - return to.MovementCost -} - -func (t *Tile) PathEstimatedCost(to astar.Pather) float64 { - return t.ManhattanDistance(to) -} -``` - -### Call Path function - -```go -// t1 and t2 are *Tile objects from inside the world. -path, distance, found := astar.Path(t1, t2) -if !found { - log.Println("Could not find path") -} -// path is a slice of Pather objects which you can cast back to *Tile. -``` - -Authors -------- - -Michael Alexander -Robin Ranjit Chauhan diff --git a/d2common/d2astar/astar.go b/d2common/d2astar/astar.go deleted file mode 100644 index 90642e8b..00000000 --- a/d2common/d2astar/astar.go +++ /dev/null @@ -1,176 +0,0 @@ -package d2astar - -import ( - "container/heap" - "sync" -) - -var nodePool *sync.Pool -var nodeMapPool *sync.Pool -var priorityQueuePool *sync.Pool - -func init() { - nodePool = &sync.Pool{ - New: func() interface{} { - return &node{} - }, - } - - nodeMapPool = &sync.Pool{ - New: func() interface{} { - return make(nodeMap, 128) - }, - } - - priorityQueuePool = &sync.Pool{ - New: func() interface{} { - return priorityQueue{} - }, - } -} - -// astar is an A* pathfinding implementation. - -// Pather is an interface which allows A* searching on arbitrary objects which -// can represent a weighted graph. -type Pather interface { - // PathNeighbors returns the direct neighboring nodes of this node which - // can be pathed to. - PathNeighbors() []Pather - // PathNeighborCost calculates the exact movement cost to neighbor nodes. - PathNeighborCost(to Pather) float64 - // PathEstimatedCost is a heuristic method for estimating movement costs - // between non-adjacent nodes. - PathEstimatedCost(to Pather) float64 -} - -// node is a wrapper to store A* data for a Pather node. -type node struct { - pather Pather - cost float64 - rank float64 - parent *node - open bool - closed bool - index int -} - -func (n *node) reset() { - n.pather = nil - n.cost = 0 - n.rank = 0 - n.parent = nil - n.open = false - n.closed = false - n.index = 0 -} - -// nodeMap is a collection of nodes keyed by Pather nodes for quick reference. -type nodeMap map[Pather]*node - -// get gets the Pather object wrapped in a node, instantiating if required. -func (nm nodeMap) get(p Pather) *node { - n, ok := nm[p] - if !ok { - n = nodePool.Get().(*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() - nodePool.Put(v) - delete(nm, k) - } - - nq = nq[0:0] - - nodeMapPool.Put(nm) - - priorityQueuePool.Put(nq) - }() - - heap.Init(&nq) - fromNode := nm.get(from) - fromNode.open = true - heap.Push(&nq, fromNode) - for { - if nq.Len() == 0 { - // There's no path, fallback to closest. - break - } - - current := heap.Pop(&nq).(*node) - - current.open = false - current.closed = true - - if current == nm.get(to) { - // Found a path to the goal. - p := make([]Pather, 0, 16) - curr := current - for curr != nil { - p = append(p, curr.pather) - curr = curr.parent - } - return p, current.cost, true - } - - for _, neighbor := range current.pather.PathNeighbors() { - cost := current.cost + current.pather.PathNeighborCost(neighbor) - if cost > maxCost { - // Out of range, tweak maxCost if this is cutting off too soon. - continue - } - - neighborNode := nm.get(neighbor) - if cost < neighborNode.cost { - if neighborNode.open { - heap.Remove(&nq, neighborNode.index) - } - neighborNode.open = false - neighborNode.closed = false - } - if !neighborNode.open && !neighborNode.closed { - neighborNode.cost = cost - neighborNode.open = true - neighborNode.rank = cost + neighbor.PathEstimatedCost(to) - neighborNode.parent = current - heap.Push(&nq, neighborNode) - } - } - } - - // No path found, use closest node available, with found false to indicate the path doesn't reach the target. - closestNode := nm.get(from) - closestNode.rank = closestNode.pather.PathEstimatedCost(to) - for _, current := range nm { - if current.parent == nil { - // This node wasn't evaluated while path finding, probably isn't a good option. - continue - } - - current.rank = current.pather.PathEstimatedCost(to) - if current.rank < closestNode.rank { - closestNode = current - } - } - - p := make([]Pather, 0, 16) - curr := closestNode - for curr != nil { - p = append(p, curr.pather) - curr = curr.parent - } - return p, closestNode.cost, false -} diff --git a/d2common/d2astar/doc.go b/d2common/d2astar/doc.go deleted file mode 100644 index 9c799931..00000000 --- a/d2common/d2astar/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package d2astar provides an a* pathing algorithm -package d2astar diff --git a/d2common/d2astar/goreland_example.go b/d2common/d2astar/goreland_example.go deleted file mode 100644 index 5f5ec0fd..00000000 --- a/d2common/d2astar/goreland_example.go +++ /dev/null @@ -1,104 +0,0 @@ -package d2astar - -// goreland_example.go implements implements Pather for -// the sake of testing. This functionality forms the back end for -// goreland_test.go, and serves as an example for how to use A* for a graph. - -// The Magical World of Goreland, is where Ted Stevens and Al Gore are from. -// -// It is composed of Big Trucks, and a Series of Tubes! -// -// Ok, it is basically just a Graph. -// Nodes are called "Trucks" and they have X, Y coordinates -// Edges are called "Tubes", they connect Trucks, and they have a cost -// -// 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. -// -// 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. -type Goreland struct { - // trucks map[int]*Truck // not needed really -} - -const ( - _tenFuckingMillion = 10000000 -) - -// Tube is an edge. They connect Trucks, and have a cost. -type Tube struct { - from *Truck - to *Truck - Cost float64 -} - -// A Truck is a Truck in a grid which implements Grapher. -type Truck struct { - - // X and Y are the coordinates of the truck. - X, Y int - - // array of tubes going to other trucks - outTo []Tube - - label string -} - -// 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 _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 -} - -// 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 deleted file mode 100644 index 2af18ed2..00000000 --- a/d2common/d2astar/goreland_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package d2astar - -import ( - "math" - "testing" -) - -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 { - tube1 := new(Tube) - tube1.Cost = cost - tube1.from = t1 - tube1.to = t2 - - t1.outTo = append(t1.outTo, *tube1) - t2.outTo = append(t2.outTo, *tube1) - - return tube1 -} - -// Consider a world with Nodes (Trucks) and Edges (Tubes), Edges each having a cost -// -// E -// /| -// / | -// S--M -// -// S=Start at (0,0) -// E=End at (1,1) -// M=Middle at (0,1) -// -// S-M and M-E are clean clear tubes. cost: 1 -// -// S-E is either: -// -// 1) TestGraphPath_ShortDiagonal : diagonal is a nice clean clear Tube , cost: 1.9 -// Solver should traverse the bridge. -// Expect solution: Start, End Total cost: 1.9 -// -// 1) TestGraphPath_LongDiagonal : diagonal is a Tube plugged full of -// "enormous amounts of material"!, cost: 10000. -// Solver should avoid the plugged tube. -// Expect solution Start,Middle,End Total cost: 2.0 - -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") - - addTube(trStart, trEnd, diagonalCost) - addTube(trStart, trMid, 1) - addTube(trMid, trEnd, 1) - - t.Logf("Goreland. Diagonal cost: %v\n\n", diagonalCost) - - p, dist, found := Path(trStart, trEnd, 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) - } -} - -func TestGraphPaths_ShortDiagonal(t *testing.T) { - createGorelandGraphPathDiagonal(t, 1.9, 1.9) -} -func TestGraphPaths_LongDiagonal(t *testing.T) { - createGorelandGraphPathDiagonal(t, 10000, 2.0) -} diff --git a/d2common/d2astar/path_test.go b/d2common/d2astar/path_test.go deleted file mode 100644 index 3aa29e22..00000000 --- a/d2common/d2astar/path_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package d2astar - -// path_test.go contains the high level tests without the testing -// implementation. testPath is used to check the calculated path distance is -// what we're expecting. - -import ( - "math" - "testing" -) - -// testPath takes a string encoded world, decodes it, calculates a path and -// checks the expected distance matches. An expectedDist of -1 expects that no -// path will be found. -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) - } -} - -// TestStraightLine checks that having no obstacles results in a straight line -// path. -func TestStraightLine(t *testing.T) { - testPath(` -.....~...... -.....MM..... -.F........T. -....MMM..... -............ -`, t, 9) -} - -// TestPathAroundMountain checks that having a round mountain in the path -// results in a path around the mountain. -func TestPathAroundMountain(t *testing.T) { - testPath(` -.....~...... -.....MM..... -.F..MMMM..T. -....MMM..... -............ -`, t, 13) -} - -// TestBlocked checks that no path is returned when there is no possible path. -func TestBlocked(t *testing.T) { - testPath(` -............ -.........XXX -.F.......XTX -.........XXX -............ -`, t, -1) -} - -// TestMaze checks that paths can double back on themselves to reach the goal. -func TestMaze(t *testing.T) { - testPath(` -FX.X........ -.X...XXXX.X. -.X.X.X....X. -...X.X.XXXXX -.XX..X.....T -`, t, 27) -} - -// TestMountainClimber checks that a path will choose to go over a mountain, -// which has a movement penalty of 3, if it's faster than going around the -// mountain. -func TestMountainClimber(t *testing.T) { - testPath(` -..F..M...... -.....MM..... -....MMMM..T. -....MMM..... -............ -`, t, 12) -} - -// TestRiverSwimmer checks that the path will prefer to cross a river, which -// has a movement penalty of 2, over a mountain which has a movement penalty of -// 3. -func TestRiverSwimmer(t *testing.T) { - testPath(` -.....~...... -.....~...... -.F...X...T.. -.....M...... -.....M...... -`, t, 11) -} - -func BenchmarkLarge(b *testing.B) { - world := ParseWorld(` -F............................~................................................. -.............................~................................................. -........M...........X........~................................................. -.......MMM.........X.........~~................................................ -........MM........X...........~................................................ -.......MM........X............~................................................ -................X.............~................................................ -...............X..............~~............................................... -..............X................~............................................... -.............X.................~...X...............~........................... -............X.......................X..............~........................... -...........X.........................X.............~........................... -..........X..................~........X............~........................... -.........X...................~.........X...........~........................... -.............................~..........X..........~...............XXXXXXXXXXXX -............................~............X..........~..............X...X...X... -............................~.............X.........~......MMM.....X.X.X.X.X.X. -............................~..............X........~......MM......X.X.X.X.X.X. -............................~...............X.......~....MMMM......X.X.X.X.X.X. -...........................~.................X.....~......MMM......X.X.X.X.X.X. -..............................................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 deleted file mode 100644 index 4686072c..00000000 --- a/d2common/d2astar/pather_test.go +++ /dev/null @@ -1,215 +0,0 @@ -package d2astar - -// pather_test.go implements a basic world and tiles that implement Pather for -// the sake of testing. This functionality forms the back end for -// path_test.go, and serves as an example for how to use A* for a grid. - -import ( - "fmt" - "strings" -) - -// Kind* constants refer to tile kinds for input and output. -const ( - // KindPlain (.) is a plain tile with a movement cost of 1. - KindPlain = iota - // KindRiver (~) is a river tile with a movement cost of 2. - KindRiver - // KindMountain (M) is a mountain tile with a movement cost of 3. - KindMountain - // KindBlocker (X) is a tile which blocks movement. - KindBlocker - // KindFrom (F) is a tile which marks where the path should be calculated - // from. - KindFrom - // KindTo (T) is a tile which marks the goal of the path. - KindTo - // KindPath (●) is a tile to represent where the path is in the output. - KindPath -) - -// KindRunes map tile kinds to output runes. -var KindRunes = map[int]rune{ // nolint:gochecknoglobals // it's a test - KindPlain: '.', - KindRiver: '~', - KindMountain: 'M', - KindBlocker: 'X', - KindFrom: 'F', - KindTo: 'T', - KindPath: '●', -} - -// RuneKinds map input runes to tile kinds. -var RuneKinds = map[rune]int{ // nolint:gochecknoglobals // it's a test - '.': KindPlain, - '~': KindRiver, - 'M': KindMountain, - 'X': KindBlocker, - 'F': KindFrom, - 'T': KindTo, -} - -// KindCosts map tile kinds to movement costs. -var KindCosts = map[int]float64{ // nolint:gochecknoglobals // it's a test - KindPlain: 1.0, - KindFrom: 1.0, - KindTo: 1.0, - KindRiver: 2.0, - KindMountain: 3.0, -} - -// A Tile is a tile in a grid which implements Pather. -type Tile struct { - // Kind is the kind of tile, potentially affecting movement. - Kind int - // X and Y are the coordinates of the tile. - X, Y int - // W is a reference to the World that the tile is a part of. - W World -} - -// PathNeighbors returns the neighbors of the tile, excluding blockers and -// tiles off the edge of the board. -func (t *Tile) PathNeighbors() []Pather { - neighbors := make([]Pather, 0) - - for _, offset := range [][]int{ - {-1, 0}, - {1, 0}, - {0, -1}, - {0, 1}, - } { - if n := t.W.Tile(t.X+offset[0], t.Y+offset[1]); n != nil && - n.Kind != KindBlocker { - neighbors = append(neighbors, n) - } - } - - return neighbors -} - -// PathNeighborCost returns the movement cost of the directly neighboring tile. -func (t *Tile) PathNeighborCost(to Pather) float64 { - toT := to.(*Tile) - return KindCosts[toT.Kind] -} - -// PathEstimatedCost uses Manhattan distance to estimate orthogonal distance -// between non-adjacent nodes. -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) -} - -// World is a two dimensional map of Tiles. -type World map[int]map[int]*Tile - -// Tile gets the tile at the given coordinates in the world. -func (w World) Tile(x, y int) *Tile { - if w[x] == nil { - return nil - } - - return w[x][y] -} - -// SetTile sets a tile at the given coordinates in the world. -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 - t.W = w -} - -// FirstOfKind gets the first tile on the board of a kind, used to get the from -// and to tiles as there should only be one of each. -func (w World) FirstOfKind(kind int) *Tile { - for _, row := range w { - for _, t := range row { - if t.Kind == kind { - return t - } - } - } - - return nil -} - -// From gets the from tile from the world. -func (w World) From() *Tile { - return w.FirstOfKind(KindFrom) -} - -// To gets the to tile from the world. -func (w World) To() *Tile { - return w.FirstOfKind(KindTo) -} - -// RenderPath renders a path on top of a world. -func (w World) RenderPath(path []Pather) string { - width := len(w) - 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) - } - } - - return w -} diff --git a/d2common/d2astar/priority_queue.go b/d2common/d2astar/priority_queue.go deleted file mode 100644 index bcb7d174..00000000 --- a/d2common/d2astar/priority_queue.go +++ /dev/null @@ -1,36 +0,0 @@ -package d2astar - -// A priorityQueue implements heap.Interface and holds Nodes. The -// priorityQueue is used to track open nodes by rank. -type priorityQueue []*node - -func (pq priorityQueue) Len() int { - return len(pq) -} - -func (pq priorityQueue) Less(i, j int) bool { - return pq[i].rank < pq[j].rank -} - -func (pq priorityQueue) Swap(i, j int) { - pq[i], pq[j] = pq[j], pq[i] - pq[i].index = i - pq[j].index = j -} - -func (pq *priorityQueue) Push(x interface{}) { - n := len(*pq) - no := x.(*node) - no.index = n - *pq = append(*pq, no) -} - -func (pq *priorityQueue) Pop() interface{} { - old := *pq - n := len(old) - no := old[n-1] - no.index = -1 - *pq = old[0 : n-1] - - return no -} diff --git a/d2common/path_tile.go b/d2common/path_tile.go deleted file mode 100644 index cf5f9879..00000000 --- a/d2common/path_tile.go +++ /dev/null @@ -1,68 +0,0 @@ -package d2common - -import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2astar" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" -) - -// PathTile represents a node in path finding -type PathTile struct { - Walkable bool - Up, Down *PathTile - Left, Right *PathTile - UpLeft, UpRight *PathTile - DownLeft, DownRight *PathTile - Position d2vector.Position -} - -// PathNeighbors returns the direct neighboring nodes of this node which can be pathed to -func (t *PathTile) PathNeighbors() []d2astar.Pather { - result := make([]d2astar.Pather, 0, 8) - if t.Up != nil { - result = append(result, t.Up) - } - - if t.Right != nil { - result = append(result, t.Right) - } - - if t.Down != nil { - result = append(result, t.Down) - } - - if t.Left != nil { - result = append(result, t.Left) - } - - if t.UpLeft != nil { - result = append(result, t.UpLeft) - } - - if t.UpRight != nil { - result = append(result, t.UpRight) - } - - if t.DownLeft != nil { - result = append(result, t.DownLeft) - } - - if t.DownRight != nil { - result = append(result, t.DownRight) - } - - return result -} - -// PathNeighborCost calculates the exact movement cost to neighbor nodes -func (t *PathTile) PathNeighborCost(_ d2astar.Pather) float64 { - return 1 // No cost specifics currently... -} - -// PathEstimatedCost is a heuristic method for estimating movement costs between non-adjacent nodes -func (t *PathTile) PathEstimatedCost(to d2astar.Pather) float64 { - delta := to.(*PathTile).Position.Clone() - delta.Subtract(&t.Position.Vector) - delta.Abs() - - return delta.X() + delta.Y() -} diff --git a/go.mod b/go.mod index fcedc1fb..d6ca4d4f 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,15 @@ module github.com/OpenDiablo2/OpenDiablo2 go 1.14 require ( - 4d63.com/gochecknoinits v0.0.0-20200108094044-eb73b47b9fc4 // indirect github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1 - github.com/hajimehoshi/ebiten v1.12.0-alpha.7.0.20200703165837-6c33ed107f28 + github.com/hajimehoshi/ebiten v1.11.4 github.com/pkg/profile v1.5.0 github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff github.com/satori/go.uuid v1.2.0 - github.com/stretchr/testify v1.4.0 - github.com/veandco/go-sdl2 v0.4.4 // indirect - golang.org/x/image v0.0.0-20200119044424-58c23975cae1 + golang.org/x/image v0.0.0-20200618115811-c13761719519 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/sourcemap.v1 v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index b2e13fa4..608ab3eb 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1: github.com/hajimehoshi/bitmapfont v1.2.0/go.mod h1:h9QrPk6Ktb2neObTlAbma6Ini1xgMjbJ3w7ysmD7IOU= github.com/hajimehoshi/ebiten v1.11.2 h1:4oixxPh5DuSNlmL/Q4LIOGFXCY8uLC+pcxOnqAGbwEY= github.com/hajimehoshi/ebiten v1.11.2/go.mod h1:aDEhx0K9gSpXw3Cxf2hCXDxPSoF8vgjNqKxrZa/B4Dg= +github.com/hajimehoshi/ebiten v1.11.4 h1:ngYF0NxKjFBsY/Bol6V0X/b0hoCCTi9nJRg7Dv8+ePc= +github.com/hajimehoshi/ebiten v1.11.4/go.mod h1:aDEhx0K9gSpXw3Cxf2hCXDxPSoF8vgjNqKxrZa/B4Dg= github.com/hajimehoshi/ebiten v1.12.0-alpha.5.0.20200627174955-aea4630b5f84 h1:BRvyD5kF4r9JGWd3xwBgQNpRhBDzvmAEXP2tv1MGjHA= github.com/hajimehoshi/ebiten v1.12.0-alpha.5.0.20200627174955-aea4630b5f84/go.mod h1:8vzUI4e0fBkbONYOY4WJN/qikY2zv/VG6kFTzJ0B//o= github.com/hajimehoshi/ebiten v1.12.0-alpha.6.0.20200629133528-780465b702ce h1:cEKWqbtxFremkIRhJxz0Z80wXqNNe8ZNk6ra8XASC1I=