115using namespace Aleph;
177 const auto& f =
from->get_info();
178 const auto& t =
to->get_info();
179 double dx = f.x - t.x;
180 double dy = f.y - t.y;
198 const auto& f =
from->get_info();
199 const auto& t =
to->get_info();
200 return abs(f.x - t.x) +
abs(f.y - t.y);
218 vector<GridGraph::Node*>&
nodes)
221 nodes.resize(width * height);
224 for (
int y = 0;
y < height; ++
y)
225 for (
int x = 0; x < width; ++x)
227 int idx =
y * width + x;
232 for (
int y = 0;
y < height; ++
y)
233 for (
int x = 0; x < width; ++x)
235 int idx =
y * width + x;
240 int right =
y * width + (x + 1);
248 int down = (
y + 1) * width + x;
259 if (x + 1 < width &&
y + 1 < height)
261 int dr = (
y + 1) * width + (x + 1);
267 if (x - 1 >= 0 &&
y + 1 < height)
269 int dl = (
y + 1) * width + (x - 1);
293 const vector<GridGraph::Node*>&
nodes,
306 auto node = it.get_current_node();
307 const auto&
info = node->get_info();
317 for (
int x = 0; x < width; ++x)
321 for (
int y = 0;
y < height; ++
y)
324 for (
int x = 0; x < width; ++x)
334template <
typename Func>
337 auto start = chrono::high_resolution_clock::now();
339 auto end = chrono::high_resolution_clock::now();
340 return chrono::duration<double, milli>(end - start).count();
349 cout <<
"╔══════════════════════════════════════════════════════════════════╗\n";
350 cout <<
"║ A* Shortest Path Algorithm - Example ║\n";
351 cout <<
"╚══════════════════════════════════════════════════════════════════╝\n\n";
357 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
358 cout <<
"Part 1: 4-Connected Grid (no diagonal movement)\n";
359 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
361 const int WIDTH = 15;
364 vector<GridGraph::Node*>
nodes4;
368 cout <<
"Nodes: " <<
g4.get_num_nodes() <<
"\n";
369 cout <<
"Edges: " <<
g4.get_num_arcs() <<
"\n\n";
375 cout <<
"Start: (" <<
start4->get_info().x <<
", " <<
start4->get_info().y <<
")\n";
376 cout <<
"End: (" <<
end4->get_info().x <<
", " <<
end4->get_info().y <<
")\n\n";
379 cout <<
"▶ A* with Manhattan heuristic:\n";
384 double cost = std::numeric_limits<double>::max();
389 if (cost == std::numeric_limits<double>::max())
391 cout <<
" No path found.\n";
395 cout <<
" Path cost: " << cost <<
"\n";
396 cout <<
" Path length: " << path.
size() <<
" nodes\n";
400 if (cost != std::numeric_limits<double>::max())
408 cout <<
"▶ A* with Euclidean heuristic:\n";
413 double cost = std::numeric_limits<double>::max();
418 if (cost == std::numeric_limits<double>::max())
420 cout <<
" No path found.\n";
424 cout <<
" Path cost: " << cost <<
"\n";
425 cout <<
" Path length: " << path.
size() <<
" nodes\n";
428 cout <<
" Note: Euclidean underestimates in 4-connected grid,\n";
429 cout <<
" but still finds optimal path (admissible).\n\n";
433 cout <<
"▶ Dijkstra (A* with zero heuristic):\n";
438 double cost = std::numeric_limits<double>::max();
443 if (cost == std::numeric_limits<double>::max())
445 cout <<
" No path found.\n";
449 cout <<
" Path cost: " << cost <<
"\n";
450 cout <<
" Path length: " << path.
size() <<
" nodes\n";
453 cout <<
" Note: Explores more nodes than A* with good heuristic.\n\n";
460 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
461 cout <<
"Part 2: 8-Connected Grid (with diagonal movement)\n";
462 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
464 vector<GridGraph::Node*>
nodes8;
468 cout <<
"Nodes: " <<
g8.get_num_nodes() <<
"\n";
469 cout <<
"Edges: " <<
g8.get_num_arcs() <<
" (includes diagonals)\n\n";
475 cout <<
"▶ A* with Euclidean heuristic (optimal for 8-connected):\n";
480 double cost = std::numeric_limits<double>::max();
485 if (cost == std::numeric_limits<double>::max())
487 cout <<
" No path found.\n";
492 cout <<
" Path length: " << path.
size() <<
" nodes\n";
496 if (cost != std::numeric_limits<double>::max())
504 cout <<
"▶ A* with Manhattan heuristic:\n";
509 double cost = std::numeric_limits<double>::max();
514 if (cost == std::numeric_limits<double>::max())
516 cout <<
" No path found.\n";
521 cout <<
" Path length: " << path.
size() <<
" nodes\n";
524 cout <<
" Note: Manhattan overestimates for 8-connected (not admissible),\n";
525 cout <<
" may not find optimal path!\n\n";
532 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
533 cout <<
"Part 3: Computing Full Shortest Paths Tree\n";
534 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
536 cout <<
"When you need shortest paths from one source to ALL destinations,\n";
537 cout <<
"use compute_min_paths_tree() or paint_min_paths_tree().\n\n";
547 cout <<
"▶ compute_min_paths_tree() from (0,0):\n";
553 cout <<
" Distances from (0,0):\n";
560 double dist =
astar.get_min_path(tree,
target, path);
561 cout <<
" to (" <<
tx <<
", " <<
ty <<
"): " << dist <<
"\n";
569 cout <<
"\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
570 cout <<
"Part 4: Using Fibonacci Heap (for dense/large graphs)\n";
571 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
573 cout <<
"For very large or dense graphs, Fibonacci heap can be faster:\n\n";
586 cout <<
" (" <<
big_g.get_num_nodes() <<
" nodes)\n\n";
596 double cost = std::numeric_limits<double>::max();
601 cout <<
"▶ A* with Binary Heap:\n";
602 cout <<
" Path cost: " << cost <<
"\n";
614 double cost = std::numeric_limits<double>::max();
619 cout <<
"▶ A* with Fibonacci Heap:\n";
620 cout <<
" Path cost: " << cost <<
"\n";
628 cout <<
"\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
629 cout <<
"Part 5: Inadmissible Heuristic Demonstration (Educational)\n";
630 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
632 cout <<
"WARNING: This example demonstrates what happens when you use an\n";
633 cout <<
"inadmissible heuristic (one that overestimates). This is for\n";
634 cout <<
"educational purposes only. In production, always use admissible\n";
635 cout <<
"heuristics to guarantee optimal paths!\n\n";
652 cout <<
"Graph structure:\n";
653 cout <<
" Node 0 (0,0) -> Node 2 (10,0): cost 15.0\n";
654 cout <<
" Node 0 (0,0) -> Node 1 (5,0): cost 3.0\n";
655 cout <<
" Node 1 (5,0) -> Node 2 (10,0): cost 5.0\n";
656 cout <<
" Optimal path: 0 -> 1 -> 2 (total: 8.0)\n\n";
661 using Distance_Type =
double;
664 const auto& f =
from->get_info();
665 const auto& t =
to->get_info();
666 double dx = f.x - t.x;
667 double dy = f.y - t.y;
674 cout <<
"▶ With Euclidean heuristic (admissible):\n";
681 cout <<
" Path cost: " << cost <<
"\n";
682 cout <<
" Path length: " << path.
size() <<
" nodes\n";
685 cout <<
"✓ Found optimal path (0 -> 1 -> 2)\n\n";
687 cout <<
"✗ Found suboptimal path\n\n";
691 cout <<
"▶ With inadmissible heuristic (10x overestimate):\n";
698 cout <<
" Path cost: " << cost <<
"\n";
699 cout <<
" Path length: " << path.
size() <<
" nodes\n";
702 cout <<
"Found optimal path (by chance)\n";
703 else if (cost == 15.0)
704 cout <<
"✗ Found suboptimal direct path (0 -> 2)\n";
706 cout <<
"Found path with cost " << cost <<
"\n";
708 cout <<
"\n Explanation: The inadmissible heuristic made node 1 look\n";
709 cout <<
" too expensive (h(1) >> actual cost), so A* chose the direct\n";
710 cout <<
" path instead of exploring through node 1.\n\n";
714 cout <<
"▶ With Dijkstra (zero heuristic, always optimal):\n";
721 cout <<
" Path cost: " << cost <<
"\n";
722 cout <<
" Path length: " << path.
size() <<
" nodes\n";
723 cout <<
" Result: ✓ Always finds optimal path\n\n";
726 cout <<
"┌─────────────────────────────────────────────────────────────────┐\n";
727 cout <<
"│ Key Takeaway: │\n";
729 cout <<
"│ An inadmissible heuristic CAN make A* return suboptimal paths! │\n";
731 cout <<
"│ Always verify your heuristic never overestimates: │\n";
732 cout <<
"│ h(n) ≤ actual_cost(n, goal) for all nodes n │\n";
734 cout <<
"│ When in doubt, use zero heuristic (Dijkstra) for correctness. │\n";
735 cout <<
"└─────────────────────────────────────────────────────────────────┘\n\n";
741 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
743 cout <<
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
745 cout <<
"┌─────────────────────────────────────────────────────────────────┐\n";
746 cout <<
"│ Heuristic Choice: │\n";
747 cout <<
"│ • 4-connected grid → Manhattan heuristic (optimal) │\n";
748 cout <<
"│ • 8-connected grid → Euclidean heuristic (optimal) │\n";
749 cout <<
"│ • Unknown graph → Zero heuristic (Dijkstra, always works) │\n";
750 cout <<
"├─────────────────────────────────────────────────────────────────┤\n";
751 cout <<
"│ Key Methods: │\n";
752 cout <<
"│ • find_path() - Find single shortest path (recommended) │\n";
753 cout <<
"│ • find_min_path() - Same but without heuristic (Dijkstra) │\n";
754 cout <<
"│ • paint_min_paths_tree() - All paths from source (paint) │\n";
755 cout <<
"│ • compute_min_paths_tree() - All paths (build tree) │\n";
756 cout <<
"├─────────────────────────────────────────────────────────────────┤\n";
757 cout <<
"│ Heap Selection: │\n";
758 cout <<
"│ • ArcHeap (Binary) - Good for most cases │\n";
759 cout <<
"│ • ArcFibonacciHeap - Better for very large/dense graphs │\n";
760 cout <<
"└─────────────────────────────────────────────────────────────────┘\n\n";
A* shortest path algorithm.
Dijkstra's shortest path algorithm.
List_Graph< GridNode, GridArc > GridGraph
GridGraph create_grid_graph(int width, int height, bool diagonal, vector< GridGraph::Node * > &nodes)
Creates a 2D grid graph.
double measure_time_ms(Func &&f)
void print_grid_with_path(int width, int height, const vector< GridGraph::Node * > &nodes, GridGraph::Node *start, GridGraph::Node *end, const Path< GridGraph > &path)
Prints the grid with the path highlighted.
A* algorithm for finding the shortest path between two nodes.
bool has_curr() const noexcept
Return true if the iterator has current item.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Iterator on nodes and arcs of a path.
size_t size() const noexcept
Return the path length in nodes.
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
NodeInfo & get_info() noexcept
Return a modifiable reference to the data contained in the node.
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
constexpr size_t get_num_arcs() const noexcept
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_sqrt_function > > sqrt(const __gmp_expr< T, U > &expr)
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_abs_function > > abs(const __gmp_expr< T, U > &expr)
DynArray< Graph::Node * > nodes
Main namespace for Aleph-w library functions.
DynList< T > maps(const C &c, Op op)
Classic map operation.
Default filter for filtered iterators on arcs.
Arc of graph implemented with double-linked adjacency lists.
Node belonging to a graph implemented with a double linked adjacency list.
Filtered iterator of adjacent arcs of a node.
Euclidean distance heuristic.
Distance_Type operator()(GridGraph::Node *from, GridGraph::Node *to) const
Node info containing 2D coordinates.
GridCell(int _x, int _y, bool _blocked=false)
Distance functor that reads arc weights.
Distance_Type operator()(GridGraph::Arc *arc) const
Manhattan distance heuristic.
Distance_Type operator()(GridGraph::Node *from, GridGraph::Node *to) const
Generic graph and digraph implementations.