home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume41 / aisearch / part04 < prev    next >
Encoding:
Text File  |  1994-02-01  |  87.6 KB  |  2,624 lines

  1. Newsgroups: comp.sources.misc
  2. From: peter@obelix.icce.rug.nl (Peter Bouthoorn)
  3. Subject: v41i130:  aisearch - C++ AI search class library, Part04/05
  4. Message-ID: <1994Feb2.040021.10436@sparky.sterling.com>
  5. X-Md4-Signature: 133bcc4f6c9a97276b163c267b667909
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Sterling Software
  8. Date: Wed, 2 Feb 1994 04:00:21 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: peter@obelix.icce.rug.nl (Peter Bouthoorn)
  12. Posting-number: Volume 41, Issue 130
  13. Archive-name: aisearch/part04
  14. Environment: C++ (UNIX, DOS)
  15.  
  16. #! /bin/sh
  17. # This is a shell archive.  Remove anything before this line, then feed it
  18. # into a shell via "sh file" or similar.  To overwrite existing files,
  19. # type "sh file -c".
  20. # Contents:  demos/demo1.cpp demos/demo3.cpp demos/demo4.cpp
  21. #   demos/demo7.cpp doc/search.tex include/list.h include/nodes.h
  22. #   src/bisear/bisearch.cpp src/unisear/gbest.cpp
  23. #   src/unisear/search.cpp
  24. # Wrapped by kent@sparky on Thu Jan 27 22:25:10 1994
  25. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
  26. echo If this archive is complete, you will see the following message:
  27. echo '          "shar: End of archive 4 (of 5)."'
  28. if test -f 'demos/demo1.cpp' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'demos/demo1.cpp'\"
  30. else
  31.   echo shar: Extracting \"'demos/demo1.cpp'\" \(3448 characters\)
  32.   sed "s/^X//" >'demos/demo1.cpp' <<'END_OF_FILE'
  33. X#include "demo1.h"
  34. X
  35. X/*                   PUZZLE_
  36. X
  37. X    Passes the start node, goal node, and number of operators on
  38. X    the DEPTH_GRAPH_'s constructor.
  39. X
  40. X*/
  41. X
  42. XPUZZLE_::PUZZLE_(PNODE_ *start, PNODE_ *goal)
  43. X    :DEPTH_GRAPH_(start, goal, 4)
  44. X{
  45. X}
  46. X
  47. X
  48. X
  49. X/*                    PNODE_
  50. X
  51. X    Creates a new board configuration.
  52. X
  53. X*/ 
  54. X
  55. XPNODE_::PNODE_(OP_USED_ op_used, int empty_x, int empty_y)
  56. X{
  57. X    operator_used = op_used;
  58. X    x = empty_x;
  59. X    y = empty_y;
  60. X}
  61. X
  62. X
  63. X
  64. X/*                       DISPLAY
  65. X
  66. X    Displays the operator applied to get to this state and the
  67. X    coordinates of the empty tile of the current state.
  68. X
  69. X*/ 
  70. X
  71. Xvoid PNODE_::display() const
  72. X{
  73. X    static char
  74. X        *move[] = { "start state",
  75. X                    "left",
  76. X                    "right",
  77. X                    "up",
  78. X                    "down"
  79. X                  };
  80. X
  81. X    printf("%s - empty spot at: [%d] [%d]\n", move[operator_used], y, x);
  82. X}
  83. X
  84. X
  85. X
  86. X/*                       EQUAL.CPP
  87. X
  88. X    Determines if two states, i.e., two PNODE_'s are the same by
  89. X    comparing the set of coordinates of both objects.
  90. X
  91. X*/
  92. X
  93. Xint PNODE_::equal(const VOBJECT_ &other) const
  94. X{
  95. X    return(x == ((const PNODE_ &)other).get_x()
  96. X          && y == ((const PNODE_ &)other).get_y());
  97. X}
  98. X
  99. X
  100. X
  101. X/*                      GET_X.CPP
  102. X
  103. X    Returns the x-coordinate of the position of the empty tile in
  104. X    the current configuration.
  105. X
  106. X*/
  107. X
  108. Xint PNODE_::get_x() const
  109. X{
  110. X    return(x);
  111. X}
  112. X
  113. X
  114. X
  115. X/*                       GET_Y.CPP
  116. X
  117. X    Returns the y-coordinate of the position of the empty tile in
  118. X    the current configuration.
  119. X
  120. X*/
  121. X
  122. Xint PNODE_::get_y() const
  123. X{
  124. X    return(y);
  125. X}
  126. X
  127. X
  128. X
  129. X/*                    DO_OPER.CPP
  130. X
  131. X    Returns a new PNODE_ object, i.e., a new state, by applying the
  132. X    appropriate operator to the current state, or NULL when the
  133. X    operator cannot be applied.
  134. X
  135. X*/
  136. X
  137. XNODE_ *PNODE_::do_operator(int index) const
  138. X{
  139. X    switch(index)
  140. X    {
  141. X        case 0:
  142. X            return(do_left());
  143. X        case 1:
  144. X            return(do_right());
  145. X        case 2:
  146. X            return(do_up());
  147. X    }
  148. X    return(do_down());
  149. X}
  150. X
  151. X
  152. X
  153. X/*                   DO_LEFT
  154. X
  155. X    Creates a new state/node representing the configuration that
  156. X    arises when the empty tile is moved left one position.
  157. X*/
  158. X
  159. XPNODE_ *PNODE_::do_left() const
  160. X{
  161. X    if (!x)
  162. X        return(NULL);    // can't go left any further
  163. X
  164. X    return(new PNODE_(left_used, x - 1, y));
  165. X}
  166. X
  167. X
  168. X
  169. X/*                   DO_RIGHT
  170. X
  171. X    Creates a new state/node representing the configuration that
  172. X    arises when the empty tile is moved right one position.
  173. X
  174. X*/ 
  175. X
  176. XPNODE_ *PNODE_::do_right() const
  177. X{
  178. X    if (x == (4 - 1))
  179. X        return(NULL);
  180. X
  181. X    return(new PNODE_(right_used, x + 1, y));
  182. X}
  183. X
  184. X
  185. X
  186. X/*                        DO_UP
  187. X
  188. X
  189. X    Creates a new state/node representing the configuration that
  190. X    arises when the empty tile is moved up one position.
  191. X
  192. X*/
  193. X
  194. XPNODE_ *PNODE_::do_up() const
  195. X{
  196. X    if (!y)
  197. X        return(NULL);       // can't go up any further
  198. X    return(new PNODE_(up_used, x, y - 1));
  199. X}
  200. X
  201. X
  202. X
  203. X/*                    DO_DOWN
  204. X
  205. X    Creates a new state/node representing the configuration that
  206. X    arises when the empty tile is moved down one position.
  207. X
  208. X*/ 
  209. X
  210. XPNODE_ *PNODE_::do_down() const
  211. X{
  212. X    if (y == (4 - 1))          // can't go down any further
  213. X        return(NULL);
  214. X
  215. X    return(new PNODE_(down_used, x, y + 1));
  216. X}
  217. X
  218. X
  219. Xint main()
  220. X{
  221. X    PUZZLE_
  222. X        puzzle(new PNODE_(none_used, 3, 3), new PNODE_(none_used, 0, 0));
  223. X
  224. X    puzzle.generate();           // start search process
  225. X
  226. X    return(1);
  227. X}
  228. END_OF_FILE
  229.   if test 3448 -ne `wc -c <'demos/demo1.cpp'`; then
  230.     echo shar: \"'demos/demo1.cpp'\" unpacked with wrong size!
  231.   fi
  232.   # end of 'demos/demo1.cpp'
  233. fi
  234. if test -f 'demos/demo3.cpp' -a "${1}" != "-c" ; then 
  235.   echo shar: Will not clobber existing file \"'demos/demo3.cpp'\"
  236. else
  237.   echo shar: Extracting \"'demos/demo3.cpp'\" \(3640 characters\)
  238.   sed "s/^X//" >'demos/demo3.cpp' <<'END_OF_FILE'
  239. X#include "demo3.h"
  240. X
  241. XPUZZLE_::PUZZLE_(PNODE_ *start, PNODE_ *target)
  242. X    :DEPTH_GRAPH_(start, target, 4)
  243. X{
  244. X}
  245. X
  246. X
  247. X/*                 PNODE
  248. X
  249. X    Initializes a PNODE_ object.
  250. X
  251. X*/
  252. X
  253. XPNODE_::PNODE_(const char *b, int empty_x, int empty_y)
  254. X{
  255. X    char
  256. X        *p = *board;
  257. X    int
  258. X        i;
  259. X
  260. X    for (i = 0; i <= 8; i++)
  261. X        *(p + i) = *(b + i);
  262. X
  263. X    x = empty_x;
  264. X    y = empty_y;
  265. X}
  266. X
  267. X
  268. X
  269. X/*                       PNODE_
  270. X
  271. X    Initializes a new configuration using it's 'parent' configuration.
  272. X    First copies the old board and then swaps the two tiles that are
  273. X    on old_x, old_y and new_x, new_t respectively.
  274. X
  275. X*/
  276. X
  277. XPNODE_::PNODE_(const char *b, int old_x, int old_y, int new_x, int new_y)
  278. X{
  279. X    char
  280. X        *p = *board;
  281. X    int
  282. X        i;
  283. X
  284. X    for (i = 0; i <= 8; i++)
  285. X        *(p + i) = *(b + i); 
  286. X
  287. X    board[old_x][old_y] = board[new_x][new_y];
  288. X    board[new_x][new_y] = 0;
  289. X
  290. X    x = new_x;
  291. X    y = new_y;
  292. X}
  293. X
  294. X
  295. X
  296. X/*                    DISPLAY
  297. X
  298. X    Displays a board configuration.
  299. X
  300. X*/
  301. X
  302. Xvoid PNODE_::display() const
  303. X{
  304. X    int
  305. X        row,
  306. X        col;
  307. X
  308. X    for (row = 0; row < 3; row++)
  309. X    {
  310. X        for (col = 0; col < 3; col++)
  311. X            printf("%d ", board[row][col]);
  312. X        putchar('\n');
  313. X    }
  314. X    putchar('\n');
  315. X}
  316. X
  317. X
  318. X
  319. X/*                    EQUAL
  320. X
  321. X    Determines if two nodes, i.e., two board positions are the same.
  322. X    First, the x- and y-coordinates of the empty tile are compared
  323. X    and next, if necessary, the two boards themselves.
  324. X
  325. X*/
  326. X
  327. Xint PNODE_::equal(const VOBJECT_ &other) const
  328. X{
  329. X     if (x != ((const PNODE_ &)other).get_x()
  330. X          && y != ((const PNODE_ &)other).get_y())
  331. X            return(0);
  332. X    return(compare_board(((const PNODE_ &)other).get_board()));
  333. X}
  334. X
  335. X
  336. X
  337. Xconst char *PNODE_::get_board() const
  338. X{
  339. X    return(*board);
  340. X}
  341. X
  342. X
  343. X
  344. Xint PNODE_::get_x() const
  345. X{
  346. X    return(x);
  347. X}
  348. X
  349. X
  350. X
  351. Xint PNODE_::get_y() const
  352. X{
  353. X    return(y);
  354. X}
  355. X
  356. X
  357. X
  358. X/*                   COMP_BOARD
  359. X
  360. X    Compares the current board configuration with another. Could
  361. X    have used memcmp() here.
  362. X
  363. X*/ 
  364. X
  365. Xint PNODE_::compare_board(const char *b) const
  366. X{
  367. X    const char
  368. X        *p = *board;
  369. X    int
  370. X        i;
  371. X
  372. X    for (i = 0; i <= 8; i++)
  373. X        if (*(p + i) != *(b + i))
  374. X            return(0);
  375. X
  376. X    return(1);
  377. X}
  378. X
  379. X
  380. X
  381. X/*                 DO_OPERATOR
  382. X
  383. X    Applies operator n to the current configuration, i.e. it moves
  384. X    the empty tile (by calling one of the do_..() functions) resulting
  385. X    in a new board configuration or NULL if the operator can't be applied.
  386. X
  387. X*/
  388. X
  389. XNODE_ *PNODE_::do_operator(int index) const
  390. X{
  391. X    switch(index)
  392. X    {
  393. X        case 0:
  394. X            return(do_down());
  395. X        case 1:
  396. X            return(do_up());
  397. X        case 2:
  398. X            return(do_right());
  399. X    }
  400. X    return(do_left());
  401. X}
  402. X
  403. X
  404. X
  405. XPNODE_ *PNODE_::do_left() const
  406. X{
  407. X    if (!y)
  408. X        return(NULL);
  409. X
  410. X    return(new PNODE_(*board, x, y, x, y - 1));
  411. X}
  412. X
  413. X
  414. X
  415. XPNODE_ *PNODE_::do_right() const
  416. X{
  417. X    if (y == (2))
  418. X        return(NULL);
  419. X
  420. X    return(new PNODE_(*board, x, y, x, y + 1));
  421. X}
  422. X
  423. X
  424. X
  425. XPNODE_ *PNODE_::do_up() const
  426. X{
  427. X    if (!x)
  428. X        return(NULL);
  429. X
  430. X    return(new PNODE_(*board, x, y, x - 1, y));
  431. X}
  432. X
  433. X
  434. X
  435. XPNODE_ *PNODE_::do_down() const
  436. X{
  437. X    if (x == (2))
  438. X        return(NULL);
  439. X
  440. X    return(new PNODE_(*board, x, y, x + 1, y));
  441. X}
  442. X
  443. X
  444. X
  445. Xint main()
  446. X{
  447. X    char
  448. X        start[3][3] = {
  449. X                        {1, 3, 4},
  450. X                        {8, 0, 2},
  451. X                        {7, 6, 5},
  452. X                      };
  453. X
  454. X    char
  455. X        goal[3][3] = {
  456. X                        {1, 2, 3},
  457. X                        {8, 0, 4},
  458. X                        {7, 6, 5},
  459. X                    };
  460. X
  461. X    PUZZLE_
  462. X        puzzle(new PNODE_(*start, 1, 1), new PNODE_(*goal, 1, 1));
  463. X
  464. X    puzzle.generate();
  465. X    return(1);
  466. X}
  467. END_OF_FILE
  468.   if test 3640 -ne `wc -c <'demos/demo3.cpp'`; then
  469.     echo shar: \"'demos/demo3.cpp'\" unpacked with wrong size!
  470.   fi
  471.   # end of 'demos/demo3.cpp'
  472. fi
  473. if test -f 'demos/demo4.cpp' -a "${1}" != "-c" ; then 
  474.   echo shar: Will not clobber existing file \"'demos/demo4.cpp'\"
  475. else
  476.   echo shar: Extracting \"'demos/demo4.cpp'\" \(4247 characters\)
  477.   sed "s/^X//" >'demos/demo4.cpp' <<'END_OF_FILE'
  478. X#include "demo4.h"
  479. X
  480. XPUZZLE_::PUZZLE_(PNODE_ *start, PNODE_ *target)
  481. X    :BEST_(start, target, 4)
  482. X{
  483. X    goal = target;
  484. X}
  485. X
  486. X
  487. X
  488. XPNODE_::PNODE_(const char *b, int empty_x, int empty_y)
  489. X{
  490. X    char
  491. X        *p = *board;
  492. X    int
  493. X        i;
  494. X
  495. X    for (i = 0; i <= 8; i++)
  496. X        *(p + i) = *(b + i);
  497. X
  498. X    x = empty_x;
  499. X    y = empty_y;
  500. X}
  501. X
  502. X
  503. X
  504. XPNODE_::PNODE_(const char *b, int old_x, int old_y, int new_x, int new_y)
  505. X{
  506. X    char
  507. X        *p = *board;
  508. X    int
  509. X        i;
  510. X
  511. X    for (i = 0; i <= 8; i++)
  512. X        *(p + i) = *(b + i);
  513. X
  514. X    board[old_x][old_y] = board[new_x][new_y];
  515. X    board[new_x][new_y] = 0;
  516. X
  517. X    x = new_x;
  518. X    y = new_y;
  519. X}
  520. X
  521. X
  522. X
  523. Xvoid PNODE_::display() const
  524. X{
  525. X    int
  526. X        row,
  527. X        col;
  528. X
  529. X    for (row = 0; row < 3; row++)
  530. X    {
  531. X        for (col = 0; col < 3; col++)
  532. X            printf("%d ", board[row][col]);
  533. X        putchar('\n');
  534. X    }
  535. X    putchar('\n');
  536. X}
  537. X
  538. X
  539. X
  540. Xint PNODE_::equal(const VOBJECT_ &other) const
  541. X{
  542. X     if (x != ((const PNODE_ &)other).get_x()
  543. X          && y != ((const PNODE_ &)other).get_y())
  544. X            return(0);
  545. X    return(compare_board(((const PNODE_ &)other).get_board()));
  546. X}
  547. X
  548. X
  549. X
  550. Xconst char (*PNODE_::get_board() const)[3]
  551. X{
  552. X    return(board);
  553. X}
  554. X
  555. X
  556. X
  557. Xint PNODE_::get_x() const
  558. X{
  559. X    return(x);
  560. X}
  561. X
  562. X
  563. X
  564. Xint PNODE_::get_y() const
  565. X{
  566. X    return(y);
  567. X}
  568. X
  569. X
  570. X
  571. Xint PNODE_::compare_board(const char bo[3][3]) const
  572. X{
  573. X    const char
  574. X        *p = *board,
  575. X        *b = *bo;
  576. X
  577. X    int
  578. X        i;
  579. X
  580. X    for (i = 0; i <= 8; i++)
  581. X        if (*(p + i) != *(b + i))
  582. X            return(0);
  583. X
  584. X    return(1);
  585. X}
  586. X
  587. X
  588. X
  589. XNODE_ *PNODE_::do_operator(int index) const
  590. X{
  591. X    switch(index)
  592. X    {
  593. X        case 0:
  594. X            return(do_left());
  595. X        case 1:
  596. X            return(do_right());
  597. X        case 2:
  598. X            return(do_up());
  599. X    }
  600. X    return(do_down());
  601. X}
  602. X
  603. X
  604. X
  605. XPNODE_ *PNODE_::do_left() const
  606. X{
  607. X    if (!y)
  608. X        return(NULL);
  609. X
  610. X    return(new PNODE_(*board, x, y, x, y - 1));
  611. X}
  612. X
  613. X
  614. X
  615. XPNODE_ *PNODE_::do_right() const
  616. X{
  617. X    if (y == (2))
  618. X        return(NULL);
  619. X
  620. X    return(new PNODE_(*board, x, y, x, y + 1));
  621. X}
  622. X
  623. X
  624. X
  625. XPNODE_ *PNODE_::do_down() const
  626. X{
  627. X    if (x == (2))
  628. X        return(NULL);
  629. X
  630. X    return(new PNODE_(*board, x, y, x + 1, y));
  631. X}
  632. X
  633. X
  634. X
  635. XPNODE_ *PNODE_::do_up() const
  636. X{
  637. X    if (!x)
  638. X        return(NULL);
  639. X
  640. X    return(new PNODE_(*board, x, y, x - 1, y));
  641. X}
  642. X
  643. X
  644. X/*                  COMPUT_G
  645. X
  646. X    In this problem all arc costs are 1, so we don't have to do any
  647. X    computation in this function.
  648. X
  649. X*/
  650. X
  651. Xint PUZZLE_::compute_g(const NODE_ &)
  652. X{
  653. X    return(1);
  654. X}
  655. X
  656. X
  657. X
  658. X/*                     COMPUT_H
  659. X
  660. X    This function returns the heuristic value associated with each
  661. X    node, by calling totdist(). 
  662. X
  663. X*/
  664. X
  665. Xint PUZZLE_::compute_h(const NODE_ &node)
  666. X{
  667. X    return(totdist(((PNODE_ &)node).get_board(), goal->get_board()));
  668. X}
  669. X
  670. X
  671. X
  672. X/*                  TOTDIST
  673. X
  674. X    Computes the total or Manhatten distance of the tiles in the 
  675. X    current configuration from their 'home squares' (= the ordering
  676. X    of the tiles in the goal configuration). The Manhatten between
  677. X    two squares S1 and S2 is the distance between S1 and S2 in the
  678. X    horizontal direction plus the distance between S1 and S2 in the
  679. X    vertical direction.
  680. X
  681. X*/
  682. X
  683. Xint PUZZLE_::totdist(const char now[3][3], const char target[3][3])
  684. X{
  685. X    int nrow, ncol,
  686. X        trow, tcol,
  687. X        found, tot = 0;
  688. X
  689. X    for (nrow = 0; nrow < 3; nrow++)
  690. X        for (ncol = 0; ncol < 3; ncol++)
  691. X        {
  692. X            found = 0;
  693. X            if (now[nrow][ncol] == 0)       // skip empty tile!
  694. X                continue;
  695. X            for (trow = 0; trow < 3 && !found; trow++)
  696. X                for (tcol = 0; tcol < 3 && !found; tcol++)
  697. X                    if (now[nrow][ncol] == target[trow][tcol])
  698. X                    {
  699. X                        tot += abs(ncol - tcol) + abs(nrow - trow);
  700. X                        found = 1;
  701. X                    }
  702. X        }
  703. X    return(tot);
  704. X}
  705. X
  706. X
  707. X
  708. Xint main()
  709. X{
  710. X    char
  711. X        start[3][3] = {
  712. X                        {2, 1, 6},
  713. X                        {4, 0, 8},
  714. X                        {7, 5, 3},
  715. X                      };
  716. X
  717. X    char
  718. X        goal[3][3] = {
  719. X                        {1, 2, 3},
  720. X                        {8, 0, 4},
  721. X                        {7, 6, 5},
  722. X                    };
  723. X
  724. X    PUZZLE_
  725. X        puzzle(new PNODE_(*start, 1, 1), new PNODE_(*goal, 1, 1));
  726. X
  727. X    puzzle.generate();
  728. X    return(1);
  729. X}
  730. END_OF_FILE
  731.   if test 4247 -ne `wc -c <'demos/demo4.cpp'`; then
  732.     echo shar: \"'demos/demo4.cpp'\" unpacked with wrong size!
  733.   fi
  734.   # end of 'demos/demo4.cpp'
  735. fi
  736. if test -f 'demos/demo7.cpp' -a "${1}" != "-c" ; then 
  737.   echo shar: Will not clobber existing file \"'demos/demo7.cpp'\"
  738. else
  739.   echo shar: Extracting \"'demos/demo7.cpp'\" \(4060 characters\)
  740.   sed "s/^X//" >'demos/demo7.cpp' <<'END_OF_FILE'
  741. X#include "demo7.h"
  742. X
  743. X/*
  744. X
  745. X    First we make some simple DCG-like rules and a small lexicon and store
  746. X    this information in two arrays, rules[] and lexicon[],  using function
  747. X    init().
  748. X*/
  749. X
  750. XITEM_ *init(int number, ITEM_ start, ...)
  751. X{
  752. X    ITEM_
  753. X        *tmp;
  754. X
  755. X    if (!(tmp = (ITEM_ *) malloc(number * sizeof(ITEM_))))
  756. X    {
  757. X        puts("Error: out of memory data");
  758. X        exit(0);
  759. X    }
  760. X    memcpy(tmp, &start, number * sizeof(ITEM_));
  761. X    return(tmp);
  762. X}
  763. X
  764. XRULE_
  765. X    rules[] = {
  766. X                {2, S, init(2, NP, VP)},
  767. X                {1, NP, init(1, SNP)},
  768. X                {2, NP, init(2, DET, N)},
  769. X                {1, VP, init(1, IV)},
  770. X                {2, VP, init(2, TV, NP)},
  771. X                {3, VP, init(3, SV, CN, S)},
  772. X                {VOID, VOID, NULL}
  773. X              };
  774. X
  775. XLEX_
  776. X    lexicon[] = {
  777. X                    {TV, "kills"},
  778. X                    {SV, "thinks"},
  779. X                    {IV, "sleeps"},
  780. X                    {SNP, "john"},
  781. X                    {N, "man"},
  782. X                    {DET, "the"},
  783. X                    {CN, "that"},
  784. X                    {VOID, NULL}
  785. X                };
  786. X
  787. Xchar        /* to print syntactic categories */
  788. X    *table[] = {"void" , "s", "vp", "iv", "tv", "sv", "np", "snp",
  789. X                "n", "det", "cn"};
  790. X
  791. X
  792. X
  793. XPARSE_::PARSE_(char **s, ELEMENT_ *start)
  794. X    : AODEPTH_TREE_(start, 0)
  795. X{
  796. X    string = s;
  797. X    index = 0;
  798. X}
  799. X
  800. X
  801. XELEMENT_::ELEMENT_(ITEM_ it)
  802. X{
  803. X    item = it;
  804. X}
  805. X
  806. X
  807. Xvoid ELEMENT_::display() const
  808. X{
  809. X   printf("%s\n", table[item]);
  810. X}
  811. X
  812. X
  813. X/*                    EQUAL
  814. X
  815. X    We don't really need this function in this class because we are
  816. X    performing a tree search, so we can be sure none of the nodes will
  817. X    be generated twice; also, nodes won't be compared against a goal
  818. X    node. Still, we must define this function because it's pure
  819. X    virtual in NODE_.
  820. X
  821. X*/
  822. X
  823. Xint ELEMENT_::equal(const VOBJECT_ &) const
  824. X{
  825. X    return(0);
  826. X}
  827. X
  828. X
  829. XITEM_ ELEMENT_::get_item() const
  830. X{
  831. X    return(item);
  832. X}
  833. X
  834. X
  835. X/*                    EXPAND
  836. X
  837. X    Expanding a node means in this problem that we must find the syntactic
  838. X    category that the node represents in the database of rules. Since a rule
  839. X    may rewrite the syntactic category to more than one new category we may
  840. X    have to, and most of the time will, produce an AND-node.
  841. X
  842. X*/
  843. X
  844. XNODE_ *ELEMENT_::expand(int ) const
  845. X{
  846. X    AONODE_
  847. X        *tmp = NULL,
  848. X        *start = NULL;
  849. X    int
  850. X        i,
  851. X        d;
  852. X
  853. X    for (i = 0; rules[i].num != VOID; i++)  // find item in rules database
  854. X    {
  855. X        if (rules[i].head == item)
  856. X        {
  857. X            if (rules[i].num == 1)          // create OR node
  858. X                tmp = new ELEMENT_(rules[i].tail[0]);
  859. X            else
  860. X            {
  861. X                tmp = new ANDNODE_();       // create AND node
  862. X/* remember that our terminology of AND and OR nodes differs
  863. X   from normal usage  */
  864. X                for (d = 0; d < rules[i].num; d++)
  865. X                    ((ANDNODE_ *)tmp)->addsucc(new ELEMENT_(rules[i].tail[d]));
  866. X            }
  867. X
  868. X            tmp->setnext(start);
  869. X            start = tmp;
  870. X        }
  871. X    }
  872. X    return(start);
  873. X}
  874. X
  875. X
  876. X/*                  IS_TERMINAL
  877. X
  878. X    To determine if a node may be considered terminal we check if the
  879. X    syntactic item to be parsed, which must of course be terminal itself,
  880. X    matches the word (pointed to by index) in the input string. Therefore,
  881. X    we first locate the syntactic category in the lexicon (if we can't find it
  882. X    there this means the syntactic category isn't terminal) and compare the
  883. X    word that accompanies it with the word in the input string.
  884. X
  885. X*/
  886. X
  887. Xint PARSE_::is_terminal(const AONODE_ &node)
  888. X{
  889. X    int
  890. X        i;
  891. X
  892. X    for (i = 0; lexicon[i].head != VOID; i++)
  893. X        if (lexicon[i].head == ((ELEMENT_ &)node).get_item()
  894. X               && !strcmp(lexicon[i].tail, string[index]))
  895. X        {
  896. X            index++;
  897. X            return(1);
  898. X        }
  899. X
  900. X    return(0);
  901. X}
  902. X
  903. X
  904. Xint main(int argc, char *argv[])
  905. X{
  906. X    if (argc == 1)
  907. X    {
  908. X        printf("Usage: %s <string>\n", argv[0]);
  909. X        exit(0);
  910. X    }
  911. X    PARSE_
  912. X        sentence(++argv, new ELEMENT_(S));
  913. X
  914. X    sentence.generate();
  915. X    return(1);
  916. X}
  917. END_OF_FILE
  918.   if test 4060 -ne `wc -c <'demos/demo7.cpp'`; then
  919.     echo shar: \"'demos/demo7.cpp'\" unpacked with wrong size!
  920.   fi
  921.   # end of 'demos/demo7.cpp'
  922. fi
  923. if test -f 'doc/search.tex' -a "${1}" != "-c" ; then 
  924.   echo shar: Will not clobber existing file \"'doc/search.tex'\"
  925. else
  926.   echo shar: Extracting \"'doc/search.tex'\" \(43426 characters\)
  927.   sed "s/^X//" >'doc/search.tex' <<'END_OF_FILE'
  928. X% NEWCOMMAND FOR \ CHARACTER:
  929. X% \bs
  930. X
  931. X\documentstyle[11pt]{article}
  932. X
  933. X\title{C$^{++}$ Search Class Library}
  934. X
  935. X\author{Peter Bouthoorn}
  936. X\date{3 December 1993}
  937. X
  938. X\newcommand{\bs}{$\backslash$}
  939. X
  940. X\begin{document}
  941. X\maketitle
  942. X\section{Introduction}
  943. X
  944. XOne of our daily activities is solving problems of any kind. AI-research has
  945. Xshown that a lot of these problems can equally well or sometimes more easily be
  946. Xsolved by computer programs. To be able to write such a problem-solving program
  947. Xit is necessary to give an exact description of the problem to be solved and to
  948. Xknow how it can be defined in terms that facilitate the translation of the
  949. Xproblem into a computer program. In this paper we first explain the theory of
  950. Xproblem-solving in AI and next describe an implementation of some of the ideas
  951. Xpresented (the description of the implementation, called the search class
  952. Xlibrary, will be quite rough, because we will concentrate on how the search
  953. Xclass library must be used. For details on the implementation see the comments
  954. Xaccompanying the source code).
  955. X
  956. X\section{Problem representation and search techniques}
  957. X
  958. X\subsection{State space representation and problem reduction}
  959. X
  960. XIn this section, we will describe two methods commonly used in problem
  961. Xrepresentation. As a sample problem the 8-puzzle will be used. The 8-puzzle
  962. Xconsists of 8 numbered, movable tiles set in a 3 X 3 frame. One of the cells of
  963. Xthe frame is always empty, which makes it possible to move the tiles.
  964. X
  965. XA sample 8-puzzle is given in fig.~\ref{figstate}. Consider the problem of
  966. Xtransforming the first configuration into the second one, our goal.
  967. X
  968. X\begin{figure}[htb]
  969. X\centering
  970. X    \begin{tabular}{rrrcrrr}
  971. X            2 & 1 & 6    \hspace{2cm}           & 1 & 2 & 3 \\
  972. X            4 & 0 & 8    \hspace{2cm}           & 8 & 0 & 4 \\
  973. X            7 & 5 & 3    \hspace{2cm}           & 7 & 6 & 5 \\
  974. X    \end{tabular}
  975. X    \caption{The 8-puzzle.}\label{figstate}
  976. X\end{figure}
  977. X
  978. X\noindent
  979. XTo solve this puzzle we try out various moves producing new configurations
  980. Xuntil we produce the goal configuration. To give a more formal
  981. Xdefinition of this problem we say that we are trying to reach a certain {\em
  982. Xgoal\,} configuration starting with an {\em initial} configuration by using
  983. Xsome set of {\em operators\,}. An operator transforms one configuration into
  984. Xanother, in the case of the 8-puzzle it is most natural to think of 4 such
  985. Xoperators, each corresponding to moving the empty tile: move empty tile left,
  986. Xmove empty tile right, move empty tile up, move empty tile down.
  987. X
  988. XWhat we just have done is defining the problem of solving the 8-puzzle in
  989. Xterms of a {\em state space search\,}. In a state space search the object is to
  990. Xreach a certain goal {\em state\,} starting with an initial state. In the case
  991. Xof the 8-puzzle the start and goal state are the configurations given in
  992. Xfig.~\ref{figstate}.
  993. XMore generally we can say that every configuration we produce when trying to
  994. Xsolve the 8-puzzle corresponds to one state in the state space. All these
  995. Xstates together, i.e., {\em all possible\,} configurations make up the state
  996. Xspace. It is possible of course to define the state space without explicitly
  997. Xenumerating all states. Indeed, most of the time this is impossible
  998. Xand the state space will be defined implicitly by providing rules specifying
  999. Xhow each state can be derived from another. The state space may be small as in
  1000. Xthe case of the 8-puzzle, but for most every day problems or other board games
  1001. Xit is quite large (e.g., in chess the total number of possible board configurations,
  1002. Xthis is the total number of possible states, equals roughly $10^{120}$).
  1003. XObviously it would be impossible to explore the entire state space and often
  1004. Xthis is not needed, because we are interested in finding only one solution to
  1005. Xa problem, i.e., only one path leading from the start state to the goal state.
  1006. XThis means that we do not have to search the state space {\em exhaustively\,},
  1007. Xbut a small(er) portion instead. The problem of course is, which portion?
  1008. X
  1009. XBut before we are going to discuss this point it will be helpful to look
  1010. Xsomewhat further at the approach we have described so far. We said that to
  1011. Xsolve a problem it is necessary to represent the problem as a state space
  1012. Xsearch. That is, we define a start state, a goal state and a set of operators
  1013. Xthat transform one state into another. The actual search consists in moving
  1014. Xaround in the state space, looking for a path from the initial state to the
  1015. Xgoal state. In this case the search process proceeds forward because we start
  1016. Xwith the initial state and move towards the goal state. Hence it is called a
  1017. X{\em forward reasoning system\,}. The opposite behaviour is also possible: a
  1018. Xsystem starting the search with the goal state and moving backward to the
  1019. Xinitial state. In this method, often called {\em backchaining\,} we {\em reason
  1020. Xbackward\,} from the goal states. These two techniques can be combined,
  1021. Xresulting in a {\em bidirectional search\,}. In the case of the 8-puzzle it
  1022. Xdoes not make much difference if we move forward or backward, because about the
  1023. Xsame number of paths will be generated in either case, but some problems can be
  1024. Xsolved more efficiently when searching in one direction rather than the other
  1025. X(see Rich (1983), p.58).
  1026. X
  1027. XA different technique, or rather a different way to represent problems, that
  1028. Xhas not been mentioned so far is that of {\em problem reduction\,}. In this
  1029. Xtype of representation each operator used may divide the problem into a set of
  1030. Xsub-problems that each have to be solved seperately. Additionally,
  1031. Xthere may be restrictions on the order in which these sub-problems have to be
  1032. Xsolved\footnote{In the search class library it is assumed that sub-problems
  1033. Xmust be solved in the order in which they are generated.}. The object of problem
  1034. Xreduction is to eventually produce a set of {\em primitive problems\,}\label{primprob}
  1035. Xwhose solutions are regarded as trivial: at this stage the process of dividing
  1036. Xproblems into sub-problems halts.
  1037. X
  1038. XThese two approaches, state space search and problem reduction, are two of the
  1039. Xmost common methods used in problem representation, although variations of
  1040. Xthese approaches, as used in, e.g., game-playing are also possible
  1041. X(see Barr(1981), p.84ff., Ritch(1983), p.113ff., Nilsson(1971), p.137ff).
  1042. X
  1043. X\subsection{Trees and graphs}
  1044. X
  1045. XSo far we have seen that a state space representation consists of the following
  1046. Xcomponents:
  1047. X\begin{itemize}
  1048. X    \item The state descriptions.
  1049. X    \item A start state, describing the situation from which the
  1050. X    problem-solving process may start.
  1051. X    \item A goal state, describing an acceptable solution to the problem.
  1052. X    \item A set of operators describing how to transform one state into
  1053. X    another.
  1054. X\end{itemize}
  1055. X
  1056. X\bigskip\noindent
  1057. XAlso, we said that to solve a problem we do not need to search the entire state
  1058. Xspace but only that part which leads to a solution, i.e., we need to search for
  1059. Xan appropriate operator sequence, transforming the initial state through a
  1060. Xnumber of intermediate states into the goal state. To perform this search
  1061. Xsystematically we need a control strategy that decides which operator to apply
  1062. Xnext during the search. These strategies are commonly represented using
  1063. X{\em trees\,}\label{treeproc}\footnote{See Knuth(1979), p.305ff. for the concept
  1064. Xof trees in computer science.}: construct a tree with the initial state as its
  1065. Xroot, next generate all the offspring of the root by applying all of the
  1066. Xapplicable operators to the initial state, next for every leaf node generate its
  1067. Xsuccessors by applying all of the applicable operators, etc. When these steps
  1068. Xare performed a structure as displayed in fig.~\ref{figtree} structure will
  1069. Xarise.
  1070. X\begin{figure}
  1071. X\begin{center}
  1072. X\unitlength=0.70mm
  1073. X\special{em:linewidth 0.4pt}
  1074. X\linethickness{0.4pt}
  1075. X\begin{picture}(75.00,55.00)
  1076. X\put(10.00,10.00){\circle{0.00}}
  1077. X\put(10.00,10.00){\circle{10.00}}
  1078. X\put(30.00,10.00){\circle{10.00}}
  1079. X\put(50.00,10.00){\circle{10.20}}
  1080. X\put(70.00,10.00){\circle{10.00}}
  1081. X\put(20.00,30.00){\circle{10.00}}
  1082. X\put(60.00,30.00){\circle{10.00}}
  1083. X\put(40.00,50.00){\circle{10.00}}
  1084. X\put(37.00,46.00){\line(-3,-2){17.00}}
  1085. X\put(43.00,46.00){\line(3,-2){17.00}}
  1086. X\put(17.00,26.00){\line(-2,-3){7.33}}
  1087. X\put(23.00,26.00){\line(2,-3){7.33}}
  1088. X\put(57.00,26.00){\line(-2,-3){7.33}}
  1089. X\put(63.00,26.00){\line(2,-3){7.33}}
  1090. X\put(40.00,50.00){\makebox(0,0)[cc]{a}}
  1091. X\put(20.00,30.00){\makebox(0,0)[cc]{b}}
  1092. X\put(60.00,30.00){\makebox(0,0)[cc]{c}}
  1093. X\put(10.00,10.00){\makebox(0,0)[cc]{d}}
  1094. X\put(30.00,10.00){\makebox(0,0)[cc]{e}}
  1095. X\put(50.00,10.00){\makebox(0,0)[cc]{f}}
  1096. X\put(70.00,10.00){\makebox(0,0)[cc]{g}}
  1097. X\end{picture}
  1098. X    \caption{Start of a search tree}\label{figtree}
  1099. X\end{center}
  1100. X\end{figure}
  1101. XIn this representation every leaf node corresponds to a state, with the root
  1102. Xnode representing the initial state. Each operator application is represented by
  1103. Xa connection between two nodes.
  1104. X
  1105. XTrees are a special case of a more general structure called a {\em graph\,}
  1106. X\footnote{See Nilsson(1971), p.22, Barr(1981), p.25/26, Ritch(1983), p.63}.
  1107. XA tree is a graph each of whose nodes has a unique parent (except for the root
  1108. Xnode, which has no parent). Searching a tree is easier than searching a graph,
  1109. Xbecause when a new node is generated in the tree we can be sure it has not
  1110. Xbeen generated before. This is true because every node has only one parent, so
  1111. Xthere cannot be two or more different paths leading to the same node. In a graph,
  1112. Xhowever, nodes usually have more than one parent.  Therefore, when
  1113. Xsearching a graph one should make provisions to deal with these situations. For
  1114. Xexample, in the case of the 8-puzzle the same state may be generated by a
  1115. Xdifferent sequence of operators. That is, the same node may be part of several
  1116. Xpaths, e.g., when we apply operators 'move empty tile left, move empty tile down'
  1117. Xwe produce exactly the same node as in the sequence 'move empty tile down, move
  1118. Xempty tile left'. Continuing the processing of both these nodes (which are
  1119. Xreally the same node) would be redundant and a waste of effort. This can be
  1120. Xavoided at the price of additional bookkeeping. Instead of traversing a tree we
  1121. Xtraverse a {\em directed graph\,}\footnote{Knuth(1979), p.371.}: every time a
  1122. Xnode is generated we examine the set of nodes generated so far to see if this
  1123. Xnode already exists in the graph. If it does, we throw it away (note: see
  1124. Xpage~\pageref{graph-keep} for exceptions to this rule), if not, we add it to
  1125. Xthe graph.
  1126. X
  1127. XThis way we will also avoid a related problem: if we did not check
  1128. Xwhether a node had been generated before, the search process would very likely
  1129. Xend up in a {\em cycle\,}, in which the same set of nodes is generated over and over
  1130. Xagain. For instance, when we apply operator 'move empty tile left', next 'move
  1131. Xempty right' and next move 'empty tile left' etc. to the 8-puzzle, the
  1132. Xproblem-solving process would go on producing the same nodes without end. When
  1133. Xwe modify the search procedure as described above, this situation will never
  1134. Xarise, because we try to look up every node that is generated, before it is
  1135. Xadded to the graph.
  1136. X
  1137. XA special kind of graph is the {\em AND/OR graph\,}\label{andorgraph} that is used in
  1138. Xproblem-solving methods involving problem reduction. In the case of a normal
  1139. Xgraph, each node represents a different alternative state to be chosen next and
  1140. Xthe search process may continue along one of these nodes arbitratily. In a
  1141. Xproblem-reduction representation, however, we also need to deal with operators
  1142. Xthat divide the original problem into a set of sub-problems {\em each\,} of which
  1143. Xneed to be solved, instead of any of them. For example, suppose problem A can be solved
  1144. Xeither by solving problems B and C or by solving problems D and E or by solving
  1145. Xproblem F. This situation is depicted in fig.~\ref{figtree_2}.
  1146. X\begin{figure}
  1147. X\begin{center}
  1148. X\unitlength=0.70mm
  1149. X\special{em:linewidth 0.4pt}
  1150. X\linethickness{0.4pt}
  1151. X\begin{picture}(75.00,55.00)
  1152. X\put(10.00,10.00){\circle{10.00}}
  1153. X\put(25.00,10.00){\circle{10.00}}
  1154. X\put(40.00,10.00){\circle{10.00}}
  1155. X\put(55.00,10.00){\circle{10.00}}
  1156. X\put(70.00,10.00){\circle{10.00}}
  1157. X\put(40.00,50.00){\circle{10.00}}
  1158. X\put(36.00,47.00){\line(-3,-4){24.00}}
  1159. X\put(39.00,45.00){\line(0,-1){30.00}}
  1160. X\put(42.00,45.00){\line(2,-5){12.00}}
  1161. X\put(40.00,50.00){\makebox(0,0)[cc]{a}}
  1162. X\put(10.00,10.00){\makebox(0,0)[cc]{b}}
  1163. X\put(25.00,10.00){\makebox(0,0)[cc]{c}}
  1164. X\put(40.00,10.00){\makebox(0,0)[cc]{d}}
  1165. X\put(55.00,10.00){\makebox(0,0)[cc]{e}}
  1166. X\put(70.00,10.00){\makebox(0,0)[cc]{f}}
  1167. X\put(40.00,33.00){\line(6,1){6.00}}
  1168. X\put(37.00,45.00){\line(-1,-3){10.00}}
  1169. X\put(44.00,47.00){\line(4,-5){25.67}}
  1170. X\put(26.00,35.00){\line(6,-5){7.00}}
  1171. X\end{picture}
  1172. X    \caption{A structure showing alternative sets of subproblems for A.}\label{figtree_2}
  1173. X\end{center}
  1174. X\end{figure}
  1175. XIn fig.~\ref{figtree_2} the nodes which form a set that has to be solved
  1176. Xentirely are indicated by a special mark linking their incoming arcs. It is
  1177. Xusual, however, to introduce some extra nodes into the structure so that each
  1178. Xset containing more than one successor problem is grouped below its own
  1179. Xparent node. With this convention the structure of fig.~\ref{figtree_2}
  1180. Xbecomes as shown in fig.~\ref{figtree_3}. In this figure the added nodes
  1181. Xlabelled N and M serve as exclusive parents for sets \{B,C\} and \{D,E\}, respectively.
  1182. XThis way one can think of N and M and F as {\em OR\,} nodes, because any of them
  1183. Xmay be solved to solve node A. Problem N, however, is reduced to a single
  1184. Xset of sub-problems B and C, and {\em each\,} of these sub-problems must
  1185. Xbe solved to solve N. For this reason nodes B, C, D and E are called {\em AND\,}
  1186. Xnodes. In fig.~\ref{figtree_3} AND nodes are indicated by a mark on their
  1187. Xincoming arcs.
  1188. X\begin{figure}
  1189. X\begin{center}
  1190. X\unitlength=0.70mm
  1191. X\special{em:linewidth 0.4pt}
  1192. X\linethickness{0.4pt}
  1193. X\begin{picture}(80.00,65.00)
  1194. X\put(10.00,10.00){\circle{10.00}}
  1195. X\put(25.00,10.00){\circle{10.00}}
  1196. X\put(40.00,10.00){\circle{10.00}}
  1197. X\put(55.00,10.00){\circle{10.00}}
  1198. X\put(18.00,30.00){\circle{10.00}}
  1199. X\put(48.00,30.00){\circle{10.00}}
  1200. X\put(75.00,30.00){\circle{10.00}}
  1201. X\put(48.00,60.00){\circle{10.00}}
  1202. X\put(48.00,55.00){\line(0,-1){19.00}}
  1203. X\put(10.00,10.00){\makebox(0,0)[cc]{b}}
  1204. X\put(25.00,10.00){\makebox(0,0)[cc]{c}}
  1205. X\put(40.00,10.00){\makebox(0,0)[cc]{d}}
  1206. X\put(55.00,10.00){\makebox(0,0)[cc]{e}}
  1207. X\put(15.00,25.00){\line(-1,-2){5.00}}
  1208. X\put(20.00,25.00){\line(1,-2){5.00}}
  1209. X\put(50.00,25.00){\rule{1.00\unitlength}{0.00\unitlength}}
  1210. X\put(45.00,25.00){\line(-1,-2){5.00}}
  1211. X\put(50.00,25.00){\line(1,-2){5.00}}
  1212. X\put(45.00,56.00){\line(-6,-5){25.00}}
  1213. X\put(51.00,55.00){\line(6,-5){24.00}}
  1214. X\put(48.00,60.00){\makebox(0,0)[cc]{a}}
  1215. X\put(18.00,30.00){\makebox(0,0)[cc]{n}}
  1216. X\put(48.00,30.00){\makebox(0,0)[cc]{m}}
  1217. X\put(75.00,30.00){\makebox(0,0)[cc]{f}}
  1218. X\put(13.00,21.00){\line(1,0){9.00}}
  1219. X\put(43.00,21.00){\line(1,0){10.00}}
  1220. X\end{picture}
  1221. X    \caption{An AND/OR graph}\label{figtree_3}
  1222. X\end{center}
  1223. X\end{figure}
  1224. X
  1225. X\subsection{Basic search methods - depth-first, breadth-first}
  1226. X
  1227. XIn section 2 we described the process of generating a search tree, in this
  1228. Xsection we will give a more precise description of this process.
  1229. X\begin{itemize}
  1230. X    \item A start node is associated with the initial state description.
  1231. X
  1232. X    \item The successors of a node are generated by applying all of the
  1233. X    applicable operators to the state description associated with the node.
  1234. X    We will call this procedure the {\em expansion\,} of a node.
  1235. X
  1236. X    \item Pointers are setup from each successor back to its parent node.
  1237. X    These pointers indicate the solution path in the game tree, leading from
  1238. X    the goal node, once it is finally found, back to the start.
  1239. X
  1240. X    \item Every successor node is checked to see if it is a goal node. When a
  1241. X    goal node has been found the process of expanding nodes finishes and we trace
  1242. X    back the solution path through the pointers.
  1243. X\end{itemize}
  1244. X
  1245. X\bigskip\noindent
  1246. XThese are the basic elements of a problem-solving process, but the order in
  1247. Xwhich nodes are to be expanded is still left open. We may choose, for instance,
  1248. Xto search one entire branch of the tree before examining nodes in the other
  1249. Xbranches. Alternatively, we may decide to expand all nodes that are on the
  1250. Xsame level, in different branches. The first option would result in what is
  1251. Xcalled a {\em depth-first\,} search: the most recently generated node gets
  1252. Xexpanded first. In a {\em breadth-first\,} search, the second option, nodes are
  1253. Xexpanded in the order in which they are generated.
  1254. X
  1255. X\subsection{Finding an optimal solution, uniform-cost search}
  1256. X
  1257. XIt should be noted that for some problems we are not interested in finding
  1258. X{\em any\,} solution, but rather the {\em optimal\,} or {\em best\,} one. What
  1259. X'best' means depends on the problem at hand, but for now we will call a
  1260. Xsolution path optimal if it contains the least possible number of nodes leading
  1261. Xto the goal node. Later on we will refine our definition to include a
  1262. Xdifferent, but related type of problems. In the case of a depth-first search we
  1263. Xcannot guarantee that the best solution will be found. This is because every
  1264. Xbranch is examined seperately, so if the search process finds a goal node in
  1265. Xone branch it will terminate. But it may well be that a better solution is
  1266. Xlocated in a different branch. Breadth-first search on the other hand is
  1267. Xguaranteed to find the shortest path, because it expands all nodes on one
  1268. Xlevel before advancing to the next.
  1269. X
  1270. XFor some problems, however, finding the best solution does not mean finding the
  1271. Xshortest path, but rather the {\em cheapest\,} path. This is true for instance
  1272. Xwhen we need to find the shortest path from one city to another. In this case
  1273. Xthere may be several routes that can be used to get from city A to city B,
  1274. Xvisiting other cities along the way. Now suppose the cities are nodes in a
  1275. Xsearch tree, clearly what we want is not the smallest number of nodes (cities)
  1276. Xthat make up a path from A to B, but the shortest route. To solve problems
  1277. Xlike this one we need to associate {\em costs\,} with the arcs in the tree (in
  1278. Xthis case the costs will represent the distances between the cities). The
  1279. Xobject is to find a path having the least cost.
  1280. X
  1281. XA more general version of the breadth-first method, called the {\em
  1282. Xuniform-cost search method\,} is guaranteed to find a path of minimal cost
  1283. Xfrom the start node to a goal node. Instead of expanding paths of equal length
  1284. Xlike the breadth-first method, this method expands paths of equal cost. To
  1285. Xcompute the cost of a path {\em s\,} to a node {\em n\,} we will use the
  1286. Xfunction {\em g(n)\,}\label{gfunc}. The cost associated with a node will consist
  1287. Xof the cost associated with its parent plus the cost of getting from the parent to
  1288. Xthis node. Using this method to order the set of nodes we are sure the
  1289. Xuniform-cost method expands nodes in order of increasing g(n).
  1290. X
  1291. XOne \label{graph-keep} should note that the graph search technique that we
  1292. Xdescribed earlier must be modified when we are looking for an optimal solution.
  1293. XWe said that during a graph search every node that is generated twice can
  1294. Xsimply be thrown away. If we would use this technique in a uniform-cost
  1295. Xsearch we could never guarantee to find the cheapest path. As said, in a graph
  1296. Xthere may be multiple paths leading to the same node. But each of these has
  1297. Xits own (possibly different) cost. So if we throw away a node without paying attention to this
  1298. Xfact we may be missing a better solution without ever noticing. Therefore,
  1299. Xthe graph search procedure must be modified in the following way: every time
  1300. Xa new node is generated we check whether it already exists in the graph. If not,
  1301. Xwe add it. If it does, we compare the cost of the old node and the cost of the
  1302. Xnewly generated node. If the old node is better (cheaper) nothing has to be
  1303. Xdone, if it is worse we change its cost and direct its pointer to the parent on
  1304. Xthe least costly path that has just been found.
  1305. X
  1306. X\subsection{Blind search vs heuristic search, best-first search}
  1307. X
  1308. XAll of the methods described so far are called {\em blind-search procedures\,} because they
  1309. Xdo not use any specific information about the problem to be solved, i.e, the
  1310. Xsearch process just continues until it happens on a solution: it is not
  1311. Xdirected in some way to the goal. The advantage of blind-search procedures is
  1312. Xthat they are easy to implement and may find a solution quickly for small
  1313. Xproblems. The obvious disadvantage of these methods is that they may be led
  1314. Xastray, expanding a lot of nodes that are not part of the solution path. For
  1315. Xexample, when traversing a tree in depth-first order we dive into the
  1316. Xleft most branch, this is fine if we happen to find a solution there. But suppose
  1317. Xthe goal node is located in a different part of the tree, e.g., at the right most
  1318. Xbranch. In this case we will have searched the entire tree before getting
  1319. Xon the right track. It would have been much better if we had known in advance
  1320. Xwhich way to go. But of course, this is impossible; if we had known this
  1321. Xwe would never have to {\em search\,} for a solution. Still, there may be
  1322. Xsituations where we do not know exactly where to go, but can give an estimate
  1323. Xof how far a node is removed from the goal node and hence determine if it is on
  1324. Xthe (best) solution path. Using this information it is possible to improve the
  1325. Xefficiency of the search process. Here, we have introduced the idea of using a
  1326. X{\em heuristic\,}.
  1327. X
  1328. XA heuristic is a rule of thumb, a technique that improves the efficiency of a
  1329. Xsearch process, possibly by sacrificing claims to completeness
  1330. X[Rich 1983, 35]. This means that, like all rules of thumb, heuristics may lead
  1331. Xthe search in the most promising way, finding a solution quickly, but also
  1332. Xthat they may take a wrong turn (but still leading to the goal) or lead to
  1333. Xdeadends. A good way to use heuristic information is by means of a {\em heuristic
  1334. Xfunction\,} that evaluates every node that is being generated, i.e. that determines
  1335. Xthe goodness or badness of a node. Using a heuristic function it will be
  1336. Xpossible to conduct the search in the most profitable direction, by suggesting
  1337. Xwhich path to follow first when more than one is available.
  1338. X
  1339. XWe will define a heuristic function {\em f(n)\,}\label{ffunc} being the sum of two components
  1340. X{\em g(n)\,} and {\em h(n)\,}:
  1341. X    \[ f(n) = g(n) + h(n) \]
  1342. XFunction g(n) is the same as the one described in section~\ref{gfunc}: a measure of the cost
  1343. Xof getting from the initial state to the current node. The function h(n) is an
  1344. Xestimate of the additional cost of getting from the current node to a goal
  1345. Xstate. Put differently, h(n) is the function in which the real heuristic
  1346. Xknowledge is imbedded. Using f(n) we are able to order the set of nodes waiting
  1347. Xfor expansion, by convention this will be done this in {\em increasing\,} order.
  1348. XAn algorithm can then be used to select the node having the smallest f(n) value
  1349. Xnext for expansion. One of the methods that uses this technique is the
  1350. X{\em best-first search method\,} or {\em $A{^*}$ algorithm\,}.
  1351. X
  1352. XIt is important to keep in mind that most heuristics are imperfect and that,
  1353. Xinevitably, the search process will be affected by this. In general, a search
  1354. Xalgorithm is called {\em admissible\,} if for any graph it terminates in an
  1355. Xoptimal path to a goal whenever a path exists. Using a heuristic function
  1356. Xto conduct the search process we cannot always make this claim because the
  1357. Xbehaviour of the search will depend on how accurately the function evaluates
  1358. Xnodes. If we use a perfect heuristic function we are guaranteed to find an
  1359. Xoptimal solution, but heuristics having this property are hard to find.
  1360. XFurthermore, the difficulty of computing the function's result affects the
  1361. Xtotal computational effort of the search process. Also, it may be less
  1362. Ximportant to find a solution whose cost is absolutely minimal than to find a
  1363. Xsolution of reasonable cost within a reasonable amount of time. In this case
  1364. Xone may prefer a heuristic function that evaluates nodes more accurately in
  1365. Xmost cases, but sometimes overestimates the distance to the goal, thus resulting
  1366. Xin an inadmissible algorithm. Most of the time we need to make this sort of
  1367. Xcompromise: the efficiency of the search process needs to be improved at the
  1368. Xsacrifice of admissibility (see Barr(1981), p.65/66, Nilsson(1971), p.59ff.)
  1369. X
  1370. X
  1371. X\section{The search class library}
  1372. X
  1373. X\subsection{Why C$^{++}$?}
  1374. X
  1375. XOne of the things to be explained will be the reason why we decided to use C$^{++}$
  1376. Xfor programming an AI-type problem while so many specialized AI programming
  1377. Xlanguages are available. For one thing, we wanted to know how much more effort
  1378. Xit would be to use a lower level programming language (lower compared to AI
  1379. Xprogramming languages like Prolog, that is) to program this type of problem.
  1380. XC$^{++}$ seemed excellent for this job because it is based on C, a third
  1381. Xgeneration programming language, and also because it follows the object
  1382. Xoriented paradigm, meaning that it supports a higher level of abstraction, in
  1383. Xthe case of OOP: the combination of procedural and data abstraction. But the
  1384. Xmain reason why we decided to use C$^{++}$ is that it supports {\em inheritance\,}.
  1385. XThis feature makes it possible to easily make use of existing software when
  1386. Xdeveloping new applications. Combined with the possibility to define {\em virtual\,}
  1387. Xfunctions this makes it possible to design foundation classes that are of no
  1388. Xuse in themselves, but can be easily extended for real applications.
  1389. X
  1390. XThe main objective was to seperate the problem-solving process that we
  1391. Xdescribed above and the representation of the problem itself. In chapter 2 we
  1392. Xshowed that a lot of problems can be solved using standard techniques, e.g.,
  1393. Xthe state space representation and search. It seemed useful therefore, to
  1394. Xdevelop some basic routines offering a number of search methods that could
  1395. Xeasily be used when designing problem-solving software. And this is exactly
  1396. Xwhat the foundation classes are for. Each of them implements a particular search
  1397. Xalgorithm while leaving open the exact nature of the problem to be solved. So,
  1398. Xusing these classes it will be possible, just like in, e.g., Prolog, to
  1399. Xconcentrate on the representation of the problem at hand without worrying
  1400. Xabout how it has to be solved. But unlike Prolog the user need not make use
  1401. Xof a fixed search method (in Prolog: depth-first), but may choose one that
  1402. Xsuits the problem best.
  1403. X
  1404. X\subsection{Techniques used, hierarchy of the search classes.}
  1405. X
  1406. XIn this section we describe a basic technique used in the different search
  1407. Xclasses and explain how these classes relate to each other.
  1408. X
  1409. XIn writing the search engine we followed the procedure outlined in
  1410. Xsection~\ref{treeproc} except that we do not build an actual tree-like structure.
  1411. XInstead, we use two lists, called OPEN and CLOSED. OPEN is a list containing nodes
  1412. Xready for expansion and CLOSED a list of those nodes that already have had the
  1413. Xexpansion procedure applied to them. The algorithm forming the search procedure
  1414. Xconsists of the following steps:
  1415. X\begin{enumerate}
  1416. X    \item Put the start node on OPEN.
  1417. X    \item Get the first node from OPEN. If OPEN is empty exit with failure,
  1418. X    otherwise continue.
  1419. X    \item Remove this node from OPEN and put it on CLOSED; call this node
  1420. X    {\em n\,}.
  1421. X    \item Expand node {\em n\,}, generating all of its successors. This is done
  1422. X    by calling function do\_operator() or expand().
  1423. X    \item Provide every successor with a pointer back to {\em n\,} and pass it
  1424. X    to function add() that may or may not put the node on OPEN.
  1425. X    \item Check each successor to see if it is a goal node. If so exit,
  1426. X    otherwise go to 2.\footnote{This step is not done in the bidirectional and
  1427. X    AND/OR search algorithms. They have a different way of determining whether
  1428. X    the problem is solved or not.}
  1429. X\end{enumerate}
  1430. X
  1431. X\bigskip\noindent
  1432. XThe algorithm that we just described can be found in function solve() which is
  1433. Xa member-function of class {\em SEARCH\_\,} and also of class {\em BISEARCH\_\,}
  1434. Xand of class {\em AOSEARCH\_\,}. These three classes are the most important
  1435. Xclasses in the search class hierarchy, each implements basic routines
  1436. Xneeded by different search algorithms, as follows:
  1437. X\begin{itemize}
  1438. X    \item SEARCH\_ : basic class for uni-directional search routines.
  1439. X    \item BISEARCH\_ : basic class for bi-directional search routines.
  1440. X    \item AOSEARCH\_ : basic class for AND/OR search routines.
  1441. X\end{itemize}
  1442. X
  1443. X\bigskip\noindent
  1444. XThe names of these three classes correspond to three different {\em kinds \,}
  1445. Xof search techniques that are offered to the user to solve different kinds
  1446. Xof problems: uni-directional, i.e., normal search, bi-directional search and
  1447. XAND/OR search. However, these classes should never be used for direct derivation,
  1448. Xthey must be thought of as skeleton classes that outline the overall search
  1449. Xmethod. Other classes, derived from these three basic classes, implement the
  1450. Xactual search algorithms, but before we are going to describe these classes we
  1451. Xmust first introduce another important class, class {\em NODE\_,}.
  1452. X
  1453. XClass NODE\_ specifies a general structure that is to be processed by class SEARCH\_
  1454. X(and by class BISEARCH\_). Class NODE\_ is itself derived from class SVOBJECT\_
  1455. X(sortable object), which, in turn, is derived from class VOBJECT\_. Class
  1456. XNODE\_ may be thought of as an abstraction of the nodes in a search tree or,
  1457. Xequivalently, of the states in a state space. When designing problem-solving
  1458. Xsoftware most time will be spent in finding a good representation of these
  1459. Xnodes/states. Once this representation is found it must be turned into a class
  1460. Xthat is derived from class NODE\_ (or from one of its derivatives, depending on
  1461. Xthe search algorithm that is used, see later), like this:
  1462. X\begin{verbatim}
  1463. Xclass PNODE_ : public NODE_
  1464. X{
  1465. X   ...
  1466. X};
  1467. X\end{verbatim}
  1468. X
  1469. XAs said, class SEARCH\_, BISEARCH\_ and AOSEARCH\_ are the most fundamental
  1470. Xsearch classes and it should never be necessary to derive directly from these
  1471. Xclasses, but rather from one of the following classes, each derived from class
  1472. XSEARCH\_:
  1473. X\begin{itemize}
  1474. X    \item DEPTH\_TREE\_ and DEPTH\_GRAPH\_. These two classes implement a
  1475. X    depth-first search, by creating a search tree or graph, respectively.
  1476. X    \item BREADTH\_TREE\_ and BREADTH\_GRAPH\_. These two classes implement a
  1477. X    breadth-first search, by creating a search tree or graph, respectively.
  1478. X    \item UNICOST\_TREE\_ and UNICOST\_GRAPH\_. These two classes implement a
  1479. X    uniform-cost search, by creating a search tree or graph, respectively.
  1480. X    \item BEST\_. This class implements a best-first search.
  1481. X\end{itemize}
  1482. X
  1483. X\bigskip\noindent
  1484. XOr from one of the following classes, derived from class BISEARCH\_:
  1485. X\begin{itemize}
  1486. X    \item BIDEPTH\_TREE\_ and BIDEPTH\_GRAPH\_. These two classes implement a
  1487. X    depth-first bidirectional search, by creating two search trees or graphs,
  1488. X    respectively.
  1489. X    \item BIBREADTH\_TREE\_ and BIBREADTH\_GRAPH\_. These two classes implement
  1490. X    a bidirectional breadth-first search, by creating two search trees or graphs,
  1491. X    respectively.
  1492. X\end{itemize}
  1493. X
  1494. X\bigskip\noindent
  1495. XOr from one of the classes derived from class AOSEARCH\_:
  1496. X\begin{itemize}
  1497. X    \item AODEPTH\_TREE\_. This class implements a depth-first AND/OR search,
  1498. X    by creating a depth-first AND/OR tree.
  1499. X    \item AOBREADTH\_TREE\_. This class implements a breadth-first AND/OR
  1500. X    search, by creating a breadth-first AND/OR tree.
  1501. X\end{itemize}
  1502. X
  1503. X\bigskip\noindent
  1504. XTo make use of any of the search algorithms that the search class library offers
  1505. Xthe user must derive a class from one of the classes above, for instance:
  1506. X\begin{verbatim}
  1507. Xclass PUZZLE_ : public DEPTH_GRAPH_
  1508. X{
  1509. X   ...
  1510. X};
  1511. X\end{verbatim}
  1512. X
  1513. XThe first four sets of classes, DEPTH\_TREE\_, DEPTH\_GRAPH\_, BREADTH\_TREE\_
  1514. Xand BREADTH\_GRAPH\_ and also the the classes derived from BISEARCH\_, i.e.,
  1515. XBIDEPTH\_TREE\_, BIDEPTH\_GRAPH\_, BIBREADTH\_TREE\_ and BIBREADTH\_TREE\_
  1516. Xmust be used in conjuntion with class NODE\_. This means that when performing,
  1517. Xfor instance, a depth-first search the class that is used to represent the nodes
  1518. Xin the search tree must be derived from NODE\_ (see first example above and also
  1519. Xdemo one and demo two).
  1520. X
  1521. XClass UNICOST\_TREE\_, UNICOST\_GRAPH\_ and BEST\_ require the nodes to have
  1522. Xsome special features. They must be used in conjunction with a derivative
  1523. Xof class NODE\_, class UNI\_NODE\_ or class BEST\_NODE\_, respectively (see
  1524. Xdemo four and five for classes that are derived from one of these two).
  1525. X
  1526. XLastly, class AODEPTH\_TREE\_ and AOBREADTH\_TREE\_ must be used in
  1527. Xconjunction with classes ORNODE\_, a derivative from class AONODE\_, which is
  1528. Xderived from class NODE\_ (see demo seven for a class derived from class
  1529. XORNODE\_). Another derivative from class AONODE\_ is class ANDNODE\_, but this
  1530. Xclass should never be used for derivation, its use will be explained later.
  1531. X
  1532. XTo summarize:
  1533. X\begin{itemize}
  1534. X    \item The depth-first and breadth-first search routines (both
  1535. X    uni-directional and bi-directional) must be used in combination with
  1536. X    class NODE\_.
  1537. X    \item The uniform-cost search routines must be used in combination with
  1538. X    class UNI\_NODE\_.
  1539. X    \item The best-first search routine must be used in combination with class
  1540. X    BEST\_NODE\_.
  1541. X    \item The AND/OR search routines must be used in combination with class
  1542. X    ORNODE\_.
  1543. X\end{itemize}
  1544. X
  1545. X\bigskip\noindent
  1546. X
  1547. X\subsection{How to use the search class library.}
  1548. X
  1549. XNow that we have introduced the search classes and have outlined their
  1550. Xhierarchy we will explain how they can and should be used. As said, class
  1551. XSEARCH\_\footnote{we will take this class as an example, what we tell here
  1552. Xapplies also to class BISEARCH\_ and class AOSEARCH\_; important differences
  1553. Xwill be discussed later.} is one of the most important and most basic classes.
  1554. XOne of the tasks of class SEARCH\_ is to keep track of the number of operators
  1555. Xthat may be applied to the nodes (in the expansion procedure). It receives this
  1556. Xinformation through its constructor, therefore, every class that is derived from SEARCH\_
  1557. Xmust call SEARCH\_'s constructor, passing to it the number of operators, as an
  1558. Xinteger. The node representing the initial state and the node representing the
  1559. Xgoal state must also be passed to this constructor, except when deriving from
  1560. Xclass AOSEARCH\_, in this case only the start node and number of operators
  1561. Xshould be passed\footnote{A problem that is to be solved using the problem reduction
  1562. Xrepresentation, i.e., using an AND/OR search algorithm, does not have a goal
  1563. Xstate, because to solve the problem we do not look for a goal state, but need to
  1564. Xdivide the problem into sub-problems that may or may not be solvable.}.
  1565. XBut as we never derive directly from class SEARCH\_ we do not pass this
  1566. Xinformation to SEARCH\_ directly, but through one of its derivatives,
  1567. Xusing the constructor of the derived class. Suppose we build a class called
  1568. XPUZZLE\_, derived from class DEPTH\_GRAPH\_, then this could be a constructor
  1569. Xof PUZZLE\_:
  1570. X\begin{verbatim}
  1571. XPUZZLE_::PUZZLE_(PNODE_ *start, PNODE_ *goal)
  1572. X    :DEPTH_GRAPH_(start, goal, 4)
  1573. X{                // pass start node, goal node and number of
  1574. X}                // of operators to DEPTH_GRAPH_ 's construc-
  1575. X                 // tor (that will pass them on to SEARCH_)
  1576. X\end{verbatim}
  1577. X
  1578. XAs PUZZLE\_ is ultimately derived from SEARCH\_ it must implement all
  1579. Xvirtual functions (in SEARCH\_) that are still left undefined (i.e., that are
  1580. Xnot instantiated by one of SEARCH\_'s derivatives). In the case of the DEPTH\_
  1581. Xand BREADTH\_ classes there are none. But some of the other search classes have
  1582. Xa couple of virtual functions that must implemented by the user. Class UNICOST\_TREE\_
  1583. Xand UNICOST\_GRAPH\_ require the implementation of funcion compute\_g() and class
  1584. XBEST\_ of both this function and of function compute\_h(). Both of these functions
  1585. Xserve to compute a cost associated with a node: compute\_g() computes the cost
  1586. Xof getting from a node's parent to the node itself, i.e. the cost associated
  1587. Xwith the arc connecting both nodes, and compute\_h() computes the
  1588. Xheuristic value of a node (see section~\ref{gfunc} and section~\ref{ffunc}, but
  1589. Xnote that function compute\_g() must only compute the second half of g(n)):
  1590. X\begin{verbatim}
  1591. Xint compute_g(const NODE_ &)   // computes cost of getting from
  1592. X                               // node's parent to node itself
  1593. Xint compute_h(const NODE_ &)   // computes heuristic value of
  1594. X                               // a node
  1595. X\end{verbatim}
  1596. X
  1597. XThe search classes derived from BISEARCH\_ do not have any non-defined virtual
  1598. Xfunctions left. But the last set of classes, AODEPTH\_TREE\_ and AOBREADTH\_TREE\_,
  1599. Xrequire the implementation of function is\_terminal() which checks whether a
  1600. Xnode represents a terminal node:\footnote{a terminal node is a node that
  1601. Xrepresents a primivite problem in a problem reduction presentation, see
  1602. Xsection~\ref{primprob}}
  1603. X\begin{verbatim}
  1604. Xint is_terminal(const AONODE_ &)    // node is terminal node?
  1605. X                                    // 1 : yes, 0 : no
  1606. X\end{verbatim}
  1607. X
  1608. XJust like class SEARCH\_ class NODE\_ also has a number of virtual functions
  1609. Xthat must be implemented by the user. One of the most important of these is
  1610. Xdo\_operator() that is used for node expansion (step 4 in the algorithm).
  1611. X\begin{verbatim}
  1612. XNODE_ *do_operator(int) const   // apply operator n and
  1613. X                                // return new node or NULL
  1614. X\end{verbatim}
  1615. XNote that the object returned by do\_operator() must have been allocated in memory.
  1616. XFunction do\_operator() is called by SEARCH\_::solve(), that passes do\_operator()
  1617. Xan integer representing one of the operators. This way the operators are numbered,
  1618. Xstarting at 0 and ending at number of operators minus 1. If the operator can be
  1619. Xapplied do\_operator() should return a new node (allocated by new), if not, it
  1620. Xshould return NULL. This is one way a node may be expanded. Another possibility
  1621. Xis by use of funtion expand(). This function is similar to do\_operator(), except
  1622. Xthat it returns a linked list of all of its successors, instead of one
  1623. Xsuccessor at a time. This is useful when dealing with problems that do not use
  1624. Xoperators or that have a variable number of operators.
  1625. X\begin{verbatim}
  1626. XNODE_ *expand(int) const  // expand node and return all of
  1627. X                          // its successors in a linked list
  1628. X\end{verbatim}
  1629. XThe linked list that expand() returns must be built using the next-pointer
  1630. Xfield in NODE\_ (NODE\_ *next). An example of how funtion expand() may be used
  1631. Xand how to build the linked list is given in demo five.
  1632. X
  1633. XApart from do\_operator() there are two other functions, which are virtual in
  1634. Xclass NODE\_, that must be implemented. One of these, equal(), tests whether
  1635. Xtwo nodes are the same, it must return 1 if true and 0 if not. The other one,
  1636. Xdisplay() is used to display a node:
  1637. X\begin{verbatim}
  1638. Xint equal(const VOBJECT_ &) const  // nodes are the same node?
  1639. X                                   // 1 : true, 0 : false
  1640. Xvoid display() const               // display the node
  1641. X\end{verbatim}
  1642. XNote that the argument in equal() is VOBJECT\_ and not NODE\_, this is because
  1643. Xequal() is inherited by NODE\_ from VOBJECT\_.
  1644. X
  1645. XUsing these funtions the implementation of user-defined problems should be
  1646. Xstraightforward. Still, it will be helpful to make some remarks concerning the
  1647. Xthe AND/OR search classes. In section~\ref{andorgraph} we explained how an
  1648. XAND/OR graph may be created and we introduced the concept of AND-nodes and
  1649. XOR-nodes. The meaning of these terms is slightly different in the
  1650. Ximplementation of the AND/OR search classes. Here we will call all nodes OR-nodes,
  1651. Xexcept those nodes that connect a set of sub-problems (nodes N and M in
  1652. Xfig.~\ref{figtree_3}), which are to be called AND-nodes. This relates to the
  1653. XAND/OR search classes in the following way. As said, the user-definable objects
  1654. Xthat serve to represent the nodes in the search tree must be derived from
  1655. Xclass ORNODE\_. But now suppose that some node A may be reduced to the set of
  1656. Xsub-problems \{B,C,D\}. Normally we would call B, C and D AND-nodes, but here,
  1657. Xas said, they are called OR-nodes. The next step is to create a node that
  1658. Xconnects nodes B, C and D and this node is called an AND-node. This AND-node
  1659. Xmust created by calling {\bf new ANDNODE\_() } and adding to this node all of its
  1660. Xsuccessors, in this case node B, C and D, by calling addsucc(), like this:
  1661. X\begin{verbatim}
  1662. XANDNODE_ *andnode;
  1663. X
  1664. Xandnode = new ANDNODE_;      // it must be allocated
  1665. Xandnode->addsucc(node_a);
  1666. Xandnode->addsucc(node_b);
  1667. Xandnode->addsucc(node_c);
  1668. X
  1669. Xreturn(andnode);
  1670. X\end{verbatim}
  1671. X
  1672. XWhen the number of successors is known in advance a different possibility is to
  1673. Xpass this number to the constructor of ANDNODE\_ and then using setsucc() to pass
  1674. Xthe successors, like this:
  1675. X\begin{verbatim}
  1676. XAND_NODE_ *andnode;
  1677. X
  1678. Xandnode = new ANDNODE_(3);      // we will add 3 nodes
  1679. Xandnode->setsucc(0, node_a);    // we start counting at 0
  1680. Xandnode->setsucc(1, node_b);
  1681. Xandnode->setsucc(2, node_c);
  1682. X
  1683. Xreturn(andnode);
  1684. X\end{verbatim}
  1685. X
  1686. XThe only problem that may, and most of the time will, arise is that we don't
  1687. Xknow before hand what kind of node will be produced, an AND-node or and OR-node,
  1688. Xso we do not know if we need and AND\_NODE\_ or an OR\_NODE\_ pointer (this is
  1689. Xespecially true when building a linked list of nodes as in expand()). But this
  1690. Xis easily solved when we use a AONODE\_ pointer, because both AND\_NODE\_ and
  1691. XOR\_NODE\_ are derived from this class, and next casting the result if needed.
  1692. XAn example of this technique is given in demo seven.
  1693. X
  1694. XOne thing has not been mentioned yet: how must the search be started? This is
  1695. Xdone by a call to function generate(), a member function of class SEARCH\_. For
  1696. Xexample:
  1697. X\begin{verbatim}
  1698. XPUZZLE_
  1699. X    puzzle;
  1700. X....
  1701. Xpuzzle.generate();      // start looking for a solution
  1702. X\end{verbatim}
  1703. X
  1704. X\subsection{Include and library files.}
  1705. X
  1706. XIn this section we describe which files must be included and which files must
  1707. Xbe linked when developing problem solving software using the search class
  1708. Xlibrary.
  1709. X
  1710. XAll include files needed by the search class library are in directory /include.
  1711. XMost of these are used internally and the only two files the user needs to
  1712. Xconsult are tree.h and graph.h:
  1713. X\begin{itemize}
  1714. X    \item tree.h. Include this file when using one of the tree search
  1715. X    algorithms.
  1716. X    \item graph.h. Include this file when using one of the graph search
  1717. X    algorithms.
  1718. X\end{itemize}
  1719. X
  1720. X\bigskip\noindent
  1721. X
  1722. XLibrary files are located in directory /lib which contains two library files 
  1723. X(only one in UNIX):
  1724. X\begin{itemize}
  1725. X    \item searchs.lib. Library containing all objects needed by the different
  1726. X    search classes, compiled in the small memory model. 
  1727. X    \item searchc.lib. Idem, but compiled in the compact memory model. 
  1728. X\end{itemize}
  1729. X
  1730. X\bigskip\noindent
  1731. X
  1732. X
  1733. X\section{Bibliography}
  1734. X
  1735. XWinston, P.H. (1984), {\em Artificial Intelligence\,} (2nd ed.), London:
  1736. XAddison-Wesley.\\
  1737. XBarr, A., Feigenbaum, E.A. (1983), {\em The Handbook of Artificial
  1738. XIntelligence}, Los Altos: Kaufmann.\\
  1739. XNillson, N.J. (1971), {\em Problem Solving Methods in Artificial
  1740. XIntelligence\,}, New York: McGraw-Hill.\\
  1741. X              (1986), {\em Principles of Artifial Intelligence\,}, Los Altos:
  1742. XKaufmann.\\
  1743. XKnuth, D.E. (1979), {\em The Art of Computer Programs\,} (2nd ed.). London:
  1744. XAddison-Wesley.\\
  1745. XPearl, J. (1984), {\em Heuristics: Intelligent Strategies for Computer Problem
  1746. XSolving\,}, London: Addison-Wesley.\\
  1747. XRich, E. (1983), {\em Artificial Intelligence\,}, New York: McGraw-Hill.
  1748. X
  1749. X\end{document}
  1750. X
  1751. END_OF_FILE
  1752.   if test 43426 -ne `wc -c <'doc/search.tex'`; then
  1753.     echo shar: \"'doc/search.tex'\" unpacked with wrong size!
  1754.   fi
  1755.   # end of 'doc/search.tex'
  1756. fi
  1757. if test -f 'include/list.h' -a "${1}" != "-c" ; then 
  1758.   echo shar: Will not clobber existing file \"'include/list.h'\"
  1759. else
  1760.   echo shar: Extracting \"'include/list.h'\" \(4228 characters\)
  1761.   sed "s/^X//" >'include/list.h' <<'END_OF_FILE'
  1762. X#ifndef _list_H_
  1763. X#define _list_H_
  1764. X
  1765. X#include <stdio.h>
  1766. X#include "object.h"
  1767. X
  1768. X
  1769. X/*                    LIST_NODE_
  1770. X
  1771. X    Class LIST_NODE_ defines objects that make up a linked list, it should
  1772. X    be used in conjuntion with class LIST_. Class LIST_NODE_ stores data
  1773. X    in the form of pointers to VOBJECT_'s. A special feature of this class
  1774. X    is that the data may or may not be deleted when the LIST_NODE_ pointing
  1775. X    to this data gets destroyed. This behaviour depends on the value of
  1776. X    the static variable NODE_::destroy, which is set by class LIST_.
  1777. X
  1778. X*/
  1779. X     
  1780. X#define DEALLOC 0
  1781. X#define NODEALLOC 1
  1782. X
  1783. Xclass LIST_NODE_
  1784. X{
  1785. X    public:
  1786. X        LIST_NODE_();
  1787. X        LIST_NODE_(VOBJECT_ &);
  1788. X        LIST_NODE_(VOBJECT_ &, LIST_NODE_ *, LIST_NODE_ *);
  1789. X        ~LIST_NODE_();
  1790. X        void setdata(VOBJECT_ &);
  1791. X        VOBJECT_ *getdata() const;
  1792. X        void setnext(LIST_NODE_ *);
  1793. X        LIST_NODE_ *getnext() const;
  1794. X        void setprev(LIST_NODE_ *);
  1795. X        LIST_NODE_ *getprev() const;
  1796. X        static int destroy;            // should data be deleted when
  1797. X    private:                // listnode is destroyed?
  1798. X        VOBJECT_ *data;            // pointer to data field
  1799. X        LIST_NODE_ *next,
  1800. X                   *prev;
  1801. X};
  1802. X
  1803. X
  1804. X/*
  1805. X            LIST_
  1806. X
  1807. X    Class LIST_ creates an un-ordered list of (pointers to) VOBJECT_'s.
  1808. X    This means that objects that are to be stored in a LIST_ object must 
  1809. X    derived from class VOBJECT_.
  1810. X
  1811. X    When objects are removed from a list (by calling, e.g, remove_head())
  1812. X    they may be kept or destroyed, i.e., get deallocated. This behaviour 
  1813. X    depends on the value passed to the remove_ and clear() functions, the
  1814. X    default value is NODEALLOC: don't destroy the object.
  1815. X
  1816. X    Similarly, objects may be kept or destroyed when the destructor of the
  1817. X    list is called, this behaviour depends on the value that is initially
  1818. X    passed to LIST_'s constructor, or on the value passed to setdestruct(int):
  1819. X    DEALLOC_ON_DESTRUCT or NO_DEALLOC_ON_DESTRUCT, by default the objects
  1820. X    in the list will get deallocated when LIST_'s destructor is called.
  1821. X
  1822. X*/ 
  1823. X
  1824. X#define DEALLOC_ON_DESTRUCT 0
  1825. X#define NO_DEALLOC_ON_DESTRUCT 1
  1826. X
  1827. Xclass LIST_
  1828. X{
  1829. X    friend class LIST_ITERATOR_;
  1830. X
  1831. X    public:
  1832. X        LIST_(int dstr = DEALLOC_ON_DESTRUCT);
  1833. X              // deallocate objects on destruct, default 
  1834. X        ~LIST_();
  1835. X
  1836. X        void addtohead(VOBJECT_ &);
  1837. X        void addtotail(VOBJECT_ &);
  1838. X
  1839. X        VOBJECT_ *gethead() const;
  1840. X    VOBJECT_ *gettail() const;
  1841. X    int getcount() const;
  1842. X        VOBJECT_ *lookup(VOBJECT_ &);
  1843. X
  1844. X        void remove_head(int destroy = NODEALLOC);
  1845. X               // destroy : DEALLOC = object gets deallocated
  1846. X               // NODEALLOC = don't deallocate object, default
  1847. X        void remove_tail(int destroy = NODEALLOC);
  1848. X        void remove_found(int destroy = NODEALLOC);
  1849. X    void clear(int destroy = NODEALLOC);
  1850. X
  1851. X    void setdestruct(int);          // get value of deallocondestr
  1852. X    int getdestruct() const;    // set value of deallocondestr
  1853. X
  1854. X    protected:         // protected because SORTEDLIST_ needs access
  1855. X    void remove_node(LIST_NODE_ *, int dstr);
  1856. X
  1857. X    LIST_NODE_ *head,
  1858. X                   *tail,
  1859. X                   *found;
  1860. X        int nodecount,
  1861. X        deallocondestr;
  1862. X};
  1863. X
  1864. X
  1865. X
  1866. X/*
  1867. X                  SORTEDLIST_
  1868. X
  1869. X   Class SORTEDLIST_ creates an ordered list of (pointers to) SVOBJECT_ 's.
  1870. X   Therefore, objects to be stored in a SORTEDLIST_ object muct be derived
  1871. X   from class SVOBJECT_. Class SORTEDLIST_ is derived from class LIST_.
  1872. X
  1873. X*/
  1874. X
  1875. Xclass SORTEDLIST_ : public LIST_
  1876. X{
  1877. X    public:
  1878. X        SORTEDLIST_(int dstr = DEALLOC_ON_DESTRUCT);
  1879. X        void insert(SVOBJECT_ &);
  1880. X};
  1881. X
  1882. X
  1883. X
  1884. X/*
  1885. X          LIST_ITERATOR_
  1886. X
  1887. X    Class LIST_ITERATOR_ iterates through the objects stored in a list.
  1888. X    While walking through the list objects may be removed, by calling
  1889. X    remove(), see also list.h! When an object is removed the current
  1890. X    pointer of the list iterator moves on one node (or back one node if it
  1891. X    is located on the last node).
  1892. X
  1893. X*/
  1894. X
  1895. Xclass LIST_ITERATOR_
  1896. X{
  1897. X    public:
  1898. X    LIST_ITERATOR_(LIST_ &);
  1899. X    VOBJECT_ *getfirst();
  1900. X    VOBJECT_ *getlast();
  1901. X    VOBJECT_ *getnext();
  1902. X    VOBJECT_ *getprev();
  1903. X    VOBJECT_ *getitem();
  1904. X    void remove(int destroy = NODEALLOC);
  1905. X        // remove current object and move on one node
  1906. X    private:
  1907. X    LIST_ *mine;
  1908. X    LIST_NODE_ *current;
  1909. X};
  1910. X
  1911. X#endif
  1912. END_OF_FILE
  1913.   if test 4228 -ne `wc -c <'include/list.h'`; then
  1914.     echo shar: \"'include/list.h'\" unpacked with wrong size!
  1915.   fi
  1916.   # end of 'include/list.h'
  1917. fi
  1918. if test -f 'include/nodes.h' -a "${1}" != "-c" ; then 
  1919.   echo shar: Will not clobber existing file \"'include/nodes.h'\"
  1920. else
  1921.   echo shar: Extracting \"'include/nodes.h'\" \(6434 characters\)
  1922.   sed "s/^X//" >'include/nodes.h' <<'END_OF_FILE'
  1923. X#ifndef _nodes_H_
  1924. X#define _nodes_H_
  1925. X
  1926. X#include <stdio.h>
  1927. X#include <stdlib.h>
  1928. X#include "object.h"
  1929. X
  1930. X/*                        NODE_
  1931. X
  1932. X    The base class NODE_ is derived from class SVOBJECT_ (sortable object)
  1933. X    and defines basic states that will be generated during the search process.
  1934. X    In other words, it defines the (abstract, since we're dealing with a base
  1935. X    class) objects that the search space consists of. Every class that is
  1936. X    derived from NODE_ must have the following functions:
  1937. X
  1938. X    do_operator(): generates and returns one successor, i.e., a new state,
  1939. X                   when operator n is applied. Returns NULL when operator n 
  1940. X                   cannot be applied.
  1941. X    or expand():   returns a linked list of successors (the linked list must 
  1942. X                   be built by using the NODE_ *next field). This function
  1943. X                   is an alternative for do_operator(), either one has to be
  1944. X                   implemented! 
  1945. X    equal():       determines if 2 objects are the same, must return 1 if
  1946. X                   true and 0 if false.
  1947. X    display():     displays the object.
  1948. X
  1949. X    Note that the virtual function eval() that is used to determine the order
  1950. X    of objects is not actually used in class NODE_, therefore it just
  1951. X    returns 1 (it can't be pure virtual because is it called in solve()).
  1952. X    For a real usage of this function see class BEST_NODE_ and UNI_NODE_.
  1953. X
  1954. X*/
  1955. X
  1956. Xclass NODE_ : public SVOBJECT_
  1957. X{
  1958. X    public:
  1959. X        NODE_();
  1960. X
  1961. X        virtual NODE_ *do_operator(int) const;
  1962. X    virtual NODE_ *expand(int) const;
  1963. X         // both of these not pure virtual since either may be implemented 
  1964. X    virtual int equal(const VOBJECT_ &) const = 0;
  1965. X        virtual int eval(const SVOBJECT_ &) const;
  1966. X             // not pure virtual since it's only needed in shortest path search
  1967. X    virtual void display() const = 0;
  1968. X
  1969. X    NODE_ *getnext() const;
  1970. X    void setnext(NODE_ *);
  1971. X        void setparent(NODE_ *);
  1972. X        NODE_ *getparent() const;
  1973. X    private:
  1974. X        NODE_ *parent,     // to trace the solution path
  1975. X              *next;
  1976. X};
  1977. X
  1978. X
  1979. X
  1980. X/*                        UNI_NODE_
  1981. X
  1982. X    The base class UNI_NODE_ is derived from class NODE_ and defines
  1983. X    a special kind of NODE_'s: nodes that will be generated during the
  1984. X    search process of a uniform cost search. These nodes will be placed in
  1985. X    an ordered list, the order of 2 nodes is determined by eval() based
  1986. X    on their 'g-values'.
  1987. X
  1988. X    G is a cost associated with the node: it's a measure of the cost
  1989. X    of getting from the start node to the current node. This value is computed
  1990. X    by compute_g().
  1991. X
  1992. X*/
  1993. X
  1994. Xclass UNI_NODE_ : public NODE_
  1995. X{
  1996. X    public:
  1997. X        UNI_NODE_();
  1998. X        int eval(const SVOBJECT_ &) const;
  1999. X        int get_g() const;
  2000. X        void set_g(int);
  2001. X    private:
  2002. X        int g;        // cost of getting from the start node to this node
  2003. X};
  2004. X
  2005. X
  2006. X
  2007. X/*                       BEST_NODE_
  2008. X
  2009. X    The base class BEST_NODE_ is derived from class UNI_NODE_ which in
  2010. X    turn is derived from class NODE_. Class BEST_NODE_  defines
  2011. X    a special kind of NODE_'s: nodes that will be generated during the
  2012. X    search process of a best first search. These nodes will be placed in
  2013. X    an ordered list, the order of 2 nodes is determined by eval() based
  2014. X    on their 'f-values'.
  2015. X
  2016. X    G (see unode.h) and F are costs associated with the node. G is a
  2017. X    measure of the cost of getting from the start node to the current node
  2018. X    and F the sum of G and H, the estimated cost of getting from the current
  2019. X    node to the goal node. These values are computed by compute_g() and
  2020. X    compute_h() respectively.
  2021. X
  2022. X*/
  2023. X
  2024. Xclass BEST_NODE_ : public UNI_NODE_
  2025. X{
  2026. X    public:
  2027. X        BEST_NODE_();
  2028. X        int eval(const SVOBJECT_ &) const;
  2029. X        int get_f() const;
  2030. X        void set_f(int);
  2031. X    private:
  2032. X        int
  2033. X            f;          // f is g + h
  2034. X};
  2035. X
  2036. X
  2037. X
  2038. X/*                       AONODE_
  2039. X
  2040. X    Class AONODE_ defines nodes that will be generated in an AND-OR search
  2041. X    process. It is derived from class NODE_. AONODE_ is a base class for
  2042. X    class ORNODE_ and ANDNODE_ and should never be used for direct derivation.
  2043. X
  2044. X*/
  2045. X#define OR 0
  2046. X#define AND 1
  2047. X#define UNSOLVABLE 0
  2048. X#define SOLVED 1
  2049. X#define DUNNO -1
  2050. X
  2051. Xclass AONODE_ : public NODE_
  2052. X{
  2053. X    public:
  2054. X    AONODE_();
  2055. X    AONODE_(int);
  2056. X    AONODE_(int, int);
  2057. X
  2058. X        void settype(int);
  2059. X        void setsolved(int);
  2060. X        int getsolved() const;
  2061. X        int gettype() const;
  2062. X        int getn_left() const;
  2063. X    void incn_left();
  2064. X        void decn_left();
  2065. X    private:
  2066. X        int type,        // is this an AND node or OR node?
  2067. X            solved,      // is the node labeled SOLVED, UNSOLVED or DUNNO?
  2068. X            n_left;      // number of successors left to be solved (AND node),
  2069. X                         // or: number of successors that may be still be
  2070. X                         // solved (OR node)
  2071. X};
  2072. X
  2073. X
  2074. X/*                     ANDNODE_
  2075. X
  2076. X    Class ANDNODE_ is derived from class AONODE_. It must be used in a 
  2077. X    AND-OR search when a set of sub-problems, i.e, a set of new nodes,
  2078. X    is generated that ALL have to be solved. In this case the user should
  2079. X    create an ANDNODE_, by new ANDNODE_(), and pass every node representing
  2080. X    a sub-problem to this ANDNODE_: ANDNODE_::addsucc(some_ornode)).
  2081. X    Alternatively, an ANDNODE_ may be created by calling the second
  2082. X    constructor: new ANDNODE_(no_of_nodes) and then the successor nodes
  2083. X    may be passed by calling ANDNODE_::setsucc(n, node_n).
  2084. X
  2085. X*/
  2086. X
  2087. Xclass ANDNODE_ : public AONODE_
  2088. X{
  2089. X    public:
  2090. X    ANDNODE_();
  2091. X    ANDNODE_(int no_nodes);
  2092. X    ~ANDNODE_();
  2093. X
  2094. X    void setsucc(int, AONODE_ *);    // set successor n in succlist to...
  2095. X    void addsucc(AONODE_ *);         // add a successor to succlist
  2096. X        int getn_nodes() const;
  2097. X    AONODE_ *getsucc(int) const;     // get successor n from succlist
  2098. X
  2099. X    void display() const;
  2100. X        int equal(const VOBJECT_ &) const;
  2101. X    private:
  2102. X    int n_nodes;                     // number of nodes in succlist
  2103. X    AONODE_ **succlist;         // this node's successors
  2104. X};
  2105. X
  2106. X
  2107. X
  2108. X/*                        ORNODE_
  2109. X
  2110. X    Class ORNODE_ is derived from class AONODE_, which in turn is derived
  2111. X    from class NODE_. It is used in the process of an AND-OR search and
  2112. X    should be used in conjunction with class AOSEARCH_. Nodes that are meant
  2113. X    to be generated in an AND-OR search should be derived from class ORNODE_.
  2114. X
  2115. X*/
  2116. X
  2117. X
  2118. Xclass ORNODE_ : public AONODE_
  2119. X{
  2120. X    public:
  2121. X    ORNODE_();
  2122. X
  2123. X    void setsucc(AONODE_ *);
  2124. X    AONODE_ *getsucc() const;
  2125. X    private:
  2126. X    AONODE_ *succ;            // this node's successor
  2127. X};
  2128. X
  2129. X#endif
  2130. X
  2131. END_OF_FILE
  2132.   if test 6434 -ne `wc -c <'include/nodes.h'`; then
  2133.     echo shar: \"'include/nodes.h'\" unpacked with wrong size!
  2134.   fi
  2135.   # end of 'include/nodes.h'
  2136. fi
  2137. if test -f 'src/bisear/bisearch.cpp' -a "${1}" != "-c" ; then 
  2138.   echo shar: Will not clobber existing file \"'src/bisear/bisearch.cpp'\"
  2139. else
  2140.   echo shar: Extracting \"'src/bisear/bisearch.cpp'\" \(5681 characters\)
  2141.   sed "s/^X//" >'src/bisear/bisearch.cpp' <<'END_OF_FILE'
  2142. X#include <new.h>
  2143. X#include "bisearch.h"
  2144. X
  2145. X/*                      BISEARCH_
  2146. X
  2147. X    The constructor puts the start node on s_open, the goal node on
  2148. X    t_open and sets the number of operators to the specified value.
  2149. X
  2150. X*/
  2151. X
  2152. XBISEARCH_::BISEARCH_(NODE_ *start, NODE_ *goal, int num)
  2153. X{
  2154. X    s_open.addtohead(*start);   // add start node to the head of s_open
  2155. X    t_open.addtohead(*goal);    // add goal node to the head of t_open
  2156. X    num_op = num;
  2157. X}
  2158. X
  2159. X
  2160. X
  2161. X/*                      ~BISEARCH_
  2162. X
  2163. X    We don't make the destructor pure virtual because we can't be sure
  2164. X    a destructor will be defined in the derived class.
  2165. X
  2166. X*/
  2167. X
  2168. XBISEARCH_::~BISEARCH_()
  2169. X{
  2170. X}
  2171. X
  2172. X
  2173. X
  2174. X/*                      GENERATE
  2175. X
  2176. X    Start looking for a solution and print it if found.
  2177. X
  2178. X*/
  2179. X
  2180. X#ifdef MSC
  2181. X    extern int no_mem(size_t);
  2182. X#else
  2183. X    extern void no_mem();
  2184. X#endif
  2185. X
  2186. Xvoid BISEARCH_::generate()
  2187. X{
  2188. X    NODE_
  2189. X        *node,
  2190. X        *s_node,
  2191. X        *t_node;
  2192. X
  2193. X#ifdef MSC
  2194. X    _set_new_handler(no_mem);
  2195. X#else
  2196. X    set_new_handler(no_mem);
  2197. X#endif
  2198. X
  2199. X    if (!(node = bisolve()))
  2200. X    {
  2201. X        puts("No solution found");
  2202. X        return;
  2203. X    }
  2204. X
  2205. X/* solve() stores the node that connects x_open with y_closed, i.e., the node
  2206. X   that is on both of these lists, at the head of x_open and of y_closed. */
  2207. X
  2208. X    s_node = (NODE_ *) s_open.gethead();
  2209. X    t_node = (NODE_ *) t_open.gethead();
  2210. X
  2211. X/* now find out on which list the node is stored: s_open or t_open. */
  2212. X
  2213. X    if (node->equal(*s_node))
  2214. X    {
  2215. X/* first we print the first half of the path. Next we check if s_node
  2216. X   matches the tail node of t_closed, that is the goal node. If it does,
  2217. X   the search paths didn't connect and this means we already printed the
  2218. X   entire solution path. If it doesn't we still need to print the second
  2219. X   half of the solution path, starting with the parent of the first node
  2220. X   on y_closed. */
  2221. X
  2222. X        print_sol(s_node);
  2223. X
  2224. X        if (!s_node->equal(*t_closed.gettail()))
  2225. X            print_sol_2(((NODE_ *) t_closed.gethead())->getparent());
  2226. X              // as we searched backward we need a different printing routine
  2227. X    }
  2228. X    else   // apply the same procedure, but reversed
  2229. X    {
  2230. X        if (!t_node->equal(*s_closed.gettail()))
  2231. X            print_sol(((NODE_ *) s_closed.gethead())->getparent());
  2232. X
  2233. X        print_sol_2(t_node);
  2234. X    }
  2235. X}
  2236. X
  2237. X
  2238. X
  2239. X/*                      PRINT_SOL
  2240. X
  2241. X    Trace back the solution path through the parent *'s. Recursively
  2242. X    prints that part of the solution path that reaches from the node
  2243. X    that connects both search paths back to the start.
  2244. X
  2245. X*/
  2246. X
  2247. Xvoid BISEARCH_::print_sol(NODE_ *sol) const
  2248. X{
  2249. X    if (!sol)
  2250. X        return;
  2251. X    print_sol(sol->getparent());
  2252. X    sol->display();
  2253. X}
  2254. X
  2255. X
  2256. X
  2257. X/*                   PRINT_SOL_2
  2258. X
  2259. X    Prints that part of the solution path that reaches from the node
  2260. X    connecting both search paths to the goal node.
  2261. X
  2262. X*/
  2263. X
  2264. Xvoid BISEARCH_::print_sol_2(NODE_ *sol) const
  2265. X{
  2266. X    while (sol)
  2267. X    {
  2268. X        sol->display();
  2269. X        sol = sol->getparent();
  2270. X    }
  2271. X}
  2272. X
  2273. X
  2274. X
  2275. X/*                     BISOLVE
  2276. X
  2277. X   This routine determines if the search will be continued backward or
  2278. X   forward. If s-open contains fewer nodes than t-open the search will
  2279. X   proceed forward, otherwise backward. If both lists are empty the
  2280. X   process ends with failure.
  2281. X
  2282. X*/
  2283. X
  2284. XNODE_ *BISEARCH_::bisolve()
  2285. X{
  2286. X    NODE_
  2287. X        *node = NULL;
  2288. X
  2289. X    while (node == NULL)
  2290. X    {
  2291. X        if(s_open.gethead() == NULL || t_open.gethead() == NULL)
  2292. X            break;    // no more nodes left in either list
  2293. X
  2294. X        if (s_open.getcount() <= t_open.getcount())
  2295. X            node = solve(&s_open, &s_closed, &t_closed);  // search forward
  2296. X        else
  2297. X            node = solve(&t_open, &t_closed, &s_closed);  // search backward
  2298. X    }
  2299. X    return(node);
  2300. X}
  2301. X
  2302. X
  2303. X
  2304. X/*                      SOLVE
  2305. X
  2306. X    This routines implements the actual search engine. It takes the first
  2307. X    node from xOPEN, if xOPEN is empty the search process fails. If not,
  2308. X    the node is moved to xCLOSED. Next, the node's successors are generated
  2309. X    by calling NODE_::expand(). Then, every successor is made to point
  2310. X    back to its parent, so that the solution path may be traced back once
  2311. X    the solution is found. Next, the routine checks for every successor if
  2312. X    it is also on yClosed, i.e., if it is part of the other path of the
  2313. X    search process. If so, this means that the particular node connects
  2314. X    both paths and the search process halts. Each successor is passed to add()
  2315. X    for further processing, if add() returns 0 this means that the
  2316. X    successor already was on xOPEN and consequently it can be thrown away,
  2317. X    i.e. it gets deallocated.
  2318. X
  2319. X    Solve() return the node that connects both paths if found and NULL
  2320. X    otherwise.
  2321. X
  2322. X*/
  2323. X
  2324. XNODE_ *BISEARCH_::solve(SORTEDLIST_ *x_open, SORTEDLIST_ *x_closed, SORTEDLIST_ *y_closed)
  2325. X{
  2326. X    NODE_
  2327. X        *connect,
  2328. X        *father,
  2329. X        *child;
  2330. X
  2331. X    father = (NODE_ *) x_open->gethead();   // get first node from open
  2332. X    x_open->remove_head();
  2333. X    x_closed->addtohead(*father);           // move it to closed
  2334. X
  2335. X    child = father->expand(num_op);         // expand node
  2336. X    while (child)
  2337. X    {
  2338. X        child->setparent(father);           // sets up solution path
  2339. X
  2340. X        if ((connect = (NODE_ *) y_closed->lookup(*child)) != NULL)
  2341. X        {                                 // here the paths connect
  2342. X            x_open->addtohead(*child);      // we store both of the nodes
  2343. X            y_closed->remove_found();       // at the start of the lists
  2344. X            y_closed->addtohead(*connect);  // so we can easily find them back
  2345. X            return(child);                // return node connecting both paths
  2346. X        }
  2347. X
  2348. X        if (!add(x_open, x_closed, child))
  2349. X            delete(child);
  2350. X        child = child->getnext();
  2351. X    }
  2352. X    return(NULL);
  2353. X}
  2354. END_OF_FILE
  2355.   if test 5681 -ne `wc -c <'src/bisear/bisearch.cpp'`; then
  2356.     echo shar: \"'src/bisear/bisearch.cpp'\" unpacked with wrong size!
  2357.   fi
  2358.   # end of 'src/bisear/bisearch.cpp'
  2359. fi
  2360. if test -f 'src/unisear/gbest.cpp' -a "${1}" != "-c" ; then 
  2361.   echo shar: Will not clobber existing file \"'src/unisear/gbest.cpp'\"
  2362. else
  2363.   echo shar: Extracting \"'src/unisear/gbest.cpp'\" \(3188 characters\)
  2364.   sed "s/^X//" >'src/unisear/gbest.cpp' <<'END_OF_FILE'
  2365. X#include "graph.h"
  2366. X
  2367. X/*            BEST_NODE_
  2368. X
  2369. X    The constructor passes the start node, goal node and the number of
  2370. X    operators to SEARCH_.
  2371. X
  2372. X*/
  2373. X
  2374. XBEST_::BEST_(BEST_NODE_ *start, BEST_NODE_ *goal, int op)
  2375. X    :SEARCH_(start, goal, op)
  2376. X// This way the G- and F-values of the start node won't be computed,
  2377. X// but this is not very problematic.
  2378. X{   
  2379. X}
  2380. X
  2381. X
  2382. X
  2383. X/*                        ADD
  2384. X
  2385. X    This functions examines each node that it is offered and decides
  2386. X    wether or not it should be added to OPEN.
  2387. X    First, it fills in the node's G- and F-values by calling compute_g()
  2388. X    and compute_f(). Next, it tries to lookup this node in OPEN and/or
  2389. X    in CLOSED. If the node is already on OPEN, but it's F-value is worse
  2390. X    than the older node (this is determined by eval()) it can simply be
  2391. X    thrown away, if it's better, the older node will be thrown away
  2392. X    (by remove_found()) and the new node will be added to OPEN
  2393. X    (by insert()). The same goes if a node is found to be already on
  2394. X    CLOSED. A node that is neither on OPEN nor on CLOSED can simply be
  2395. X    added to OPEN (by insert(), which creates an ordered list).
  2396. X
  2397. X*/
  2398. X
  2399. Xint BEST_::add(NODE_ *succ)
  2400. X{
  2401. X    BEST_NODE_
  2402. X    *parent,
  2403. X        *old = NULL,
  2404. X        *bsucc = (BEST_NODE_ *) succ;
  2405. X
  2406. X    int
  2407. X        g;
  2408. X
  2409. X    parent = (BEST_NODE_ *) bsucc->getparent();
  2410. X
  2411. X    g = parent->get_g() + compute_g(*bsucc);
  2412. X       /* a node's g-value is composed of the overall cost so far, i.e, the
  2413. X          the cost of getting from the start node to the parent of this node
  2414. X          and the added cost of getting from the parent to this node. */
  2415. X    bsucc->set_g(g);
  2416. X    bsucc->set_f(g + compute_h(*bsucc));
  2417. X       /* a node's f-value consists of its g-value and the value returned by
  2418. X          the heurisctic function compute_h(). */
  2419. X
  2420. X    if ((old = (BEST_NODE_ *) open.lookup(*succ)) != NULL)
  2421. X    {                         // node already on open
  2422. X        if (bsucc->eval(*old) < 0)            // new node better
  2423. X        {
  2424. X            open.remove_found(DEALLOC);       // remove & destroy old node
  2425. X            open.insert(*bsucc);              // add this node to the graph
  2426. X            return(1);
  2427. X        }
  2428. X        return(0);
  2429. X    }
  2430. X    if ((old = (BEST_NODE_ *) closed.lookup(*succ)) != NULL)
  2431. X    {                       // node already on closed
  2432. X        if (bsucc->eval(*old) < 0)            // new node better
  2433. X        {
  2434. X            closed.remove_found(DEALLOC);     // remove  & destroy old node
  2435. X            open.insert(*bsucc);
  2436. X            return(1);
  2437. X        }
  2438. X        return(0);
  2439. X    }
  2440. X// node neither on open nor on closed
  2441. X    open.insert(*bsucc);
  2442. X    return(1);
  2443. X}
  2444. X/*
  2445. X   A disadvantage of this method is that if a node is found to be
  2446. X   already on CLOSED and if it's better than the old node it will again
  2447. X   be added to OPEN, meaning that it will re-examined: its successors
  2448. X   will be again be generated etc, which may result in a lot of work being
  2449. X   done twice. A better solution that circumvents this problem is described
  2450. X   by Rich, 80/81 (note that in the algorithm that Ritch describes nodes
  2451. X   also have a pointer to their successors beside a pointer to their parent,
  2452. X   meaning that the algorithm we use would have to undergo major changes to
  2453. X   support this solution).
  2454. X*/
  2455. END_OF_FILE
  2456.   if test 3188 -ne `wc -c <'src/unisear/gbest.cpp'`; then
  2457.     echo shar: \"'src/unisear/gbest.cpp'\" unpacked with wrong size!
  2458.   fi
  2459.   # end of 'src/unisear/gbest.cpp'
  2460. fi
  2461. if test -f 'src/unisear/search.cpp' -a "${1}" != "-c" ; then 
  2462.   echo shar: Will not clobber existing file \"'src/unisear/search.cpp'\"
  2463. else
  2464.   echo shar: Extracting \"'src/unisear/search.cpp'\" \(3065 characters\)
  2465.   sed "s/^X//" >'src/unisear/search.cpp' <<'END_OF_FILE'
  2466. X#include <new.h>
  2467. X#include "search.h"
  2468. X
  2469. X/*                      SEARCH_
  2470. X
  2471. X    The constructor puts the start node on open, saves the goal node
  2472. X    and sets the number of operators to the specified value.
  2473. X
  2474. X*/
  2475. X
  2476. XSEARCH_::SEARCH_(NODE_ *start, NODE_ *goal, int op)
  2477. X{
  2478. X    open.addtohead(*start);    // put the start node on open
  2479. X    num_op = op;
  2480. X    goalnode = goal;
  2481. X}
  2482. X
  2483. X
  2484. X
  2485. X/*                      ~SEARCH_
  2486. X
  2487. X    The destructor only deallocates the memory occupied by the goalnode.
  2488. X
  2489. X*/
  2490. X
  2491. XSEARCH_::~SEARCH_()
  2492. X{
  2493. X    delete(goalnode);
  2494. X}
  2495. X
  2496. X
  2497. X
  2498. X/*                    PRINT_SOL
  2499. X
  2500. X    Prints the solution by tracing back through the parent *'s. So, we
  2501. X    recurse a little (well...)
  2502. X
  2503. X*/
  2504. X
  2505. Xvoid SEARCH_::print_sol(NODE_ *sol) const
  2506. X{
  2507. X    if (!sol)
  2508. X        return;
  2509. X    print_sol(sol->getparent());
  2510. X    sol->display();
  2511. X}
  2512. X
  2513. X
  2514. X
  2515. X/*                   GENERATE
  2516. X
  2517. X    Starts the search process by calling solve() and prints the
  2518. X    solution if found by calling print_sol().
  2519. X
  2520. X*/
  2521. X
  2522. X#ifdef MSC
  2523. X    extern int no_mem(size_t);
  2524. X#else
  2525. X    extern void no_mem();
  2526. X#endif
  2527. X
  2528. Xvoid SEARCH_::generate()
  2529. X{
  2530. X    NODE_
  2531. X        *sol;
  2532. X
  2533. X#ifdef MSC
  2534. X    _set_new_handler(no_mem);
  2535. X#else
  2536. X    set_new_handler(no_mem);
  2537. X#endif
  2538. X
  2539. X    if (!(sol = solve()))
  2540. X    {
  2541. X        puts("No solution found");
  2542. X        return;
  2543. X    }
  2544. X    print_sol(sol);
  2545. X}
  2546. X
  2547. X
  2548. X
  2549. X/*                      SOLVE
  2550. X
  2551. X    This routines implements the actual search engine. It takes the first
  2552. X    node from OPEN, if OPEN is empty the search process fails. If not, the
  2553. X    node is moved to CLOSED. Next, the node's successors are generated by
  2554. X    calling NODE_::expand(). Then, every successor is made to point
  2555. X    back to its parent, so that the solution path may be traced back once
  2556. X    the solution is found. Each successor is passed to add() for further
  2557. X    processing, if add() returns 0 this means that the the successor
  2558. X    already was on OPEN and consequently it can be thrown away, i.e. it gets
  2559. X    deallocated.
  2560. X
  2561. X    Solve() returns the goal node if found and NULL otherwise.
  2562. X*/
  2563. X
  2564. XNODE_ *SEARCH_::solve()
  2565. X{
  2566. X    NODE_
  2567. X        *father,
  2568. X        *child,
  2569. X        *goal = NULL;
  2570. X
  2571. X    while((father = (NODE_ *) open.gethead()) != NULL)
  2572. X    {                                          // get first node from open
  2573. X        open.remove_head();
  2574. X        closed.addtohead(*father);             // put it on closed
  2575. X
  2576. X        child = father->expand(num_op);        // expand the node 
  2577. X        while (child)
  2578. X        {
  2579. X            child->setparent(father);          // so I can remember my daddie
  2580. X
  2581. X            if (goalnode->equal(*child))       // found a goal node
  2582. X                goal = (goal == NULL || goal->eval(*child)) < 0 ?
  2583. X                       goal : child;
  2584. X/* We don't stop immediately after finding the goal node, because we may be
  2585. X   looking for the best or cheapest path and a better/cheaper goal node may
  2586. X   still be ahead in the list */
  2587. X            if (!add(child))
  2588. X                delete(child);               // child aready in graph: kill it!
  2589. X            child = child->getnext();        
  2590. X        }
  2591. X        if (goal)
  2592. X            return(goal);
  2593. X    }
  2594. X    return(NULL);
  2595. X}
  2596. X
  2597. END_OF_FILE
  2598.   if test 3065 -ne `wc -c <'src/unisear/search.cpp'`; then
  2599.     echo shar: \"'src/unisear/search.cpp'\" unpacked with wrong size!
  2600.   fi
  2601.   # end of 'src/unisear/search.cpp'
  2602. fi
  2603. echo shar: End of archive 4 \(of 5\).
  2604. cp /dev/null ark4isdone
  2605. MISSING=""
  2606. for I in 1 2 3 4 5 ; do
  2607.     if test ! -f ark${I}isdone ; then
  2608.     MISSING="${MISSING} ${I}"
  2609.     fi
  2610. done
  2611. if test "${MISSING}" = "" ; then
  2612.     echo You have unpacked all 5 archives.
  2613.     rm -f ark[1-9]isdone
  2614. else
  2615.     echo You still must unpack the following archives:
  2616.     echo "        " ${MISSING}
  2617. fi
  2618. exit 0
  2619. exit 0 # Just in case...
  2620.