GithubHelp home page GithubHelp logo

go-astar's Introduction

go-astar

A* pathfinding implementation for Go

Build Status

The A* pathfinding 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.

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

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 to estimate orthogonal distance between tiles.

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

// 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 [email protected] Robin Ranjit Chauhan [email protected]

go-astar's People

Contributors

beefsack avatar pathway avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-astar's Issues

Diagonal traversal

Do you think it would be desirable to add diagonal traversal to this algorithm?

astar.go will not always choose the optimal path

The "Found a path to the goal" condition is checked when expanding neighbours. But to be optimal, this condition should be checked for a node freshly popped from the priority queue instead.

Its taking me longer than Id like to produce a failing testcase, but I can show a simple thought experiment that illustrates the problem. It's not as easy to illustrate in the grid world (and Im not even certain I could illustrate it in that world, because it does not use Edge weights), but your algorithm appears to be designed to be more general that just solving the grids.

Consider a world with Nodes and Edges, Edges each having a cost

      E
    /  |
  /    |
S---- M

S=Start
E=End
M=Middle

Edges: S-E is a giant chasm, cost: 10000
S-M and M-E are paved roads. cost: 1

Currently, astar.go would do this.
-Push S onto the Queue
-Pop S
-Expand neighbors of S using Edges
-Neighbor E found. END.

Resulting solution would be S-E cost 10000.
But optimal solution is S-M-E, cost 2.

Returns path in wrong direction

While defined as func Path(from, to Pather) ..., the resulting path actually starts at to node and ends at from.

This can be seen using this patch to pather_test.go:

diff --git pather_test.go pather_test.go
index b9d78bd..331ee0d 100644
--- pather_test.go
+++ pather_test.go
@@ -159,18 +159,19 @@ func (w World) RenderPath(path []Pather) string {
                return ""
        }
        height := len(w[0])
-       pathLocs := map[string]bool{}
-       for _, p := range path {
+       pathLocs := map[string]rune{}
+       for i, p := range path {
                pT := p.(*Tile)
-               pathLocs[fmt.Sprintf("%d,%d", pT.X, pT.Y)] = true
+               nr := []rune("0123456789abc")[i%12] // "clever" rendering of node index in path.
+               pathLocs[fmt.Sprintf("%d,%d", pT.X, pT.Y)] = nr
        }
        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]
+                       if nr := pathLocs[fmt.Sprintf("%d,%d", x, y)]; nr != 0 {
+                               r = nr
                        } else if t != nil {
                                r = KindRunes[t.Kind]
                        }

which renders 0 on T and 9 on F:

=== RUN   TestStraightLine
    path_test.go:14: Input world
        .....~......
        .....MM.....
        .F........T.
        ....MMM.....
        ............
    path_test.go:19: Resulting path
        .....~......
        .....MM.....
        .9876543210.
        ....MMM.....
        ............
--- PASS: TestStraightLine (0.01s)

Typo in the README

In the example in the README, the methods on Tile use the English spelling of "Neighbour", whereas the interface Pather requires the methods to spell it "Neighbor".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.