;;; Extract from TEACH PRBRIVER ;;; Aaron Sloman. Updated April 1996 /* Search for "define :ruleset" Look at rules "start", "done" "undo" "move_man" "check_constraints" */ define instruct(); pr('\nInitially the man, fox, chicken and grain are on the left bank\ \ [man fox chicken grain {boat} ............ ]\ \ and the man can move only one item over at a time. This program works\ out a plan to achieve a goal subject to the constraint that the fox is\ not left alone with the chicken, or the chicken with the grain\ \Type:\ \tgo1(); - to get grain at right and man at left\ OR\ \tgo2(); - to get everything on the right\ OR\ \tgo3([[man isat right][chicken isat left]]);\ \ - or something similar with a different goal [[note the double brackets]]\ \ OR\ \tbye; - to leave poplog\ \ \tWhen prompted with "?" just press RETURN to continue.\n'); enddefine; uses prblib; uses poprulebase; true -> prb_copy_modify; ;;; normally safer false -> prb_walk; ;;; Make this true for more interaction false -> prb_chatty; ;;; Make this true or an integer for tracing false -> prb_show_conditions; ;;; Make true for detailed tracing of ;;; condition testing false -> prb_allrules; ;;; Always run the first applicable rule true -> prb_repeating; ;;; The rules do their own checking true -> prb_pausing; ;;; Make [PAUSE] actions work ;;; Because prb_sortrules is not re-defined, the default strategy of ;;; selecting the first applicable rule will operate. true -> prb_explain_trace; ;;; run [EXPLAIN ...] actions /* DEFINE SOME UTILITIES */ define thing_data() -> data; ;;; Return alphabetically ordered list of database facts ;;; Convenient "canonical" form, for detecting circular moves lvars data; [%prb_foreach([= isat =], procedure(); prb_found endprocedure)%] -> data; syssort(data, procedure(item1,item2) -> boole; lvars item1, item2, boole; alphabefore(front(item1), front(item2)) -> boole endprocedure) -> data enddefine; define display_data; ;;; Prints out a visual display, showing what isat left and right vars x; 'THE WORLD' => ;;; create a list and print it out [% prb_foreach([?x isat left], procedure(); x endprocedure), '.....', prb_foreach([?x isat right], procedure(); x endprocedure) %] ==> enddefine; /* NOW DEFINE THE RULES */ ;;; This rule sets up the database and fires the rest off. ;;; It runs only once and then sets up the ruleset "solve_rules". ;;; So it does not require any further action to stop itself being ;;; re-invoked, as it is not in that ruleset. define :ruleset prb_rules; RULE start ==> ;;; initial state [chicken isat left] [fox isat left] [grain isat left] [man isat left] [plan] [history] ;;; some useful facts [opposite right left] [fox can eat chicken] [chicken can eat grain] ;;; Now the constraints - checked by rule check ;;; first constraint - fail if something can eat something [constraint Eat [[?thing1 isat ?side] [NOT man isat ?side] [?thing1 can eat ?thing2] [?thing2 isat ?side]] [?thing1 can eat ?thing2 GO BACK]] ;;; second constraint, is the current state one that's in the history? [constraint Loop [[state ?state] [history == [= ?state] == ]] ['LOOP found - Was previously in state: ' ?state]] [EXPLAIN 'Setting up "solve" ruleset'] [RESTORE RULES solve_rules] ;;; describe the goal as a list of patterns ;;; set up initial state record in the database [state [apply thing_data]] ;;; display initial state [POP11 display_data()] enddefine; ;;; The next rule is used to complete the book-keeping for different ;;; move actions, and display the result. It is invoked only after ;;; [complete_move] has been added to the database by a move operator define :ruleset solve_rules; RULE complete_move ;;; in solve_rules [complete_move ?move][->>It1] [state ?state][->>It2] ==> [DEL ?It1] ;;; Extend the history - used for checking circularity [PUSH [?move ?state] history] ;;; Extend the plan [PUSH ?move plan] [SAY 'plan is' [popval rev(tl(prb_present([plan ==])))]] ;;; Create up to date state record (in canonical form) [MODIFY ?It2 state [apply thing_data]] ;;; record that this move has been tried in this state [tried ?move ?state] ;;; report move [SAY Trying ?move] ;;; print result [POP11 display_data()] ;;; Next line is simply to pause till user presses RETURN key [PAUSE] [RESTORE RULES check_rules] ;;; the check_rules ruleset is below ;;; The next rule checks whether the problem has been solved, and ;;; if so aborts the program. The goal description is stored in ;;; the database, to make it easy to change, instead of always using the ;;; same goal. RULE done ;;; in solve_rules [goal ?goallist] [ALL ?goallist] ;;; As if the conditions were in this rule. [plan ??plan] ==> [SAY 'Goal state achieved'] [POP11 display_data()] [SAY 'THE SUCCESSFUL PLAN WAS'] [SAY [apply rev ?plan]] [PAUSE] [STOP 'Everything successfully moved over'] ;;; Now the move operators. Because prb_allrules is set ;;; FALSE, only one will be fired on each cycle. (Note the use of ;;; the word quotes in the "WHERE" condition.) vars thing; ;;; used in the WHERE condition. RULE move_thing ;;; in solve_rules [man isat ?place][->>It1] [?thing isat ?place][->>It2] [WHERE thing /== "man"] [OR [opposite ?place ?other][opposite ?other ?place]] [state ?state] [NOT tried [move ?thing] ?state] [NOT history [[move ?thing] =] ==] ;;; not last thing moved ==> [SAY 'trying to move the' ?thing] [MODIFY ?It1 isat ?other] [MODIFY ?It2 isat ?other] [complete_move [move ?thing]] RULE move_man ;;; in solve_rules [man isat ?place][->>It] [OR [opposite ?place ?other][opposite ?other ?place]] [state ?state] [NOT tried [move man] ?state] [NOT history [move man] ==] ;;; man not last thing moved ==> [SAY 'trying to move the man'] [MODIFY ?It isat ?other] [complete_move [move man]] ;;; This must be the last rule. ;;; When there's nothing left to try, try to undo last move RULE goback ;;; in solve_rules [history = ==] ==> [SAY 'no more options - retracing'] [RESTORE RULES backtrack_rules] ;;; defined below enddefine; ;;; Now the backtracking ruleset define :ruleset backtrack_rules; ;;; On failure this rule is triggered to back-track by restoring a ;;; previous state, and re-displaying it. RULE undo ;;; in backtrack_rules [history [?move ?oldstate] ??history] [state =][->>It] ==> [SAY undoing ?move] [MODIFY ?It state ?oldstate] [NOT = isat = ] [POP history] ;;; Remove latest addition to history [POP plan ] ;;; ... and to plan [ADDALL ??oldstate] ;;; Restore the previous [... isat ...] items [POP11 display_data()] [SAY 'Restored previous state'] [SAY 'plan is' [popval rev(tl(prb_present([plan ==])))]] [PAUSE] [RESTORE RULES solve_rules] ;;; solve_rules defined above enddefine; ;;; The next rule checks that an attempted move is legal, and if ;;; not causes back-tracking, by restoring a previous state, ;;; and re-displaying it. Legality is determined by constraints ;;; in the constraints database. define :ruleset check_rules; RULE check_constraints ;;; in check_rules [constraint ?name ?checks ?message] [ALL ?checks] ;;; ( [WHERE prb_allpresent(?checks)] ) ==> [SAY Constraint ?name violated] [SAY ??message] [RESTORE RULES backtrack_rules] ;;; defined above RULE checks_ok ;;; in check_rules ;;; If no constraint violations were detected by previous rules, ;;; restore normal rules to continue searching for a solution. ==> [RESTORE RULES solve_rules] enddefine; ;;; Uncomment the following for selective tracing of rules ;;;prb_trace([goback complete_move undo done]); ;;; Set up goal state. This will be initial database item. vars goal_state = [goal [[man isat right] [fox isat right] [chicken isat right] [grain isat right]]]; 2 -> prb_chatty; define go1(); 'Trying to achieve [goal [[man isat left] [grain isat right]] ]'=> prb_run(prb_rules, [[goal [[man isat left] [grain isat right]] ]]); instruct(); enddefine; define go2(); 'Trying to get everything across' => goal_state ==> prb_run(prb_rules, [^goal_state]); /* ;;; Or equivalently prb_run(prb_rules, [[goal [[NOT = isat left]] ]]); ;;; It should also be able to achieve the following goal */ instruct(); enddefine; define go3(goal); prb_run(prb_rules, [[goal ^goal]]); instruct(); enddefine; ;;; instruct(); pr('\nFor information type \n\tinstruct();\n'); /* --- $poplocal/local/newprb/teach/prbrunriver.p --- Copyright University of Birmingham 1996. All rights reserved. ------ */