From 2f9ec38ffab0482c0b50dbc07e0a2aed15dc053a Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Mon, 10 Nov 2025 11:00:56 +0100 Subject: [PATCH 1/8] tries to implement ada c++ wrappers --- .gitignore | 1 + Core/Pathfinder/CMakeLists.txt | 10 ++++ .../ada_backend/libpathfinding_ada.gpr | 8 +++ Core/Pathfinder/inc/pathfinding.h | 60 ++++++++++++++++++- .../Pathfinder/src/pathfinding_a_star_ada.cpp | 49 +++++++++++++++ .../src/pathfinding_dijkstra_ada.cpp | 45 ++++++++++++++ 6 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 Core/Pathfinder/ada_backend/libpathfinding_ada.gpr create mode 100644 Core/Pathfinder/src/pathfinding_a_star_ada.cpp create mode 100644 Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp diff --git a/.gitignore b/.gitignore index b88509b..c5c0709 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ # Custom cmake-build-debug/ +build/ .idea/ Paper/out *.log diff --git a/Core/Pathfinder/CMakeLists.txt b/Core/Pathfinder/CMakeLists.txt index af72fc3..41d8ed6 100644 --- a/Core/Pathfinder/CMakeLists.txt +++ b/Core/Pathfinder/CMakeLists.txt @@ -1,7 +1,17 @@ # Glob for the inc and src directories in each module file(GLOB SOURCE_FILES "src/*.cpp") +file(GLOB ADA_SOURCE_FILES "ada_backend/src/*.adb" "ada_backend/src/*.ads") + # Add each module as a library and set its dependencies add_library(Pathfinder ${SOURCE_FILES}) target_include_directories(Pathfinder PUBLIC inc) target_link_libraries(Pathfinder GridGenerator) + +set(CMAKE_Ada_CREATE_STATIC_LIBRARY ON) + +add_library(Pathfinder_Ada STATIC ${ADA_SOURCE_FILES}) +target_include_directories(Pathfinder_Ada PUBLIC ada_backend/inc) +target_link_libraries(Pathfinder Pathfinder_Ada) +set_target_properties(Pathfinder_Ada PROPERTIES LINKER_LANGUAGE Ada) +set_target_properties(Pathfinder_Ada PROPERTIES COMPILE_OPTIONS "-gnaty") diff --git a/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr b/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr new file mode 100644 index 0000000..14da86f --- /dev/null +++ b/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr @@ -0,0 +1,8 @@ +project libpathfinding_ada is + for Main use ("hello_world.adb"); + for Object_Dir use ".objs"; + + package Compiler is + for Default_Switches ("Ada") use ("-g"); + end Compiler; +end libpathfinding_ada; \ No newline at end of file diff --git a/Core/Pathfinder/inc/pathfinding.h b/Core/Pathfinder/inc/pathfinding.h index b026b82..0f764e9 100644 --- a/Core/Pathfinder/inc/pathfinding.h +++ b/Core/Pathfinder/inc/pathfinding.h @@ -28,7 +28,9 @@ namespace Pathfinder { enum class PathfinderStrategy { PATHFINDER_DIJKSTRA = 1, /**< Dijkstra's algorithm pathfinding. */ PATHFINDER_A_STAR = 2, /**< A* algorithm pathfinding. */ - PATHFINDER_JUMP_POINT_SEARCH = 3 /**< Jump-Point-Search algorithm pathfinding. */ + PATHFINDER_JUMP_POINT_SEARCH = 3, /**< Jump Point Search pathfinding. */ + PATHFINDER_DIJKSTRA_ADA = 4, /**< Dijkstra's algorithm pathfinding using Ada backend. */ + PATHFINDER_A_STAR_ADA = 5 /**< A* algorithm pathfinding using the Ada backend. */ }; /** @@ -258,4 +260,60 @@ namespace Pathfinder { std::pair direction); }; + /** + * @class DijkstraSolveAda + * @brief Extension of the PathfindingParent class. Defines an object which applies the Dijkstra algorithm for pathfinding using the Ada backend. + */ + class DijkstraSolveAda : public PathfindingParent { + public: + /** + * @brief Constructor for the JumpPointSolve class. Uses the parent Class constructor. + * @param grid Reference to the Grid object where the Jump-Point-Search algorithm will be applied. + */ + explicit DijkstraSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) {} + + /** + * @fn void nextStep + * @brief Overrides the nextStep() function to implement the Dijkstra algorithm's next step. + */ + void nextStep() override; + + private: + /** + * @fn getStrat + * @brief Overrides the getStrat() function to get the strategy (Dijkstra). + */ + [[nodiscard]] inline PathfinderStrategy getStrat() const override { + return PathfinderStrategy::PATHFINDER_DIJKSTRA_ADA; + } + }; + + /** + * @class AStarSolveAda + * @brief Extension of the PathfindingParent class. Defines an object which applies the A* algorithm for pathfinding using the Ada backend. + */ + class AStarSolveAda : public PathfindingParent { + public: + /** + * @brief Constructor for the AStarSolve class. Uses the parent Class constructor. + * @param grid Reference to the Grid object where the A* algorithm will be applied. + */ + explicit AStarSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) {} + + /** + * @fn void nextStep + * @brief Overrides the nextStep() function to implement the A* algorithm's next step. + */ + void nextStep() override; + + private: + /** + * @fn getStrat + * @brief Overrides the getStrat() function to get the strategy (A*). + */ + [[nodiscard]] inline PathfinderStrategy getStrat() const override { + return PathfinderStrategy::PATHFINDER_A_STAR_ADA; + } + }; + } \ No newline at end of file diff --git a/Core/Pathfinder/src/pathfinding_a_star_ada.cpp b/Core/Pathfinder/src/pathfinding_a_star_ada.cpp new file mode 100644 index 0000000..c7af2c1 --- /dev/null +++ b/Core/Pathfinder/src/pathfinding_a_star_ada.cpp @@ -0,0 +1,49 @@ +#include "pathfinding.h" + +#include + +void Pathfinder::AStarSolveAda::nextStep() { + // TODO: implement Ada backend call and translate for Ada interfacing + timer.stepBegin(); + GridGenerator::GridCoordinate endCoordinates = grid.getEndCoordinates(); + + GridGenerator::GridCoordinate currentCoordinates = nextCellQueue.top(); + nextCellQueue.pop(); + + GridGenerator::Cell ¤tCell = grid(currentCoordinates); + if (currentCell.getState() == GridGenerator::CellState::CELL_CLOSED) return; + if (currentCoordinates == endCoordinates) { + timer.stepEnd(); + grid.markPathByParentCells(true); + return; + } + + + std::vector neighbors = grid.getNeighborsCoordinates(currentCoordinates); + + for (const auto &neighborCoordinates: neighbors) { + GridGenerator::Cell &neighborCell = grid(neighborCoordinates); + double neighborCellGCostFromCurrentCell = currentCell.getCost().gCost + + neighborCoordinates.getOctileDistanceTo(currentCoordinates); + + if (neighborCell.getState() == GridGenerator::CellState::CELL_OPEN) { + neighborCell.setGCost(neighborCellGCostFromCurrentCell); + neighborCell.setHCost(neighborCoordinates.getAbsDistanceTo(endCoordinates)); + neighborCell.setParentCellPointer(&grid(currentCoordinates)); + neighborCell.markVisited(); + nextCellQueue.push(neighborCoordinates); + grid.incrementVisitedCellCount(); + } else if (neighborCell.getState() == GridGenerator::CellState::CELL_VISITED) { + if (neighborCell.getCost().gCost > neighborCellGCostFromCurrentCell) { + neighborCell.setGCost(neighborCellGCostFromCurrentCell); + neighborCell.setParentCellPointer(&grid(currentCoordinates)); + nextCellQueue.push(neighborCoordinates); + grid.incrementVisitedCellCount(); + } + } + } + currentCell.markClosed(); + grid.incrementClosedCellCount(); + timer.stepEnd(); +} + diff --git a/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp b/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp new file mode 100644 index 0000000..92cda71 --- /dev/null +++ b/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp @@ -0,0 +1,45 @@ +#include "pathfinding.h" + +#include + +void Pathfinder::DijkstraSolveAda::nextStep() { + // TODO: implement Ada backend call and translate for Ada interfacing + timer.stepBegin(); + GridGenerator::GridCoordinate endCoordinates = grid.getEndCoordinates(); + + GridGenerator::GridCoordinate currentCoordinates = nextCellQueue.top(); + nextCellQueue.pop(); + GridGenerator::Cell ¤tCell = grid(currentCoordinates); + if (currentCoordinates == endCoordinates) { + timer.stepEnd(); + grid.markPathByParentCells(true); + return; + } + + std::vector neighbors = grid.getNeighborsCoordinates(currentCoordinates); + + for (const auto &neighborCoordinates: neighbors) { + GridGenerator::Cell &neighborCell = grid(neighborCoordinates); + double neighborCellGCostFromCurrentCell = currentCell.getCost().gCost + + neighborCoordinates.getOctileDistanceTo(currentCoordinates); + if (neighborCell.getState() == GridGenerator::CellState::CELL_OPEN) { + neighborCell.setGCost(neighborCellGCostFromCurrentCell); + neighborCell.setParentCellPointer(&grid(currentCoordinates)); + neighborCell.markVisited(); + nextCellQueue.push(neighborCoordinates); + grid.incrementVisitedCellCount(); + } else if (neighborCell.getState() == GridGenerator::CellState::CELL_VISITED) { + if (neighborCell.getCost().gCost > neighborCellGCostFromCurrentCell) { + neighborCell.setGCost(neighborCellGCostFromCurrentCell); + neighborCell.setParentCellPointer(&grid(currentCoordinates)); + nextCellQueue.push(neighborCoordinates); + grid.incrementVisitedCellCount(); + } + } + } + currentCell.markClosed(); + grid.incrementClosedCellCount(); + timer.stepEnd(); +} + + From c7e953835f203bd61bc4a1432632ee1b5eb62234 Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Mon, 19 Jan 2026 14:29:15 +0100 Subject: [PATCH 2/8] removing custom structs fixes segfaults and illegal instructions --- .gitignore | 2 + .vscode/launch.json | 20 ++++ Core/Pathfinder/CMakeLists.txt | 40 +++++--- .../ada_backend/libpathfinding_ada.gpr | 12 ++- Core/Pathfinder/ada_backend/src/a_star.adb | 71 ++++++++++++++ Core/Pathfinder/ada_backend/src/a_star.ads | 17 ++++ Core/Pathfinder/ada_backend/src/dijkstra.adb | 68 ++++++++++++++ Core/Pathfinder/ada_backend/src/dijkstra.ads | 11 +++ .../ada_backend/src/pathfinder_c_inf.ads | 68 ++++++++++++++ .../ada_backend/src/pathfinder_common.adb | 39 ++++++++ .../ada_backend/src/pathfinder_common.ads | 8 ++ Core/Pathfinder/c_abi/inc/pathfinding_c_inf.h | 52 +++++++++++ .../c_abi/src/pathfinding_c_inf.cpp | 92 +++++++++++++++++++ Core/Pathfinder/inc/pathfinding.h | 9 +- Core/Pathfinder/src/pathfinding.cpp | 17 +++- .../Pathfinder/src/pathfinding_a_star_ada.cpp | 51 +++++----- .../src/pathfinding_dijkstra_ada.cpp | 41 ++++----- 17 files changed, 543 insertions(+), 75 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 Core/Pathfinder/ada_backend/src/a_star.adb create mode 100644 Core/Pathfinder/ada_backend/src/a_star.ads create mode 100644 Core/Pathfinder/ada_backend/src/dijkstra.adb create mode 100644 Core/Pathfinder/ada_backend/src/dijkstra.ads create mode 100644 Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads create mode 100644 Core/Pathfinder/ada_backend/src/pathfinder_common.adb create mode 100644 Core/Pathfinder/ada_backend/src/pathfinder_common.ads create mode 100644 Core/Pathfinder/c_abi/inc/pathfinding_c_inf.h create mode 100644 Core/Pathfinder/c_abi/src/pathfinding_c_inf.cpp diff --git a/.gitignore b/.gitignore index c5c0709..db14d7e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,8 @@ # Custom cmake-build-debug/ build/ +Core/Pathfinder/ada_backend/lib +Core/Pathfinder/ada_backend/obj .idea/ Paper/out *.log diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c0cfb78 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Pathfinding (GUI)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/Pathfinding_Bench_Program", + "args": ["--gui"], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "setupCommands": [ + { "description": "Enable pretty-printing", "text": "-enable-pretty-printing", "ignoreFailures": true } + ], + "preLaunchTask": "CMake: build" + } + ] +} \ No newline at end of file diff --git a/Core/Pathfinder/CMakeLists.txt b/Core/Pathfinder/CMakeLists.txt index 41d8ed6..900f524 100644 --- a/Core/Pathfinder/CMakeLists.txt +++ b/Core/Pathfinder/CMakeLists.txt @@ -1,17 +1,33 @@ -# Glob for the inc and src directories in each module -file(GLOB SOURCE_FILES "src/*.cpp") +find_program(GPRBUILD_EXECUTABLE NAMES gprbuild gnatmake) +if(NOT GPRBUILD_EXECUTABLE) + message(FATAL_ERROR "gprbuild not found; please install GNAT or ensure gprbuild is on PATH") +endif() + +include(ExternalProject) + +set(ADA_PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ada_backend) +set(ADA_PROJECT_GPR libpathfinding_ada.gpr) +set(ADA_OUTPUT_LIB ${ADA_PROJECT_DIR}/lib/libpathfinding_ada.so) + +ExternalProject_Add(Pathfinder_Ada_Proj + SOURCE_DIR ${ADA_PROJECT_DIR} + CONFIGURE_COMMAND "" + BUILD_COMMAND ${GPRBUILD_EXECUTABLE} -p -P ${ADA_PROJECT_GPR} + BUILD_IN_SOURCE 1 + INSTALL_COMMAND "" +) -file(GLOB ADA_SOURCE_FILES "ada_backend/src/*.adb" "ada_backend/src/*.ads") +add_library(Pathfinder_C_ABI STATIC ${CMAKE_CURRENT_SOURCE_DIR}/c_abi/src/pathfinding_c_inf.cpp) +target_include_directories(Pathfinder_C_ABI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/c_abi/inc) +target_link_libraries(Pathfinder_C_ABI PRIVATE GridGenerator) -# Add each module as a library and set its dependencies +add_library(Pathfinder_Ada SHARED IMPORTED) +set_target_properties(Pathfinder_Ada PROPERTIES IMPORTED_LOCATION ${ADA_OUTPUT_LIB}) +add_dependencies(Pathfinder_Ada Pathfinder_Ada_Proj Pathfinder_C_ABI) +target_link_libraries(Pathfinder_Ada INTERFACE Pathfinder_C_ABI) + +file(GLOB SOURCE_FILES "src/*.cpp") add_library(Pathfinder ${SOURCE_FILES}) target_include_directories(Pathfinder PUBLIC inc) -target_link_libraries(Pathfinder GridGenerator) - -set(CMAKE_Ada_CREATE_STATIC_LIBRARY ON) +target_link_libraries(Pathfinder GridGenerator Pathfinder_Ada Pathfinder_C_ABI) -add_library(Pathfinder_Ada STATIC ${ADA_SOURCE_FILES}) -target_include_directories(Pathfinder_Ada PUBLIC ada_backend/inc) -target_link_libraries(Pathfinder Pathfinder_Ada) -set_target_properties(Pathfinder_Ada PROPERTIES LINKER_LANGUAGE Ada) -set_target_properties(Pathfinder_Ada PROPERTIES COMPILE_OPTIONS "-gnaty") diff --git a/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr b/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr index 14da86f..a3df94f 100644 --- a/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr +++ b/Core/Pathfinder/ada_backend/libpathfinding_ada.gpr @@ -1,8 +1,10 @@ -project libpathfinding_ada is - for Main use ("hello_world.adb"); - for Object_Dir use ".objs"; - +library project libpathfinding_ada is + for Source_Dirs use ("src"); + for Library_Dir use "lib"; + for Object_Dir use "obj"; + for Library_Name use "pathfinding_ada"; + for Library_Kind use "dynamic"; package Compiler is - for Default_Switches ("Ada") use ("-g"); + for Default_Switches ("Ada") use ("-g", "-O0"); end Compiler; end libpathfinding_ada; \ No newline at end of file diff --git a/Core/Pathfinder/ada_backend/src/a_star.adb b/Core/Pathfinder/ada_backend/src/a_star.adb new file mode 100644 index 0000000..514b937 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/a_star.adb @@ -0,0 +1,71 @@ +with Pathfinder_C_Inf; use Pathfinder_C_Inf; +with Pathfinder_Common; use Pathfinder_Common; +with Interfaces.C; use Interfaces.C; +with Interfaces; use Interfaces; +with Ada.Text_IO; use Ada.Text_IO; + +package body a_star is + procedure nextStep + (grid : Grid_Handle; + current_coord_x : Pathfinder_C_Inf.C_Long; + current_coord_y : Pathfinder_C_Inf.C_Long; + end_coord_x : Pathfinder_C_Inf.C_Long; + end_coord_y : Pathfinder_C_Inf.C_Long; + queue : Cell_Queue_Handle) + with SPARK_Mode => On + is + current_coord : Pathfinder_C_Inf.Grid_Coord := + Pathfinder_C_Inf.Grid_Coord'(current_coord_x, current_coord_y); + end_coord : Pathfinder_C_Inf.Grid_Coord := + Pathfinder_C_Inf.Grid_Coord'(end_coord_x, end_coord_y); + neighbors : Neighbors_Array := + get_neighbors_from_coord (grid, current_coord.x, current_coord.y); + current_costs : Cell_Cost := + get_cell_cost (grid, current_coord.x, current_coord.y); + current_g : C_Double := current_costs.gCost; + neighbor_coord : Grid_Coord; + neighbor_costs : Cell_Cost; + neighbor_state : Cell_State; + neighbor_g_from_current : C_Double; + begin + for I in GridCoordArray'Range loop + neighbor_coord := neighbors.coords (I); + + -- skip empty sentinel entries (wrapper pads remaining slots with U32_MAX) + exit when neighbor_coord.x = -1 and neighbor_coord.y = -1; + + neighbor_g_from_current := + current_g + Octile_Distance (neighbor_coord, current_coord); + + neighbor_state := + get_cell_state (grid, neighbor_coord.x, neighbor_coord.y); + + if neighbor_state = CELL_OPEN then + set_cell_costs + (grid, + neighbor_coord.x, + neighbor_coord.y, + neighbor_g_from_current, + Absolute_Distance (neighbor_coord, end_coord)); + set_parent_cell_pointer (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); + mark_cell_visited (grid, neighbor_coord.x, neighbor_coord.y); + push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); + increment_visited_cell_count (grid); + + elsif neighbor_state = CELL_VISITED then + neighbor_costs := get_cell_cost (grid, neighbor_coord.x, neighbor_coord.y); + if neighbor_costs.gCost > neighbor_g_from_current then + -- preserve previous h-cost + set_cell_costs + (grid, + neighbor_coord.x, neighbor_coord.y, + neighbor_g_from_current, neighbor_costs.hCost); + set_parent_cell_pointer (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); + push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); + increment_visited_cell_count (grid); + end if; + end if; + end loop; + end nextStep; + +end a_star; diff --git a/Core/Pathfinder/ada_backend/src/a_star.ads b/Core/Pathfinder/ada_backend/src/a_star.ads new file mode 100644 index 0000000..2859530 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/a_star.ads @@ -0,0 +1,17 @@ +with Pathfinder_C_Inf; use Pathfinder_C_Inf; + +package a_star is + procedure nextStep + (grid : Grid_Handle; + current_coord_x : Pathfinder_C_Inf.C_Long; + current_coord_y : Pathfinder_C_Inf.C_Long; + end_coord_x : Pathfinder_C_Inf.C_Long; + end_coord_y : Pathfinder_C_Inf.C_Long; + queue : Cell_Queue_Handle) + with + Convention => C, + SPARK_Mode => On, + Export => True, + External_Name => "ada_astar_next_step"; + +end a_star; diff --git a/Core/Pathfinder/ada_backend/src/dijkstra.adb b/Core/Pathfinder/ada_backend/src/dijkstra.adb new file mode 100644 index 0000000..5369b34 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/dijkstra.adb @@ -0,0 +1,68 @@ +with Pathfinder_C_Inf; use Pathfinder_C_Inf; +with Pathfinder_Common; use Pathfinder_Common; +with Interfaces.C; use Interfaces.C; +with Interfaces; use Interfaces; +with Ada.Text_IO; use Ada.Text_IO; + +package body dijkstra is + procedure nextStep + (grid : Grid_Handle; + current_coord_x : Pathfinder_C_Inf.C_Long; + current_coord_y: Pathfinder_C_Inf.C_Long; + queue : Cell_Queue_Handle) + with SPARK_Mode => On + is + current_coord : Pathfinder_C_Inf.Grid_Coord := (x => current_coord_x, y => current_coord_y); + neighbors : Neighbors_Array; + current_costs : Cell_Cost; + current_g : C_Double; + neighbor_coord : Grid_Coord; + neighbor_costs : Cell_Cost; + neighbor_state : Cell_State; + neighbor_g_from_current : C_Double; + begin + + neighbors := + get_neighbors_from_coord (grid, current_coord.x, current_coord.y); + current_costs := + get_cell_cost (grid, current_coord.x, current_coord.y); + current_g := current_costs.gCost; + for I in GridCoordArray'Range loop + neighbor_coord := neighbors.coords (I); + + -- skip empty sentinel entries (wrapper pads remaining slots with U32_MAX) + exit when neighbor_coord.x = -1 and neighbor_coord.y = -1; + + neighbor_g_from_current := + current_g + Octile_Distance (neighbor_coord, current_coord); + + neighbor_state := get_cell_state (grid, neighbor_coord.x, neighbor_coord.y); + + if neighbor_state = CELL_OPEN then + set_cell_costs + (grid, + neighbor_coord.x, neighbor_coord.y, + neighbor_g_from_current, 0.0); + set_parent_cell_pointer (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); + mark_cell_visited (grid, neighbor_coord.x, neighbor_coord.y); + push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); + increment_visited_cell_count (grid); + + elsif neighbor_state = CELL_VISITED then + neighbor_costs := get_cell_cost (grid, neighbor_coord.x, neighbor_coord.y); + if neighbor_costs.gCost > neighbor_g_from_current then + set_cell_costs + (grid, + neighbor_coord.x, + neighbor_coord.y, + neighbor_g_from_current, 0.0); + set_parent_cell_pointer + (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); + push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); + increment_visited_cell_count (grid); + end if; + end if; + end loop; + end nextStep; + +end dijkstra; diff --git a/Core/Pathfinder/ada_backend/src/dijkstra.ads b/Core/Pathfinder/ada_backend/src/dijkstra.ads new file mode 100644 index 0000000..8e5e0a6 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/dijkstra.ads @@ -0,0 +1,11 @@ +with Pathfinder_C_Inf; use Pathfinder_C_Inf; +package dijkstra is + procedure nextStep(grid : Grid_Handle; current_coord_x : Pathfinder_C_Inf.C_Long; + current_coord_y: Pathfinder_C_Inf.C_Long; queue : Cell_Queue_Handle) + with + Convention => C, + SPARK_Mode => On, + Export => True, + External_Name => "ada_dijkstra_next_step"; + +end dijkstra; diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads b/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads new file mode 100644 index 0000000..ab6ecb9 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads @@ -0,0 +1,68 @@ +with Interfaces; +with Interfaces.C; +with System; + +package Pathfinder_C_Inf is + subtype Grid_Handle is System.Address; + subtype Cell_Queue_Handle is System.Address; + + -- Basic integer/byte aliases + subtype C_Long is Interfaces.C.long; + subtype C_Double is Interfaces.C.double; + + -- C-types + type Grid_Coord is record + x : C_Long; + y : C_Long; + end record + with Convention => C; + + type GridCoordArray is array (0 .. 7) of Grid_Coord with Convention => C; + + type Neighbors_Array is record + coords : GridCoordArray; + end record + with Convention => C; + + type Cell_Cost is record + gCost : C_Double; + hCost : C_Double; + end record + with Convention => C; + + -- C-style state Enum + type Cell_State is + (CELL_OBSTACLE, CELL_OPEN, CELL_VISITED, CELL_CLOSED, CELL_PATH) + with Convention => C; + + -- Imported functions + function get_neighbors_from_coord + (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) return Neighbors_Array + with Import => True, Convention => C; + + function get_cell_cost + (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) return Cell_Cost + with Import => True, Convention => C; + + function get_cell_state + (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) return Cell_State + with Import => True, Convention => C; + + procedure increment_visited_cell_count (grid : Grid_Handle) + with Import => True, Convention => C; + + procedure mark_cell_visited (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) + with Import => True, Convention => C; + + procedure set_cell_costs + (grid : Grid_Handle; coordX : C_Long; coordY: C_Long; gCost: C_Double; hCost: C_Double) + with Import => True, Convention => C; + + procedure set_parent_cell_pointer + (grid : Grid_Handle; coordX : C_Long; coordY: C_Long; parentCoordX : C_Long; parentCoordY: C_Long) + with Import => True, Convention => C; + + procedure push_to_cell_queue (queue : Cell_Queue_Handle; coordX : C_Long; coordY: C_Long) + with Import => True, Convention => C; + +end Pathfinder_C_Inf; diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_common.adb b/Core/Pathfinder/ada_backend/src/pathfinder_common.adb new file mode 100644 index 0000000..7017a91 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/pathfinder_common.adb @@ -0,0 +1,39 @@ +with Ada.Numerics.Long_Elementary_Functions; use Ada.Numerics.Long_Elementary_Functions; + +package body Pathfinder_Common is + + function Octile_Distance (A, B : Grid_Coord) return C_Double is + DX, DY : Integer; + MinXY : Integer; + Diff : Integer; + Val : Long_Float; + begin + DX := Integer (A.x) - Integer (B.x); + if DX < 0 then DX := -DX; end if; + DY := Integer (A.y) - Integer (B.y); + if DY < 0 then DY := -DY; end if; + + -- octile: min*sqrt(2) + abs(dx-dy) + if DX > DY then + Val := Long_Float (DY) * Sqrt2 + Long_Float (DX-DY); + else + Val := Long_Float (DX) * Sqrt2 + Long_Float (DY-DX); + end if; + return C_Double (Val); + end Octile_Distance; + + function Absolute_Distance (A, B : Grid_Coord) return C_Double is + DX, DY : Integer; + MinXY : Integer; + Diff : Integer; + Val : Long_Float; + begin + DX := Integer (A.x) - Integer (B.x); + DY := Integer (A.y) - Integer (B.y); + + return C_Double (Sqrt(Long_Float (DX * DX + DY * DY))); + end Absolute_Distance; + +end Pathfinder_Common; + + \ No newline at end of file diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_common.ads b/Core/Pathfinder/ada_backend/src/pathfinder_common.ads new file mode 100644 index 0000000..ee6c411 --- /dev/null +++ b/Core/Pathfinder/ada_backend/src/pathfinder_common.ads @@ -0,0 +1,8 @@ +with Pathfinder_C_Inf; use Pathfinder_C_Inf; + +package Pathfinder_Common is + Sqrt2 : constant Long_Float := 1.4142135623730951; + + function Octile_Distance (A, B : Grid_Coord) return C_Double; + function Absolute_Distance (A, B : Grid_Coord) return C_Double; +end Pathfinder_Common; \ No newline at end of file diff --git a/Core/Pathfinder/c_abi/inc/pathfinding_c_inf.h b/Core/Pathfinder/c_abi/inc/pathfinding_c_inf.h new file mode 100644 index 0000000..e67feb8 --- /dev/null +++ b/Core/Pathfinder/c_abi/inc/pathfinding_c_inf.h @@ -0,0 +1,52 @@ +#pragma once + +#include "grid.h" +#include + +extern "C" { + using grid_handle_t = void*; + using cell_queue_handle_t = void*; + + typedef struct { + long x; + long y; + } grid_coordinate_struct_t; + + typedef struct { + grid_coordinate_struct_t coords[8]; + } neigbors_array_t; + + typedef struct { + double gCost; + double hCost; + } cost_struct_t; + + enum Cell_State { + CELL_OBSTACLE = 0, /**< Represents cells that are obstacles. */ + CELL_OPEN = 1, /**< Represents open cells. */ + CELL_VISITED = 2, /**< Represents cells that have been visited. */ + CELL_CLOSED = 3, /**< Represents cells that have been closed. */ + CELL_PATH = 4 /**< Represents cells that are part of the path. */ + }; + + typedef void (*push_fn_t)(void *queue_handle, long x, long y); + + void register_queue_push(push_fn_t f); + + void push_to_cell_queue(void *queue_handle, long x, long y); + + neigbors_array_t get_neighbors_from_coord(grid_handle_t grid_handle, long x, long y); + + cost_struct_t get_cell_cost(grid_handle_t grid_handle, long x, long y); + + Cell_State get_cell_state(grid_handle_t grid_handle, long x, long y); + + void increment_visited_cell_count(grid_handle_t grid_handle); + + void mark_cell_visited(grid_handle_t grid_handle, long x, long y); + + void set_cell_costs(grid_handle_t grid_handle, long x, long y, double gCost, double hCost); + + void set_parent_cell_pointer(grid_handle_t grid_handle, long x, long y, + long parentX, long parentY); +} \ No newline at end of file diff --git a/Core/Pathfinder/c_abi/src/pathfinding_c_inf.cpp b/Core/Pathfinder/c_abi/src/pathfinding_c_inf.cpp new file mode 100644 index 0000000..71a1565 --- /dev/null +++ b/Core/Pathfinder/c_abi/src/pathfinding_c_inf.cpp @@ -0,0 +1,92 @@ +#include "pathfinding_c_inf.h" + +#include + +static std::atomic registered_push{nullptr}; + +extern "C" void register_queue_push(push_fn_t f) +{ + registered_push.store(f, std::memory_order_release); +} + +extern "C" void push_to_cell_queue(void *queue_handle, long x, long y) +{ + auto f = registered_push.load(std::memory_order_acquire); + if (f) + { + f(queue_handle, x, y); + } +} + +neigbors_array_t get_neighbors_from_coord(grid_handle_t grid_handle, long x, long y) +{ + auto &grid = *reinterpret_cast(grid_handle); + GridGenerator::GridCoordinate gc{x, y}; + auto neighbors = grid.getNeighborsCoordinates(gc); + neigbors_array_t result; + size_t i = 0; + for (const auto &neighbor : neighbors) + { + result.coords[i].x = neighbor.x; + result.coords[i].y = neighbor.y; + i++; + } + for (; i < 8; i++) + { + result.coords[i].x = -1; + result.coords[i].y = -1; + } + return result; +} + +cost_struct_t get_cell_cost(grid_handle_t grid_handle, long x, long y) +{ + auto &grid = *reinterpret_cast(grid_handle); + GridGenerator::GridCoordinate gc{x, y}; + auto &cell = grid(gc); + cost_struct_t result; + result.gCost = cell.getCost().gCost; + result.hCost = cell.getCost().hCost; + return result; +} + +Cell_State get_cell_state(grid_handle_t grid_handle, long x, long y) +{ + auto &grid = *reinterpret_cast(grid_handle); + GridGenerator::GridCoordinate gc{x, y}; + auto &cell = grid(gc); + return static_cast(cell.getState()); +} + +void increment_visited_cell_count(grid_handle_t grid_handle) +{ + auto &grid = *reinterpret_cast(grid_handle); + grid.incrementVisitedCellCount(); +} + +void mark_cell_visited(grid_handle_t grid_handle, long x, long y) +{ + auto &grid = *reinterpret_cast(grid_handle); + GridGenerator::GridCoordinate gc{x, y}; + auto &cell = grid(gc); + cell.markVisited(); +} + +void set_cell_costs(grid_handle_t grid_handle, long x, long y, double gCost, double hCost) +{ + auto &grid = *reinterpret_cast(grid_handle); + GridGenerator::GridCoordinate gc{x, y}; + auto &cell = grid(gc); + cell.setGCost(gCost); + cell.setHCost(hCost); +} + +void set_parent_cell_pointer(grid_handle_t grid_handle, long x, long y, + long parentX, long parentY) +{ + auto &grid = *reinterpret_cast(grid_handle); + GridGenerator::GridCoordinate gc{x, y}; + GridGenerator::GridCoordinate parent_gc{parentX, parentY}; + auto &cell = grid(gc); + cell.setParentCellPointer(&grid(parent_gc)); +} \ No newline at end of file diff --git a/Core/Pathfinder/inc/pathfinding.h b/Core/Pathfinder/inc/pathfinding.h index 0f764e9..edd3a68 100644 --- a/Core/Pathfinder/inc/pathfinding.h +++ b/Core/Pathfinder/inc/pathfinding.h @@ -76,6 +76,9 @@ namespace Pathfinder { */ class PathfindingParent { public: + using compareCellsFunction = std::function; + /** * @brief Constructor for the PathfindingParent class. * @param grid Reference to the Grid object used in the pathfinding. @@ -132,8 +135,6 @@ namespace Pathfinder { void reInit(); protected: - using compareCellsFunction = std::function; /** * @fn virtual getStrat @@ -270,7 +271,7 @@ namespace Pathfinder { * @brief Constructor for the JumpPointSolve class. Uses the parent Class constructor. * @param grid Reference to the Grid object where the Jump-Point-Search algorithm will be applied. */ - explicit DijkstraSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) {} + explicit DijkstraSolveAda(GridGenerator::Grid &grid); /** * @fn void nextStep @@ -298,7 +299,7 @@ namespace Pathfinder { * @brief Constructor for the AStarSolve class. Uses the parent Class constructor. * @param grid Reference to the Grid object where the A* algorithm will be applied. */ - explicit AStarSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) {} + explicit AStarSolveAda(GridGenerator::Grid &grid); /** * @fn void nextStep diff --git a/Core/Pathfinder/src/pathfinding.cpp b/Core/Pathfinder/src/pathfinding.cpp index 88f26c5..0b2e5d2 100644 --- a/Core/Pathfinder/src/pathfinding.cpp +++ b/Core/Pathfinder/src/pathfinding.cpp @@ -4,7 +4,9 @@ const std::map Pathfinder::PathfinderStrategyParser::pathfindingStrategyToDisplayableText = {{PathfinderStrategy::PATHFINDER_DIJKSTRA, "Dijkstra's Algorithm"}, {PathfinderStrategy::PATHFINDER_A_STAR, "A* Algorithm"}, - {PathfinderStrategy::PATHFINDER_JUMP_POINT_SEARCH, "Jump Point Search"}}; + {PathfinderStrategy::PATHFINDER_JUMP_POINT_SEARCH, "Jump Point Search"}, + {PathfinderStrategy::PATHFINDER_DIJKSTRA_ADA, "Dijkstra's Algorithm (Ada Backend)"}, + {PathfinderStrategy::PATHFINDER_A_STAR_ADA, "A* Algorithm (Ada Backend)"}}; std::unique_ptr Pathfinder::PathfinderStrategyParser::parsePathfinderStrategy( PathfinderStrategy strat, @@ -16,6 +18,10 @@ std::unique_ptr Pathfinder::PathfinderStrategyPar return std::make_unique(grid); case PathfinderStrategy::PATHFINDER_JUMP_POINT_SEARCH: return std::make_unique(grid); + case PathfinderStrategy::PATHFINDER_A_STAR_ADA: + return std::make_unique(grid); + case PathfinderStrategy::PATHFINDER_DIJKSTRA_ADA: + return std::make_unique(grid); } return nullptr; } @@ -60,3 +66,12 @@ void Pathfinder::PathfindingParent::reInit() { grid.resetGrid(); initGenericSolver(); } + +extern "C" void pathfinder_push_impl(void *queue_handle, uint32_t x, uint32_t y) { + GridGenerator::GridCoordinate gc{x, y}; + auto& queue = *reinterpret_cast, + Pathfinder::PathfindingParent::compareCellsFunction>*>(queue_handle); + queue.push(gc); +} \ No newline at end of file diff --git a/Core/Pathfinder/src/pathfinding_a_star_ada.cpp b/Core/Pathfinder/src/pathfinding_a_star_ada.cpp index c7af2c1..413cf14 100644 --- a/Core/Pathfinder/src/pathfinding_a_star_ada.cpp +++ b/Core/Pathfinder/src/pathfinding_a_star_ada.cpp @@ -1,8 +1,16 @@ #include "pathfinding.h" +#include "pathfinding_c_inf.h" -#include +extern "C" void ada_astar_next_step(void *grid_handle, grid_coordinate_struct_t current, grid_coordinate_struct_t end, void *queue_handle); +extern "C" void pathfinder_push_impl(void *queue_handle, long x, long y); -void Pathfinder::AStarSolveAda::nextStep() { +Pathfinder::AStarSolveAda::AStarSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) +{ + register_queue_push(pathfinder_push_impl); +} + +void Pathfinder::AStarSolveAda::nextStep() +{ // TODO: implement Ada backend call and translate for Ada interfacing timer.stepBegin(); GridGenerator::GridCoordinate endCoordinates = grid.getEndCoordinates(); @@ -11,39 +19,26 @@ void Pathfinder::AStarSolveAda::nextStep() { nextCellQueue.pop(); GridGenerator::Cell ¤tCell = grid(currentCoordinates); - if (currentCell.getState() == GridGenerator::CellState::CELL_CLOSED) return; - if (currentCoordinates == endCoordinates) { + if (currentCell.getState() == GridGenerator::CellState::CELL_CLOSED) + return; + if (currentCoordinates == endCoordinates) + { timer.stepEnd(); grid.markPathByParentCells(true); return; } + grid_coordinate_struct_t current = {static_cast(currentCoordinates.x), + static_cast(currentCoordinates.y)}; + grid_coordinate_struct_t end = {static_cast(endCoordinates.x), + static_cast(endCoordinates.y)}; + + ada_astar_next_step(reinterpret_cast(&grid), + current, + end, + reinterpret_cast(&nextCellQueue)); - std::vector neighbors = grid.getNeighborsCoordinates(currentCoordinates); - - for (const auto &neighborCoordinates: neighbors) { - GridGenerator::Cell &neighborCell = grid(neighborCoordinates); - double neighborCellGCostFromCurrentCell = currentCell.getCost().gCost + - neighborCoordinates.getOctileDistanceTo(currentCoordinates); - - if (neighborCell.getState() == GridGenerator::CellState::CELL_OPEN) { - neighborCell.setGCost(neighborCellGCostFromCurrentCell); - neighborCell.setHCost(neighborCoordinates.getAbsDistanceTo(endCoordinates)); - neighborCell.setParentCellPointer(&grid(currentCoordinates)); - neighborCell.markVisited(); - nextCellQueue.push(neighborCoordinates); - grid.incrementVisitedCellCount(); - } else if (neighborCell.getState() == GridGenerator::CellState::CELL_VISITED) { - if (neighborCell.getCost().gCost > neighborCellGCostFromCurrentCell) { - neighborCell.setGCost(neighborCellGCostFromCurrentCell); - neighborCell.setParentCellPointer(&grid(currentCoordinates)); - nextCellQueue.push(neighborCoordinates); - grid.incrementVisitedCellCount(); - } - } - } currentCell.markClosed(); grid.incrementClosedCellCount(); timer.stepEnd(); } - diff --git a/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp b/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp index 92cda71..aa455b0 100644 --- a/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp +++ b/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp @@ -1,8 +1,16 @@ #include "pathfinding.h" +#include "pathfinding_c_inf.h" -#include +extern "C" void ada_dijkstra_next_step(void *grid_handle, long currentX, long currentY, void *queue_handle); +extern "C" void pathfinder_push_impl(void *queue_handle, long x, long y); -void Pathfinder::DijkstraSolveAda::nextStep() { +Pathfinder::DijkstraSolveAda::DijkstraSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) +{ + register_queue_push(pathfinder_push_impl); +} + +void Pathfinder::DijkstraSolveAda::nextStep() +{ // TODO: implement Ada backend call and translate for Ada interfacing timer.stepBegin(); GridGenerator::GridCoordinate endCoordinates = grid.getEndCoordinates(); @@ -10,36 +18,19 @@ void Pathfinder::DijkstraSolveAda::nextStep() { GridGenerator::GridCoordinate currentCoordinates = nextCellQueue.top(); nextCellQueue.pop(); GridGenerator::Cell ¤tCell = grid(currentCoordinates); - if (currentCoordinates == endCoordinates) { + if (currentCoordinates == endCoordinates) + { timer.stepEnd(); grid.markPathByParentCells(true); return; } - std::vector neighbors = grid.getNeighborsCoordinates(currentCoordinates); + ada_dijkstra_next_step(reinterpret_cast(&grid), + static_cast(currentCoordinates.x), + static_cast(currentCoordinates.y), + reinterpret_cast(&nextCellQueue)); - for (const auto &neighborCoordinates: neighbors) { - GridGenerator::Cell &neighborCell = grid(neighborCoordinates); - double neighborCellGCostFromCurrentCell = currentCell.getCost().gCost + - neighborCoordinates.getOctileDistanceTo(currentCoordinates); - if (neighborCell.getState() == GridGenerator::CellState::CELL_OPEN) { - neighborCell.setGCost(neighborCellGCostFromCurrentCell); - neighborCell.setParentCellPointer(&grid(currentCoordinates)); - neighborCell.markVisited(); - nextCellQueue.push(neighborCoordinates); - grid.incrementVisitedCellCount(); - } else if (neighborCell.getState() == GridGenerator::CellState::CELL_VISITED) { - if (neighborCell.getCost().gCost > neighborCellGCostFromCurrentCell) { - neighborCell.setGCost(neighborCellGCostFromCurrentCell); - neighborCell.setParentCellPointer(&grid(currentCoordinates)); - nextCellQueue.push(neighborCoordinates); - grid.incrementVisitedCellCount(); - } - } - } currentCell.markClosed(); grid.incrementClosedCellCount(); timer.stepEnd(); } - - From efd704773f6e162737a8b13fed2b09db37e6e396 Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Mon, 19 Jan 2026 14:33:15 +0100 Subject: [PATCH 3/8] refactors ada interfacing method signatures --- Core/Pathfinder/src/pathfinding_a_star_ada.cpp | 14 +++++--------- Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp | 1 - 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Core/Pathfinder/src/pathfinding_a_star_ada.cpp b/Core/Pathfinder/src/pathfinding_a_star_ada.cpp index 413cf14..8b9466a 100644 --- a/Core/Pathfinder/src/pathfinding_a_star_ada.cpp +++ b/Core/Pathfinder/src/pathfinding_a_star_ada.cpp @@ -1,7 +1,7 @@ #include "pathfinding.h" #include "pathfinding_c_inf.h" -extern "C" void ada_astar_next_step(void *grid_handle, grid_coordinate_struct_t current, grid_coordinate_struct_t end, void *queue_handle); +extern "C" void ada_astar_next_step(void *grid_handle, long currentX, long currentY, long endX, long endY, void *queue_handle); extern "C" void pathfinder_push_impl(void *queue_handle, long x, long y); Pathfinder::AStarSolveAda::AStarSolveAda(GridGenerator::Grid &grid) : PathfindingParent(grid) @@ -11,7 +11,6 @@ Pathfinder::AStarSolveAda::AStarSolveAda(GridGenerator::Grid &grid) : Pathfindin void Pathfinder::AStarSolveAda::nextStep() { - // TODO: implement Ada backend call and translate for Ada interfacing timer.stepBegin(); GridGenerator::GridCoordinate endCoordinates = grid.getEndCoordinates(); @@ -28,14 +27,11 @@ void Pathfinder::AStarSolveAda::nextStep() return; } - grid_coordinate_struct_t current = {static_cast(currentCoordinates.x), - static_cast(currentCoordinates.y)}; - grid_coordinate_struct_t end = {static_cast(endCoordinates.x), - static_cast(endCoordinates.y)}; - ada_astar_next_step(reinterpret_cast(&grid), - current, - end, + static_cast(currentCoordinates.x), + static_cast(currentCoordinates.y), + static_cast(endCoordinates.x), + static_cast(endCoordinates.y), reinterpret_cast(&nextCellQueue)); currentCell.markClosed(); diff --git a/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp b/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp index aa455b0..a120a43 100644 --- a/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp +++ b/Core/Pathfinder/src/pathfinding_dijkstra_ada.cpp @@ -11,7 +11,6 @@ Pathfinder::DijkstraSolveAda::DijkstraSolveAda(GridGenerator::Grid &grid) : Path void Pathfinder::DijkstraSolveAda::nextStep() { - // TODO: implement Ada backend call and translate for Ada interfacing timer.stepBegin(); GridGenerator::GridCoordinate endCoordinates = grid.getEndCoordinates(); From c21ca20274cccd0b37d59a4566dac2c9fb4d4b23 Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Sat, 7 Feb 2026 20:30:14 +0100 Subject: [PATCH 4/8] implements spark proving for pathfinding methods --- Core/Pathfinder/ada_backend/src/a_star.adb | 34 +++-- Core/Pathfinder/ada_backend/src/a_star.ads | 11 +- Core/Pathfinder/ada_backend/src/dijkstra.adb | 119 ++++++++++-------- Core/Pathfinder/ada_backend/src/dijkstra.ads | 22 ++-- .../ada_backend/src/pathfinder_c_inf.ads | 61 +++++---- .../ada_backend/src/pathfinder_common.adb | 69 ++++++---- .../ada_backend/src/pathfinder_common.ads | 15 ++- 7 files changed, 200 insertions(+), 131 deletions(-) diff --git a/Core/Pathfinder/ada_backend/src/a_star.adb b/Core/Pathfinder/ada_backend/src/a_star.adb index 514b937..cb64015 100644 --- a/Core/Pathfinder/ada_backend/src/a_star.adb +++ b/Core/Pathfinder/ada_backend/src/a_star.adb @@ -7,10 +7,10 @@ with Ada.Text_IO; use Ada.Text_IO; package body a_star is procedure nextStep (grid : Grid_Handle; - current_coord_x : Pathfinder_C_Inf.C_Long; - current_coord_y : Pathfinder_C_Inf.C_Long; - end_coord_x : Pathfinder_C_Inf.C_Long; - end_coord_y : Pathfinder_C_Inf.C_Long; + current_coord_x : Pathfinder_C_Inf.Grid_Idx; + current_coord_y : Pathfinder_C_Inf.Grid_Idx; + end_coord_x : Pathfinder_C_Inf.Grid_Idx; + end_coord_y : Pathfinder_C_Inf.Grid_Idx; queue : Cell_Queue_Handle) with SPARK_Mode => On is @@ -22,7 +22,7 @@ package body a_star is get_neighbors_from_coord (grid, current_coord.x, current_coord.y); current_costs : Cell_Cost := get_cell_cost (grid, current_coord.x, current_coord.y); - current_g : C_Double := current_costs.gCost; + current_g : Safe_Cost := current_costs.gCost; neighbor_coord : Grid_Coord; neighbor_costs : Cell_Cost; neighbor_state : Cell_State; @@ -33,7 +33,6 @@ package body a_star is -- skip empty sentinel entries (wrapper pads remaining slots with U32_MAX) exit when neighbor_coord.x = -1 and neighbor_coord.y = -1; - neighbor_g_from_current := current_g + Octile_Distance (neighbor_coord, current_coord); @@ -47,20 +46,33 @@ package body a_star is neighbor_coord.y, neighbor_g_from_current, Absolute_Distance (neighbor_coord, end_coord)); - set_parent_cell_pointer (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); + set_parent_cell_pointer + (grid, + neighbor_coord.x, + neighbor_coord.y, + current_coord.x, + current_coord.y); mark_cell_visited (grid, neighbor_coord.x, neighbor_coord.y); push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); increment_visited_cell_count (grid); elsif neighbor_state = CELL_VISITED then - neighbor_costs := get_cell_cost (grid, neighbor_coord.x, neighbor_coord.y); + neighbor_costs := + get_cell_cost (grid, neighbor_coord.x, neighbor_coord.y); if neighbor_costs.gCost > neighbor_g_from_current then -- preserve previous h-cost set_cell_costs (grid, - neighbor_coord.x, neighbor_coord.y, - neighbor_g_from_current, neighbor_costs.hCost); - set_parent_cell_pointer (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); + neighbor_coord.x, + neighbor_coord.y, + neighbor_g_from_current, + neighbor_costs.hCost); + set_parent_cell_pointer + (grid, + neighbor_coord.x, + neighbor_coord.y, + current_coord.x, + current_coord.y); push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); increment_visited_cell_count (grid); end if; diff --git a/Core/Pathfinder/ada_backend/src/a_star.ads b/Core/Pathfinder/ada_backend/src/a_star.ads index 2859530..5d63b19 100644 --- a/Core/Pathfinder/ada_backend/src/a_star.ads +++ b/Core/Pathfinder/ada_backend/src/a_star.ads @@ -3,15 +3,16 @@ with Pathfinder_C_Inf; use Pathfinder_C_Inf; package a_star is procedure nextStep (grid : Grid_Handle; - current_coord_x : Pathfinder_C_Inf.C_Long; - current_coord_y : Pathfinder_C_Inf.C_Long; - end_coord_x : Pathfinder_C_Inf.C_Long; - end_coord_y : Pathfinder_C_Inf.C_Long; + current_coord_x : Pathfinder_C_Inf.Grid_Idx; + current_coord_y : Pathfinder_C_Inf.Grid_Idx; + end_coord_x : Pathfinder_C_Inf.Grid_Idx; + end_coord_y : Pathfinder_C_Inf.Grid_Idx; queue : Cell_Queue_Handle) with Convention => C, SPARK_Mode => On, Export => True, - External_Name => "ada_astar_next_step"; + External_Name => "ada_astar_next_step", + Global => null; end a_star; diff --git a/Core/Pathfinder/ada_backend/src/dijkstra.adb b/Core/Pathfinder/ada_backend/src/dijkstra.adb index 5369b34..4951666 100644 --- a/Core/Pathfinder/ada_backend/src/dijkstra.adb +++ b/Core/Pathfinder/ada_backend/src/dijkstra.adb @@ -5,64 +5,75 @@ with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; package body dijkstra is - procedure nextStep - (grid : Grid_Handle; - current_coord_x : Pathfinder_C_Inf.C_Long; - current_coord_y: Pathfinder_C_Inf.C_Long; - queue : Cell_Queue_Handle) - with SPARK_Mode => On - is - current_coord : Pathfinder_C_Inf.Grid_Coord := (x => current_coord_x, y => current_coord_y); - neighbors : Neighbors_Array; - current_costs : Cell_Cost; - current_g : C_Double; - neighbor_coord : Grid_Coord; - neighbor_costs : Cell_Cost; - neighbor_state : Cell_State; - neighbor_g_from_current : C_Double; - begin + procedure nextStep + (grid : Grid_Handle; + current_coord_x : Pathfinder_C_Inf.Grid_Idx; + current_coord_y : Pathfinder_C_Inf.Grid_Idx; + queue : Cell_Queue_Handle) + with SPARK_Mode => On + is + current_coord : Pathfinder_C_Inf.Grid_Coord := + (x => current_coord_x, y => current_coord_y); + neighbors : constant Neighbors_Array := + get_neighbors_from_coord (grid, current_coord.x, current_coord.y); + current_costs : constant Cell_Cost := + get_cell_cost (grid, current_coord.x, current_coord.y); + current_g : constant Pathfinder_C_Inf.Safe_Cost := + current_costs.gCost; + neighbor_coord : Grid_Coord; + neighbor_costs : Cell_Cost; + neighbor_state : Cell_State; + neighbor_g_from_current : Pathfinder_C_Inf.C_Double; + begin + for I in GridCoordArray'Range loop + neighbor_coord := neighbors.coords (I); - neighbors := - get_neighbors_from_coord (grid, current_coord.x, current_coord.y); - current_costs := - get_cell_cost (grid, current_coord.x, current_coord.y); - current_g := current_costs.gCost; - for I in GridCoordArray'Range loop - neighbor_coord := neighbors.coords (I); + -- skip empty sentinel entries (wrapper pads remaining slots with U32_MAX) + exit when neighbor_coord.x = -1 and neighbor_coord.y = -1; + neighbor_g_from_current := + current_g + Octile_Distance (neighbor_coord, current_coord); - -- skip empty sentinel entries (wrapper pads remaining slots with U32_MAX) - exit when neighbor_coord.x = -1 and neighbor_coord.y = -1; + neighbor_state := + get_cell_state (grid, neighbor_coord.x, neighbor_coord.y); - neighbor_g_from_current := - current_g + Octile_Distance (neighbor_coord, current_coord); + if neighbor_state = CELL_OPEN then + set_cell_costs + (grid, + neighbor_coord.x, + neighbor_coord.y, + neighbor_g_from_current, + 0.0); + set_parent_cell_pointer + (grid, + neighbor_coord.x, + neighbor_coord.y, + current_coord.x, + current_coord.y); + mark_cell_visited (grid, neighbor_coord.x, neighbor_coord.y); + push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); + increment_visited_cell_count (grid); - neighbor_state := get_cell_state (grid, neighbor_coord.x, neighbor_coord.y); - - if neighbor_state = CELL_OPEN then - set_cell_costs - (grid, - neighbor_coord.x, neighbor_coord.y, - neighbor_g_from_current, 0.0); - set_parent_cell_pointer (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); - mark_cell_visited (grid, neighbor_coord.x, neighbor_coord.y); - push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); - increment_visited_cell_count (grid); - - elsif neighbor_state = CELL_VISITED then - neighbor_costs := get_cell_cost (grid, neighbor_coord.x, neighbor_coord.y); - if neighbor_costs.gCost > neighbor_g_from_current then - set_cell_costs - (grid, - neighbor_coord.x, - neighbor_coord.y, - neighbor_g_from_current, 0.0); - set_parent_cell_pointer - (grid, neighbor_coord.x, neighbor_coord.y, current_coord.x, current_coord.y); - push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); - increment_visited_cell_count (grid); - end if; + elsif neighbor_state = CELL_VISITED then + neighbor_costs := + get_cell_cost (grid, neighbor_coord.x, neighbor_coord.y); + if neighbor_costs.gCost > neighbor_g_from_current then + set_cell_costs + (grid, + neighbor_coord.x, + neighbor_coord.y, + neighbor_g_from_current, + 0.0); + set_parent_cell_pointer + (grid, + neighbor_coord.x, + neighbor_coord.y, + current_coord.x, + current_coord.y); + push_to_cell_queue (queue, neighbor_coord.x, neighbor_coord.y); + increment_visited_cell_count (grid); end if; - end loop; - end nextStep; + end if; + end loop; + end nextStep; end dijkstra; diff --git a/Core/Pathfinder/ada_backend/src/dijkstra.ads b/Core/Pathfinder/ada_backend/src/dijkstra.ads index 8e5e0a6..68ad950 100644 --- a/Core/Pathfinder/ada_backend/src/dijkstra.ads +++ b/Core/Pathfinder/ada_backend/src/dijkstra.ads @@ -1,11 +1,17 @@ with Pathfinder_C_Inf; use Pathfinder_C_Inf; +with Interfaces.C; use type Interfaces.C.double; + package dijkstra is - procedure nextStep(grid : Grid_Handle; current_coord_x : Pathfinder_C_Inf.C_Long; - current_coord_y: Pathfinder_C_Inf.C_Long; queue : Cell_Queue_Handle) - with - Convention => C, - SPARK_Mode => On, - Export => True, - External_Name => "ada_dijkstra_next_step"; - + procedure nextStep + (grid : Grid_Handle; + current_coord_x : Pathfinder_C_Inf.Grid_Idx; + current_coord_y : Pathfinder_C_Inf.Grid_Idx; + queue : Cell_Queue_Handle) + with + Convention => C, + SPARK_Mode => On, + Export => True, + External_Name => "ada_dijkstra_next_step", + Global => null; + end dijkstra; diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads b/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads index ab6ecb9..d07f28f 100644 --- a/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads +++ b/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads @@ -1,5 +1,5 @@ -with Interfaces; -with Interfaces.C; +with Interfaces.C; use type Interfaces.C.double; +with Interfaces.C; use type Interfaces.C.long; with System; package Pathfinder_C_Inf is @@ -9,11 +9,14 @@ package Pathfinder_C_Inf is -- Basic integer/byte aliases subtype C_Long is Interfaces.C.long; subtype C_Double is Interfaces.C.double; + -- Grid indices are implemented in the backend as uint32_t and not long + subtype Grid_Idx is C_Long range 0 .. 2**32-1; + subtype Safe_Cost is C_Double range 0.0 .. 6.1E9; -- C-types type Grid_Coord is record - x : C_Long; - y : C_Long; + x : Grid_Idx; + y : Grid_Idx; end record with Convention => C; @@ -25,8 +28,8 @@ package Pathfinder_C_Inf is with Convention => C; type Cell_Cost is record - gCost : C_Double; - hCost : C_Double; + gCost : Safe_Cost; + hCost : Safe_Cost; end record with Convention => C; @@ -37,32 +40,46 @@ package Pathfinder_C_Inf is -- Imported functions function get_neighbors_from_coord - (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) return Neighbors_Array - with Import => True, Convention => C; + (grid : Grid_Handle; coordX : Grid_Idx; coordY : Grid_Idx) + return Neighbors_Array + with Import => True, Convention => C, Global => null; function get_cell_cost - (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) return Cell_Cost - with Import => True, Convention => C; + (grid : Grid_Handle; coordX : Grid_Idx; coordY : Grid_Idx) return Cell_Cost + with + Import => True, + Convention => C, + Global => null; function get_cell_state - (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) return Cell_State - with Import => True, Convention => C; + (grid : Grid_Handle; coordX : Grid_Idx; coordY : Grid_Idx) return Cell_State + with Import => True, Convention => C, Global => null; procedure increment_visited_cell_count (grid : Grid_Handle) - with Import => True, Convention => C; + with Import => True, Convention => C, Global => null, Always_Terminates; - procedure mark_cell_visited (grid : Grid_Handle; coordX : C_Long; coordY: C_Long) - with Import => True, Convention => C; + procedure mark_cell_visited + (grid : Grid_Handle; coordX : Grid_Idx; coordY : Grid_Idx) + with Import => True, Convention => C, Global => null, Always_Terminates; procedure set_cell_costs - (grid : Grid_Handle; coordX : C_Long; coordY: C_Long; gCost: C_Double; hCost: C_Double) - with Import => True, Convention => C; + (grid : Grid_Handle; + coordX : Grid_Idx; + coordY : Grid_Idx; + gCost : C_Double; + hCost : C_Double) + with Import => True, Convention => C, Global => null, Always_Terminates; procedure set_parent_cell_pointer - (grid : Grid_Handle; coordX : C_Long; coordY: C_Long; parentCoordX : C_Long; parentCoordY: C_Long) - with Import => True, Convention => C; - - procedure push_to_cell_queue (queue : Cell_Queue_Handle; coordX : C_Long; coordY: C_Long) - with Import => True, Convention => C; + (grid : Grid_Handle; + coordX : Grid_Idx; + coordY : Grid_Idx; + parentCoordX : Grid_Idx; + parentCoordY : Grid_Idx) + with Import => True, Convention => C, Global => null, Always_Terminates; + + procedure push_to_cell_queue + (queue : Cell_Queue_Handle; coordX : Grid_Idx; coordY : Grid_Idx) + with Import => True, Convention => C, Global => null, Always_Terminates; end Pathfinder_C_Inf; diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_common.adb b/Core/Pathfinder/ada_backend/src/pathfinder_common.adb index 7017a91..dfa7e61 100644 --- a/Core/Pathfinder/ada_backend/src/pathfinder_common.adb +++ b/Core/Pathfinder/ada_backend/src/pathfinder_common.adb @@ -1,39 +1,54 @@ -with Ada.Numerics.Long_Elementary_Functions; use Ada.Numerics.Long_Elementary_Functions; +with Ada.Numerics.Long_Elementary_Functions; +use Ada.Numerics.Long_Elementary_Functions; package body Pathfinder_Common is - function Octile_Distance (A, B : Grid_Coord) return C_Double is - DX, DY : Integer; - MinXY : Integer; - Diff : Integer; + function Octile_Distance (A, B : Grid_Coord) return Safe_Cost + with SPARK_Mode => On + is + DX, DY : Long_Float; Val : Long_Float; begin - DX := Integer (A.x) - Integer (B.x); - if DX < 0 then DX := -DX; end if; - DY := Integer (A.y) - Integer (B.y); - if DY < 0 then DY := -DY; end if; - + DX := Long_Float (A.x) - Long_Float (B.x); + DY := Long_Float (A.y) - Long_Float (B.y); + if DX < 0.0 then + DX := -DX; + end if; + if DY < 0.0 then + DY := -DY; + end if; + -- octile: min*sqrt(2) + abs(dx-dy) if DX > DY then - Val := Long_Float (DY) * Sqrt2 + Long_Float (DX-DY); + Val := Long_Float (DY) * Sqrt2 + Long_Float (DX - DY); else - Val := Long_Float (DX) * Sqrt2 + Long_Float (DY-DX); - end if; - return C_Double (Val); - end Octile_Distance; - - function Absolute_Distance (A, B : Grid_Coord) return C_Double is - DX, DY : Integer; - MinXY : Integer; - Diff : Integer; + Val := Long_Float (DX) * Sqrt2 + Long_Float (DY - DX); + end if; + + -- clamp to Safe_Cost'Last + if Val > Long_Float (Safe_Cost'Last) then + Val := Long_Float (Safe_Cost'Last); + end if; + return Safe_Cost (Val); + end Octile_Distance; + + function Absolute_Distance (A, B : Grid_Coord) return Safe_Cost + with SPARK_Mode => On + is + DX, DY : Long_Float; Val : Long_Float; begin - DX := Integer (A.x) - Integer (B.x); - DY := Integer (A.y) - Integer (B.y); - - return C_Double (Sqrt(Long_Float (DX * DX + DY * DY))); - end Absolute_Distance; + DX := Long_Float (A.x) - Long_Float (B.x); + DY := Long_Float (A.y) - Long_Float (B.y); + + Val := Sqrt (Long_Float (DX * DX + DY * DY)); + + -- clamp to Safe_Cost'Last + if Val > Long_Float (Safe_Cost'Last) then + Val := Long_Float (Safe_Cost'Last); + end if; + + return Safe_Cost (Val); + end Absolute_Distance; end Pathfinder_Common; - - \ No newline at end of file diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_common.ads b/Core/Pathfinder/ada_backend/src/pathfinder_common.ads index ee6c411..8f40c42 100644 --- a/Core/Pathfinder/ada_backend/src/pathfinder_common.ads +++ b/Core/Pathfinder/ada_backend/src/pathfinder_common.ads @@ -1,8 +1,15 @@ with Pathfinder_C_Inf; use Pathfinder_C_Inf; +with Interfaces.C; +use type Interfaces.C.double; package Pathfinder_Common is - Sqrt2 : constant Long_Float := 1.4142135623730951; + Sqrt2 : constant Long_Float := 1.4142135623730951; - function Octile_Distance (A, B : Grid_Coord) return C_Double; - function Absolute_Distance (A, B : Grid_Coord) return C_Double; -end Pathfinder_Common; \ No newline at end of file + function Octile_Distance (A, B : Grid_Coord) return Safe_Cost + with + SPARK_Mode => On; + + function Absolute_Distance (A, B : Grid_Coord) return Safe_Cost + with + SPARK_Mode => On; +end Pathfinder_Common; From 9dfad9d375a95e76494cc492e94ef605ac5b0546 Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Wed, 11 Feb 2026 16:42:36 +0100 Subject: [PATCH 5/8] fixes gnatprove for distance calculation --- Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads | 1 + Core/Pathfinder/ada_backend/src/pathfinder_common.adb | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads b/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads index d07f28f..bb755a3 100644 --- a/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads +++ b/Core/Pathfinder/ada_backend/src/pathfinder_c_inf.ads @@ -11,6 +11,7 @@ package Pathfinder_C_Inf is subtype C_Double is Interfaces.C.double; -- Grid indices are implemented in the backend as uint32_t and not long subtype Grid_Idx is C_Long range 0 .. 2**32-1; + subtype Grid_Idx_Dev is C_Long range -2**32-1 .. 2**32-1; subtype Safe_Cost is C_Double range 0.0 .. 6.1E9; -- C-types diff --git a/Core/Pathfinder/ada_backend/src/pathfinder_common.adb b/Core/Pathfinder/ada_backend/src/pathfinder_common.adb index dfa7e61..a497cdd 100644 --- a/Core/Pathfinder/ada_backend/src/pathfinder_common.adb +++ b/Core/Pathfinder/ada_backend/src/pathfinder_common.adb @@ -1,20 +1,21 @@ with Ada.Numerics.Long_Elementary_Functions; use Ada.Numerics.Long_Elementary_Functions; +with Interfaces.C; use Interfaces.C; package body Pathfinder_Common is function Octile_Distance (A, B : Grid_Coord) return Safe_Cost with SPARK_Mode => On is - DX, DY : Long_Float; + DX, DY : Grid_Idx_Dev; Val : Long_Float; begin - DX := Long_Float (A.x) - Long_Float (B.x); - DY := Long_Float (A.y) - Long_Float (B.y); - if DX < 0.0 then + DX := Grid_Idx_Dev(A.x - B.x); + DY := Grid_Idx_Dev(A.y - B.y); + if DX < 0 then DX := -DX; end if; - if DY < 0.0 then + if DY < 0 then DY := -DY; end if; From b1ff243b0942a1680550e0771c49952e0ddea0f8 Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Wed, 11 Feb 2026 16:57:29 +0100 Subject: [PATCH 6/8] cleans up cmakelists --- CMakeLists.txt | 61 +++++++++++++++++++--------------- Core/Pathfinder/CMakeLists.txt | 34 +++++++++++-------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93b344f..cf9840f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,9 @@ project( LANGUAGES C CXX ASM ) +option(WITH_DOCS "Build documentation" OFF) +option(WITH_PAPER "Build paper output" OFF) + enable_testing() set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -35,33 +38,37 @@ add_subdirectory(Core/GUI) add_executable(Pathfinding_Bench_Program Core/main.cpp) target_link_libraries(Pathfinding_Bench_Program Application GridGenerator Pathfinder GUI RunInterface GUI_widgets) -# doxygen documentation -find_package(Doxygen) - -if (DOXYGEN_FOUND) - set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) - set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - - configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) - add_custom_command(TARGET Pathfinding_Bench_Program POST_BUILD - COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} > /dev/null 2>&1 - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - ) - - find_program(MAKE_EXECUTABLE make) - if (MAKE_EXECUTABLE) - add_custom_command(TARGET Pathfinding_Bench_Program POST_BUILD - COMMAND ${MAKE_EXECUTABLE} > /dev/null 2>&1 - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs/latex - COMMENT "Generating PDF documentation" - ) - else (MAKE_EXECUTABLE) - message("Could not find 'make' command for generating PDF documentation.") - endif () -else (DOXYGEN_FOUND) - message("Doxygen needs to be installed to generate the doxygen documentation") -endif (DOXYGEN_FOUND) +if(WITH_DOCS) + # doxygen documentation + find_package(Doxygen) + + if (DOXYGEN_FOUND) + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + add_custom_command(TARGET Pathfinding_Bench_Program POST_BUILD + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} > /dev/null 2>&1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + ) + else (DOXYGEN_FOUND) + message("Doxygen needs to be installed to generate the doxygen documentation") + endif (DOXYGEN_FOUND) +endif(WITH_DOCS) + +if(WITH_PAPER) + find_program(MAKE_EXECUTABLE make) + if (MAKE_EXECUTABLE) + add_custom_command(TARGET Pathfinding_Bench_Program POST_BUILD + COMMAND ${MAKE_EXECUTABLE} > /dev/null 2>&1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs/latex + COMMENT "Generating PDF documentation" + ) + else (MAKE_EXECUTABLE) + message("Could not find 'make' command for generating PDF documentation.") + endif () +endif(WITH_PAPER) # set up testing find_package(GTest) diff --git a/Core/Pathfinder/CMakeLists.txt b/Core/Pathfinder/CMakeLists.txt index 900f524..96bfc07 100644 --- a/Core/Pathfinder/CMakeLists.txt +++ b/Core/Pathfinder/CMakeLists.txt @@ -5,26 +5,34 @@ endif() include(ExternalProject) -set(ADA_PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ada_backend) -set(ADA_PROJECT_GPR libpathfinding_ada.gpr) -set(ADA_OUTPUT_LIB ${ADA_PROJECT_DIR}/lib/libpathfinding_ada.so) - -ExternalProject_Add(Pathfinder_Ada_Proj - SOURCE_DIR ${ADA_PROJECT_DIR} - CONFIGURE_COMMAND "" - BUILD_COMMAND ${GPRBUILD_EXECUTABLE} -p -P ${ADA_PROJECT_GPR} - BUILD_IN_SOURCE 1 - INSTALL_COMMAND "" +set(ADA_OUTPUT_LIB "${CMAKE_CURRENT_SOURCE_DIR}/ada_backend/lib/libpathfinding_ada.so") +set(ADA_PROJECT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ada_backend/libpathfinding_ada.gpr") + +add_custom_command( + OUTPUT "${ADA_OUTPUT_LIB}" + COMMAND gprbuild -p -P "${ADA_PROJECT_FILE}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + COMMENT "Building Ada library via gprbuild" + VERBATIM +) + +add_custom_target(Pathfinder_Ada_Proj ALL + DEPENDS "${ADA_OUTPUT_LIB}" ) add_library(Pathfinder_C_ABI STATIC ${CMAKE_CURRENT_SOURCE_DIR}/c_abi/src/pathfinding_c_inf.cpp) target_include_directories(Pathfinder_C_ABI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/c_abi/inc) -target_link_libraries(Pathfinder_C_ABI PRIVATE GridGenerator) -add_library(Pathfinder_Ada SHARED IMPORTED) -set_target_properties(Pathfinder_Ada PROPERTIES IMPORTED_LOCATION ${ADA_OUTPUT_LIB}) +add_library(Pathfinder_Ada SHARED IMPORTED GLOBAL) +set_target_properties(Pathfinder_Ada PROPERTIES + IMPORTED_LOCATION "${ADA_OUTPUT_LIB}" +) + add_dependencies(Pathfinder_Ada Pathfinder_Ada_Proj Pathfinder_C_ABI) + target_link_libraries(Pathfinder_Ada INTERFACE Pathfinder_C_ABI) +add_dependencies(Pathfinder_Ada Pathfinder_Ada_Proj Pathfinder_C_ABI) +target_link_libraries(Pathfinder_C_ABI PRIVATE GridGenerator) file(GLOB SOURCE_FILES "src/*.cpp") add_library(Pathfinder ${SOURCE_FILES}) From 21e0b90d4c32ea5086b59ace00709f7f6e08c208 Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Wed, 11 Feb 2026 17:00:58 +0100 Subject: [PATCH 7/8] setup for dependencies --- .devcontainer/Dockerfile | 3 ++- .github/workflows/full.yml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5cca158..feba331 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -16,7 +16,8 @@ RUN apt-get update && apt-get install -y \ qtbase5-dev \ qt5-qmake \ libgtest-dev \ - libopencv-dev + libopencv-dev \ + gprbuild RUN mkdir /usr/src/gtest/build RUN cd /usr/src/gtest/build && cmake .. && make && cd lib && cp *.a /usr/lib diff --git a/.github/workflows/full.yml b/.github/workflows/full.yml index 6715e8a..aab03ac 100644 --- a/.github/workflows/full.yml +++ b/.github/workflows/full.yml @@ -15,7 +15,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y qtcreator qtbase5-dev texlive-full qt5-qmake doxygen build-essential - sudo apt-get install -y cmake graphviz ninja-build make libgtest-dev libopencv-dev + sudo apt-get install -y cmake graphviz ninja-build make libgtest-dev libopencv-dev gprbuild sudo mkdir /usr/src/gtest/build cd /usr/src/gtest/build && sudo cmake .. && sudo make && cd lib && sudo cp *.a /usr/lib sudo rm -r /usr/src/gtest/build @@ -23,8 +23,8 @@ jobs: - name: Configure and Build CMake run: | - cmake -GNinja -S ${{github.workspace}} -B ${{github.workspace}}/build - cmake --build ${{github.workspace}}/build + cmake -GNinja -S ${{github.workspace}} -B ${{github.workspace}}/build -DWITH_DOCS=ON -DWITH_PAPER=ON + cmake --build ${{github.workspace}}/build - name: Test working-directory: ${{github.workspace}}/build From ef64516f1f91c3641303e04ec54c5e7aafecc72e Mon Sep 17 00:00:00 2001 From: Felix-Renzikowski Date: Wed, 11 Feb 2026 17:33:47 +0100 Subject: [PATCH 8/8] adapts pipeline, devcontainer and readme to ada backend --- .devcontainer/Dockerfile | 6 ++++-- .github/workflows/build_test_only.yml | 2 +- .github/workflows/full.yml | 2 +- README.md | 22 ++++++++++++++++++---- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index feba331..ac4327d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:25.10 LABEL maintainer="Felix Renzikowski " @@ -17,7 +17,9 @@ RUN apt-get update && apt-get install -y \ qt5-qmake \ libgtest-dev \ libopencv-dev \ - gprbuild + gprbuild \ + ninja-build \ + gnat RUN mkdir /usr/src/gtest/build RUN cd /usr/src/gtest/build && cmake .. && make && cd lib && cp *.a /usr/lib diff --git a/.github/workflows/build_test_only.yml b/.github/workflows/build_test_only.yml index 625233a..fdcedff 100644 --- a/.github/workflows/build_test_only.yml +++ b/.github/workflows/build_test_only.yml @@ -14,7 +14,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y qtcreator qtbase5-dev qt5-qmake cmake ninja-build make libgtest-dev libopencv-dev + sudo apt-get install -y qtcreator qtbase5-dev qt5-qmake cmake ninja-build make libgtest-dev libopencv-dev gprbuild gnat sudo mkdir /usr/src/gtest/build sudo cd /usr/src/gtest/build && sudo cmake .. && sudo make && sudo cd lib && sudo cp *.a /usr/lib sudo rm -r /usr/src/gtest/build diff --git a/.github/workflows/full.yml b/.github/workflows/full.yml index aab03ac..98ba86e 100644 --- a/.github/workflows/full.yml +++ b/.github/workflows/full.yml @@ -15,7 +15,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y qtcreator qtbase5-dev texlive-full qt5-qmake doxygen build-essential - sudo apt-get install -y cmake graphviz ninja-build make libgtest-dev libopencv-dev gprbuild + sudo apt-get install -y cmake graphviz ninja-build make libgtest-dev libopencv-dev gprbuild gnat sudo mkdir /usr/src/gtest/build cd /usr/src/gtest/build && sudo cmake .. && sudo make && cd lib && sudo cp *.a /usr/lib sudo rm -r /usr/src/gtest/build diff --git a/README.md b/README.md index e84fe30..84395ab 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,19 @@ There are three key pathfinding algorithms that are currently implemented in thi - **Dijkstra**: This is a renowned pathfinding algorithm that operates by exploring the neighbouring nodes of the start point. It then traverses towards the goal by frequently jumping to the node with the lowest cost. The Dijkstra - pathfinding algorithm guarantees the shortest path. + pathfinding algorithm guarantees the shortest path. This algorithm is implemented in C++ and in Ada. - **A\* Algorithm**: This pathfinding algorithm employs a heuristic to guide its way towards the goal. The key distinguishing factor between the A\* and Dijkstra's algorithm is that, while Dijkstra's algorithm seeks the shortest path to all possible destinations, the A\* algorithm uses a heuristic to estimate the cost to the goal, which allows it to focus on more promising paths and reach the goal more efficiently. In other words, A\* is more strategic in terms of selecting the next node for expansion, quickly finding an optimal path in large graphs compared to Dijkstra's - algorithm. + algorithm. This algorithm is implemented in C++ and in Ada. - **Jump Point Search**: This search algorithm is essentially an optimization of the A\* algorithm for uniform-cost grid maps, and this is achieved without affecting optimality. Here, instead of examining each individual cell, the - algorithm has the liberty to "jump" in between points in the direction of the goal, bypassing any irrelevant nodes. + algorithm has the liberty to "jump" in between points in the direction of the goal, bypassing any irrelevant nodes. + This algorithm is only implemented in C++. ## Obstacle Generation @@ -55,6 +56,7 @@ cmake qtcreator qtbase5-dev qt5-qmake +gprbuild ``` For the Doxygen documentation generation, you will need to have the following dependencies: @@ -75,7 +77,19 @@ fontenc In order to simplify the setup and usage process, a Devcontainer has been provided. This essentially allows you to use the project inside a Docker environment. This isolates your development environment from your local machine. To get -started all you need to do is clone the Git repository and then use the Devcontainer. +started all you need to do is clone the Git repository and then use the Devcontainer. Building the Container may take some +time as a texlive-full installation is a part of the container to be able to build the documentation and PDF-Paper. +To build the project using the Devcontainer, set it up and then run the following commands in the devcontainer shell: +```console +cmake -GNinja -S . -B ./build -DWITH_DOCS=ON -DWITH_PAPER=ON +cmake --build ./build +``` +To only build the executable and not the documentation and the PDF-Paper run the following commands instead: +```console +cmake -GNinja -S . -B ./build +cmake --build ./build +``` +When operating on Windows, WSL can be used to execute the compiled binary afterwards. ## Documentation