TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
a_star_search.cpp
1
22#include <algorithm>
23#include <array>
24#include <cassert>
25#include <functional>
26#include <iostream>
27#include <map>
28#include <memory>
29#include <set>
30#include <vector>
35namespace machine_learning {
41namespace aystar_search {
59template <size_t N = 3>
61 std::array<std::array<uint32_t, N>, N>
62 board;
63
64 std::vector<std::pair<int8_t, int8_t>> moves = {
65 {0, 1},
66 {1, 0},
67 {0, -1},
68 {-1,
69 0}};
75 std::pair<uint32_t, uint32_t> find_zero() {
76 for (size_t i = 0; i < N; ++i) {
77 for (size_t j = 0; j < N; ++j) {
78 if (!board[i][j]) {
79 return {i, j};
80 }
81 }
82 }
83 return {-1, -1};
84 }
90 inline bool in_range(const uint32_t value) const { return value < N; }
91
92 public:
102 uint32_t get(size_t i, size_t j) const {
103 if (in_range(i) && in_range(j)) {
104 return board[i][j];
105 }
106 return -1;
107 }
111 std::array<std::array<uint32_t, N>, N> get_state() { return board; }
112
117 inline size_t get_size() const { return N; }
122 for (size_t i = 0; i < N; ++i) {
123 for (size_t j = 0; j < N; ++j) {
124 board[i][j] = ((i * 3 + j + 1) % (N * N));
125 }
126 }
127 }
132 explicit EightPuzzle(const std::array<std::array<uint32_t, N>, N> &init)
133 : board(init) {}
134
139 EightPuzzle(const EightPuzzle<N> &A) : board(A.board) {}
140
145 EightPuzzle(const EightPuzzle<N> &&A) noexcept
146 : board(std::move(A.board)) {}
150 ~EightPuzzle() = default;
151
157 board = A.board;
158 return *this;
159 }
160
166 board = std::move(A.board);
167 return *this;
168 }
169
176 std::vector<EightPuzzle<N>> generate_possible_moves() {
177 auto zero_pos = find_zero();
178 // vector which will contain all possible state from current state
179 std::vector<EightPuzzle<N>> NewStates;
180 for (auto &move : moves) {
181 if (in_range(zero_pos.first + move.first) &&
182 in_range(zero_pos.second + move.second)) {
183 // swap with the possible moves
184 std::array<std::array<uint32_t, N>, N> new_config = board;
185 std::swap(new_config[zero_pos.first][zero_pos.second],
186 new_config[zero_pos.first + move.first]
187 [zero_pos.second + move.second]);
188 EightPuzzle<N> new_state(new_config);
189 // Store new state and calculate heuristic value, and depth
190 NewStates.emplace_back(new_state);
191 }
192 }
193 return NewStates;
194 }
200 bool operator==(const EightPuzzle<N> &check) const {
201 if (check.get_size() != N) {
202 return false;
203 }
204 for (size_t i = 0; i < N; ++i) {
205 for (size_t j = 0; j < N; ++j) {
206 if (board[i][j] != check.board[i][j]) {
207 return false;
208 }
209 }
210 }
211 return true;
212 }
218 bool operator<(const EightPuzzle<N> &check) const {
219 for (size_t i = 0; i < N; ++i) {
220 for (size_t j = 0; j < N; ++j) {
221 if (board[i][j] != check.board[i][j]) {
222 return board[i][j] < check.board[i][j];
223 }
224 }
225 }
226 return false;
227 }
233 bool operator<=(const EightPuzzle<N> &check) const {
234 for (size_t i = 0; i < N; ++i) {
235 for (size_t j = 0; j < N; ++j) {
236 if (board[i][j] != check.board[i][j]) {
237 return board[i][j] < check.board[i][j];
238 }
239 }
240 }
241 return true;
242 }
243
250 friend std::ostream &operator<<(std::ostream &op,
251 const EightPuzzle<N> &SomeState) {
252 for (size_t i = 0; i < N; ++i) {
253 for (size_t j = 0; j < N; ++j) {
254 op << SomeState.board[i][j] << " ";
255 }
256 op << "\n";
257 }
258 return op;
259 }
260};
287template <typename Puzzle>
293 typedef struct Info {
294 std::shared_ptr<Puzzle> state;
295 size_t heuristic_value = 0;
296 size_t depth = 0;
297
301 Info() = default;
302
307 explicit Info(const Puzzle &A) : state(std::make_shared<Puzzle>(A)) {}
308
315 Info(const Puzzle &A, size_t h_value, size_t d)
316 : state(std::make_shared<Puzzle>(A)),
317 heuristic_value(h_value),
318 depth(d) {}
319
324 Info(const Info &A)
325 : state(std::make_shared<Puzzle>(A.state)),
327 depth(A.depth) {}
328
333 Info(const Info &&A) noexcept
334 : state(std::make_shared<Puzzle>(std::move(A.state))),
335 heuristic_value(std::move(A.heuristic_value)),
336 depth(std::move(A.depth)) {}
337
342 Info &operator=(const Info &A) {
343 state = A.state;
345 depth = A.depth;
346 return *this;
347 }
348
353 Info &operator=(Info &&A) noexcept {
354 state = std::move(A.state);
355 heuristic_value = std::move(A.heuristic_value);
356 depth = std::move(A.depth);
357 return *this;
358 }
362 ~Info() = default;
364
365 std::shared_ptr<Info> Initial; // Initial state of the AyStarSearch
366 std::shared_ptr<Info> Final; // Final state of the AyStarSearch
371 bool operator()(const std::shared_ptr<Info> &a,
372 const std::shared_ptr<Info> &b) const {
373 return *(a->state) < *(b->state);
374 }
375 };
376
377 public:
378 using MapOfPuzzleInfoWithPuzzleInfo =
379 std::map<std::shared_ptr<Info>, std::shared_ptr<Info>,
381
382 using MapOfPuzzleInfoWithInteger =
383 std::map<std::shared_ptr<Info>, uint32_t, comparison_operator>;
384
385 using SetOfPuzzleInfo =
386 std::set<std::shared_ptr<Info>, comparison_operator>;
392 AyStarSearch(const Puzzle &initial, const Puzzle &final) {
393 Initial = std::make_shared<Info>(initial);
394 Final = std::make_shared<Info>(final);
395 }
405 std::vector<Puzzle> Solution(
406 std::shared_ptr<Info> FinalState,
407 const MapOfPuzzleInfoWithPuzzleInfo &parent_of) {
408 // Useful for traversing from final state to current state.
409 auto current_state = FinalState;
410 /*
411 * For storing the solution tree starting from initial state to
412 * final state
413 */
414 std::vector<Puzzle> answer;
415 while (current_state != nullptr) {
416 answer.emplace_back(*current_state->state);
417 current_state = parent_of.find(current_state)->second;
418 }
419 return answer;
420 }
429 std::vector<Puzzle> a_star_search(
430 const std::function<uint32_t(const Puzzle &, const Puzzle &)> &dist,
431 const uint32_t permissible_depth = 30) {
432 MapOfPuzzleInfoWithPuzzleInfo
433 parent_of;
434 MapOfPuzzleInfoWithInteger g_score;
435 SetOfPuzzleInfo open_list;
436 SetOfPuzzleInfo closed_list;
437
438 // Before starting the AyStartSearch, initialize the set and maps
439 open_list.emplace(Initial);
440 parent_of[Initial] = nullptr;
441 g_score[Initial] = 0;
442
443 while (!open_list.empty()) {
444 // Iterator for state having having lowest f_score.
445 typename SetOfPuzzleInfo::iterator it_low_f_score;
446 uint32_t min_f_score = 1e9;
447 for (auto iter = open_list.begin(); iter != open_list.end();
448 ++iter) {
449 // f score here is evaluated by g score (depth) and h score
450 // (distance between current state and final state)
451 uint32_t f_score = (*iter)->heuristic_value + (*iter)->depth;
452 if (f_score < min_f_score) {
453 min_f_score = f_score;
454 it_low_f_score = iter;
455 }
456 }
457
458 // current_state, stores lowest f score so far for this state.
459 std::shared_ptr<Info> current_state = *it_low_f_score;
460
461 // if this current state is equal to final, return
462 if (*(current_state->state) == *(Final->state)) {
463 return Solution(current_state, parent_of);
464 }
465 // else remove from open list as visited.
466 open_list.erase(it_low_f_score);
467 // if current_state has exceeded the allowed depth, skip
468 // neighbor checking
469 if (current_state->depth >= permissible_depth) {
470 continue;
471 }
472 // Generate all possible moves (neighbors) given the current
473 // state
474 std::vector<Puzzle> total_possible_moves =
475 current_state->state->generate_possible_moves();
476
477 for (Puzzle &neighbor : total_possible_moves) {
478 // calculate score of neighbors with respect to
479 // current_state
480 std::shared_ptr<Info> Neighbor = std::make_shared<Info>(
481 neighbor, dist(neighbor, *(Final->state)),
482 current_state->depth + 1U);
483 uint32_t temp_g_score = Neighbor->depth;
484
485 // Check whether this state is explored.
486 // If this state is discovered at greater depth, then discard,
487 // else remove from closed list and explore the node
488 auto closed_list_iter = closed_list.find(Neighbor);
489 if (closed_list_iter != closed_list.end()) {
490 // 1. If state in closed list has higher depth, then remove
491 // from list since we have found better option,
492 // 2. Else don't explore this state.
493 if (Neighbor->depth < (*closed_list_iter)->depth) {
494 closed_list.erase(closed_list_iter);
495 } else {
496 continue;
497 }
498 }
499 auto neighbor_g_score_iter = g_score.find(Neighbor);
500 // if the neighbor is already created and has minimum
501 // g_score, then update g_score and f_score else insert new
502 if (neighbor_g_score_iter != g_score.end()) {
503 if (neighbor_g_score_iter->second > temp_g_score) {
504 neighbor_g_score_iter->second = temp_g_score;
505 parent_of[Neighbor] = current_state;
506 }
507 } else {
508 g_score[Neighbor] = temp_g_score;
509 parent_of[Neighbor] = current_state;
510 }
511 // If this is a new state, insert into open_list
512 // else update if the this state has better g score than
513 // existing one.
514 auto iter = open_list.find(Neighbor);
515 if (iter == open_list.end()) {
516 open_list.emplace(Neighbor);
517 } else if ((*iter)->depth > Neighbor->depth) {
518 (*iter)->depth = Neighbor->depth;
519 }
520 }
521 closed_list.emplace(current_state);
522 }
523 // Cannot find the solution, return empty vector
524 return std::vector<Puzzle>(0);
525 }
526};
527} // namespace aystar_search
528} // namespace machine_learning
529
534static void test() {
535 // Renaming for simplicity
536 using matrix3 = std::array<std::array<uint32_t, 3>, 3>;
537 using row3 = std::array<uint32_t, 3>;
538 using matrix4 = std::array<std::array<uint32_t, 4>, 4>;
539 using row4 = std::array<uint32_t, 4>;
540 // 1st test: A* search for simple EightPuzzle problem
541 matrix3 puzzle;
542 puzzle[0] = row3({0, 2, 3});
543 puzzle[1] = row3({1, 5, 6});
544 puzzle[2] = row3({4, 7, 8});
545
546 matrix3 ideal;
547 ideal[0] = row3({1, 2, 3});
548 ideal[1] = row3({4, 5, 6});
549 ideal[2] = row3({7, 8, 0});
550
551 /*
552 * Heuristic function: Manhattan distance
553 */
554 auto manhattan_distance =
557 uint32_t ret = 0;
558 for (size_t i = 0; i < first.get_size(); ++i) {
559 for (size_t j = 0; j < first.get_size(); ++j) {
560 uint32_t find = first.get(i, j);
561 size_t m = first.get_size(), n = first.get_size();
562 for (size_t k = 0; k < second.get_size(); ++k) {
563 for (size_t l = 0; l < second.get_size(); ++l) {
564 if (find == second.get(k, l)) {
565 std::tie(m, n) = std::make_pair(k, l);
566 break;
567 }
568 }
569 if (m != first.get_size()) {
570 break;
571 }
572 }
573 if (m != first.get_size()) {
574 ret += (std::max(m, i) - std::min(m, i)) +
575 (std::max(n, j) - std::min(n, j));
576 }
577 }
578 }
579 return ret;
580 };
581
586 search(Puzzle, Ideal);
587
588 std::vector<matrix3> answer;
589
590 answer.push_back(
591 matrix3({row3({0, 2, 3}), row3({1, 5, 6}), row3({4, 7, 8})}));
592 answer.push_back(
593 matrix3({row3({1, 2, 3}), row3({0, 5, 6}), row3({4, 7, 8})}));
594 answer.push_back(
595 matrix3({row3({1, 2, 3}), row3({4, 5, 6}), row3({0, 7, 8})}));
596 answer.push_back(
597 matrix3({row3({1, 2, 3}), row3({4, 5, 6}), row3({7, 0, 8})}));
598 answer.push_back(
599 matrix3({row3({1, 2, 3}), row3({4, 5, 6}), row3({7, 8, 0})}));
600
601 auto Solution = search.a_star_search(manhattan_distance);
602 std::cout << Solution.size() << std::endl;
603
604 assert(Solution.size() == answer.size());
605
606 uint32_t i = 0;
607 for (auto it = Solution.rbegin(); it != Solution.rend(); ++it) {
608 assert(it->get_state() == answer[i]);
609 ++i;
610 }
611
612 // 2nd test: A* search for complicated EightPuzzle problem
613 // Initial state
614 puzzle[0] = row3({5, 7, 3});
615 puzzle[1] = row3({2, 0, 6});
616 puzzle[2] = row3({1, 4, 8});
617 // Final state
618 ideal[0] = row3({1, 2, 3});
619 ideal[1] = row3({4, 5, 6});
620 ideal[2] = row3({7, 8, 0});
621
624
625 // Initialize the search object
628
629 Solution = search.a_star_search(manhattan_distance);
630 std::cout << Solution.size() << std::endl;
631 // Static assertion due to large solution
632 assert(13 == Solution.size());
633 // Check whether the final state is equal to expected one
634 assert(Solution[0].get_state() == ideal);
635 for (auto it = Solution.rbegin(); it != Solution.rend(); ++it) {
636 std::cout << *it << std::endl;
637 }
638
639 // 3rd test: A* search for 15-Puzzle
640 // Initial State of the puzzle
641 matrix4 puzzle2;
642 puzzle2[0] = row4({10, 1, 6, 2});
643 puzzle2[1] = row4({5, 8, 4, 3});
644 puzzle2[2] = row4({13, 0, 7, 11});
645 puzzle2[3] = row4({14, 9, 15, 12});
646 // Final state of the puzzle
647 matrix4 ideal2;
648 ideal2[0] = row4({1, 2, 3, 4});
649 ideal2[1] = row4({5, 6, 7, 8});
650 ideal2[2] = row4({9, 10, 11, 12});
651 ideal2[3] = row4({13, 14, 15, 0});
652
653 // Instantiate states for a*, initial state and final states
655 Ideal2(ideal2);
656 // Initialize the search object
659 search2(Puzzle2, Ideal2);
663 auto manhattan_distance2 =
666 uint32_t ret = 0;
667 for (size_t i = 0; i < first.get_size(); ++i) {
668 for (size_t j = 0; j < first.get_size(); ++j) {
669 uint32_t find = first.get(i, j);
670 size_t m = first.get_size(), n = first.get_size();
671 for (size_t k = 0; k < second.get_size(); ++k) {
672 for (size_t l = 0; l < second.get_size(); ++l) {
673 if (find == second.get(k, l)) {
674 std::tie(m, n) = std::make_pair(k, l);
675 break;
676 }
677 }
678 if (m != first.get_size()) {
679 break;
680 }
681 }
682 if (m != first.get_size()) {
683 ret += (std::max(m, i) - std::min(m, i)) +
684 (std::max(n, j) - std::min(n, j));
685 }
686 }
687 }
688 return ret;
689 };
690
691 auto sol2 = search2.a_star_search(manhattan_distance2);
692 std::cout << sol2.size() << std::endl;
693
694 // Static assertion due to large solution
695 assert(24 == sol2.size());
696 // Check whether the final state is equal to expected one
697 assert(sol2[0].get_state() == ideal2);
698
699 for (auto it = sol2.rbegin(); it != sol2.rend(); ++it) {
700 std::cout << *it << std::endl;
701 }
702}
707int main() {
708 test(); // run self-test implementations
709 return 0;
710}
void test()
A class defining A* search algorithm. for some initial state and final state.
std::vector< Puzzle > Solution(std::shared_ptr< Info > FinalState, const MapOfPuzzleInfoWithPuzzleInfo &parent_of)
A helper solution: launches when a solution for AyStarSearch is found.
std::vector< Puzzle > a_star_search(const std::function< uint32_t(const Puzzle &, const Puzzle &)> &dist, const uint32_t permissible_depth=30)
AyStarSearch(const Puzzle &initial, const Puzzle &final)
Parameterized constructor for AyStarSearch.
struct machine_learning::aystar_search::AyStarSearch::Info Info
Struct that handles all the information related to the current state.
A class defining EightPuzzle/15-Puzzle game.
EightPuzzle & operator=(EightPuzzle &&A) noexcept
Move assignment operator.
~EightPuzzle()=default
Destructor of EightPuzzle.
std::vector< EightPuzzle< N > > generate_possible_moves()
Find all possible states after processing all possible moves, given the current state of the puzzle.
EightPuzzle()
Default constructor for EightPuzzle.
EightPuzzle & operator=(const EightPuzzle &A)
Copy assignment operator.
bool in_range(const uint32_t value) const
check whether the index value is bounded within the puzzle area
bool operator<(const EightPuzzle< N > &check) const
check whether one board is lexicographically smaller
std::pair< uint32_t, uint32_t > find_zero()
A helper array to evaluate the next state from current state;.
friend std::ostream & operator<<(std::ostream &op, const EightPuzzle< N > &SomeState)
friend operator to display EightPuzzle<>
bool operator==(const EightPuzzle< N > &check) const
check whether two boards are equal
uint32_t get(size_t i, size_t j) const
get the value from i units from right and j units from left side of the board
std::vector< std::pair< int8_t, int8_t > > moves
N x N array to store the current state of the Puzzle.
EightPuzzle(const std::array< std::array< uint32_t, N >, N > &init)
Parameterized Constructor for EightPuzzle.
EightPuzzle(const EightPuzzle< N > &A)
Copy constructor.
std::array< std::array< uint32_t, N >, N > get_state()
Returns the current state of the board.
size_t get_size() const
returns the size of the EightPuzzle (number of row / column)
EightPuzzle(const EightPuzzle< N > &&A) noexcept
Move constructor.
bool operator<=(const EightPuzzle< N > &check) const
check whether one board is lexicographically smaller or equal
double k(double x)
Another test function.
double l(double x)
Another test function.
int main()
Main function.
Functions for A* Search implementation.
A* search algorithm
for std::assert
Struct that handles all the information related to the current state.
Info(const Puzzle &A)
constructor having Puzzle as parameter
Info(const Info &&A) noexcept
Move constructor.
Info & operator=(const Info &A)
copy assignment operator
Info(const Puzzle &A, size_t h_value, size_t d)
constructor having three parameters
Info & operator=(Info &&A) noexcept
move assignment operator