├── .gitignore ├── LICENSE ├── README.md ├── common.hpp ├── examples ├── MissioCanib │ ├── main.cpp │ ├── makefile │ ├── missiocanib.cpp │ └── missiocanib.h ├── RomaniaCities │ ├── main.cpp │ ├── makefile │ ├── romaniacities.cpp │ └── romaniacities.h ├── TwoMadeUpProblems │ ├── generating.cpp │ ├── generating.h │ ├── main.cpp │ ├── makefile │ ├── treenumber.cpp │ └── treenumber.h └── makefile ├── my_fun_stuffs.hpp ├── node.hpp ├── problem.hpp └── search.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | \.* 6 | a.out 7 | 8 | 9 | # Compiled Dynamic libraries 10 | *.so 11 | *.dylib 12 | 13 | # Compiled Static libraries 14 | *.lai 15 | *.la 16 | *.a 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Hamidreza Davoodi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AIMA-CPP 2 | ======== 3 | 4 | C++(1y) implementation of algorithms from Norvig And Russell's "Artificial Intelligence - A Modern Approach 3rd Edition." 5 | 6 | ### typename COPYRIGHT = MIT 7 | 8 | I really want others to contribute over this ( at least the examples are so few ) :) 9 | 10 | It would be great if someone try to to solve the questions in the book by this little library. 11 | 12 | ### TODO 13 | 14 | - I'd like to write a little GUI (with Qt) to show how these algorithms work. 15 | 16 | - Currently I'm just implementing step by step while I read the book :) 17 | 18 | Let's hope I'll do the rest or someone else would do it. 19 | 20 | ### Examples 21 | 22 | For now there are only there way to define a problem: 23 | - use Macros ```MAKE_PROBLEM``` and ``` END_PROBLEM ``` to make a problem with a goal and a generator for each state: 24 | - You have to generate successors for each state. 25 | 26 | - Use function ``` makeProblem``` to make problem for using the generated tree/graph to find the defined goal. 27 | - There'll be no generator for this problem. 28 | - The graph will get expanded only by the leafs of each state in the graph/tree. 29 | 30 | - Inherit from Problem : 31 | - Override ```void watch(const node_ptr &state) const;``` to watch what state is getting tested. 32 | - Override ```bool isGoal(const T &state) const;``` to say which state is the goal of the problem. 33 | - Override ```leafs_list successors( const node_ptr &state) const;``` to generate/expand the successors of the current state. 34 | - Override ```long F(const node_ptr &node, const long &gn, const long &pcost) const ;``` to calculate ```node```'s cost. 35 | 36 | 37 | ### Output 38 | Using bestFirstGS ( best first graph search ) using ``` f(n) = g(n) + h(n) ``` : 39 | 40 | ``` 41 | Visited node "Arad" with cost of 0 and depth of 0 42 | Visited node "Sibiu" with cost of 393 and depth of 1 43 | Visited node "Rimnicu Vilcea" with cost of 273 and depth of 2 44 | Visited node "Pitesti" with cost of 197 and depth of 3 45 | Visited node "Bucharest" with cost of 101 and depth of 4 46 | 47 | Route to root: -> Bucharest -> Pitesti -> Rimnicu Vilcea -> Sibiu -> Arad 48 | ``` 49 | Take a look at Test.hpp and Test.cpp to see what I mean :P . 50 | There will be a separated directory for examples in close future. 51 | 52 | ### Compiled ON/BY 53 | 54 | ``` 55 | Linux debian 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1 x86_64 GNU/Linux 56 | gcc version 4.8.1 (GCC) 57 | clang version 3.3 (tags/RELEASE_33/final) 58 | ``` 59 | With: 60 | ```g++ -std=c++1y -O2 test.cpp``` 61 | ```clang++ -std=c++1y -O2 test.cpp``` 62 | -------------------------------------------------------------------------------- /common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HPP 2 | #define COMMON_HPP 3 | 4 | #include 5 | #include 6 | 7 | using std::cout; 8 | using std::endl; 9 | using std::get; 10 | using std::tuple; 11 | using std::ostream; 12 | 13 | 14 | namespace AI 15 | { 16 | 17 | template 18 | struct TuplePrint 19 | { 20 | void operator()(const T& t) const 21 | { 22 | TuplePrint()(t); 23 | cout <<"," << get(t); 24 | } 25 | }; 26 | 27 | template 28 | struct TuplePrint 29 | { 30 | void operator()(const T& t) const 31 | { 32 | cout << get<0>(t); 33 | } 34 | }; 35 | 36 | template 37 | ostream& operator<< (ostream& stream, const tuple& data) 38 | { 39 | stream << "<"; 40 | TuplePrint()(data); 41 | stream << ">"; 42 | 43 | return stream; 44 | } 45 | 46 | template 47 | void mapToRoot(const R &node, Functor f) 48 | { 49 | if ( !node ) 50 | return; 51 | f( node ); 52 | mapToRoot( node->parent(), f ); 53 | } 54 | 55 | template 56 | void showRoute(const R & node ) 57 | { 58 | using std::cout; 59 | using std::endl; 60 | 61 | cout << "Route to root: "<< node->getState(); 62 | mapToRoot( 63 | node->parent(), 64 | [](const R &n) { cout << " -> " << n->getState(); } 65 | ); 66 | cout << endl; 67 | } 68 | 69 | 70 | namespace Private 71 | { 72 | 73 | template 74 | struct NodePtrCompare : std::less 75 | { 76 | bool operator()(const T &lhs, const T &rhs) const 77 | { return lhs->getState() < rhs->getState(); } 78 | }; 79 | 80 | 81 | } // endnamespace Private 82 | 83 | } // endnamespace AI 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /examples/MissioCanib/main.cpp: -------------------------------------------------------------------------------- 1 | #include "missiocanib.h" 2 | #include "search.hpp" 3 | 4 | int main() 5 | { 6 | cout << endl<< "Missionaries and cannibals problem:" << endl ; 7 | 8 | auto resMCB = bestFirstGS(MiCaBo()); 9 | 10 | showRoute(resMCB); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /examples/MissioCanib/makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | C-FLAGS= -std=c++1y -c -I./../../ 3 | 4 | all: missiocanib.o main.o 5 | $(CC) main.o missiocanib.o -o mcb.out 6 | 7 | missiocanib.o: 8 | $(CC) $(C-FLAGS) missiocanib.cpp -o missiocanib.o 9 | 10 | main.o: 11 | $(CC) $(C-FLAGS) main.cpp -o main.o 12 | 13 | clean: 14 | rm *.o 15 | rm mcb.out 16 | -------------------------------------------------------------------------------- /examples/MissioCanib/missiocanib.cpp: -------------------------------------------------------------------------------- 1 | #include "missiocanib.h" 2 | 3 | DefConstructorProblem(MiCaBo, mcbVec(3,3,1)) 4 | { } 5 | 6 | MiCaBo::leafs_list MiCaBo::successors(const node_ptr &state) const 7 | { 8 | leafs_list leafs; 9 | for( const auto &act : actions ) { 10 | const auto next = apply(state, act); 11 | if ( isValid(next) && isValid(otherSide(next)) ) 12 | leafs.push_back(makeNode(next, state, H(next))); 13 | } 14 | 15 | return leafs; 16 | } 17 | 18 | bool MiCaBo::checkLimit(const int &c) const 19 | { 20 | return ( c <= 3 && c >= 0 ); 21 | } 22 | 23 | mcbVec MiCaBo::otherSide(const mcbVec &succ) const 24 | { 25 | const int &ms = get<0>(succ); 26 | const int &cs = get<1>(succ); 27 | const int &b = get<2>(succ); 28 | 29 | return mcbVec(3-ms, 3-cs, 1-b); 30 | } 31 | 32 | bool MiCaBo::isValid(const mcbVec &succ) const 33 | { 34 | const int &ms = get<0>(succ); 35 | const int &cs = get<1>(succ); 36 | const int &b = get<2>(succ); 37 | 38 | return checkLimit(ms) && checkLimit(cs) && ( ms == 0 ? true : ms >= cs ) && (b == 0 || b == 1); 39 | } 40 | 41 | long MiCaBo::H(const node_ptr &node) const 42 | { 43 | const auto &state = node ->getState(); 44 | return H(state); 45 | } 46 | 47 | 48 | long MiCaBo::H(const mcbVec &state) const 49 | { 50 | return get<0>(state) + get<1>(state); 51 | } 52 | 53 | 54 | mcbVec MiCaBo::apply(const node_ptr &node, const mcbVec &action) const 55 | { 56 | const auto &state = node->getState(); 57 | return mcbVec( 58 | get<0>(state) + get<0>(action), 59 | get<1>(state) + get<1>(action), 60 | get<2>(state) + get<2>(action) 61 | ); 62 | } 63 | 64 | bool MiCaBo::isGoal(const mcbVec &state) const 65 | { 66 | return state == mcbVec(0,0,0); 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /examples/MissioCanib/missiocanib.h: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | #include "problem.hpp" 3 | 4 | using std::vector; 5 | using namespace AI; 6 | 7 | typedef tuple mcbVec; 8 | 9 | DefClassProblem(MiCaBo, mcbVec) 10 | { 11 | MiCaBo(); 12 | 13 | leafs_list successors(const node_ptr &state) const; 14 | bool isGoal (const mcbVec &state) const; 15 | long H(const node_ptr &node) const; 16 | 17 | private: 18 | bool checkLimit(const int &c) const; 19 | 20 | bool isValid(const mcbVec &succ) const; 21 | mcbVec otherSide(const mcbVec &succ) const; 22 | 23 | long H(const mcbVec &state) const; 24 | 25 | mcbVec apply(const node_ptr &node, const mcbVec &action) const; 26 | 27 | const vector actions = { 28 | mcbVec(-1,-1,-1), mcbVec(-1,0,-1), mcbVec(0,-1,-1), mcbVec(-2,0,-1), mcbVec(0,-2,-1), 29 | mcbVec(1,1,1), mcbVec(1,0,1), mcbVec(0,1,1), mcbVec(2,0,1), mcbVec(0,2,1), 30 | }; 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /examples/RomaniaCities/main.cpp: -------------------------------------------------------------------------------- 1 | #include "romaniacities.h" 2 | #include "search.hpp" 3 | 4 | int main() 5 | { 6 | cout << endl << "City problems: " << endl ; 7 | 8 | auto romania = recursiveBestFirstSearch(RomaniaCities()); 9 | 10 | showRoute(romania); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /examples/RomaniaCities/makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | C-FLAGS= -std=c++1y -c -I./../../ 3 | 4 | all: romaniacities.o main.o 5 | $(CC) main.o romaniacities.o -o cities.out 6 | 7 | romaniacities.o: 8 | $(CC) $(C-FLAGS) romaniacities.cpp -o romaniacities.o 9 | 10 | main.o: 11 | $(CC) $(C-FLAGS) main.cpp -o main.o 12 | 13 | clean: 14 | rm *.o 15 | rm cities.out 16 | -------------------------------------------------------------------------------- /examples/RomaniaCities/romaniacities.cpp: -------------------------------------------------------------------------------- 1 | #include "romaniacities.h" 2 | 3 | DefConstructorProblem(RomaniaCities, "Arad") 4 | { 5 | Node::Maker maker; 6 | auto zerind = maker("Zerind"); 7 | auto oradea = maker("Oradea"); 8 | auto sibiu = maker("Sibiu"); 9 | auto fagaras = maker("Fagaras"); 10 | auto bucharest = maker("Bucharest"); 11 | auto rimincu = maker("Rimnicu Vilcea"); 12 | auto pitesi = maker("Pitesti"); 13 | auto giurgiu = maker("Giurgiu"); 14 | auto craiova = maker("Craiova"); 15 | auto drobeta = maker("Drobeta"); 16 | auto mehadia = maker("Mehadia"); 17 | auto lugoj = maker("Lugoj"); 18 | auto timisoara = maker("Timisoara"); 19 | auto urziceni = maker("Urziceni"); 20 | auto hirsova = maker("Hirsova"); 21 | auto valsui = maker("Valsui"); 22 | auto iasi = maker("Iasi"); 23 | auto neamt = maker("Neamt"); 24 | auto eforie = maker("Eforie"); 25 | 26 | Node::Edge e; 27 | 28 | initial()->connect( e(zerind,75), e(sibiu, 140), e(timisoara, 118) ); 29 | oradea->connect( e(zerind,71), e(sibiu,151) ); 30 | sibiu->connect( e(fagaras,99), e(rimincu, 80) ); 31 | rimincu->connect( e(pitesi, 97), e(craiova,146) ); 32 | lugoj->connect( e(timisoara,111), e(mehadia,70) ); 33 | drobeta->connect( e(mehadia,75), e(craiova,120) ); 34 | pitesi->connect( e(craiova,138), e(bucharest,101) ); 35 | bucharest->connect( e(fagaras,211), e(urziceni, 85), e(giurgiu,90)); 36 | hirsova->connect(e(urziceni,98), e(eforie,86)); 37 | valsui->connect( e(urziceni, 142), e(iasi,92)); 38 | neamt->connect( e(iasi,87) ); 39 | 40 | hTable.emplace(initial(), 366); 41 | hTable.emplace(bucharest, 0); 42 | hTable.emplace(craiova, 160); 43 | hTable.emplace(drobeta, 242); 44 | hTable.emplace(eforie, 161); 45 | hTable.emplace(fagaras, 176); 46 | hTable.emplace(giurgiu, 77); 47 | hTable.emplace(hirsova, 151); 48 | hTable.emplace(iasi, 226); 49 | hTable.emplace(lugoj, 244); 50 | hTable.emplace(mehadia, 241); 51 | hTable.emplace(neamt, 234); 52 | hTable.emplace(oradea, 380); 53 | hTable.emplace(pitesi, 100); 54 | hTable.emplace(rimincu, 193); 55 | hTable.emplace(sibiu, 253); 56 | hTable.emplace(timisoara, 329); 57 | hTable.emplace(urziceni, 80); 58 | hTable.emplace(valsui, 199); 59 | hTable.emplace(zerind, 374); 60 | } 61 | 62 | 63 | bool RomaniaCities::isGoal (const string &city) const 64 | { 65 | return city == "Bucharest"; 66 | } 67 | 68 | long RomaniaCities::F(const node_ptr &n, const long &gn, const long &parent_cost) const 69 | { 70 | return H(n) + gn; 71 | } 72 | 73 | long RomaniaCities::H(const node_ptr &n) const 74 | { 75 | return hTable.at(n); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /examples/RomaniaCities/romaniacities.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "problem.hpp" 4 | 5 | using namespace AI; 6 | using std::string; 7 | 8 | DefClassProblem(RomaniaCities, string) 9 | { 10 | RomaniaCities(); 11 | 12 | bool isGoal (const string &city) const ; 13 | 14 | long F(const node_ptr &n, const long &gn, const long &parent_cost) const; 15 | 16 | long H(const node_ptr &n) const; 17 | 18 | private: 19 | node_type::nodeptr_cost_map hTable; 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /examples/TwoMadeUpProblems/generating.cpp: -------------------------------------------------------------------------------- 1 | #include "generating.h" 2 | 3 | Generating::Generating() 4 | : Problem(0) 5 | { } 6 | 7 | Generating::leafs_list 8 | Generating::successors( const node_ptr &state) const 9 | { 10 | leafs_list eles; 11 | 12 | static int k = 0, j = 0; 13 | 14 | ++k; 15 | ++j; 16 | 17 | if ( k >= 15 ) 18 | return eles; 19 | 20 | for(int i = j; i < j+2; ++i) 21 | eles.push_back(makeNode(state->getState()+i, state)); 22 | 23 | return eles; 24 | } 25 | 26 | bool Generating::isGoal (const int & value) const 27 | { 28 | return ( value == 104 ); 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/TwoMadeUpProblems/generating.h: -------------------------------------------------------------------------------- 1 | 2 | #include "problem.hpp" 3 | 4 | using namespace AI; 5 | 6 | DefClassProblem(Generating, int) 7 | { 8 | Generating(); 9 | 10 | leafs_list successors( const node_ptr &state) const; 11 | 12 | bool isGoal (const int & value) const; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /examples/TwoMadeUpProblems/main.cpp: -------------------------------------------------------------------------------- 1 | #include "treenumber.h" 2 | #include "generating.h" 3 | 4 | #include "search.hpp" 5 | 6 | int main() 7 | { 8 | cout << endl<< "Made-up test problem using class (Generating on the air):" << endl ; 9 | auto testNum = depthFirstGS(Generating()); 10 | showRoute(testNum); 11 | 12 | 13 | cout << endl << "Tree number problem: " << endl ; 14 | auto num = iterativeDeepeningSearch(TreeNumbers()); 15 | showRoute(num); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /examples/TwoMadeUpProblems/makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | C-FLAGS= -std=c++1y -c -I./../../ 3 | 4 | all: generating.o treenumber.o main.o 5 | $(CC) generating.o main.o treenumber.o -o madeup.out 6 | 7 | treenumber.o: 8 | $(CC) $(C-FLAGS) treenumber.cpp -o treenumber.o 9 | 10 | generating.o: 11 | $(CC) $(C-FLAGS) generating.cpp -o generating.o 12 | 13 | main.o: 14 | $(CC) $(C-FLAGS) main.cpp -o main.o 15 | 16 | clean: 17 | rm *.o 18 | rm *.out 19 | -------------------------------------------------------------------------------- /examples/TwoMadeUpProblems/treenumber.cpp: -------------------------------------------------------------------------------- 1 | #include "treenumber.h" 2 | 3 | TreeNumbers::TreeNumbers() 4 | : Problem(10) 5 | { 6 | auto &_40 = getRoot().connect1(40,50); 7 | _40.connect1(22); 8 | _40.connect1(23); 9 | auto &_12 = getRoot().connect1(12); 10 | _12.connect1(6); 11 | _12.connect1(8).connect1(7,4); 12 | _12.connect1(20); 13 | auto &_11 = getRoot().connect1(11,12); 14 | _11.connect1(9); 15 | _11.connect1(25).connect1(26); 16 | } 17 | 18 | bool TreeNumbers::isGoal (const int & value) const { 19 | return ( value == 7 ); 20 | } 21 | 22 | long TreeNumbers::F(const node_ptr &n, const long &gn, const long &parent_cost) const 23 | { return parent_cost + gn; } 24 | -------------------------------------------------------------------------------- /examples/TwoMadeUpProblems/treenumber.h: -------------------------------------------------------------------------------- 1 | #include "problem.hpp" 2 | 3 | using namespace AI; 4 | 5 | // 6 | // ___10__ 7 | // / | \ 8 | // 40 12_ 11 ___ 9 | // / | / | \ \ \ 10 | // 22 23 6 8 20 9 25 11 | // | | 12 | // 7 26 13 | // 14 | 15 | DefClassProblem(TreeNumbers, int) 16 | { 17 | TreeNumbers(); 18 | 19 | bool isGoal (const int & value) const; 20 | 21 | long F(const node_ptr &n, const long &gn, const long &parent_cost) const; 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /examples/makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS= MissioCanib RomaniaCities TwoMadeUpProblems 2 | 3 | all: 4 | for dir in $(SUBDIRS); do \ 5 | $(MAKE) -C $$dir; \ 6 | done 7 | 8 | clean: 9 | for dir in $(SUBDIRS); do \ 10 | $(MAKE) -C $$dir clean; \ 11 | done 12 | 13 | -------------------------------------------------------------------------------- /my_fun_stuffs.hpp: -------------------------------------------------------------------------------- 1 | 2 | template < 3 | typename T, 4 | typename F, 5 | typename Rf = typename std::result_of::type, 6 | typename R = Node::value, Rf>::type> 7 | > 8 | R map(const Node &node, const F &f) 9 | { 10 | R r(f(node.getValue())); 11 | for( const auto &l : node.getLeafs() ) 12 | r.addLeaf(map(*l, f)); 13 | return r; 14 | } 15 | 16 | 17 | template < 18 | typename T, 19 | typename F, 20 | typename Rf = typename std::result_of::type 21 | > 22 | typename std::enable_if::value,void>::type 23 | map(const Node &node, const F &f) 24 | { 25 | f(node.getValue()); 26 | 27 | for( const auto &l : node.getLeafs() ) 28 | map(*l, f); 29 | } 30 | 31 | template < typename T, typename L > 32 | void toSequencial( const Node &n, L &seq ) 33 | { 34 | auto itr = seq.begin(); 35 | map(n, [&](const T &e) { 36 | seq.emplace(itr, e); 37 | itr = seq.end(); 38 | }); 39 | } 40 | 41 | template < typename T, typename F > 42 | void mapOnce( const Node &n, const F &f) 43 | { 44 | std::set*> uns; 45 | mapOnce(n,f,uns); 46 | } 47 | 48 | 49 | template < 50 | typename T, 51 | typename F, 52 | typename Rf = typename std::result_of::type 53 | > 54 | typename std::enable_if::value,void>::type 55 | mapOnce(const Node &node, const F &f, std::set*> &unions) 56 | { 57 | if ( unions.insert((Node*)&node).second == false ) 58 | return; 59 | 60 | f(node.getValue()); 61 | for( auto &l : node.getLeafs() ) 62 | mapOnce(*l, f, unions); 63 | } 64 | 65 | -------------------------------------------------------------------------------- /node.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NODE_HPP 2 | #define NODE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.hpp" 9 | 10 | namespace AI 11 | { 12 | 13 | using std::move; 14 | using std::forward; 15 | using std::make_shared; 16 | using std::shared_ptr; 17 | using std::map; 18 | using Private::NodePtrCompare; 19 | 20 | 21 | template 22 | struct Node : public std::enable_shared_from_this> 23 | { 24 | using state_type = T; 25 | using node_type = Node; 26 | using node_ptr = std::shared_ptr; 27 | using leafs_list = std::vector; 28 | typedef map> nodeptr_cost_map ; 29 | 30 | struct Maker { 31 | template 32 | inline node_ptr operator()(Args... args) const 33 | { return makeNode(forward(args)...); } 34 | 35 | template inline 36 | static node_ptr makeNode(Args... args) 37 | { return make_shared>(forward(args)...); } 38 | }; 39 | 40 | struct Edge { 41 | 42 | Edge() {} 43 | Edge(Edge&&) = default; 44 | 45 | Edge( std::pair &&data ) 46 | : mPath(move(data)) { } 47 | 48 | inline Edge 49 | operator()(const node_ptr &node, const long &gnlength) const 50 | { return Edge(make_pair(node, gnlength)); } 51 | 52 | inline long cost() const 53 | { return mPath.second; } 54 | 55 | inline node_ptr destination() const 56 | { return mPath.first; } 57 | 58 | inline const std::pair &getInner() const 59 | { return mPath; } 60 | 61 | private: 62 | const std::pair mPath; 63 | }; 64 | 65 | 66 | inline Node(T value) 67 | : d_this(make_shared(forward(value))) 68 | { } 69 | 70 | inline Node(T value, const node_ptr &p, const long &c ) 71 | : d_this(make_shared(forward(value))), mParent(p), mCost(c), mDepth(p->depth() + 1) 72 | { } 73 | 74 | Node( const node_ptr &node, const node_ptr &p ) 75 | : Node(node, p, node->cost()) 76 | { } 77 | 78 | template 79 | Node( N node, N p, const long &cost ) 80 | : d_this(node->d_this), mParent(forward(p)), mCost(cost), mDepth(p->depth() + 1) 81 | { } 82 | 83 | inline const node_ptr &parent() const 84 | { return mParent; } 85 | 86 | inline T getState() const 87 | { return d_this->mState; } 88 | 89 | auto edges() const 90 | { return make_pair(d_this->mEdges.begin(), d_this->mEdges.end()); } 91 | 92 | void setCost(const long &f) 93 | { mCost = f; } 94 | 95 | long cost() const 96 | { return mCost; } 97 | 98 | long g() 99 | { 100 | if (!mParent) 101 | return 0; 102 | try { 103 | return mParent->d_this->mEdges.at(this->shared_from_this()); 104 | } catch (const std::out_of_range &ex) { 105 | cout << "sorry! at "<< __func__ << "(n)" << endl; 106 | exit(1); 107 | } 108 | } 109 | 110 | long depth() const 111 | { return mDepth; } 112 | 113 | template 114 | node_type &connect1(K value, const long &cost = 1) 115 | { 116 | auto leaf = Maker::makeNode(forward(value)); 117 | d_this->mEdges.insert(make_pair(leaf, cost)); 118 | return *leaf; 119 | } 120 | 121 | void connect() {} 122 | template < typename... Leafs > 123 | void connect(Edge &&leaf, Leafs... ls) 124 | { 125 | this->connect2(move(leaf)); 126 | this->connect(forward(ls)...); 127 | } 128 | 129 | void connect2(Edge &&leaf) 130 | { 131 | leaf.destination()->d_this->mEdges.insert(make_pair(this->shared_from_this(), leaf.cost())); 132 | d_this->mEdges.insert(move(leaf.getInner())); 133 | } 134 | 135 | int size() const { return d_this->mEdges.size(); } 136 | 137 | private: 138 | struct NodeData { 139 | NodeData(T state) 140 | : mState(forward(state)) {} 141 | 142 | const T mState; 143 | nodeptr_cost_map mEdges; 144 | }; 145 | 146 | private: 147 | long mCost = 0; 148 | const long mDepth = 0; 149 | const node_ptr mParent; 150 | const shared_ptr d_this; 151 | }; 152 | 153 | 154 | template , typename R = shared_ptr > 155 | R makeNode(T value) 156 | { 157 | return E::Maker::makeNode(forward(value)); 158 | } 159 | 160 | template 161 | typename std::enable_if::value, R>::type 162 | makeNode(T node, R parent, const long &c = 1) 163 | { 164 | return Node::Maker::makeNode(forward(node), forward(parent), c); 165 | } 166 | 167 | template 168 | R makeNode(R node, R parent, Rest...args ) 169 | { 170 | return Node::Maker::makeNode(forward(node), forward(parent), forward(args)...); 171 | } 172 | 173 | } 174 | 175 | 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /problem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROBLEM_HPP 2 | #define PROBLEM_HPP 3 | 4 | #include "node.hpp" 5 | 6 | namespace AI 7 | { 8 | 9 | using std::shared_ptr; 10 | using std::make_shared; 11 | 12 | using std::cout; 13 | using std::endl; 14 | 15 | 16 | template 17 | struct Problem 18 | { 19 | using state_type = T; 20 | using node_type = Node; 21 | using node_ptr = typename node_type::node_ptr; 22 | using leafs_list = typename node_type::leafs_list; 23 | 24 | Problem(node_ptr head) 25 | : mTree(move(head)) 26 | { } 27 | 28 | Problem(T head) 29 | : mTree(make_shared(head)) 30 | { } 31 | 32 | inline node_type & getRoot() const 33 | { return *mTree; } 34 | 35 | inline node_ptr initial() const 36 | { return mTree; } 37 | 38 | bool testGoal(const node_ptr &node) const 39 | { 40 | T && val = node->getState(); 41 | getImpl().watch(node); 42 | return getImpl().isGoal(val); 43 | } 44 | 45 | leafs_list successors(const node_ptr &state) const 46 | { 47 | leafs_list leafs; 48 | const auto edgeBoarder = state->edges(); 49 | 50 | for(auto itr = edgeBoarder.first; itr != edgeBoarder.second; ++itr) 51 | { 52 | const node_ptr &node = itr->first; 53 | const long &cost = itr->second; 54 | 55 | leafs.push_back(makeNode(node, state, getImpl().F(node, cost, state->cost()))); 56 | } 57 | 58 | return leafs; 59 | } 60 | 61 | void watch(const node_ptr &node) const 62 | { 63 | cout << "Visited node \"" 64 | << node->getState() 65 | << "\" with cost of " 66 | << node->cost() 67 | << " and depth of " 68 | << node->depth() 69 | << endl; 70 | } 71 | 72 | long F(const node_ptr &n, const long &gn, const long &pcost) const 73 | { return gn; } 74 | 75 | private: 76 | const Impl &getImpl() const 77 | { return *static_cast(this); } 78 | 79 | const node_ptr mTree; 80 | }; 81 | 82 | 83 | 84 | namespace Private 85 | { 86 | 87 | template 88 | struct ProblemMaker : public Problem> 89 | { 90 | 91 | using daddy_type = Problem>; 92 | using node_ptr = typename daddy_type::node_ptr; 93 | 94 | ProblemMaker(T root, T goal, const G &gen ) 95 | : daddy_type(forward(root)), mGoal(makeNode(forward(goal))), mGenerator(gen) 96 | { } 97 | 98 | ProblemMaker(const node_ptr &node, T goal, const G &gen ) 99 | : daddy_type(node), mGoal(makeNode(forward(goal))), mGenerator(gen) 100 | { } 101 | 102 | ProblemMaker(node_ptr node, node_ptr goal, const G &gen ) 103 | : daddy_type(node), mGoal(goal), mGenerator(gen) 104 | { } 105 | 106 | bool isGoal (const T & value) const 107 | { return value == mGoal->getState(); } 108 | 109 | typename Node::leafs_list 110 | successors(const node_ptr &state) const 111 | { return mGenerator(state); } 112 | 113 | private: 114 | const node_ptr mGoal; 115 | const G &mGenerator; 116 | }; 117 | 118 | 119 | } 120 | 121 | using namespace Private; 122 | 123 | template 124 | ProblemMaker makeProblem(T root, T goal, G gen ) 125 | { 126 | return ProblemMaker(forward(root), forward(goal), gen); 127 | } 128 | 129 | template 130 | ProblemMaker makeProblem(const T &graph, const T &goal) 131 | { 132 | G gen; 133 | return ProblemMaker(graph, goal, gen); 134 | } 135 | 136 | 137 | #define MAKE_PROBLEM(ROOT, GOAL, NODE, STATE, GEN) makeProblem(ROOT, GOAL, [&]( \ 138 | const Node::node_ptr & NODE) { \ 139 | typename Node::leafs_list GEN; \ 140 | auto STATE = NODE ->getState(); 141 | 142 | #define END_PROBLEM }) 143 | 144 | #define DefClassProblem(name, type) struct name : public Problem 145 | #define DefConstructorProblem(p, initial) p::p() : Problem(initial) 146 | 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /search.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SEARCH_HPP 2 | #define SEARCH_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include "problem.hpp" 16 | 17 | namespace AI 18 | { 19 | 20 | namespace Private 21 | { 22 | 23 | using std::set; 24 | using std::stack; 25 | using std::deque; 26 | using std::priority_queue; 27 | using std::sort; 28 | using std::min; 29 | using std::max; 30 | using std::less; 31 | 32 | template 33 | struct MyQueue : public deque 34 | { 35 | using deque::deque; 36 | 37 | void push(const T val) 38 | { this->push_back(val); } 39 | 40 | T pop() 41 | { 42 | auto val = this->front(); 43 | this->pop_front(); 44 | return val; 45 | } 46 | }; 47 | 48 | template 49 | struct MyStack : public stack 50 | { 51 | using stack::stack; 52 | 53 | T pop() 54 | { 55 | auto val = this->top(); 56 | stack::pop(); // oops :P watch out the recurisve bug! 57 | return val; 58 | } 59 | }; 60 | 61 | 62 | template < typename C, typename P, typename E = typename C::value_type > 63 | E treeSearch(const P &problem) 64 | { 65 | C fringe; 66 | fringe.push(problem.initial()); 67 | 68 | while(! fringe.empty()) 69 | { 70 | E node = fringe.pop(); 71 | 72 | if ( problem.testGoal(node) ) 73 | return node; 74 | 75 | auto nodes = problem.successors(node); 76 | for(const E &e : nodes) 77 | fringe.push(e); 78 | } 79 | 80 | return problem.initial(); 81 | } 82 | 83 | template < typename C, typename P, typename E = typename C::value_type, typename Compare = NodePtrCompare > 84 | E graphSearch(const P &problem) 85 | { 86 | C fringe; 87 | fringe.push(problem.initial()); 88 | 89 | set explored; 90 | 91 | while(! fringe.empty()) 92 | { 93 | E node = fringe.pop(); 94 | 95 | if ( explored.insert(node).second == false ) 96 | continue; 97 | 98 | if ( problem.testGoal(node) ) 99 | return node; 100 | 101 | auto nodes = problem.successors(node); 102 | for( const E &e : nodes) 103 | fringe.push(e); 104 | } 105 | 106 | return problem.initial(); 107 | } 108 | 109 | 110 | template 111 | struct NodeCostCompare : public less 112 | { 113 | bool operator()(const T &t1, const T &t2) 114 | { return t1->cost() > t2->cost(); } 115 | }; 116 | 117 | template 118 | struct NodeDepthCompare : public less 119 | { 120 | bool operator()(const T &t1, const T &t2) 121 | { return t1->depth() > t2->depth(); } 122 | }; 123 | 124 | template , 126 | typename Parent = priority_queue::container_type, Compare> > 127 | struct MyPriorityQueue : public Parent 128 | { 129 | using typename Parent::priority_queue; 130 | 131 | T pop() 132 | { 133 | T val = this->top(); 134 | Parent::pop(); 135 | return val; 136 | } 137 | }; 138 | 139 | 140 | template 141 | R recursiveDLS(const R &node, P &p, int &&limit) 142 | { 143 | if ( p.testGoal(node) ) 144 | return node; 145 | else if ( limit == 0 ) 146 | return p.initial(); 147 | else { 148 | auto nodes = p.successors(node); 149 | for ( const R &n : nodes ) { 150 | auto result = recursiveDLS(n, p, limit - 1); 151 | if ( result != p.initial() ) 152 | return result; 153 | } 154 | } 155 | return p.initial(); 156 | } 157 | 158 | template 159 | R RBFS(const R &node, P &p, long limit) 160 | { 161 | if ( p.testGoal(node) ) 162 | return node; 163 | 164 | auto nodes = p.successors(node); 165 | if ( !nodes.size() ) 166 | return p.initial(); 167 | 168 | for( const auto &s : nodes ) 169 | s->setCost(max(s->g() + p.H(s), node->cost())); 170 | 171 | while ( true ) 172 | { 173 | sort(nodes.begin(), nodes.end(), [](const R& n1, const R &n2) { return n1->cost() < n2->cost(); }); 174 | 175 | auto best = nodes.at(0); 176 | if ( best->cost() > limit ) 177 | return p.initial(); 178 | 179 | auto alternative = nodes.at(1); 180 | 181 | auto result = RBFS(best, p, min(limit, alternative->cost())); 182 | if ( result != p.initial() ) 183 | return result; 184 | } 185 | } 186 | 187 | } 188 | 189 | using namespace Private; 190 | 191 | template 192 | R breadthFirstTS(P p) 193 | { 194 | return treeSearch>( p ); 195 | } 196 | 197 | template 198 | R depthFirstTS(P p) 199 | { 200 | return treeSearch>( p ); 201 | } 202 | 203 | template 204 | R depthFirstGS(P p) 205 | { 206 | return graphSearch>( p ); 207 | } 208 | 209 | template 210 | R breadthFirstGS(P p) 211 | { 212 | return graphSearch>( p ); 213 | } 214 | 215 | template 216 | R bestFirstTS(P p) 217 | { 218 | return treeSearch>( p ); 219 | } 220 | 221 | template 222 | R bestFirstGS(P p) // abstract can be everything 223 | { 224 | return graphSearch>( p ); 225 | } 226 | 227 | /* Following search strategies have only different f(n) : 228 | * UCS: parent's cost + g(n) 229 | * greedy: g(n) 230 | * greedyBestFirst: h(n) 231 | * A* : h(n) + g(n) 232 | */ 233 | 234 | template 235 | R depthLimitedSearch(P p, int limit) 236 | { 237 | return recursiveDLS(p.initial(), p, move(limit)); 238 | } 239 | 240 | template 241 | R iterativeDeepeningSearch(P p, const int stop = 100) 242 | { 243 | int i = 1; 244 | while ( i != stop ) { 245 | auto result = depthLimitedSearch(p, i++); 246 | if ( result != p.initial() ) 247 | return result; 248 | } 249 | 250 | return p.initial(); 251 | } 252 | 253 | template 254 | R recursiveBestFirstSearch(P p) 255 | { 256 | return RBFS(p.initial(), p, std::numeric_limits::max()); 257 | } 258 | 259 | } 260 | 261 | #endif 262 | --------------------------------------------------------------------------------