liveBook · Manning

A-star algorithm

As explained in Classic Computer Science Problems in Swift: Essential techniques for practicing programmers

A* search explores a state graph using the sum of a path cost and a heuristic estimate of the remaining cost to prioritize states.

Breadth-first search finds a shortest maze path by exploring states layer by layer, but those layers can be costly to search. A* also seeks a shortest path while using an estimate of the remaining distance to choose which state to explore next. section 2.2

For a state n, g(n) is the cost of reaching it, h(n) estimates the cost from it to the goal, and f(n) = g(n) + h(n). A* takes the state with the lowest f(n) from its frontier. If h never overestimates the remaining cost—an admissible heuristic—the path found is optimal. section 2.2

Choosing a maze heuristic

In a grid maze, g counts steps taken. Straight-line, or Euclidean, distance estimates the distance left. If movement is limited to up, down, left, and right, Manhattan distance adds the horizontal and vertical distances to the goal. Walls can force a longer route; the estimate need not predict the exact path. section 2.2

func astar<StateType:
 Hashable>(initialState: StateType, goalTestFn: (StateType)
 -> Bool, successorFn: (StateType) -> [StateType],
 heuristicFn: (StateType) -> Float) -> Node<StateType>? {
    // frontier is where we've yet to go
    var frontier: PriorityQueue<Node<StateType>> =
 PriorityQueue<Node<StateType>>(ascending:
 true, startingValues: [Node(state: initialState, parent:
 nil, cost: 0, heuristic: heuristicFn(initialState))])
    // explored is where we've been
    var explored = Dictionary<StateType, Float>()
    explored[initialState] = 0
    // keep going while there is more to explore
    while let currentNode = frontier.pop() {
        let currentState = currentNode.state
        // if we found the goal, we're done
        if goalTestFn(currentState) { return currentNode }
        // check where we can go next and haven't explored
        for child in successorFn(currentState) {
            let newcost = currentNode.cost + 1  //1 assumes a grid, there
 should be a cost function for more sophisticated applications
            if (explored[child] == nil) || (explored[child]! > newcost) {
                explored[child] = newcost
                frontier.push(Node(state: child, parent: currentNode,
 cost: newcost, heuristic: heuristicFn(child)))
            }
        }
    }
    return nil // never found the goal
}

The generic Swift implementation takes goal-test, successor, and heuristic functions. Its frontier is a priority queue of nodes, and a dictionary records costs for explored states. section 2.2

A* is useful beyond mazes, including route planning and game-character pathfinding. Breadth-first search may suffice for a small graph; when a larger graph has a good heuristic, A* offers a way to focus the search. section 2.4

How Classic Computer Science Problems in Swift: Essential techniques for practicing programmers covers it

How deep it goes
Defines cost and heuristic scores, gives the optimality condition, and implements generic Swift search without a formal proof.
Examples
Uses a grid maze with blocked cells, four-direction movement, and Swift functions for Euclidean and Manhattan distance.
What it assumes
Builds on Swift maze states and breadth-first search while introducing A* scores and admissible heuristics.
What you can do after
Build it from scratch with a Swift priority queue, goal test, successor function, and heuristic function.
How it connects
Connects breadth-first search, priority queues, admissible heuristics, Euclidean distance, Manhattan distance, and Dijkstra’s algorithm.

Also explained in

Compare how 5 books explain A-star algorithm →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Classic Computer Science Problems in Swift: Essential techniques for practicing programmers. The section links point to the parts of the book it draws on.