A* search algorithm: Difference between revisions

Content added Content deleted
(→‎{{header|Haskell}}: added solution)
No edit summary
Line 1: Line 1:
{{draft task|Routing algorithms}}
{{draft task|Routing algorithms}}
<!-- A* is pronounced as: A star !-->
<!-- A* is pronounced as: A star !-->


The A* search algorithm is an extension of [[Dijkstra's algorithm]] useful for finding the lowest cost path between two nodes (aka vertices) of a graph. The path may traverse any number of nodes connected by edges (aka arcs) with each edge having an associated cost. The algorithm uses a heuristic which associates an estimate of the lowest cost path from this node to the goal node, such that this estimate is never greater than the actual cost.
The A* search algorithm is an extension of [[Dijkstra's algorithm]] useful for finding the lowest cost path between two nodes (aka vertices) of a graph. The path may traverse any number of nodes connected by edges (aka arcs) with each edge having an associated cost. The algorithm uses a heuristic which associates an estimate of the lowest cost path from this node to the goal node, such that this estimate is never greater than the actual cost.


The algorithm should not assume that all edge costs are the same. It should be possible to start and finish on any node, including ones identified as a barrier in the task.
The algorithm should not assume that all edge costs are the same. It should be possible to start and finish on any node, including ones identified as a barrier in the task.


;Task
;Task
Consider the problem of finding a route across the diagonal of a chess board-like 8x8 grid. The rows are numbered from 0 to 7. The columns are also numbered 0 to 7. The start position is (0, 0) and the end position is (7, 7). Movement is allow by one square in any direction including diagonals, similar to a king in chess. The standard movement cost is 1. To make things slightly harder, there is a barrier that occupy certain positions of the grid. Moving into any of the barrier positions has a cost of 100.
Consider the problem of finding a route across the diagonal of a chess board-like 8x8 grid. The rows are numbered from 0 to 7. The columns are also numbered 0 to 7. The start position is (0, 0) and the end position is (7, 7). Movement is allow by one square in any direction including diagonals, similar to a king in chess. The standard movement cost is 1. To make things slightly harder, there is a barrier that occupy certain positions of the grid. Moving into any of the barrier positions has a cost of 100.


The barrier occupies the positions (2,4), (2,5), (2,6), (3,6), (4,6), (5,6), (5,5), (5,4), (5,3), (5,2), (4,2) and (3,2).
The barrier occupies the positions (2,4), (2,5), (2,6), (3,6), (4,6), (5,6), (5,5), (5,4), (5,3), (5,2), (4,2) and (3,2).


A route with the lowest cost should be found using the A* search algorithm (there are multiple optimal solutions with the same total cost).
A route with the lowest cost should be found using the A* search algorithm (there are multiple optimal solutions with the same total cost).
Line 339: Line 339:
}
}


// Class Cell, with the cost to reach it, the values g and f, and the coordinates
// Class Cell, with the cost to reach it, the values g and f, and the coordinates
// of the cell that precedes it in a possible path
// of the cell that precedes it in a possible path
public class Cell
public class Cell
Line 399: Line 399:
if (newCell.row == finishCell.row && newCell.col == finishCell.col)
if (newCell.row == finishCell.row && newCell.col == finishCell.col)
{
{
cells[newCell.row, newCell.col].g = cells[currentCell.row,
cells[newCell.row, newCell.col].g = cells[currentCell.row,
currentCell.col].g + cells[newCell.row, newCell.col].cost;
currentCell.col].g + cells[newCell.row, newCell.col].cost;
cells[newCell.row, newCell.col].parent.row = currentCell.row;
cells[newCell.row, newCell.col].parent.row = currentCell.row;
Line 410: Line 410:
else if (!opened.Contains(newCell) && !closed.Contains(newCell))
else if (!opened.Contains(newCell) && !closed.Contains(newCell))
{
{
cells[newCell.row, newCell.col].g = cells[currentCell.row,
cells[newCell.row, newCell.col].g = cells[currentCell.row,
currentCell.col].g + cells[newCell.row, newCell.col].cost;
currentCell.col].g + cells[newCell.row, newCell.col].cost;
cells[newCell.row, newCell.col].f =
cells[newCell.row, newCell.col].f =
cells[newCell.row, newCell.col].g + Heuristic(newCell);
cells[newCell.row, newCell.col].g + Heuristic(newCell);
cells[newCell.row, newCell.col].parent.row = currentCell.row;
cells[newCell.row, newCell.col].parent.row = currentCell.row;
Line 419: Line 419:
}
}


// If the cost to reach the considered cell from the actual one is
// If the cost to reach the considered cell from the actual one is
// smaller than the previous one
// smaller than the previous one
else if (cells[newCell.row, newCell.col].g > cells[currentCell.row,
else if (cells[newCell.row, newCell.col].g > cells[currentCell.row,
currentCell.col].g + cells[newCell.row, newCell.col].cost)
currentCell.col].g + cells[newCell.row, newCell.col].cost)
{
{
cells[newCell.row, newCell.col].g = cells[currentCell.row,
cells[newCell.row, newCell.col].g = cells[currentCell.row,
currentCell.col].g + cells[newCell.row, newCell.col].cost;
currentCell.col].g + cells[newCell.row, newCell.col].cost;
cells[newCell.row, newCell.col].f =
cells[newCell.row, newCell.col].f =
cells[newCell.row, newCell.col].g + Heuristic(newCell);
cells[newCell.row, newCell.col].g + Heuristic(newCell);
cells[newCell.row, newCell.col].parent.row = currentCell.row;
cells[newCell.row, newCell.col].parent.row = currentCell.row;
Line 483: Line 483:
}
}


// It select the cell between those in the list opened that have the smaller
// It select the cell between those in the list opened that have the smaller
// value of f
// value of f
public Coordinates ShorterExpectedPath()
public Coordinates ShorterExpectedPath()
Line 492: Line 492:
for (int i = 1; i < opened.Count; i++)
for (int i = 1; i < opened.Count; i++)
{
{
if (cells[opened[i].row, opened[i].col].f < cells[opened[sep].row,
if (cells[opened[i].row, opened[i].col].f < cells[opened[sep].row,
opened[sep].col].f)
opened[sep].col].f)
{
{
Line 508: Line 508:
for (int i = -1; i <= 1; i++)
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
for (int j = -1; j <= 1; j++)
if (c.row+i >= 0 && c.row+i < 8 && c.col+j >= 0 && c.col+j < 8 &&
if (c.row+i >= 0 && c.row+i < 8 && c.col+j >= 0 && c.col+j < 8 &&
(i != 0 || j != 0))
(i != 0 || j != 0))
{
{
Line 519: Line 519:
public bool IsAWall(int row, int col)
public bool IsAWall(int row, int col)
{
{
int[,] walls = new int[,] { { 2, 4 }, { 2, 5 }, { 2, 6 }, { 3, 6 }, { 4, 6 },
int[,] walls = new int[,] { { 2, 4 }, { 2, 5 }, { 2, 6 }, { 3, 6 }, { 4, 6 },
{ 5, 6 }, { 5, 5 }, { 5, 4 }, { 5, 3 }, { 5, 2 }, { 4, 2 }, { 3, 2 } };
{ 5, 6 }, { 5, 5 }, { 5, 4 }, { 5, 3 }, { 5, 2 }, { 4, 2 }, { 3, 2 } };
bool found = false;
bool found = false;
Line 584: Line 584:
#include <algorithm>
#include <algorithm>
#include <iostream>
#include <iostream>

class point {
class point {
public:
public:
Line 592: Line 592:
int x, y;
int x, y;
};
};

class map {
class map {
public:
public:
Line 611: Line 611:
int w, h;
int w, h;
};
};

class node {
class node {
public:
public:
Line 620: Line 620:
int dist, cost;
int dist, cost;
};
};

class aStar {
class aStar {
public:
public:
Line 629: Line 629:
neighbours[6] = point( 0, 1 ); neighbours[7] = point( 1, 0 );
neighbours[6] = point( 0, 1 ); neighbours[7] = point( 1, 0 );
}
}

int calcDist( point& p ){
int calcDist( point& p ){
// need a better heuristic
// need a better heuristic
Line 635: Line 635:
return( x * x + y * y );
return( x * x + y * y );
}
}

bool isValid( point& p ) {
bool isValid( point& p ) {
return ( p.x >-1 && p.y > -1 && p.x < m.w && p.y < m.h );
return ( p.x >-1 && p.y > -1 && p.x < m.w && p.y < m.h );
}
}

bool existPoint( point& p, int cost ) {
bool existPoint( point& p, int cost ) {
std::list<node>::iterator i;
std::list<node>::iterator i;
Line 654: Line 654:
return false;
return false;
}
}

bool fillOpen( node& n ) {
bool fillOpen( node& n ) {
int stepCost, nc, dist;
int stepCost, nc, dist;
Line 664: Line 664:
neighbour = n.pos + neighbours[x];
neighbour = n.pos + neighbours[x];
if( neighbour == end ) return true;
if( neighbour == end ) return true;

if( isValid( neighbour ) && m( neighbour.x, neighbour.y ) != 1 ) {
if( isValid( neighbour ) && m( neighbour.x, neighbour.y ) != 1 ) {
nc = stepCost + n.cost;
nc = stepCost + n.cost;
Line 671: Line 671:
node m;
node m;
m.cost = nc; m.dist = dist;
m.cost = nc; m.dist = dist;
m.pos = neighbour;
m.pos = neighbour;
m.parent = n.pos;
m.parent = n.pos;
open.push_back( m );
open.push_back( m );
Line 679: Line 679:
return false;
return false;
}
}

bool search( point& s, point& e, map& mp ) {
bool search( point& s, point& e, map& mp ) {
node n; end = e; start = s; m = mp;
node n; end = e; start = s; m = mp;
n.cost = 0; n.pos = s; n.parent = 0; n.dist = calcDist( s );
n.cost = 0; n.pos = s; n.parent = 0; n.dist = calcDist( s );
open.push_back( n );
open.push_back( n );
while( !open.empty() ) {
while( !open.empty() ) {
Line 693: Line 693:
return false;
return false;
}
}

int path( std::list<point>& path ) {
int path( std::list<point>& path ) {
path.push_front( end );
path.push_front( end );
int cost = 1 + closed.back().cost;
int cost = 1 + closed.back().cost;
path.push_front( closed.back().pos );
path.push_front( closed.back().pos );
point parent = closed.back().parent;
point parent = closed.back().parent;

for( std::list<node>::reverse_iterator i = closed.rbegin(); i != closed.rend(); i++ ) {
for( std::list<node>::reverse_iterator i = closed.rbegin(); i != closed.rend(); i++ ) {
if( ( *i ).pos == parent && !( ( *i ).pos == start ) ) {
if( ( *i ).pos == parent && !( ( *i ).pos == start ) ) {
Line 709: Line 709:
return cost;
return cost;
}
}

map m; point end, start;
map m; point end, start;
point neighbours[8];
point neighbours[8];
Line 715: Line 715:
std::list<node> closed;
std::list<node> closed;
};
};

int main( int argc, char* argv[] ) {
int main( int argc, char* argv[] ) {
map m;
map m;
point s, e( 7, 7 );
point s, e( 7, 7 );
aStar as;
aStar as;

if( as.search( s, e, m ) ) {
if( as.search( s, e, m ) ) {
std::list<point> path;
std::list<point> path;
Line 736: Line 736:
std::cout << "\n";
std::cout << "\n";
}
}

std::cout << "\nPath cost " << c << ": ";
std::cout << "\nPath cost " << c << ": ";
for( std::list<point>::iterator i = path.begin(); i != path.end(); i++ ) {
for( std::list<point>::iterator i = path.begin(); i != path.end(); i++ ) {
Line 898: Line 898:
;; Output some information each counter or nothing if information
;; Output some information each counter or nothing if information
;; equals 0.
;; equals 0.
(when (and (not (zerop information))
(when (and (not (zerop information))
(zerop (mod counter information)))
(zerop (mod counter information)))
(format t "~Dth Node, heap size: ~D, current costs: ~D~%"
(format t "~Dth Node, heap size: ~D, current costs: ~D~%"
counter (heap-count queue)
counter (heap-count queue)
(node-cost current-node)))
(node-cost current-node)))

;; If the target is not reached continue
;; If the target is not reached continue
(until (equalp current-position goal))
(until (equalp current-position goal))
Line 946: Line 946:
import std.range;
import std.range;
import std.array;
import std.array;

struct Point {
struct Point {
int x;
int x;
Line 963: Line 963:
];
];
}
}

struct Node {
struct Node {
Point pos;
Point pos;
Line 987: Line 987:
return( x * x + y * y );
return( x * x + y * y );
}
}

bool isValid(Point b) {
bool isValid(Point b) {
return ( b.x >-1 && b.y > -1 && b.x < m.w && b.y < m.h );
return ( b.x >-1 && b.y > -1 && b.x < m.w && b.y < m.h );
}
}

bool existPoint(Point b, int cost) {
bool existPoint(Point b, int cost) {
auto i = closed.countUntil(b);
auto i = closed.countUntil(b);
Line 1,005: Line 1,005:
return false;
return false;
}
}

bool fillOpen( ref Node n ) {
bool fillOpen( ref Node n ) {
int stepCost;
int stepCost;
Line 1,011: Line 1,011:
int dist;
int dist;
Point neighbour;
Point neighbour;

for( int x = 0; x < 8; ++x ) {
for( int x = 0; x < 8; ++x ) {
// one can make diagonals have different cost
// one can make diagonals have different cost
Line 1,017: Line 1,017:
neighbour = n.pos + neighbours[x];
neighbour = n.pos + neighbours[x];
if( neighbour == end ) return true;
if( neighbour == end ) return true;

if( isValid( neighbour ) && m.m[neighbour.y][neighbour.x] != 1 ) {
if( isValid( neighbour ) && m.m[neighbour.y][neighbour.x] != 1 ) {
nc = stepCost + n.cost;
nc = stepCost + n.cost;
Line 1,024: Line 1,024:
Node m;
Node m;
m.cost = nc; m.dist = dist;
m.cost = nc; m.dist = dist;
m.pos = neighbour;
m.pos = neighbour;
m.parent = n.pos;
m.parent = n.pos;
open ~= m;
open ~= m;
Line 1,032: Line 1,032:
return false;
return false;
}
}

bool search( ref Point s, ref Point e, ref Map mp ) {
bool search( ref Point s, ref Point e, ref Map mp ) {
Node n; end = e; start = s; m = mp;
Node n; end = e; start = s; m = mp;
n.cost = 0;
n.cost = 0;
n.pos = s;
n.pos = s;
n.parent = Point();
n.parent = Point();
n.dist = calcDist( s );
n.dist = calcDist( s );
open ~= n ;
open ~= n ;
while( !open.empty() ) {
while( !open.empty() ) {
Line 1,049: Line 1,049:
return false;
return false;
}
}

int path( ref Point[] path ) {
int path( ref Point[] path ) {
path = end ~ path;
path = end ~ path;
int cost = 1 + closed.back().cost;
int cost = 1 + closed.back().cost;
path = closed.back().pos ~ path;
path = closed.back().pos ~ path;
Point parent = closed.back().parent;
Point parent = closed.back().parent;

foreach(ref i ; closed.retro) {
foreach(ref i ; closed.retro) {
if( i.pos == parent && !( i.pos == start ) ) {
if( i.pos == parent && !( i.pos == start ) ) {
Line 1,066: Line 1,066:
}
}
};
};

int main(string[] argv) {
int main(string[] argv) {
Map m;
Map m;
Line 1,072: Line 1,072:
Point e = Point( 7, 7 );
Point e = Point( 7, 7 );
AStar as;
AStar as;

if( as.search( s, e, m ) ) {
if( as.search( s, e, m ) ) {
Point[] path;
Point[] path;
Line 1,088: Line 1,088:
writeln();
writeln();
}
}

write("\nPath cost ", c, ": ");
write("\nPath cost ", c, ": ");
foreach( i; path ) {
foreach( i; path ) {
Line 1,334: Line 1,334:
aCell.row = nCell.row
aCell.row = nCell.row
loop
loop

'Drawing the path
'Drawing the path
for i = 0 to 7
for i = 0 to 7
Line 1,348: Line 1,348:
print
print
next
next

'Writing the cells sequence and the path length
'Writing the cells sequence and the path length
print
print
Line 1,569: Line 1,569:


instance Ord a => Semigroup (PQueue a) where
instance Ord a => Semigroup (PQueue a) where
h1@(Node (w1, x1) l1 r1) <> h2@(Node (w2, x2) l2 r2)
h1@(Node (w1, x1) l1 r1) <> h2@(Node (w2, x2) l2 r2)
| w1 < w2 = Node (w1, x1) (h2 <> r1) l1
| w1 < w2 = Node (w1, x1) (h2 <> r1) l1
| otherwise = Node (w2, x2) (h1 <> r2) l2
| otherwise = Node (w2, x2) (h1 <> r2) l2
EmptyQueue <> h = h
EmptyQueue <> h = h
Line 1,614: Line 1,614:
Finally, the search algorythm, as given in Wikipedia.
Finally, the search algorythm, as given in Wikipedia.


<lang haskell>get :: (Ord k, Bounded a) => Map k a -> k -> a
<lang haskell>get :: (Ord k, Bounded a) => Map k a -> k -> a
get m x = Map.findWithDefault maxBound x m
get m x = Map.findWithDefault maxBound x m


set :: Ord k => Map k a -> k -> a -> Map k a
set :: Ord k => Map k a -> k -> a -> Map k a
set m k x = Map.insert k x m
set m k x = Map.insert k x m


data AstarData n = SetData { cameFrom :: Map n n
data AstarData n = SetData { cameFrom :: Map n n
Line 1,672: Line 1,672:
| i <- [0..8] ]
| i <- [0..8] ]
| j <- [0..8] ]
| j <- [0..8] ]
in do
in do
print path
print path
mapM_ putStrLn picture
mapM_ putStrLn picture
Line 1,678: Line 1,678:
<pre>λ> main
<pre>λ> main
[(1,1),(2,1),(3,1),(4,1),(5,1),(6,2),(6,3),(6,4),(6,5),(6,6),(7,7)]
[(1,1),(2,1),(3,1),(4,1),(5,1),(6,2),(6,3),(6,4),(6,5),(6,6),(7,7)]

*****
*****
###*
###*
#*
#*
# #*
# #*
# #*
# #*
####*
####*
*
*

Path score: 11</pre>
Path score: 11</pre>


Line 1,707: Line 1,707:
private int xend, yend;
private int xend, yend;
private final boolean diag;
private final boolean diag;

// Node class for convienience
// Node class for convienience
static class Node implements Comparable {
static class Node implements Comparable {
Line 1,816: Line 1,816:
Collections.sort(this.open);
Collections.sort(this.open);
}
}

public static void main(String[] args) {
public static void main(String[] args) {
// -1 = blocked
// -1 = blocked
Line 1,860: Line 1,860:
{{out}}
{{out}}
<pre>
<pre>
[0, 0] [1, 0] [2, 0] [3, 0] [4, 0] [5, 1] [6, 2] [7, 3] [6, 4] [6, 5] [6, 6] [7, 7]
[0, 0] [1, 0] [2, 0] [3, 0] [4, 0] [5, 1] [6, 2] [7, 3] [6, 4] [6, 5] [6, 6] [7, 7]
Total cost: 11,00
Total cost: 11,00
*****___
*****___
Line 1,875: Line 1,875:
Animated.<br />To see how it works on a random map go [http://paulo-jorente.de/tests/astar/ here]
Animated.<br />To see how it works on a random map go [http://paulo-jorente.de/tests/astar/ here]
<lang javascript>
<lang javascript>
var ctx, map, opn = [], clsd = [], start = {x:1, y:1, f:0, g:0},
var ctx, map, opn = [], clsd = [], start = {x:1, y:1, f:0, g:0},
goal = {x:8, y:8, f:0, g:0}, mw = 10, mh = 10, neighbours, path;
goal = {x:8, y:8, f:0, g:0}, mw = 10, mh = 10, neighbours, path;


Line 1,970: Line 1,970:
}
}
}
}
map[5][3] = map[6][3] = map[7][3] = map[3][4] = map[7][4] = map[3][5] =
map[5][3] = map[6][3] = map[7][3] = map[3][4] = map[7][4] = map[3][5] =
map[7][5] = map[3][6] = map[4][6] = map[5][6] = map[6][6] = map[7][6] = 1;
map[7][5] = map[3][6] = map[4][6] = map[5][6] = map[6][6] = map[7][6] = 1;
//map[start.x][start.y] = 2; map[goal.x][goal.y] = 3;
//map[start.x][start.y] = 2; map[goal.x][goal.y] = 3;
Line 1,981: Line 1,981:
document.body.appendChild( canvas );
document.body.appendChild( canvas );
neighbours = [
neighbours = [
{x:1, y:0, c:1}, {x:-1, y:0, c:1}, {x:0, y:1, c:1}, {x:0, y:-1, c:1},
{x:1, y:0, c:1}, {x:-1, y:0, c:1}, {x:0, y:1, c:1}, {x:0, y:-1, c:1},
{x:1, y:1, c:1.4}, {x:1, y:-1, c:1.4}, {x:-1, y:1, c:1.4}, {x:-1, y:-1, c:1.4}
{x:1, y:1, c:1.4}, {x:1, y:-1, c:1.4}, {x:-1, y:1, c:1.4}, {x:-1, y:-1, c:1.4}
];
];
Line 1,991: Line 1,991:
[(1, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (3, 7) (4, 8) (5, 8) (6, 8) (7, 8) (8, 8) ]
[(1, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (3, 7) (4, 8) (5, 8) (6, 8) (7, 8) (8, 8) ]
</pre>
</pre>
Implementation using recursive strategy
<lang javascript>
function manhattan(x1, y1, x2, y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}


function aStar (board, startx, starty, goalx, goaly,
open = Array(8 * 8).fill(null),
closed = Array(8 * 8).fill(null),
current = {
"coord": [startx, starty],
"distance": 0,
"heuristic": manhattan(startx, starty, goalx, goaly),
"previous": null
}) {
const [x, y] = [...current.coord];

if (x === goalx && y === goaly) {
closed[x + y * 8] = current;
return (lambda = (closed, x, y, startx, starty) => {
if (x === startx && y === starty) {
return [[x, y]];
}
const [px, py] = closed.filter(e => e !== null)
.find(({coord: [nx, ny]}) => {
return nx === x && ny === y
}).previous;
return lambda(closed, px, py, startx, starty).concat([[x,y]]);
})(closed, x, y, startx, starty);
}

let newOpen = open.slice();
[
[x + 1, y + 1], [x - 1, y - 1], [x + 1, y], [x, y + 1],
[x - 1, y + 1], [x + 1, y - 1], [x - 1, y], [x, y - 1]
].filter(([x,y]) => x >= 0 && x < 8 &&
y >= 0 && y < 8 &&
closed[x + y * 8] === null
).forEach(([x,y]) => {
newOpen[x + y * 8] = {
"coord": [x,y],
"distance": current.distance + (board[x + y * 8] === 1 ? 100 : 1),
"heuristic": manhattan(x, y, goalx, goaly),
"previous": [...current.coord]
};
});

let newClosed = closed.slice();
newClosed[x + y * 8] = current;

const [newCurrent,] = newOpen.filter(e => e !== null)
.sort((a, b) => {
return (a.distance + a.heuristic) - (b.distance + b.heuristic);
});

const [newx, newy] = [...newCurrent.coord];
newOpen[newx + newy * 8] = null;

return aStar(board, startx, starty, goalx, goaly,
newOpen, newClosed, newCurrent);
}

const board = [
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,1,1,1,0,
0,0,1,0,0,0,1,0,
0,0,1,0,0,0,1,0,
0,0,1,1,1,1,1,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
];

console.log(aStar(board, 0,0, 7,7));
</lang>
{{output}} <pre>
[ [ 0, 0 ], [ 1, 1 ], [ 2, 2 ], [ 3, 1 ], [ 4, 1 ], [ 5, 1 ], [ 6, 1 ], [ 7, 2 ], [ 7, 3 ], [ 7, 4 ], [ 7, 5 ], [ 7, 6 ], [ 7, 7 ] ]
</pre>
=={{header|Julia}}==
=={{header|Julia}}==
The graphic in this solution is displayed in the more standard orientation of origin at bottom left and goal at top right.
The graphic in this solution is displayed in the more standard orientation of origin at bottom left and goal at top right.
Line 2,183: Line 2,260:
if self[i]:F() < self[1]:F() then
if self[i]:F() < self[1]:F() then
s = self[1]
s = self[1]
self[1] = self[i]
self[1] = self[i]
self[i] = s
self[i] = s
end
end
Line 2,241: Line 2,318:
-- A-STAR ----------------------------------------------------------------------
-- A-STAR ----------------------------------------------------------------------
local nbours = {
local nbours = {
{ 1, 0, 1 }, { 0, 1, 1 }, { 1, 1, 1.4 }, { 1, -1, 1.4 },
{ 1, 0, 1 }, { 0, 1, 1 }, { 1, 1, 1.4 }, { 1, -1, 1.4 },
{ -1, -1, 1.4 }, { -1, 1, 1.4 }, { 0, -1, 1 }, { -1, 0, 1 }
{ -1, -1, 1.4 }, { -1, 1, 1.4 }, { 0, -1, 1 }, { -1, 0, 1 }
}
}
local map = {
local map = {
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
Line 2,254: Line 2,331:
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1
1,1,1,1,1,1,1,1,1,1
}
}
local open, closed, start, goal,
local open, closed, start, goal,
mapW, mapH = Queue:new(), List:new(), Point:new(), Point:new(), 10, 10
mapW, mapH = Queue:new(), List:new(), Point:new(), Point:new(), 10, 10
start:set( 2, 2 ); goal:set( 9, 9 )
start:set( 2, 2 ); goal:set( 9, 9 )
Line 2,269: Line 2,346:
end
end
function isValid( pos )
function isValid( pos )
return pos.x > 0 and pos.x <= mapW
return pos.x > 0 and pos.x <= mapW
and pos.y > 0 and pos.y <= mapH
and pos.y > 0 and pos.y <= mapH
and map[pos.x + mapW * pos.y - mapW] == 0
and map[pos.x + mapW * pos.y - mapW] == 0
end
end
Line 2,285: Line 2,362:
nNode.cost = node.cost + nbours[n][3]
nNode.cost = node.cost + nbours[n][3]
nNode.dist = calcDist( nNode.pos )
nNode.dist = calcDist( nNode.pos )

if isValid( nNode.pos ) then
if isValid( nNode.pos ) then
if nNode.pos:equals( goal ) then
if nNode.pos:equals( goal ) then
closed:push( nNode )
closed:push( nNode )
return true
return true
end
end
nx = hasNode( closed, nNode.pos )
nx = hasNode( closed, nNode.pos )
Line 2,295: Line 2,372:
nx = hasNode( open, nNode.pos )
nx = hasNode( open, nNode.pos )
if( nx < 0 ) or ( nx > 0 and nNode:F() < open[nx]:F() ) then
if( nx < 0 ) or ( nx > 0 and nNode:F() < open[nx]:F() ) then
if( nx > 0 ) then
if( nx > 0 ) then
table.remove( open, nx )
table.remove( open, nx )
end
end
Line 2,338: Line 2,415:
if node == nil then break end
if node == nil then break end
closed:push( node )
closed:push( node )
if addToOpen( node ) == true then
if addToOpen( node ) == true then
makePath()
makePath()
return true
return true
end
end
end
end
Line 2,868: Line 2,945:
=={{header|Phix}}==
=={{header|Phix}}==
rows and columns are numbered 1 to 8. start position is {1,1} and end position is {8,8}.
rows and columns are numbered 1 to 8. start position is {1,1} and end position is {8,8}.
barriers are simply avoided, rather than costed at 100.
barriers are simply avoided, rather than costed at 100.
Note that the 23 visited nodes does not count walls, but with them this algorithm exactly matches the 35 of Racket.
Note that the 23 visited nodes does not count walls, but with them this algorithm exactly matches the 35 of Racket.


Line 2,882: Line 2,959:
::::::::
::::::::
"""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span>
"""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span>

<span style="color: #008080;">constant</span> <span style="color: #000000;">permitted</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">permitted</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}}</span>
<span style="color: #0000FF;">{-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}}</span>

<span style="color: #004080;">sequence</span> <span style="color: #000000;">key</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- chebyshev, cost</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">key</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- chebyshev, cost</span>
<span style="color: #000000;">moves</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}},</span>
<span style="color: #000000;">moves</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}},</span>
Line 2,986: Line 3,063:
<span style="color: #0000FF;">{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- left</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- left</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">}}</span> <span style="color: #000080;font-style:italic;">-- right</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">}}</span> <span style="color: #000080;font-style:italic;">-- right</span>

<span style="color: #008080;">function</span> <span style="color: #000000;">get_moves</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">mtm</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">get_moves</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">mtm</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">valid</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">valid</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
Line 3,002: Line 3,079:
<span style="color: #008080;">return</span> <span style="color: #000000;">valid</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">valid</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>

<span style="color: #008080;">function</span> <span style="color: #000000;">make_move</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">move</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">make_move</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">move</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">p0</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">),</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">p0</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">),</span>
Line 3,013: Line 3,090:
<span style="color: #008080;">return</span> <span style="color: #000000;">grid</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">grid</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>

<span style="color: #008080;">function</span> <span style="color: #000000;">manhattan</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">manhattan</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
Line 3,021: Line 3,098:
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>

<span style="color: #004080;">sequence</span> <span style="color: #000000;">problem</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">new_grid</span><span style="color: #0000FF;">,</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">problem</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">new_grid</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">moves</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">next_moves</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">move</span>
<span style="color: #000000;">moves</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">next_moves</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">move</span>

<span style="color: #008080;">procedure</span> <span style="color: #000000;">show_grid</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">show_grid</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s\n"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join_by</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">),</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s\n"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join_by</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">),</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>

<span style="color: #000000;">grid</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">target</span>
<span style="color: #000000;">grid</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">target</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1000</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1000</span> <span style="color: #008080;">do</span>
Line 3,040: Line 3,117:
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"problem (manhattan cost is %d):\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">manhattan</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"problem (manhattan cost is %d):\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">manhattan</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">show_grid</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">show_grid</span><span style="color: #0000FF;">()</span>

<span style="color: #004080;">integer</span> <span style="color: #000000;">todo</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">pq_new</span><span style="color: #0000FF;">(),</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">todo</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">pq_new</span><span style="color: #0000FF;">(),</span>
<span style="color: #000000;">seen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">seen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">()</span>
Line 3,062: Line 3,139:
<span style="color: #000000;">move</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">next_moves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">move</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">next_moves</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">new_grid</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">make_move</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span><span style="color: #000000;">move</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">new_grid</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">make_move</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span><span style="color: #000000;">move</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">mc</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">manhattan</span><span style="color: #0000FF;">(</span><span style="color: #000000;">new_grid</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">mc</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">manhattan</span><span style="color: #0000FF;">(</span><span style="color: #000000;">new_grid</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">mc</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">mc</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">new_grid</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">target</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">new_grid</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">target</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
Line 3,263: Line 3,340:
<lang python>from __future__ import print_function
<lang python>from __future__ import print_function
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt

class AStarGraph(object):
class AStarGraph(object):
#Define a class board like grid with two barriers
#Define a class board like grid with two barriers

def __init__(self):
def __init__(self):
self.barriers = []
self.barriers = []
Line 3,279: Line 3,356:
dy = abs(start[1] - goal[1])
dy = abs(start[1] - goal[1])
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)
return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)

def get_vertex_neighbours(self, pos):
def get_vertex_neighbours(self, pos):
n = []
n = []
Line 3,290: Line 3,367:
n.append((x2, y2))
n.append((x2, y2))
return n
return n

def move_cost(self, a, b):
def move_cost(self, a, b):
for barrier in self.barriers:
for barrier in self.barriers:
Line 3,296: Line 3,373:
return 100 #Extremely high cost to enter barrier squares
return 100 #Extremely high cost to enter barrier squares
return 1 #Normal movement cost
return 1 #Normal movement cost

def AStarSearch(start, end, graph):
def AStarSearch(start, end, graph):

G = {} #Actual movement cost to each position from the start position
G = {} #Actual movement cost to each position from the start position
F = {} #Estimated movement cost of start to end going via this position
F = {} #Estimated movement cost of start to end going via this position

#Initialize starting values
#Initialize starting values
G[start] = 0
G[start] = 0
F[start] = graph.heuristic(start, end)
F[start] = graph.heuristic(start, end)

closedVertices = set()
closedVertices = set()
openVertices = set([start])
openVertices = set([start])
cameFrom = {}
cameFrom = {}

while len(openVertices) > 0:
while len(openVertices) > 0:
#Get the vertex in the open list with the lowest F score
#Get the vertex in the open list with the lowest F score
Line 3,328: Line 3,405:
path.reverse()
path.reverse()
return path, F[end] #Done!
return path, F[end] #Done!

#Mark the current vertex as closed
#Mark the current vertex as closed
openVertices.remove(current)
openVertices.remove(current)
closedVertices.add(current)
closedVertices.add(current)

#Update scores for vertices near the current position
#Update scores for vertices near the current position
for neighbour in graph.get_vertex_neighbours(current):
for neighbour in graph.get_vertex_neighbours(current):
if neighbour in closedVertices:
if neighbour in closedVertices:
continue #We have already processed this node exhaustively
continue #We have already processed this node exhaustively
candidateG = G[current] + graph.move_cost(current, neighbour)
candidateG = G[current] + graph.move_cost(current, neighbour)

if neighbour not in openVertices:
if neighbour not in openVertices:
openVertices.add(neighbour) #Discovered a new vertex
openVertices.add(neighbour) #Discovered a new vertex
elif candidateG >= G[neighbour]:
elif candidateG >= G[neighbour]:
continue #This G score is worse than previously found
continue #This G score is worse than previously found

#Adopt this G score
#Adopt this G score
cameFrom[neighbour] = current
cameFrom[neighbour] = current
Line 3,349: Line 3,426:
H = graph.heuristic(neighbour, end)
H = graph.heuristic(neighbour, end)
F[neighbour] = G[neighbour] + H
F[neighbour] = G[neighbour] + H

raise RuntimeError("A* failed to find a solution")
raise RuntimeError("A* failed to find a solution")

if __name__=="__main__":
if __name__=="__main__":
graph = AStarGraph()
graph = AStarGraph()
Line 3,432: Line 3,509:
(define count 0)
(define count 0)
<a-star-setup>
<a-star-setup>

(begin0
(begin0
(let/ec esc
(let/ec esc
<a-star-loop>
<a-star-loop>
#f)
#f)

(printf "visited ~a nodes\n" count))))
(printf "visited ~a nodes\n" count))))


Line 3,448: Line 3,525:
<a-star-setup-closed>
<a-star-setup-closed>
(define node->best-path (make-hash))
(define node->best-path (make-hash))
(define node->best-path-cost (make-hash))
(define node->best-path-cost (make-hash))
(hash-set! node->best-path initial empty)
(hash-set! node->best-path initial empty)
(hash-set! node->best-path-cost initial 0))
(hash-set! node->best-path-cost initial 0))
Line 3,472: Line 3,549:
(define h-x (node-cost x))
(define h-x (node-cost x))
(define path-x (hash-ref node->best-path x))
(define path-x (hash-ref node->best-path x))

(when (zero? h-x)
(when (zero? h-x)
(esc (reverse path-x))))
(esc (reverse path-x))))
Line 3,479: Line 3,556:
<a-star-loop-body>
<a-star-loop-body>
<a-star-loop-stop?>
<a-star-loop-stop?>

(define g-x (hash-ref node->best-path-cost x))
(define g-x (hash-ref node->best-path-cost x))
(for ([x->y (in-list (node-edges x))])
(for ([x->y (in-list (node-edges x))])
Line 3,532: Line 3,609:
(define-unit map@
(define-unit map@
(import) (export graph^)
(import) (export graph^)

(define node? map-node?)
(define node? map-node?)
(define edge? map-edge?)
(define edge? map-edge?)
(define edge-src map-edge-src)
(define edge-src map-edge-src)
(define edge-dest map-edge-dest)
(define edge-dest map-edge-dest)

<map-graph-cost>
<map-graph-cost>
<map-graph-edges>))
<map-graph-edges>))
Line 3,573: Line 3,650:
2htdp/image
2htdp/image
racket/runtime-path)
racket/runtime-path)

<graph-sig>
<graph-sig>

<map-generation>
<map-generation>
<map-graph-rep>
<map-graph-rep>
<map-graph>
<map-graph>

<a-star>
<a-star>

<map-node-cost>
<map-node-cost>
<map-example>
<map-example>
Line 3,589: Line 3,666:
(cons dx dy)])
(cons dx dy)])
random-path))
random-path))

<map-display>
<map-display>
<path-display-line>
<path-display-line>
Line 3,847: Line 3,924:
newActual[i,j] := 0 foreach i within 1...width, j within 1...height;
newActual[i,j] := 0 foreach i within 1...width, j within 1...height;
newCameFrom[i,j] := (x : 0, y : 0) foreach i within 1...width, j within 1...height;
newCameFrom[i,j] := (x : 0, y : 0) foreach i within 1...width, j within 1...height;

searchResults := search((open : [start], closed : [], estimate : newEstimate, actual : newActual, cameFrom : newCameFrom), barriers, end);
searchResults := search((open : [start], closed : [], estimate : newEstimate, actual : newActual, cameFrom : newCameFrom), barriers, end);
shortestPath := path(searchResults.cameFrom, start, end) ++ [end];
shortestPath := path(searchResults.cameFrom, start, end) ++ [end];
Line 3,906: Line 3,983:
y := n.y + p.y;
y := n.y + p.y;
in
in
(x : x, y : y) when x >= 1 and x <= w and y >= 1 and y <= h;
(x : x, y : y) when x >= 1 and x <= w and y >= 1 and y <= h;


calculateCost(barriers(1), start, end) := 100 when elementOf(end, barriers) else 1;
calculateCost(barriers(1), start, end) := 100 when elementOf(end, barriers) else 1;
Line 4,439: Line 4,516:
path: 0,0 → 1,0 → 2,0 → 3,0 → 4,0 → 5,1 → 6,2 → 7,3 → 7,4 → 7,5 → 7,6 → 7,7
path: 0,0 → 1,0 → 2,0 → 3,0 → 4,0 → 5,1 → 6,2 → 7,3 → 7,4 → 7,5 → 7,6 → 7,7
01234567
01234567
0◉
0◉
1↓
1↓
2↓ ■■■
2↓ ■■■
3↓ ■ ■
3↓ ■ ■
4↘ ■ ■
4↘ ■ ■
5 ↘■■■■■
5 ↘■■■■■
6 ↘
6 ↘
7 →→→→✪
7 →→→→✪
</pre>
</pre>
Line 4,462: Line 4,539:
F[kstart]=graph.heuristic(start,end);
F[kstart]=graph.heuristic(start,end);
closedVertices,openVertices,cameFrom := List(),List(start),Dictionary();
closedVertices,openVertices,cameFrom := List(),List(start),Dictionary();

while(openVertices){
while(openVertices){
# Get the vertex in the open list with the lowest F score
# Get the vertex in the open list with the lowest F score
Line 4,470: Line 4,547:
if(current==Void or F[kpos]<currentFscore)
if(current==Void or F[kpos]<currentFscore)
currentFscore,current = F[kpos],pos;
currentFscore,current = F[kpos],pos;

# Check if we have reached the goal
# Check if we have reached the goal
if(current==end){ # Yes! Retrace our route backward
if(current==end){ # Yes! Retrace our route backward
Line 4,484: Line 4,561:
openVertices.remove(current);
openVertices.remove(current);
if(not closedVertices.holds(current)) closedVertices.append(current);
if(not closedVertices.holds(current)) closedVertices.append(current);

# Update scores for vertices near the current position
# Update scores for vertices near the current position
foreach neighbor in (graph.get_vertex_neighbors(current)){
foreach neighbor in (graph.get_vertex_neighbors(current)){
Line 4,491: Line 4,568:
kneighbor:=toKey(neighbor);
kneighbor:=toKey(neighbor);
candidateG:=G[toKey(current)] + graph.move_cost(current, neighbor);
candidateG:=G[toKey(current)] + graph.move_cost(current, neighbor);

if(not openVertices.holds(neighbor))
if(not openVertices.holds(neighbor))
openVertices.append(neighbor); # Discovered a new vertex
openVertices.append(neighbor); # Discovered a new vertex
else if(candidateG>=G[kneighbor])
else if(candidateG>=G[kneighbor])
continue; # This G score is worse than previously found
continue; # This G score is worse than previously found

# Adopt this G score
# Adopt this G score
cameFrom[kneighbor]=current;
cameFrom[kneighbor]=current;
Line 4,542: Line 4,619:
foreach x,y in (graph.barriers){ grid[x][y]="#" }
foreach x,y in (graph.barriers){ grid[x][y]="#" }
foreach x,y in (route){ grid[x][y]="+" }
foreach x,y in (route){ grid[x][y]="+" }
grid[0][0] = "S"; grid[7][7] = "E";
grid[0][0] = "S"; grid[7][7] = "E";
foreach line in (grid){ println(line.concat()) }</lang>
foreach line in (grid){ println(line.concat()) }</lang>
{{out}}
{{out}}
Line 4,548: Line 4,625:
Route: (0,0),(1,1),(2,2),(3,1),(4,0),(5,1),(6,2),(7,3),(7,4),(7,5),(7,6),(7,7)
Route: (0,0),(1,1),(2,2),(3,1),(4,0),(5,1),(6,2),(7,3),(7,4),(7,5),(7,6),(7,7)
Cost: 11
Cost: 11
S
S
+
+
+ ###
+ ###
+# #
+# #
+ # #
+ # #
+#####
+#####
+
+
++++E
++++E
</pre>
</pre>