Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: TRC - expert system building tool (part 5 of 8) Message-ID: <1389@panda.UUCP> Date: 9 Feb 86 04:32:19 GMT Sender: jpn@panda.UUCP Lines: 1492 Approved: jpn@panda.UUCP Mod.sources: Volume 3, Issue 113 Submitted by: ihnp4!dicomed!ndsuvax!nckary (Daniel D. Kary) This is NOT a shell archive. Simply delete everything up to and including the cut mark and save the result as reference.3.doc. Dan Kary ihnp4!dicomed!ndsuvax!nckary -------------- cut here --------------- - 46 - that an object is 'in-use'. The MARK variable is not needed for it's original purpose when it is in the backtrack stack. OUTPUT: relink_A_struct(start) int start; { backtrack->mark_A++; token[A]--; } relink_B_struct(start) int start; { int i, j; struct B_struct *temp; for(i = start; i < B_max; i++) if(B_list[i]){ temp = B_list[0]; j = 0; while(temp != B_list[i]){ temp = temp->next; j++; } if(B_list[i]->prev == NULL) B_list[0] = B_list[i]->next; else B_list[i]->prev->next = B_list[i]->next; if(B_list[i]->next) B_list[i]->next->prev = B_list[i]->prev; B_list[i]->MARK = j; B_list[i]->next = backtrack->mark_B; backtrack->mark_B = B_list[i]; B_list[i] = NULL; i = B_max; token[B]--; } } The backtracking action itself is initiated after the label 'End:' in the procedure 'loop'. If there is something on the backtrack stack, the index of the last rule that fired is copied out and the procedure 'backup', which undoes the actions of the last rule that fired, is called. Execu- tion continues with the rule that follows the last rule that fired. The procedure 'backup' first restores objects that were MARKed by the last rule and then removes objects that were ADDed. The MARKed objects are restored to their origi- nal positions in the list. OUTPUT: backup() - 47 - { int i; struct back_track_stack *temp; struct B_struct *B_temp, *B_temp2; if(backtrack == NULL) return; token[A] += backtrack->mark_A; token[A] -= backtrack->Add_A; while(backtrack->mark_B){ B_temp2 = backtrack->mark_B; backtrack->mark_B = backtrack->mark_B->next; B_temp2->prev = NULL; B_temp2->next = NULL; B_temp = B_list[0]; if(B_temp){ for(i = 0; i < B_temp2->MARK; i++) if(B_temp->next) B_temp = B_temp->next; else i = B_temp2->MARK + 1; } else i = -1; if(i == B_temp2->MARK){ B_temp2->next = B_temp; B_temp2->prev = B_temp->prev; if(B_temp->prev) B_temp->prev->next = B_temp2; else B_list[0] = B_temp2; B_temp->prev = B_temp2; } else{ if(B_temp){ B_temp->next = B_temp2; B_temp2->prev = B_temp; B_temp2->next = NULL; } else B_list[0] = B_temp2; } B_temp2->MARK = 0; token[B]++; } for(i = 0; i < backtrack->Add_B; i++){ B_list[1] = B_list[0]; free_B_struct(1); } temp = backtrack; backtrack = backtrack->next; free(temp); } The procedures generated by the BACKTRACKing option can - 48 - be called from embedded c-code to implement user controlled backtracking. A rule may undo itself with the following two statements: backup(); goto NEXT_RULE; The label 'NEXT_RULE' is replaced with the label of the rule that follows the current rule (this is done by the knowledge engineer, not TRC). To undo the current rule and the rule that fired previous to the current rule, use the following two statements: backup(); goto End; The label 'End' always refers to the point that follows the action part of the last rule. Appendix C contains a small expert system that implements backtracking with calls in embedded c-code. _1_3._5. _Z_E_R_O The ZERO option generates code that will free all dynamic structures allocated by TRC generated code. It is useful in systems where the loop procedure is entered more than once. It may be necessary to clean up the remains of a previous execution before beginning a new one. A single procedure, 'zero()', is written on the file 'zero.c'. The zero procedure will free all the elements and all the objects in STM and zero the arrays that hold the counts of objects in each list. If BACKTRACKing is enabled, zero will free all the objects and elements in the backtrack stack. If the TRACE option is enabled, zero will free all the entries in the trace list. If the PROFILE option is enabled, zero will set the value of all the integer array elements to zero. The actual code produced for the zero procedure is uninteresting and is not reproduced here. _1_3._6. _S_A_V_E The SAVE option writes a set of procedures on the file 'save.c' that simplify building expert systems capable of checkpointing their own execution. Procedures are generated for saving and reloading each type of dynamic structure that might be generated. In each case the procedures are passed a file pointer that points to an open file. The code pro- duced by the save procedures is uninteresting so only the names are listed here. The purpose of each procedure should be obvious from it's name: save_stm(fp) load_stm(fp) - 49 - save_backtrack(fp) load_backtrack(fp) save_profile(fp) load_profile(fp) save_trace(fp) load_trace(fp) Appendix C presents a small expert system that uses the SAVE option to generate code for checkpointing the execution of the system. The example includes an automatic restart capability. - 50 - APPENDIX A: TRC GRAMMAR The grammar for TRC which is presented throughout PART ONE of this document is presented in it's entirety here. identifier ::= letter { underscore | letter | digit} letter ::= upper-case-letter | lower-case-letter f-p ::= [ minus ] digits dot digits integer-literal ::= [ minus ] digits digits ::= digit { digit } string-literal ::= quote { [ character ] } quote comment ::= slash asterisk { [ character ] } asterisk slash c_code ::= left-bracket { [character] | [c_code] } right-bracket definition ::= identifier definition ::= identifier left-paren item-list right-paren item-list ::= { [ item ] } item ::= identifier colon type type ::= INT | FLOAT | STRING | POINTER stm ::= { [ entry ] } entry ::= [ integer-literal ] identifier entry ::= [ integer-literal ] identifier left-paren { [ init-item ] } right-paren init-item ::= identifier arrow value value ::= integer-literal value ::= floating-point-literal value ::= string-literal ltm ::= { [option] } { rule } option ::= ZERO | PROFILE | BACKTRACK | DUMP | RECURS | NORECURS | SAVE | TRACE | PREFIX identifier rule ::= label situation arrow action semicolon label ::= identifier colon | colon situation ::= { [ s-option ] } { [ match ] } s-option ::= EMPTY identifier identifier s-option ::= RECURS | NORECURS match ::= [ integer-literal ] identifier match ::= NOT identifier match ::= [ integer-literal ] left-paren name match-list right-paren match ::= c-code name ::= hat identifier identifier match-list ::= { match-item } match-item ::= identifier dot identifier relop literal match-item ::= identifier dot identifier relop identifier dot identifier relop ::= equality | not-equal | less-than relop ::= greater-than | greater-than-or-equal relop ::= less-than-or-equal action ::= statements c-code statements ::= { [statement] } statement ::= MARK mark-list statement ::= ADD add-list statement ::= OPTIMIZE identifier - 51 - mark-list ::= { [ mark-item ] } mark-item ::= [ integer-literal ] identifier add-list ::= { [ entry ] } - 52 - APPENDIX B: ERROR MESSAGES Error messages are listed and explained here in alpha- betical order. All messages that refer to the input file begin with the line number of the input file where the error was noticed. This line number is not necessarily the line where the error occurred, the actual error could be on an earlier line. The notations %s and %o mean that a string or an octal number, respectively, from the input file is included in the error message. %s is not an element of %s The named object does not include the named ele- ment. cannot translate %s in rule %s A c-code in rule %s contains an identifier %s that is preceded by a dollar character. The identifier is not known to TRC. This will occur if a dollar character occurs at some random point in the c- code. This error will also occur if an identifier is misspelled. can't have %s and NOT %s in the same rule NOT %s is a test that is true only when %s is an empty list. Obviously a list may not contain an object and be empty so the statement is meaning- less to TRC. can't MARK an EMPTY object An EMPTY object is an object that exists outside of STM. The scope of an EMPTY object is the rule in which it is declared. Since EMPTY objects are not in STM, attempting to MARK one is meaningless. can't mark more %s's than are found Unless STM is searched for an object and the object is found it is not possible to remove that object from STM. STM is searched only in the situation part, anything to be deleted in the action part must have been found in the situation part. - 53 - count on free variables undefined The purpose of a free variable is to assign a name to a specific object. Placing a count in front of a free variable definition is meaningless because the name can be assigned to only one object. degenerate case please rewrite A match in a rule compares an element to itself. The result of comparing an element to itself is known without performing any test. TRC refuses to generate the extra code that this useless test requires. duplicate declaration -> %s Object names must be unique, %s is the name of the object that is mentioned twice in the definition section. duplicate name in definition -> %s Each element in an object definition must have a unique name. free variable already defined -> %s The scope of a free variable is a single rule. Free variable names may be reused in every rule, but only once per rule. label repeats object declaration -> %s Rule labels and object names must be distinct from one another to prevent name conflicts in the out- put source code. negative count is undefined Be serious. How can a list contain less than zero items? newline embedded in string The scanner attempts to prevent errors caused by forgetting to terminate a string. For that reason - 54 - literal newlines are not permitted in strings. A newline and other control characters can be embed- ded in a string using the normal UNIX and C escapes. no code produced due to errors in source TRC will generate code only when there are no errors in the source. object field must be a string object field must be double object field must be integer TRC enforces strong type checking for the three data types, all assignments and relational tests must involve elements of the same type. objects in a complex test must match Here is an example of this type of error: (A.A1 == 2 B.B1 == 3) Because a single set of parens bracket this test it is presumed to be a test for a single object. A single object can not be in both list A (A.A1) and list B (B.B1). Either there is a typo in one of object names or some parens are missing. OUT OF MEMORY TRC IS EXITING This message is not generated by TRC, rather it is generated by the code that TRC produces. It will occur when the TRC generated inference engine attempts to dynamically allocate a data object. If the allocate fails, the TRC generated code prints this message and exits. redefined label -> %s Every rule must have a distinct label. semantic error: use a free variable This message suggests a solution to the perceived - 55 - problem. It is printed when the right hand side of a relational test mentions an object name that is different from the object name mentioned on the right hand side. This type of test can be accom- plished, but the item on the right hand side must be found first and must have a free variable name assigned to it. syntax error syntax error in ADD statement syntax error in MARK statement syntax error in OPTIMIZE statement syntax error in definitions syntax error in header syntax error in previous rule syntax error in short term memory syntax error in trailer A syntax error is generated by the parser when it can not reduce the input tokens with any of it's rules. The current input token will be discarded and the parser will attempt to reduce the new input. At least three input tokens must be parsed before the parser will assume it is in sync. Once the parser finds an error it will throw tokens out until it can sync. This is the reason why semi- colons are used as a rule terminator, they provide an absolute point where the parser can sync no matter how badly the input is botched. This behavior is common to YACC generated parsers. All syntax error messages indicate the line that was being scanned when the error was noticed and most inform the user of what section of the code was being parsed. types of element (%s) and value (%s) do not match Strong type checking is enforced by TRC. Literals must be of the same type as the element they are being assigned to. Floating point literals MUST contain a decimal point. There can be no cross assignment between integer and floating point ele- ments. types of %s.%s and %s.%s do not match Strong type checking is enforced by TRC. Only elements of identical types may be compared. unable to attach %s to the standard input - 56 - The scanner actually reads the standard input. The file named on the command line could not be opened for reading and attached to the standard input. unable to open %s Open failed on one of the output files. TRC aborts. unable to recover from earlier errors The parser completely wigged out, this usually happens when the input terminates before the parser can resync. undefined element -> %s.%s The object %s does not have an element %s. undefined flag (%c) A command line argument included a compiler flag that is not defined. Use 'man trc' to get a manual page. undefined free variable -> %s A reference was made to a free variable on the right hand side of a relational test. That free variable was not attached to an object in the current rule. Remember that the scope of a free variable is a single rule. undefined object -> %s The name %s was used as an object name but not defined as such in the definition section. undefined object field -> %s.%s The object %s was defined, but it did not contain an element %s. unexpected '!' unexpected '%' - 57 - unexpected '=' unexpected or undefined character: %o These messages are generated by the scanner. These characters, when not embedded in a comment, string or code section, are meaningful only as part of a compound symbol (e.g !=, ==, %%). A single character is not returned to the parser since it will only propagate errors. unterminated C code unterminated comment These elements of the input are handled completely by the scanner. These messages are printed if the end of the input file is reached before the ter- minating character is found. Each of these mes- sages indicate the line of the input where scan- ning began. usage: trc [options] filename Command line error. Use 'man trc' to get a manual page. zero count is undefined Nice try. If you really want to search STM for zero occurrences of something use the NOT state- ment described in Section 6.3.1 of this document. - 58 - APPENDIX C: STYLE NOTES TRC was designed to produce fast code, but it is not the least bit difficult to produce very slow code with TRC. The intent of this section is to suggest some things that can be done that will lead to fast code and to suggest some ways to avoid creating slow code. The central issue is reducing the amount of time spent searching STM. In the battle against long search times, TRC's first line of defense is the definition section. Think of STM as a data base. When a data base is designed two issues are central: first the data base must be capable of representing all the facts that are to be stored and retrieved and second the data base should be arranged in a manner that will facilitate searching the data base. In a relational data base, the data base manager will designate primary keys based, in part, on the way that users are likely to specify searches. Think of STM as a data base, the rules in LTM are the users that are searching the data base, design the data base (STM) for searching. Suppose an expert system for routing cargo on commer- cial air carriers is being built. The objects that this expert system will deal with include airplanes and cargo. It is certainly possible to define a single TRC object whose elements can describe either an airplane or a piece of cargo. When a rule searches for an airplane in this system, it has to wade thru cargo and airplanes to find the airplane it needs. Why not define two different objects, one that describes airplanes and one that describes cargo. Then a rule that is searching for an airplane can search only the airplane list without having to wade thru the cargo too. Carried to an extreme, this suggestion implies a dif- ferent object definition for every combination of attributes that can exist in STM. This extreme will often not be feas- able. There is a trade off to be made; by defining more objects, the length of each list should be reduced which should reduce execution time. The penalty is that for each object there is a code overhead for the procedures that manipulate those objects. Code size is being traded for execution speed. For object definitions that do not include any elements there is a very low code overhead. Object definitions that do not include elements are useful when the objects do not differ from one another and only a count of how many there are is needed. Since objects of this type do not differ, no list is maintained. If STM can be represented entirely with objects that contain no elements, all searching will be eliminated. This situation usually leads to the fastest executing systems. - 59 - The situation part of a rule is the second line of defense against slow expert systems. On each pass thru LTM, only one rule is selected for firing. This implies that most rules fail most of the time and that is is somewhat unusual for a rule to actually fire. Since rules generally fail far more often than they fire, wouldn't it be reason- able to design rules to be good at failing, i.e. fail quickly? The preconditions automatically placed on every rule by TRC are an initial attempt to cause a rule to fail without doing any searching. If a rule searches for an object in list A, one in list B and one in list C, the order that the list are searched may not be significant. The order will not be significan unless one of the searches refers to something that was pre- viously found. If the order is not significant, why not first search for the object that is least likely to exist? If the objects are equally likely, why not first search the list that is likely to be shortest? The search is carried out in the order that the objects are listed in the situa- tion part. Remember that rules usually fail and design your rules to fail quickly wherever possible. The optimizer is the third line of defense, use it. - 60 - A SAMPLE SYSTEM This sample expert system demonstrates some of the features of the TRC language. The expert system finds a path from one node to another node in a 'dungeon'. Some of the nodes are marked as 'dangerous', and no path may go through that node. The main procedure prints a map of the 'dungeon' and asks the user for start and end nodes. It then initializes STM and calls loop. On return from loop it prints out the path (if one was found) and the execution time and profile. The path is not necessarily the shortest path, only the first path found. Cycles are not permitted. This sample system uses backtracking that is initiated by embedded c-code. It also uses the SAVE option to sim- plify the checkpoint and reloading procedures. The pro- cedure 'checkpoint' saves the state of all dynamic struc- tures and 're_do' restores from a previous checkpoint. If the system is started with no command line arguments, it simply queries the user for start and end points. If it is started one or more command line arguments, it will restart from a previously saved snapshot. Since it is possible for a system to crash while the checkpoint files are being written, this system writes alternately on two sets of files. A flag file indicates which set of files is complete. INPUT: %% END (E1:STRING) NODE (N1:STRING N2:STRING N3:STRING) PATH (P1:STRING) START (S1:STRING) %% NODE ( N1 => "ANEMONIE" N2 => "DANGER" N3 => "QUAGGA") NODE ( N1 => "ANEMONIE" N2 => "DANGER" N3 => "YENTI") NODE ( N1 => "ANEMONIE" N2 => "SAFE" N3 => "MEADOW") NODE ( N1 => "ANEMONIE" N2 => "SAFE" N3 => "KESTREL") NODE ( N1 => "BANDIT" N2 => "SAFE" N3 => "JABBERWOCK") NODE ( N1 => "BANDIT" N2 => "SAFE" N3 => "PEGASUS") NODE ( N1 => "BANDIT" N2 => "SAFE" N3 => "LAPIS LASULI") NODE ( N1 => "CAVERN" N2 => "SAFE" N3 => "ICE ROOM") NODE ( N1 => "CAVERN" N2 => "SAFE" N3 => "TREASURE") NODE ( N1 => "CAVERN" N2 => "DANGER" N3 => "HOBGOBLIN") NODE ( N1 => "DUBLOON" N2 => "SAFE" N3 => "ICE ROOM") NODE ( N1 => "DUBLOON" N2 => "DANGER" N3 => "HOBGOBLIN") NODE ( N1 => "DUBLOON" N2 => "SAFE" N3 => "SPRING") NODE ( N1 => "ELVES" N2 => "SAFE" N3 => "NYMPH") NODE ( N1 => "ELVES" N2 => "SAFE" N3 => "URCHIN") NODE ( N1 => "FOUNTAIN" N2 => "SAFE" N3 => "RUBY") - 61 - NODE ( N1 => "FOUNTAIN" N2 => "SAFE" N3 => "NYMPH") NODE ( N1 => "FOUNTAIN" N2 => "DANGER" N3 => "XEROC") NODE ( N1 => "GROTTO" N2 => "DANGER" N3 => "WRAITH") NODE ( N1 => "GROTTO" N2 => "SAFE" N3 => "OGRE") NODE ( N1 => "GROTTO" N2 => "SAFE" N3 => "RUBY") NODE ( N1 => "HOBGOBLIN" N2 => "SAFE" N3 => "TREASURE") NODE ( N1 => "HOBGOBLIN" N2 => "SAFE" N3 => "CAVERN") NODE ( N1 => "HOBGOBLIN" N2 => "SAFE" N3 => "ICE ROOM") NODE ( N1 => "HOBGOBLIN" N2 => "SAFE" N3 => "DUBLOON") NODE ( N1 => "HOBGOBLIN" N2 => "SAFE" N3 => "MEADOW") NODE ( N1 => "ICE ROOM" N2 => "SAFE" N3 => "CAVERN") NODE ( N1 => "ICE ROOM" N2 => "DANGER" N3 => "HOBGOBLIN") NODE ( N1 => "ICE ROOM" N2 => "SAFE" N3 => "DUBLOON") NODE ( N1 => "JABBERWOCK" N2 => "SAFE" N3 => "MEADOW") NODE ( N1 => "JABBERWOCK" N2 => "SAFE" N3 => "VERMIN") NODE ( N1 => "JABBERWOCK" N2 => "SAFE" N3 => "LAPIS LASULI") NODE ( N1 => "JABBERWOCK" N2 => "DANGER" N3 => "BANDIT") NODE ( N1 => "JABBERWOCK" N2 => "SAFE" N3 => "PEGASUS") NODE ( N1 => "JABBERWOCK" N2 => "DANGER" N3 => "ZOMBIE") NODE ( N1 => "KESTREL" N2 => "DANGER" N3 => "YENTI") NODE ( N1 => "KESTREL" N2 => "SAFE" N3 => "ANEMONIE") NODE ( N1 => "KESTREL" N2 => "DANGER" N3 => "QUAGGA") NODE ( N1 => "LAPIS LASULI" N2 => "SAFE" N3 => "VERMIN") NODE ( N1 => "LAPIS LASULI" N2 => "SAFE" N3 => "JABBERWOCK") NODE ( N1 => "LAPIS LASULI" N2 => "DANGER" N3 => "BANDIT") NODE ( N1 => "MEADOW" N2 => "DANGER" N3 => "HOBGOBLIN") NODE ( N1 => "MEADOW" N2 => "SAFE" N3 => "ANEMONIE") NODE ( N1 => "MEADOW" N2 => "SAFE" N3 => "JABBERWOCK") NODE ( N1 => "MEADOW" N2 => "SAFE" N3 => "NYMPH") NODE ( N1 => "MEADOW" N2 => "DANGER" N3 => "WRAITH") NODE ( N1 => "NYMPH" N2 => "SAFE" N3 => "MEADOW") NODE ( N1 => "NYMPH" N2 => "SAFE" N3 => "ELVES") NODE ( N1 => "NYMPH" N2 => "SAFE" N3 => "URCHIN") NODE ( N1 => "NYMPH" N2 => "DANGER" N3 => "XEROC") NODE ( N1 => "NYMPH" N2 => "SAFE" N3 => "FOUNTAIN") NODE ( N1 => "NYMPH" N2 => "SAFE" N3 => "RUBY") NODE ( N1 => "NYMPH" N2 => "SAFE" N3 => "URCHIN") NODE ( N1 => "OGRE" N2 => "SAFE" N3 => "SPRING") NODE ( N1 => "OGRE" N2 => "DANGER" N3 => "WRAITH") NODE ( N1 => "OGRE" N2 => "SAFE" N3 => "GROTTO") NODE ( N1 => "PEGASUS" N2 => "DANGER" N3 => "BANDIT") NODE ( N1 => "PEGASUS" N2 => "SAFE" N3 => "JABBERWOCK") NODE ( N1 => "PEGASUS" N2 => "DANGER" N3 => "ZOMBIE") NODE ( N1 => "QUAGGA" N2 => "SAFE" N3 => "KESTREL") NODE ( N1 => "QUAGGA" N2 => "SAFE" N3 => "ANEMONIE") NODE ( N1 => "QUAGGA" N2 => "SAFE" N3 => "VERMIN") NODE ( N1 => "RUBY" N2 => "SAFE" N3 => "GROTTO") NODE ( N1 => "RUBY" N2 => "SAFE" N3 => "NYMPH") NODE ( N1 => "RUBY" N2 => "SAFE" N3 => "FOUNTAIN") NODE ( N1 => "SPRING" N2 => "SAFE" N3 => "DUBLOON") NODE ( N1 => "SPRING" N2 => "DANGER" N3 => "WRAITH") NODE ( N1 => "SPRING" N2 => "SAFE" N3 => "OGRE") NODE ( N1 => "TREASURE" N2 => "SAFE" N3 => "CAVERN") NODE ( N1 => "TREASURE" N2 => "DANGER" N3 => "HOBGOBLIN") - 62 - NODE ( N1 => "TREASURE" N2 => "DANGER" N3 => "YENTI") NODE ( N1 => "URCHIN" N2 => "SAFE" N3 => "ELVES") NODE ( N1 => "URCHIN" N2 => "SAFE" N3 => "NYMPH") NODE ( N1 => "URCHIN" N2 => "DANGER" N3 => "XEROC") NODE ( N1 => "VERMIN" N2 => "DANGER" N3 => "QUAGGA") NODE ( N1 => "VERMIN" N2 => "SAFE" N3 => "LAPIS LASULI") NODE ( N1 => "VERMIN" N2 => "SAFE" N3 => "JABBERWOCK") NODE ( N1 => "WRAITH" N2 => "SAFE" N3 => "MEADOW") NODE ( N1 => "WRAITH" N2 => "SAFE" N3 => "SPRING") NODE ( N1 => "WRAITH" N2 => "SAFE" N3 => "OGRE") NODE ( N1 => "WRAITH" N2 => "SAFE" N3 => "GROTTO") NODE ( N1 => "XEROC" N2 => "SAFE" N3 => "URCHIN") NODE ( N1 => "XEROC" N2 => "SAFE" N3 => "NYMPH") NODE ( N1 => "XEROC" N2 => "SAFE" N3 => "FOUNTAIN") NODE ( N1 => "YENTI" N2 => "SAFE" N3 => "KESTREL") NODE ( N1 => "YENTI" N2 => "SAFE" N3 => "ANEMONIE") NODE ( N1 => "YENTI" N2 => "SAFE" N3 => "TREASURE") NODE ( N1 => "ZOMBIE" N2 => "SAFE" N3 => "JABBERWOCK") NODE ( N1 => "ZOMBIE" N2 => "SAFE" N3 => "PEGASUS") %% BACKTRACK DUMP TRACE PROFILE SAVE ZERO R1: /* See if we are at the end */ (^START HERE) (END.E1 == HERE.S1) => { printf("Found a path"); remove_checkpoint(); return; } ; R2: /* See if the next node that would be selected is already in the path. If it is, remove it from consideration */ (^START HERE) (^NODE NEXT NODE.N2 != "DANGER" NODE.N1 == HERE.S1) (PATH.P1 == NEXT.N3) => MARK NODE { } ; R3: /* Select the first non-dangerous node as the next node */ - 63 - (^START HERE) (^NODE NEXT NODE.N2 != "DANGER" NODE.N1 == HERE.S1) => MARK START NODE ADD START(S1 => NEXT.N3) PATH (P1 => NEXT.N3) { } ; R4: /* A dead end has been reached. Undo the last path taken and mark it as dangerous in R5 */ => { /* undo this rule */ backup(); /* check if there was a previous rule */ while(backtrack){ if(backtrack->next_rule == 5) backtrack = backtrack->next; else{ backup(); /* undo it */ goto R5; /* and mark it as dangerous */ } } /* no solution */ goto R6; } ; R5: /* Grab the first available path and call it dangerous */ (^START HERE) (^NODE NEXT NODE.N2 != "DANGER" NODE.N1 == HERE.S1) => MARK NODE ADD NODE (N1 => NEXT.N1 N2 => "DANGER" N3 => NEXT.N3) { checkpoint(); } ; R6: /* No solution */ => { printf("No Solution"); remove_checkpoint(); return; - 64 - } ; %% MAIN.C #include #include #include #include "X_loop.h" extern char *rule_names[]; char *node_names[26] = { "ANEMONIE", "BANDIT", "CAVERN", "DUBLOON", "ELVES", "FOUNTAIN", "GROTTO", "HOBGOBLIN", "ICE ROOM", "JABBERWOCK", "KESTREL", "LAPIS LASULI", "MEADOW", "NYMPH", "OGRE", "PEGASUS", "QUAGGA", "RUBY", "SPRING", "TREASURE", "URCHIN", "VERMIN", "WRAITH", "XEROC", "YENTI", "ZOMBIE" }; int danger[26] = { 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 }; extern int X_fire_profile[], X_test_profile[]; char *start, *xit; main(argc, argv) int argc; char *argv[]; { int i, fire, test; char c[512]; double d1, d2, d3; struct timeval tp, old_tp; struct timezone tzp; setbuf(stdout, NULL); if(argc > 1){ if(re_do()){ X_dump_PATH_struct(); test = fire = 0; for(i = 0; i < 17; i++) test += X_test_profile[i]; for(i = 0; i < 7; i++) fire += X_fire_profile[i]; X_zero(); printf("%d rules tested",test); printf("%d rules fired",fire); } printf("Continue [y or n] "); - 65 - scanf("%s",c); if(isupper(c[0])) c[0] = tolower(c[0]); if(c[0] == 'n') exit(0); } while(1){ start = xit = NULL; print_map(); while(start == NULL){ printf("Input start node "); scanf("%s",c); if(isupper(c[0])) c[0]=tolower(c[0]); if(islower(c[0])) start = node_names[c[0]-'a']; } while(xit == NULL){ printf("Input exit node "); scanf("%s",c); if(isupper(c[0])) c[0]=tolower(c[0]); if(islower(c[0])) xit = node_names[c[0]-'a']; } printf("initializing"); X_init(); X_backtrack = (struct X_back_track_stack *) malloc(sizeof(struct X_back_track_stack)); X_add_END_struct(xit); X_add_START_struct(start); X_add_PATH_struct(start); free(X_backtrack); X_backtrack = NULL; gettimeofday(&old_tp, &tzp); X_loop(); gettimeofday(&tp, &tzp); X_dump_PATH_struct(); d1 = old_tp.tv_sec; d2 = old_tp.tv_usec; d1 += d2/1000000; d2 = tp.tv_sec; d3 = tp.tv_usec; d2 += d3/1000000; d3 = d2 - d1; test = fire = 0; for(i = 0; i < 17; i++) test += X_test_profile[i]; for(i = 0; i < 7; i++) fire += X_fire_profile[i]; X_zero(); d1 = test; d2 = fire; printf("Elapsed time = %f seconds", d3); - 66 - printf("%d rules tested (%f rules/sec)",test, d1/d3); printf("%d rules fired (%f rules/sec)",fire, d2/d3); printf("Continue [y or n] "); scanf("%s",c); if(isupper(c[0])) c[0] = tolower(c[0]); if(c[0] == 'n') exit(0); } } print_map() { printf(" NODE NAMES (* DANGEROUS NODES) C - I "); printf(" ------------------------------- / \\ / \\ "); printf(" ANEMONIE NYMPH T - H - D "); printf(" BANDIT* OGRE / | \\ "); printf(" CAVERN PEGASUS Y | S "); printf(" DUBLOON QUAGGA* / | | | \\ "); printf(" ELVES RUBY K - A --- M --- W - O "); printf(" FOUNTAIN SPRING \\ / / \\ \\ / "); printf(" GROTTO TREASURE Q / \\ G "); printf(" HOBGOBLIN* URCHIN | / \\ | "); printf(" ICE ROOM VERMIN V / \\ R "); printf(" JABBERWOCK WRAITH* / \\ / \\ / \\ "); printf(" KESTREL XEROC* L - J - Z E - N - F "); printf(" LAPIS LASULI YENTI* \\ / \\ / \\ / \\ / "); printf(" MEADOW ZOMBIE* B - P U - X "); } int state = 0; char *lock = "lock.0"; char *stm = "stm.0"; char *back = "back.0"; char *pro = "pro.0"; char *trace = "trace.0"; checkpoint() /* save a snapshot of stm, back_track_stack, etc. */ { FILE *fp; lock[5] = '0' + state; stm[4] = '0' + state; back[5] = '0' + state; pro[4] = '0' + state; trace[6] = '0' + state; if(state) state = 0; else state = 1; unlink(lock); fp = fopen(stm, "w"); X_save_stm(fp); - 67 - fclose(fp); fp = fopen(back, "w"); X_save_backtrack(fp); fclose(fp); fp = fopen(pro, "w"); X_save_profile(fp); fclose(fp); fp = fopen(trace, "w"); X_save_trace(fp); fclose(fp); fp = fopen(lock, "w"); fprintf(fp,"good"); fclose(fp); } remove_checkpoint() /* remove all old snapshots */ { unlink(lock); unlink(stm); unlink(back); unlink(pro); unlink(trace); lock[5] = '0' + state; stm[4] = '0' + state; back[5] = '0' + state; pro[4] = '0' + state; trace[6] = '0' + state; unlink(lock); unlink(stm); unlink(back); unlink(pro); unlink(trace); } re_do() /* restore from a snapshot and continue execution */ { char c[512]; FILE *fp; if((fp = fopen(lock, "r")) != NULL) fscanf(fp,"%s", c); if(strncmp(c, "good", 4)){ if(fp) fclose(fp); if(state) state = 0; else state = 1; lock[5] = '0' + state; stm[4] = '0' + state; back[5] = '0' + state; pro[4] = '0' + state; - 68 - trace[6] = '0' + state; if((fp = fopen(lock, "r")) != NULL) fscanf(fp,"%s", c); if(strncmp(c, "good", 4)){ remove_checkpoint(); printf("No checkpoint files"); return(0); } } fclose(fp); fp = fopen(stm, "r"); X_load_stm(fp); fclose(fp); fp = fopen(back, "r"); X_load_backtrack(fp); fclose(fp); fp = fopen(pro, "r"); X_load_profile(fp); fclose(fp); fp = fopen(trace, "r"); X_load_trace(fp); fclose(fp); X_loop(); return(1); }