astar

package module
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2024 License: MIT Imports: 1 Imported by: 0

README

A* GoDoc Build status

Implementation of the A* Search algorithm.

Install

$ go get github.com/pietv/astar

Overview

A* Search is one of the intelligent exhaustive search algorithms which gets from Start to Finish by exploring successor states.

It's intelligent because it uses a special guidance in selecting the states that are going to be explored next. The algorithm uses a minimum value of a sum of the next successor cost and the heuristic estimate (distance, for example) of how close that next successor is to Finish. These values are named Cost and Estimate in this implementation.

Without any guidance (that is when both Cost and Estimate values are constant), it explores all successor states, making it essentially the Breadth First Search algorithm (Go's container/heap implementation behaves like a queue if keys are equal).

Depending on whether Cost and Estimate are constant or not, A* Search behaves like other well-known algorithms:

Cost Estimate Behavior
const const Breadth First Search
variable const [Dijkstra's Shortest Path] (http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) / Uniform-Cost Search
const variable Greedy Best First Search
variable variable A*

It is not necessary to use a graph data structure. Crawling the internet and feeding harvested links as successors would do or, as another example, the provided maze demo uses a rectangular matrix and uses surrounding cells as successors.

Maze Demo Install the demo:

$ go get github.com/pietv/astar/cmd/maze

Documentation

Overview

Package astar implements the A* (“A Star”) search algorithm as described in the paper by Peter E. Hart et al, “A Formal Basis for the Heuristic Determination of Minimum Cost Paths” http://ai.stanford.edu/~nilsson/OnlinePubs-Nils/PublishedPapers/astar.pdf

The “open” and “closed” sets in this implementation are named “priority queue” and “explored set” respectively.

Time complexity of the algorithm depends on the quality of the heuristic function Estimate().

If Estimate() is constant, the complexity is the same as for the uniform cost search (UCS) algorithm – O(b^m), where b is the branching factor (how many successor states on average) and m is the maximum depth of the decision tree.

If Estimate() is optimal, the complexity is O(n).

The algorithm is implemented as a Search() function which takes astar.Interface as a parameter.

Basic usage (counting to 10):

type Number int

func (n Number) Start() interface{}             { return Number(1) }
func (n Number) Finish() bool                   { return n == Number(10) }
func (n *Number) Move(x interface{})            { *n = x.(Number) }
func (n Number) Successors() []interface{}      { return []interface{}{n - 1, n + 1} }
func (n Number) Cost(x interface{}) float64     { return 1 }
func (n Number) Estimate(x interface{}) float64 {
  return math.Abs(10 - float64(x.(Number)))
}

var n Number = 10
if path, walk, err := astar.Search(&n); err == nil {
  fmt.Println(path)
  fmt.Println(walk)
}
// Output: [1 2 3 4 5 6 7 8 9 10] —— the solution.
// [1 2 3 4 5 6 7 8 9 10]         —— states explored by the algorithm
                                     before it could find the best solution.

You could allow only “subtract 7” and “add 5” operations to get to 10:

func (n Number) Successors() []interface{} { return []interface{}{n - 7, n + 5} }

// Output: [1 6 11 4 9 14 7 12 5 10]
// [1 6 11 4 9 16 14 7 12 -1 2 5 10]

Or subtract 3, 7, and multiply by 9:

func (n Number) Successors() []interface{} { return []interface{}{n - 3, n - 7, n * 9} }

// Output: [1 9 6 3 27 20 13 10]
// [1 9 6 2 3 18 11 8 15 12 4 5 -2 -1 0 -5 -6 -4 -3 27 20 13 10]

Etc.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Interface

type Interface interface {
	// Initial state.
	Start() interface{}

	// Is this state final or canceled?
	Finish() bool

	// Move to a new state.
	Move(interface{})

	// Available moves from the current state.
	Successors(current StatePointer) []interface{}

	// Path cost between the current and the given state.
	Cost(interface{}) float64

	// Heuristic estimate of “how far to go?” between the given
	// and the final state. Smaller values mean closer.
	Estimate(interface{}) float64
}

Interface describes a type suitable for A* search. Any type can do as long as it can change its current state and tell legal moves from it. Knowing costs and estimates helps, but not necessary.

type OptionalState

type OptionalState *state
func Search(p Interface) OptionalState

Search finds the p.Finish() state from the given p.Start() state by invoking p.Successors() and p.Move() at each step. Search returns the final state. If the shortest path cannot be found, nil is returned.

Example (PouringPuzzle)
// Water pouring puzzle.
//
// Measure out 6 ounces of water using two glasses of 9 and 4 oz.
// You're allowed to pour water from one glass to another as well as emptying
// and refilling them.
//
// Use A* Search to solve it.
//
//	             =()=
//	            .-||--|
//	           .   ___|
//	           '==’
//	            ||
//	            ||
//	|     |     ||
//	|     |
//	|     |   |    |
//	|     |   |    |
//	|     |   |    |
//	|     |   |    |
//	+-----+   +----+
//	 9 oz.     4 oz.
package main

import (
	"os"
	"text/template"
)

// Glasses’ capacities and the goal.
type Puzzle struct {
	CapFirst, CapSecond int
	Goal                int
}

// Glasses state.
type State struct {
	p      *Puzzle
	Action string
	First  int
	Second int
}

func (s State) Start() interface{} { return State{s.p, "Both Empty", 0, 0} }
func (s State) Finish() bool {
	// One of the glasses contains the goal amount.
	return s.p.Goal == s.First || s.p.Goal == s.Second
}
func (s *State) Move(x interface{})            { *s = x.(State) }
func (s State) Cost(x interface{}) float64     { return 1 }
func (s State) Estimate(x interface{}) float64 { return 1 }
func (s State) Successors(current StatePointer) []interface{} {
	succ := []interface{}{}

	First, Second, CapFirst, CapSecond := s.First, s.Second, s.p.CapFirst, s.p.CapSecond

	// Fill first glass.
	succ = append(succ, State{s.p, "Fill First", CapFirst, Second})

	// Fill second glass.
	succ = append(succ, State{s.p, "Fill Second", First, CapSecond})

	// Empty first glass.
	succ = append(succ, State{s.p, "Empty First", 0, Second})

	// Empty second glass.
	succ = append(succ, State{s.p, "Empty Second", First, 0})

	// Pour from the first glass into the second.
	if First+Second > CapSecond {
		succ = append(succ, State{s.p, "First –> Second", First - (CapSecond - Second), CapSecond})
	} else {
		succ = append(succ, State{s.p, "First –> Second", 0, First + Second})
	}

	// Pour from the second glass into the first.
	if First+Second > CapFirst {
		succ = append(succ, State{s.p, "Second –> First", CapFirst, Second - (CapFirst - First)})
	} else {
		succ = append(succ, State{s.p, "Second –> First", First + Second, 0})
	}
	return succ
}

const PouringTmpl = `
{{range .}}  {{printf "%-16s (%v %v)\n" .Action .First .Second}}{{end}}
`

func main() {
	if state := Search(&State{p: &Puzzle{
		CapFirst:  9,
		CapSecond: 4,
		Goal:      6,
	}}); state != nil {
		path := []State{}
		for ; state != nil; state = state.Previous {
			path = append(path, state.Pather.(State))
		}
		for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
			path[i], path[j] = path[j], path[i]
		}

		template.Must(template.New("Pouring Puzzle").Parse(PouringTmpl)).Execute(os.Stdout, path)
	}
}
Output:

  Both Empty       (0 0)
  Fill First       (9 0)
  First –> Second  (5 4)
  Empty Second     (5 0)
  First –> Second  (1 4)
  Empty Second     (1 0)
  First –> Second  (0 1)
  Fill First       (9 1)
  First –> Second  (6 4)

type StatePointer

type StatePointer *state

Directories

Path Synopsis
cmd
maze
Use A* search algorithm to traverse a maze.
Use A* search algorithm to traverse a maze.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL