Skip to content

Commit

Permalink
Merge pull request #1 from velocitatem/add-astar
Browse files Browse the repository at this point in the history
Add A* algorithm to library
  • Loading branch information
velocitatem authored Dec 16, 2024
2 parents 426f66f + a81229e commit f3d7182
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 24 deletions.
24 changes: 19 additions & 5 deletions examples/main.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
//
// Created by velocitatem on 12/15/24.
//

#include "search.h"
#include <iostream>

Expand Down Expand Up @@ -194,4 +190,22 @@ int main () {

}

}
{
MazeProblem maze_problem;
Search *search = create_search(SearchAlgorithmIndex::A_STAR, &maze_problem);
auto node = search->search();

if (node) {
std::cout << "Solution found using A*!" << std::endl;
State *maze_state = node->state.get();
maze_state->print();
Solution solution(node.get());
solution.print();
} else {
std::cout << "Solution not found using A*!" << std::endl;
}

delete search;
}

}
18 changes: 8 additions & 10 deletions include/search.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
//
// Created by velocitatem on 12/15/24.
//

#ifndef SEARCH_H
#define SEARCH_H

Expand All @@ -10,6 +6,7 @@
#include "definitions.h"
#include <memory>


class Node {
public:
std::shared_ptr<Node> parent; // Weak pointer to prevent circular references
Expand Down Expand Up @@ -49,18 +46,19 @@ class BreadthFirstSearch : public Search {
~BreadthFirstSearch();
};



class AStarSearch : public Search {
public:
AStarSearch(Problem *problem) : Search(problem) {}
std::shared_ptr<Node> search() override;
~AStarSearch();
};

enum SearchAlgorithmIndex {
BREADTH_FIRST_SEARCH,
UNIFORM_COST_SEARCH,
A_STAR
};

Search *create_search(SearchAlgorithmIndex search_algorithm_index, Problem *problem);



Search *create_search(SearchAlgorithmIndex search_algorithm_index, Problem *problem); // DEFINED IN search.cpp

#endif //SEARCH_H
56 changes: 52 additions & 4 deletions src/search.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
//
// Created by velocitatem on 12/15/24.
//

#include "search.h"
#include "utils.cpp"
#include <queue>
#include <memory>
#include <iostream>
#include <unordered_set>

Search *create_search(SearchAlgorithmIndex search_algorithm_index, Problem *problem) {
switch (search_algorithm_index) {
case BREADTH_FIRST_SEARCH:
return new BreadthFirstSearch(problem);
case A_STAR:
return new AStarSearch(problem);
default:
return nullptr;
}
Expand Down Expand Up @@ -76,3 +75,52 @@ std::shared_ptr<Node> BreadthFirstSearch::search() {
// Return nullptr if no solution is found
return nullptr;
}

AStarSearch::~AStarSearch() { }

std::shared_ptr<Node> AStarSearch::search() {
std::priority_queue<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>>, NodeComparator> frontier;
// Initialize the root node with the problem's initial state
std::unordered_set<std::shared_ptr<State>> explored; // Set of explored states
auto initial_state = problem->initial_state();
auto root = std::make_shared<Node>(
nullptr, // Parent node
std::shared_ptr<State>(initial_state), // Initial state (shared_ptr)
nullptr, // No action
0, // Path cost
problem->heuristic(initial_state) // Heuristic value
);
frontier.push(root);

while (!frontier.empty()) {
auto node = frontier.top();
frontier.pop();

// Check if the goal state is reached
State *state = node->state.get();
if (problem->goal_test(state)) {
return node;
}

// Skip the node if it has already been explored
if (explored.find(node->state) != explored.end()) {
continue;
}
explored.insert(node->state);

// Expand the node by generating its child nodes
for (const auto &action : problem->actions(node->state)) {
auto child = std::make_shared<Node>(
std::shared_ptr<Node>(node), // Parent node
action->effect,
action, // Pointer to the action
node->path_cost + action->cost, // Path cost
problem->heuristic(action->effect.get()) // Heuristic value
);
frontier.push(child);
}
}

// Return nullptr if no solution is found
return nullptr;
}
5 changes: 4 additions & 1 deletion src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
// node copmarator for priority queue
class NodeComparator {
public:
bool operator()(Node *a, Node *b) {
bool operator()(const std::shared_ptr<Node> &a, const std::shared_ptr<Node> &b) {
if (!a || !b) {
throw std::invalid_argument("Null node pointer in comparison");
}
return a->path_cost + a->heuristic > b->path_cost + b->heuristic;
}
};
Expand Down
40 changes: 36 additions & 4 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,50 @@
//
// Created by velocitatem on 12/8/24.
//

#include <gtest/gtest.h>
#include "definitions.h"
#include "search.h"

class TestState : public State {
public:
int value;
TestState(int value) : value(value) {}
void print() override {}
};

class TestProblem : public Problem {
public:
TestProblem() {
initial_state_ = new TestState(0);
}
~TestProblem() {
}
bool goal_test(State *state) override {
auto *test_state = dynamic_cast<TestState *>(state);
return test_state->value == 10;
}
std::vector<std::shared_ptr<Action>> actions(std::shared_ptr<State> state) override {
auto test_state = std::dynamic_pointer_cast<TestState>(state);
std::vector<std::shared_ptr<Action>> actions;
actions.push_back(std::make_shared<Action>("Increment", 1, state, std::make_shared<TestState>(test_state->value + 1)));
return actions;
}
double heuristic(State *state) override {
auto *test_state = dynamic_cast<TestState *>(state);
return 10 - test_state->value;
}
};

TEST(Definitions, State) {
State state;
EXPECT_NO_THROW(state.print());
}

TEST(Search, AStarSearch) {
TestProblem problem;
Search *search = create_search(SearchAlgorithmIndex::A_STAR, &problem);
std::shared_ptr<Node> node = search->search();
ASSERT_NE(node, nullptr);
EXPECT_EQ(dynamic_cast<TestState *>(node->state.get())->value, 10);
delete search;
}

int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
Expand Down

0 comments on commit f3d7182

Please sign in to comment.