home *** CD-ROM | disk | FTP | other *** search
- #include <new.h>
- #include "aosearch.h"
-
- /* AOSEARCH_
-
- The constructor puts the start node on open and sets the number
- of operators to the specified value.
-
- */
-
- AOSEARCH_::AOSEARCH_(AONODE_ *start, int num)
- {
- open.addtohead(*start); // put start node on open
- startnode = start; // needed to print solution afterwards
- num_op = num;
- }
-
-
-
- /* ~AOSEARCH_
-
- We don't make the destructor pure virtual because we can't be sure
- a destructor will be defined in the derived class.
-
- */
-
- AOSEARCH_::~AOSEARCH_()
- {
- }
-
-
-
- /* GENERATE
-
- Calls solve() and if it returns success prints the solution by
- calling print_sol().
-
- */
-
- #ifdef MSC
- extern int no_mem(size_t);
- #else
- extern void no_mem();
- #endif
-
- void AOSEARCH_::generate()
- {
- #ifdef MSC
- _set_new_handler(no_mem);
- #else
- set_new_handler(no_mem);
- #endif
-
- if (!solve())
- {
- puts("No solution found");
- return;
- }
-
- print_sol(startnode);
- }
-
-
-
- /* PRINT_SOL
-
- Traverses the solution path from the start and prints every node
- it finds on its way. When it finds a node of type AND if calls
- itself recursively, passing every successor node in turn.
-
- */
-
- void AOSEARCH_::print_sol(AONODE_ *node)
- {
- AONODE_
- *cur = node;
-
- int
- i,
- num;
-
- while (1)
- {
- if (cur->gettype() == AND)
- {
- num = ((ANDNODE_ *) cur)->getn_nodes();
- for (i = 0; i < num; i++)
- print_sol(((ANDNODE_ *) cur)->getsucc(i)); // recurse
- break;
- }
- else
- {
- cur->display(); // we suppose everything should be printed
- // not just terminal nodes
- if (!(cur = ((ORNODE_ *) cur)->getsucc())) // end of branch
- break;
- }
- }
- }
-
-
-
- /* SOLVABLE
-
- This routine applies a solve labeling procedure to the search tree.
- First, the terminal node that it gets passed is labelled solved. Next,
- the search tree is traversed upwards through all of the node's ancestors
- to see if they can be labelled solved. An AND node may be labelled
- solved iff all of its successors are labelled solved, i.e., n_left
- is 0. An OR node may be labelled solved iff any of its successors is
- labelled solved. Also, when an OR node is labelled solved we make it
- point to its successor that was just labelled solved (an AND node
- already points to all of its successors), so we can find back the
- solution path afterwards.
-
- Solvable() returns 1 if the start node is labelled solved (meaning that
- the search process ends with success), and 0 if not.
-
- */
-
- int AOSEARCH_::solvable(AONODE_ *node)
- {
- AONODE_
- *parent;
-
- node->setsolved(SOLVED);
-
- for ( ; (parent = (AONODE_ *) node->getparent()) != NULL;
- node = (AONODE_ *) node->getparent())
- {
- if (parent->gettype() == AND) // parent is AND node
- {
- parent->decn_left();
- if (parent->getn_left() != 0) // more successors to be solved left
- return(0);
- parent->setsolved(SOLVED);
- }
- else // parent is OR node, so parent is now solved too
- {
- parent->setsolved(SOLVED);
- ((ORNODE_ *)parent)->setsucc(node);
- // make parent point to its successor
- }
- }
- return(!parent);
- }
-
-
-
- /* UNSOLVABLE
-
- This routine applies an unsolvable labeling procedure to the search
- tree. First, the terminal node that it gets passed is labelled
- unsolvable. Next, the search tree is traversed upwards through all of
- the node's ancestors to see if they can be labelled unsolvable. An AND
- node may be labelled unsolvable iff one of its successors is labelled
- unsolvable. An OR node may be labelled unsolvable iff all of its
- successors are labelled unsolvable.
-
- Unsolvable() returns 1 if the start node is labelled unsolvable (meaning
- that the search process ends with failure), and 0 if not.
-
- */
-
- int AOSEARCH_::unsolvable(AONODE_ *node)
- {
- AONODE_
- *parent;
-
- node->setsolved(UNSOLVABLE);
-
- for (parent = (AONODE_ *) node->getparent(); parent;
- parent = (AONODE_ *) parent->getparent())
- {
- if (parent->gettype() == AND) // parent is AND node
- parent->setsolved(UNSOLVABLE); // so it is unsolvable too
- else // OR node
- {
- parent->decn_left();
- if (parent->getn_left() == 0) // no more successors left?
- parent->setsolved(UNSOLVABLE);
- else
- return(0);
- }
- }
- return(!parent);
- }
-
-
-
- /* DELETABLE
-
- This routine determines if a node can be pruned. A node may be
- pruned when itself or one of its ancestors is labelled solved
- or unsolved (depending on what kind of nodes need to be pruned)
- so we traverse the tree upwards through the parent *'s.
-
- */
-
- int AOSEARCH_::deletable(AONODE_ *node, int s)
- {
- AONODE_
- *parent;
-
- for (parent = node; parent; parent = (AONODE_ *) parent->getparent())
- if (parent->getsolved() == s)
- return(1);
- return(0);
- }
-
-
-
- /* PRUNE
-
- This routine prunes nodes from open. This is done using a list
- iterator and calling deletable() for each node.
-
- */
-
- void AOSEARCH_::prune(int s)
- {
- AONODE_
- *node;
- LIST_ITERATOR_
- iter(open);
-
- node = (AONODE_ *)iter.getfirst();
-
- while (node)
- {
- if (deletable(node, s)) // may node be pruned?
- {
- iter.remove(DEALLOC); // remove & destroy node
- node = (AONODE_ *)iter.getitem();
- }
- else
- node = (AONODE_ *)iter.getnext();
- }
- }
-
-
-
- /* SOLVE
-
- This routines implements the actual search engine. It takes the first
- node from OPEN, if OPEN is empty the search process fails. If not, the
- node is moved to CLOSED. Next, it checks if the node is a terminal node.
- If so, it starts the solved labelling procedure, if this procedure returns
- 1 (meaning that the start node is labelled solved) the search process ends
- with success, if not, prune() is called to prune all nodes on open that
- are labelled solved or that have a solved ancestor.
-
- Next, the node's successors are generated by calling NODE_::expand(). If
- expand() returns NULL this means that the node has no successors and,
- therefore, is unsolvable; then, the unsolvable labelling is called, if it
- returns success (meaning that the start node is labelled unsolvable) the
- search process ends with failure, if not, prune() will be called to prune
- from open all nodes that are labelled unsolvable or that have an
- unsolvable ancestor.
-
- Next, every successor is made to point back to its parent, so that the
- solution path may be traced once the solution is found and to enable
- the solved and unsolvable labelling routines to get to the ancestors of
- a node. Also the value of n_left of the parent node is incremented,
- denoting the number of paths leading downward from it, i.e., denoting the
- number of possibilities left by which the parent can be solved. Each
- successor is passed to add() for further processing.
-
- Solve() returns 1 if the start node is labelled solved and 0 if it's
- labelled unsolvable or if there are no more nodes left on open.
-
- */
-
- int AOSEARCH_::solve()
- {
- AONODE_
- *father,
- *child;
-
- while((father = (AONODE_ *) open.gethead()) != NULL)
- { // get first node from open
- open.remove_head();
- closed.addtohead(*father); // move it to closed
-
- if (is_terminal(*father)) // terminal node
- {
- if (solvable(father)) // apply solved-labeling procedure
- return(1); // start node is solved: success
- prune(SOLVED); // prune solved nodes from open
- continue;
- }
-
- child = (AONODE_ *) father->expand(num_op); // expand node
-
- if (child == NULL) // no successors
- {
- if (unsolvable(father)) // apply unsolvable-labelling procedure
- return(0); // start node unsolvable: fail
- prune(UNSOLVABLE); // prune unsolvable nodes from open
- continue;
- }
-
- while (child)
- {
- child->setparent(father); // so I can remember my daddie
- father->incn_left(); // increase branches pointing downward
-
- if (child->gettype() == AND) // since an AND node serves no
- closed.addtohead(*child); // further purpose (it can't be
- // expanded) it is moved to closed
- /* remember that our terminology for AND and OR nodes differs from
- normal usage */
- add(child); // add node (or in case of an AND node:
- // its successor nodes) to open
- child = (AONODE_ *) child->getnext();
- }
- }
- return(0);
- }
-