home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / AIE8906.ZIP / AIAPP.CDE next >
Encoding:
Text File  |  1988-05-04  |  28.0 KB  |  1,021 lines

  1. Date:  03-Apr-89 18:26 PDT
  2. From:  Marc Rettig [76703,1037]
  3. Subj:  Minasi Code
  4.  
  5.  
  6. ************* HEX.PAS CODE FOLLOWS *************************
  7.  
  8.  
  9. {Hexapawn version 2.0}
  10. {Copyright 1989 Mark Minasi}
  11. {written for Turbo Pascal versions 5.0 or 4.0}
  12.  
  13. {$B-}    {Boolean complete evaluation off}
  14. {$S+}    {Stack checking on}
  15. {$I+}    {I/O checking on}
  16.  
  17. Program Hexapawn;
  18. {This demonstrates a straightforward approach to computer game playing: an
  19.  N level search followed by minimax-based back pruning}
  20.  
  21. Uses
  22.   Dos;
  23.  
  24. Const
  25. {$I tconh.ins}
  26.  
  27.   Black = 'B';
  28.   White = 'W';
  29.   Neither = '[';
  30.   Expanded = 1;
  31.   Unexpanded = 2;
  32.   BigWin = 32000;
  33.   BigLoss = -32000;
  34.   UnEvaluated = -Maxint;
  35.   {Change the next three things to alter the dimension of the board}
  36.   BD = 4; {Board Dimension}
  37.   emptyctype='';
  38.  
  39. Type CType = array[1..BD,1..BD] of char;
  40.      {$i ttyph.ins}
  41.  
  42. Var
  43.  
  44. {$I tvarh.ins}
  45. Human, Computer,Side:                  char;
  46. WhoWon:                                Char;
  47. CurrentBoard:                          Ctype;
  48. Nlevels:                               Integer; {Number of levels to search}
  49. T1,T2:                                 Real; {for timekeeping}
  50. LastFound:                             Integer; {used to speed up search}
  51.  
  52. {$I tprch.ins}
  53. {$I time.ins}
  54.  
  55. Procedure Init(var CurrentBoard:Ctype);
  56. Var I,J:Integer;
  57. Begin
  58.   {Set up Board}
  59.   For I:=1 to BD Do For j:=1 to BD do CurrentBoard[I,J]:=Neither;
  60.   For J:=1 to BD Do
  61.   begin
  62.     CurrentBoard[1,J]:=Black;
  63.     CurrentBoard[BD,J]:=White;
  64.   end;
  65.   LastFound:=Root; {initialize for search}
  66.   Assign(outfile,'');
  67.   Rewrite(outfile);
  68. End;
  69.  
  70. Function Opposite(Side:char):char;
  71. Begin
  72.   If Side=White then Opposite:=Black
  73.   Else If Side=Black then Opposite:=White
  74.   Else Writeln('ERROR IN OPPOSITE.  ASKED FOR SIDE=',Side);
  75. End;
  76.  
  77. Procedure ShowBoard(Board:Ctype);
  78. Var I,J:Integer;
  79. {Prints out the selected board string}
  80. Begin
  81.   For I:=1 to BD do
  82.   begin
  83.     for j:=1 to bd do write(board[i,j]);
  84.     writeln;
  85.   end;
  86. end;
  87.  
  88. Procedure ChooseColor(var computer,human:char);
  89. Var Answer:char;
  90. Begin
  91.   Write('Would you like to go first? ');
  92.   Readln(Answer);Answer:=UpCase(Answer);
  93.   If Answer='Y' then Begin
  94.                        Human:=White;
  95.                        Computer:=Black;
  96.                      End Else
  97.                      Begin
  98.                        Human:=Black;
  99.                        Computer:=White;
  100.                      End;
  101. End;
  102.  
  103. Function MoveDirection(Side:char):Integer;
  104. Begin
  105.   If Side=White then MoveDirection:=-1
  106.   Else If Side=Black Then MoveDirection:=1
  107.   Else Writeln('Error query to MoveDirection.  Side=',Side);
  108. End;
  109.  
  110. Procedure Convert(TestBoard:Ctype;Var B:Ctype;Var BSum,WSum:Integer);
  111. Var
  112.   I,J,L: Integer;
  113.  
  114. Begin;
  115.   {Set up board for ease of analysis}
  116.   BSum := 0;
  117.   WSum := 0;
  118.   B:=TestBoard;
  119.   For I:=1 to BD do
  120.   For J:=1 to BD do
  121.   begin
  122.     if b[i,j]=black then bsum:=bsum+1 else
  123.     if b[i,j]=white then wsum:=wsum+1;
  124.   end;
  125. End;
  126.  
  127. Function Evaluate(Board:Ctype;Side:char):Integer;
  128. {Returns the integer evaluation function value for a given board position
  129.  and a given side.}
  130. Var
  131.   Enemy:                 char;
  132.   I,j,sum:               Integer;
  133. Begin
  134.   {Presumably, this position has already been checked for a win.
  135.    Therefore, this must be an intermediate position.}
  136.   Sum:=0;
  137.   Enemy := Opposite(Side);
  138.   for i:=1 to bd do for j:=1 to bd do
  139.   if board[i,j]=side then sum:=sum+1 else if board[i,j]=enemy then
  140.   sum:=sum-1;
  141.   Evaluate := sum;
  142. End;
  143.  
  144. Function Won(B1:Ctype;WhosMove:char):char;
  145. {Examines board TestBoard to see, given that WhosMove just moved, whether or
  146.  not WhosMove just won.}
  147.  
  148. Var
  149.   SoFar : char;
  150.   B:                    Ctype;
  151.   I,J,L,BSum,WSum,DI: Integer;
  152.   Iplus, jright, jleft:integer;
  153.   CheckedColor:char;
  154.   istart,iend:integer;
  155.   AllBlocked, blackback, whiteback:               boolean;
  156. Begin
  157.   SoFar := Neither;
  158.  
  159.   {check back row}
  160.   j:=1;
  161.   repeat
  162.     if b1[1,j]=white then sofar:=white else if b1[bd,j]=black then sofar:=black;
  163.     j:=j+1;
  164.   until (J>BD) or (SoFar<>Neither);
  165.  
  166.    {check for blocked moves}
  167.    If SoFar = Neither Then
  168.    Begin
  169.       {Check for blocked pieces}
  170.       CheckedColor := Opposite(WhosMove);
  171.       if CheckedColor=white then
  172.       begin
  173.         istart:=2;iend:=4;
  174.       end else
  175.       begin
  176.         istart:=1;iend:=3;
  177.       end;
  178.       DI := MoveDirection(CheckedColor);
  179.       AllBlocked := True;
  180.       For I:=1 to BD Do
  181.         For J:=1 to BD Do
  182.         Begin     {check piece at i,j}
  183.           If b1[I,J] = CheckedColor Then
  184.           Begin  {Look to see if the piece cannot move}
  185.             Iplus := I+DI;
  186.             Jleft := J-1;
  187.             Jright := J+1;
  188.             If (b1[iplus,j]<>whosmove)
  189.                                           {white not in front}
  190.             Or ((jleft>0) and (jleft<=bd) and (b1[iplus,jleft]=whosmove))
  191.                                           {white to right and ahead}
  192.             Or ((jright>0) and (jright<=bd) and (b1[iplus,jright]=whosmove))
  193.                                           {white to left and ahead}
  194.             Then Allblocked:=False;
  195.           End;
  196.         End;     {Checking piece at i,j}
  197.  
  198.       If AllBlocked Then SoFar := WhosMove;
  199.     End; {Looking for blocked pieces}
  200.  
  201.  
  202.   if SoFar=Neither Then
  203.   begin
  204.     Convert(B1,B,BSum,WSum);
  205.     {Check to see if either side is depleted}
  206.     If BSum = 0 Then SoFar := White
  207.     Else If WSum = 0 Then SoFar := Black;
  208.   end;
  209.  
  210.   Won:=SoFar;
  211. End; {Procedure}
  212.  
  213. Procedure Mirror(Board:Ctype;var B:Ctype);
  214. {Given a board, it inverts it about the middle column}
  215. var
  216.     I,J,MJ,Bsum,Wsum:Integer;
  217.     NSwaps:Integer;
  218.     T: Char;
  219.  
  220. Begin
  221.   NSwaps := BD div 2;
  222.   B:=Board;
  223.   For J:=1 To NSwaps Do
  224.   Begin
  225.     MJ := BD + 1 - J;
  226.     For I:=1 To BD Do
  227.     Begin
  228.       T:=B[I,J];
  229.       B[I,J]:=B[I,MJ];
  230.       B[I,MJ]:=T;
  231.     End;
  232.   End;
  233. End;
  234.  
  235.  
  236. Procedure CheckBeforeAdding(NewBoard:Ctype;Var OkayToAdd:Boolean;
  237.                             PreviousMove:Integer);
  238. {This is where any kind of control strategy would go.  In the particular
  239.  case of Hexapawn, you can never have an ancestor identical to a descendant
  240.  node, as no moves are irreversible.  However, to reduce the tree, no
  241.  siblings will be allowed that are mirror images of each other.
  242.  PreviousMove is the node number of the previous board position.}
  243.  
  244. Var
  245.   RBoard :Ctype;
  246.   I,J,N:      Integer;
  247.   temp:ctype;
  248.   match:boolean;
  249. Begin
  250.  
  251. OkayToAdd:=true;
  252.  
  253. (*  Mirror code seems useless, so it's commented out *)
  254.   Mirror(NewBoard,RBoard);
  255.   N := FirstOffspring(PreviousMove);
  256.   OkayToAdd:=True;
  257.   While (N<>Null) and (OkayToAdd) Do
  258.   Begin
  259.     Characteristic(N,Temp);
  260.     match:=true;
  261.     for i:=1 to bd do for j:=1 to bd do
  262.      if temp[i,j]<>rboard[i,j] then match:=false;
  263.     If match Then OkayToAdd:=False;
  264.     N:=RSibling(N);
  265.   End;
  266.  
  267.  
  268. End;
  269.  
  270.  
  271. Procedure ConsiderMove(I,J:Integer;MoveNum:integer;B:Ctype;ThisSide:char;
  272.          Var FilledTree:Boolean;PreviousMove:Integer;OriginalSide:char;
  273.          Nlevels:integer);
  274. {Consider move number MoveNum from location (I,J) on the board.
  275.  Other useful information is the side that is doing the moving (ThisSide),
  276.  whether or not this move fills the tree (FilledTree), and what was the node
  277.  number of the previous move (PreviousMove).  OriginalSide is the side that is
  278.  doing the analysis.}
  279.  
  280. Const
  281.   DJ: array[1..3] of integer = (0,1,-1);
  282. Var
  283.   A: Ctype;
  284.   CanDo,  OkayToAdd: boolean;
  285.   MD, IP,NN                : integer;
  286.   NewBoard        :  Ctype;
  287.   WinFlag:               Char;
  288. Begin
  289.   FilledTree:=False;
  290.   CanDo := False;
  291.   MD := MoveDirection(ThisSide);
  292.   IP := I + MD;
  293.   {Is the proposed move possible?  Is there a blank space to occupy or an
  294.    enemy to attack?}
  295.   If (MoveNum=1) and (B[IP,J]=Neither) Then CanDo := True;
  296.   If ((J+1)<=BD) and (MoveNum=2)
  297.                  and (B[IP,J+1]=Opposite(ThisSide)) Then CanDo := True;
  298.   If ((J-1)>0) and (MoveNum=3)
  299.                 and (B[IP,J-1]=Opposite(ThisSide)) Then CanDo := True;
  300.   If CanDo Then
  301.   Begin
  302.     A := B;                  {Set up A to receive new board position}
  303.     A[I,J] := Neither;
  304.     A[IP,J+DJ[MoveNum]] := ThisSide;
  305.     NewBoard:=A;
  306.     {Control Strategies}
  307.     CheckBeforeAdding(NewBoard,OkaytoAdd,PreviousMove);
  308.     If OkaytoAdd Then
  309.     Begin              {Adding node to tree}
  310.       CreateNode(PreviousMove,NewBoard,NN);
  311.       If NN=Null then FilledTree := True Else
  312.       Begin {Check to see if the new node is a terminal node.  This would be
  313.              either due to its being at maximum depth, or a "game over" node.}
  314.         WinFlag := Won(NewBoard,ThisSide);
  315.         If (Depth(NN) = Nlevels) or (WinFlag<>Neither) Then
  316.         Begin   {This is a terminal node.  Set its value}
  317.           SetValue(NN,Expanded);
  318.           If WinFlag=Neither Then SetGValue(NN,UnEvaluated) else
  319.           If WinFlag=OriginalSide Then SetGValue(NN,BigWin-Depth(NN)) else
  320.           If WinFlag=Opposite(OriginalSide) Then
  321.                    SetGvalue(NN,BigLoss+Depth(NN));
  322.         End Else
  323.         Begin {Non terminal node}
  324.           SetValue(NN,Unexpanded);
  325.           SetGValue(NN,UnEvaluated);
  326.         End
  327.       End {Tree not filled, marking nodes};
  328.     End {adding to tree} ;
  329.   End {"Can Do" clause};
  330. End;
  331.  
  332. Procedure Expand(N:Integer;OriginalSide:char;var treefull:boolean;
  333.                  Nlevels:integer);
  334. label escape;
  335. Const
  336.   NMoves = 3;
  337. { This expands node N, producing all of its offspring nodes.  Any control
  338.   strategies are implemented here.  One such in Hexapawn would be to remove
  339.   mirror images among siblings.
  340. When done, sets node N to "expanded."
  341. Generated offspring are set to "unexpanded" unless they are terminal nodes or
  342. at max depth, in which case they are set to "expanded".
  343. }
  344. Var
  345.   ThisSide:                        char;
  346.   B:                               Ctype;
  347.   Bsum,Wsum,I,J,K:                 Integer;
  348.   Board:                           Ctype;
  349.  
  350. Begin
  351.   If N=Null then writeln('Error in Expand.  Null node ',N,' passed.');
  352.   {First, we've got to know which side is doing the moving.}
  353.   If Odd(Depth(N)) Then ThisSide := Opposite(OriginalSide)
  354.                    Else ThisSide:=OriginalSide;
  355.   Characteristic(N,Board);
  356.   Convert(Board,B,Bsum,Wsum);
  357.   {Find the pieces, then see if they can move}
  358.   For I:=1 to BD do For J:=1 to BD Do
  359.   Begin
  360.     If B[I,J]=ThisSide Then For K:=1 to Nmoves Do
  361.     begin
  362.       ConsiderMove(I,J,K,B,ThisSide,TreeFull,N,OriginalSide,Nlevels);
  363.       If TreeFull Then Goto Escape;
  364.     end;
  365.     SetValue(N,Expanded);
  366.   End;
  367. escape:
  368. End;
  369.  
  370. Procedure FindFirst(Var N:integer);
  371. Var
  372.   M:         Integer;
  373.   Alldone:   Boolean;
  374. { Locates first unexpanded node.  This version uses a depth-first search.}
  375. {starts from last created node}
  376. Begin
  377.   M:=Lastfound;
  378.   Alldone:=False;
  379.   While not Alldone do
  380.   Begin
  381.     If M=Null then Alldone:=True else
  382.     Begin
  383.       If NodeValue(M) = Unexpanded then Alldone:=True else M:=NextNode(M);
  384.     End;
  385.   End;
  386.   N:=M;
  387.   LastFound:=N;
  388. End;
  389.  
  390. Procedure Gentree(var Nlevels:Integer;Side:char);
  391. {Generates a game move tree Nlevels deep for side Side}
  392. Var
  393.   N:Integer;
  394.   AllDone:Boolean;
  395.   Treefull:boolean;
  396. Begin
  397. Repeat
  398.   Treefull:=False;
  399.   LastFound:=Root; {so search is initialized properly}
  400.   Inittree;
  401.   SetCharacteristic(Root,CurrentBoard);
  402.   SetValue(Root,Unexpanded);
  403.   SetGValue(Root,Unevaluated);
  404.   Alldone:=false;
  405.   While (not Alldone) and (not TreeFull) Do
  406.   Begin        {main loop}
  407.     {Find first unexpanded node.}
  408.     FindFirst(N);
  409.     IF N=Null then Alldone:=True Else
  410.     Begin      {expand node n}
  411.       Expand(N,Side,TreeFull,Nlevels);
  412.       If treefull then
  413.       begin    {reduce search depth}
  414.         Nlevels:=Nlevels-1;
  415.         writeln('GENTREE:Tree overflow.  Reducing plies temporarily to ',
  416.                nlevels);
  417.         LastFound:=Root; {reset this or the thing goes crazy}
  418.       end;     {reduce search depth}
  419.     end;       {expand node n}
  420.   End;         {main loop}
  421. Until Not Treefull;
  422. End;
  423.  
  424. Procedure GetInputMove(var Board:Ctype;Side:char);
  425. {Gets latest move from human player.  Checks for validity of moves}
  426. Var Valid:boolean;
  427.     Row1,Row2,Column1,Column2:Integer;
  428.     EN:                       Integer;
  429. Begin
  430.   EN := 0; {No errors to start}
  431.   Repeat
  432.     Valid:=True;
  433.     If EN<>0 Then Writeln('Encountered error number ',EN);
  434.     EN := 0;
  435.     Writeln('Please enter the source and destination row & column for the',
  436.           ' piece to move.');
  437.     Write('(separate row/col with a space, like "3 3 2 3" ? ');
  438.     Readln(Row1,Column1,Row2,Column2);
  439.     {Check validity}
  440.     If (Row1<1) or (Row1>BD) or (Column1<1) or (Column1>BD) or (Row2<1) or
  441.        (Row2>BD) or (Column2<1) or (Column2>BD) Then
  442.                                      Begin Valid:=False;EN:=1;End;
  443.     If Valid Then If (board[Row1,Column1] <> Side)
  444.              Then Begin Valid:=False;En:=2;End;
  445.     {Check that the piece exists to move}
  446.     If Valid Then If MoveDirection(Side) <> (Row2-Row1)
  447.              Then Begin Valid := False; EN:=3;End;{Check Move direction}
  448.     If Valid Then If (Column1=Column2) And (board[Row2,Column2] <>
  449.               Neither) Then Begin Valid:=False; EN:=4;End;
  450.                {Must move forward to open square}
  451.     If Valid Then If (Column1<>Column2) and (board[Row2,Column2]
  452.               <> Opposite(Side)) Then Begin Valid:=False;EN:=5;End;
  453.                {Must attack enemy only}
  454.   Until Valid;
  455.   Writeln('Valid Move.');
  456.   {Register Change in Board}
  457.   Board[Row1,Column1] := Neither;
  458.   Board[Row2,Column2] := Side;
  459. End; {Procedure}
  460.  
  461. Procedure Minimax(Var Board:Ctype;Side:char;BottomLevel:Integer);
  462. Const
  463.   Maximum = 10;
  464.   Minimum = 11;
  465. Var
  466.   N,Lev,F,NN:          Integer;
  467.   Temp:ctype;
  468.   MoveFound:boolean;
  469.  
  470.   Procedure FindOptOffspr(Node:Integer;Var BestValue,BestNode:Integer;
  471.                           Direction:Integer);
  472.   {Searches all offspring of node Node to find optimal (minimum or maximum)
  473.    G values.  Best G value and node where it was found are stored in BestValue
  474.    and BestNode.  Direction indicates whether to find minimum or maximum.}
  475.   Var
  476.     N,X,BestSoFar,BestNodeSoFar,Factor:            Integer;
  477.   Begin
  478.     N:=FirstOffspring(Node);
  479.     If Direction=Maximum Then BestSoFar :=-Maxint Else BestSoFar := Maxint;
  480.     BestNodeSoFar := Null;
  481.     While N<>Null Do    {Search all offspring}
  482.     Begin
  483.       X := GValue(N) ;
  484.       If ((X > BestSoFar) and (Direction=Maximum)) Or ((X < BestSoFar) and
  485.                                         (Direction=Minimum)) Then
  486.       Begin
  487.         BestSoFar := X;
  488.         BestNodeSoFar := N;
  489.       End;
  490.       N:=RSibling(N);
  491.     End; {Search all offspring}
  492.     BestNode := BestNodeSoFar;
  493.     BestValue := BestSoFar;
  494.     If BestNode = Null then Writeln('Warning: No moves found in',
  495.      ' FindOptOffspr');
  496.   End; {Procedure}
  497.  
  498. Begin
  499.   MoveFound:=False;
  500.   {First, evaluate the bottom level}
  501.   N := FirLevel(BottomLevel);
  502.   If N=Null Then Writeln('No bottom level (',bottomlevel,') found in ',
  503.                           'Minimax.');
  504.   While N<>Null Do
  505.   Begin
  506.     If GValue(N)=Unevaluated Then
  507.     begin
  508.       Characteristic(N,Temp);
  509.       SetGValue(N,Evaluate(Temp,Side));
  510.     end;
  511.     N := NNTL(N);
  512.   End;
  513.   {Now do the folding/pruning process}
  514.   Lev := Bottomlevel - 1;
  515.   While Lev >= 0 Do
  516.   Begin
  517.     {For each node on this level that is not already evaluated (nonterminal
  518.      nodes), fold the offspring values into the node.}
  519.     N := FirLevel(Lev);
  520.     While N<>Null Do
  521.     Begin
  522.       If Gvalue(N)=Unevaluated Then
  523.       Begin
  524.         {Alternate looking for maxima and minima}
  525.         If Odd(Lev) Then FindOptOffspr(N,F,NN,Minimum)
  526.                     Else FindOptOffspr(N,F,NN,Maximum);
  527.         {In case this isn't clear, recall (for instance) that the root is
  528.          at depth = 0.  Here, we examine the descendants of the root and
  529.          look for the maximum.}
  530.         SetGValue(N,F);
  531.         {If level = 0, this is the final pass.  Save the result.}
  532.         If Lev=0 Then
  533.         begin
  534.           Characteristic(NN,Board);
  535.           MoveFound:=true;
  536.         end;
  537.  
  538.       End;
  539.       N:=NNTL(N); {Get next node on this level}
  540.     End;          {Until finished with this level}
  541.     Lev := Lev - 1;
  542.   End; {Get next level}
  543.   If not movefound then writeln('No move found in Minimax.');
  544. End; {Procedure}
  545.  
  546.  
  547. Procedure ComputeAMove(var Board:Ctype;Side:char;Nlevels:integer);
  548. Begin
  549.   T1 := Time;
  550.   Gentree(Nlevels,Side); {Generates move tree}
  551.   Minimax(Board,Side,Nlevels); {Returns new board position}
  552.   T2 := Time;
  553.   Writeln('Computed value of move: ',GValue(Root),'.  ',NNodes,
  554.   ' examined.  Required ',(t2-t1):7:2,' seconds.');
  555. End;
  556.  
  557. Procedure MoveSide(Side:char;var CurrentBoard:ctype);
  558. Var
  559.   NewBoard:                    Ctype;
  560.  
  561. {This routine moves the white pieces and checks for win}
  562. Begin
  563.   If Human=Side then
  564.     GetInputMove(CurrentBoard,Human)
  565.   Else
  566.     ComputeAMove(CurrentBoard,Computer,Nlevels);
  567. End;
  568.  
  569.  
  570. Begin
  571.   debug[1]:=false;debug[2]:=true;debug[3]:=true;
  572.   Init(Currentboard);
  573.  
  574.   Write('How many plies to examine? ');Readln(Nlevels);
  575.   ChooseColor(Computer,Human);  {Play white or black?}
  576.   ShowBoard(Currentboard);
  577.   Side:=White;
  578.   Repeat
  579.     MoveSide(Side,CurrentBoard);
  580.     ShowBoard(CurrentBoard);
  581.     WhoWon:=Won(CurrentBoard,Side);
  582.     Side:=Opposite(Side);
  583.   Until WhoWon<>Neither;
  584.   If WhoWon=Computer then Writeln('I have won.')
  585.                      else Writeln('You have won.');
  586. End.
  587.  
  588.  
  589.  
  590. *************** TCONH.INS FOLLOWS **********************
  591.  
  592. (* Insert file for main Constant section *)
  593.      null = 0;
  594.      root = 1;
  595.      maxsize=15000; {maximum # of records that nodelist can handle}
  596.  
  597.  
  598. *************** TTYPH.INS FOLLOWS **********************
  599.  
  600.  
  601. {Insert file for type section}
  602.  
  603.   NodeType = Record
  604.                Parnt,   {node # of parent}
  605.                Levl,    {depth of node, 0=root}
  606.                Rsib,    {sibling to the immediate right}
  607.                Foff,    {node # of first offspring}
  608.                GV,
  609.                EvalF:   integer;
  610.                Charact: Ctype;  {user must define ctype}
  611.              end;
  612.  
  613.   PNodeType = ^NodeType;
  614.  
  615. *************** TVARH.INS FOLLOWS **********************
  616.  
  617.  
  618. (* Insert file for main Vars section *)
  619.     nodelist:                   array[1..maxsize] of PNodeType;
  620.     nnodes:                     integer (* Number of nodes used *);
  621.     debug:      array[1..10] of boolean (* debug print flag *);
  622.                { debug[1] is a general "dump it all" flag.
  623.                  debug[2] reports when every 1000th node is created.
  624.                  debug[3] reports every try to generate a node when tree is
  625.                           full }
  626.     lastnum:                    integer (* speeds up node generation *);
  627.     outfile:                    text    (* Debug output file *);
  628.     maxnode:                    integer  {max # nodes to use};
  629.  
  630.  
  631. *************** TPRCH.INS FOLLOWS **********************
  632.  
  633. (* Insert file for tree handling procedures *)
  634.  
  635. {Copyright 1989 Mark Minasi}
  636.  
  637. (* T R E E   R E A D / W R I T E   F U N C T I O N S
  638.  
  639.    The following six functions and six procedures read from and write to the
  640.    tree's six items of information.  They are:
  641.  
  642. Item                    Name of Read Function      Name of Write Procedure
  643.  
  644. Characteristic         Characteristic()            Setcharacteristic()
  645. Parent                 Parent()                    SetParent()
  646. Depth or Level         Depth()                     SetDepth()
  647. Sibling to Right       RSibling()                  SetRSibling()
  648. First Offspring        FirstOffspring()            SetFirstOffspring()
  649. Evaluation Func. Value Nodevalue()                 SetValue()
  650. *)
  651.  
  652. Function Parent(node:integer):integer;
  653.  
  654. (* Returns node number of parent to input node number.  Does not check if
  655.    input node number exists. *)
  656.  
  657.  
  658. var nn: PNodeType;  {DO NOT say ^NodeType -- Pascal will squawk that it's
  659.                      foreign to PNodeType}
  660.  
  661. begin
  662.   if debug[1] then if ((node<1) or (node>maxnode)) then
  663.    writeln(outfile,'PARENT:',' requested for illegal node.  Node #=',node);
  664.   nn:=NodeList[node];
  665.   parent:=nn^.Parnt;
  666. end;
  667.  
  668. Procedure Characteristic(node:integer;var Outc:ctype);
  669.  
  670. var nn: PNodeType;
  671.  
  672. begin
  673.   nn:=NodeList[node];
  674.   OutC:=nn^.Charact;
  675. end;
  676.  
  677. Function Depth(node:integer):integer;
  678.  
  679. var nn:PNodeType;
  680.  
  681. begin
  682.   nn:=NodeList[node];
  683.   depth:=nn^.Levl;
  684. end;
  685.  
  686. Function RSibling(node:integer):integer;
  687.  
  688. var nn:PNodeType;
  689.  
  690. begin
  691.   nn:=NodeList[node];
  692.   RSibling:=nn^.RSib;
  693. end;
  694.  
  695. Function FirstOffspring(node:integer):integer;
  696.  
  697. var nn:PNodeType;
  698.  
  699. begin
  700.   nn:=NodeList[node];
  701.   FirstOffspring:=nn^.Foff;
  702. end;
  703.  
  704. Function NodeValue(node:integer):integer;
  705.  
  706. var nn:PNodeType;
  707.  
  708. begin
  709.   nn:=NodeList[node];
  710.   NodeValue:=nn^.EvalF;
  711. end;
  712.  
  713. Function GValue(Node:Integer):Integer;
  714. Var nn:PNodeType;
  715. Begin
  716.   NN := NodeList[node];
  717.   GValue:=nn^.GV;
  718. End;
  719.  
  720. Procedure Setcharacteristic(node:integer;i:ctype);
  721.  
  722. var nn:PNodeType;
  723. begin
  724.   NN := NodeList[node];
  725.   nn^.Charact := i
  726. end;
  727.  
  728. Procedure SetParent(node,i:integer);
  729.  
  730. var nn:PNodeType;
  731.  
  732. begin
  733.   nn:=NodeList[node];
  734.   nn^.Parnt := i
  735. end;
  736.  
  737. Procedure SetDepth(node,i:integer);
  738.  
  739. var nn:PNodeType;
  740.  
  741. begin
  742.   nn:=NodeList[node];
  743.   nn^.Levl := i
  744. end;
  745.  
  746. Procedure SetRSibling(node,i:integer);
  747.  
  748. var nn:PNodeType;
  749.  
  750. begin
  751.   nn:=NodeList[node];
  752.   nn^.Rsib := i
  753. end;
  754.  
  755. Procedure SetFirstOffspring(node,i:integer);
  756.  
  757. var nn:PNodeType;
  758.  
  759. begin
  760.   nn:=NodeList[node];
  761.   nn^.Foff:= i
  762. end;
  763.  
  764. Procedure SetValue(node,i:integer);
  765.  
  766. var nn:PNodeType;
  767.  
  768. begin
  769.   nn:=NodeList[node];
  770.   nn^.EvalF := i
  771. end;
  772.  
  773. Procedure SetGValue(node,i:integer);
  774.  
  775. var nn:PNodeType;
  776.  
  777. begin
  778.   nn:=NodeList[node];
  779.   nn^.GV := i
  780. end;
  781.  
  782. Procedure Inittree;
  783.  
  784. (* Initializes tree by resetting root node *)
  785.  
  786. var i:         integer;
  787.     numrecs:   integer;
  788.     maxrecs:   longint;
  789.     temprec:   NodeType;
  790.  
  791. begin (* Clean out nodelist*)
  792.  
  793.   if debug[1] then writeln('INITTREE: Initializing tree.');
  794.  
  795.   release(heaporg);  {that statement cleans out the heap}
  796.   {How much space do we have?}
  797.   maxrecs:=Maxavail div sizeof(temprec);
  798.   if maxrecs>maxsize then maxnode:=maxsize else maxnode:=maxrecs;
  799.   maxnode:=maxnode-1; {leave a little room}
  800.   if debug[2] then writeln('INITTREE: There is room for ',maxnode,' nodes.',
  801.                ' Maxrecs=',maxrecs);
  802.   {create root}
  803.     GetMem(nodelist[root],sizeof(temprec));
  804.     SetParent(root,null);
  805.     SetRSibling(Root,Null);
  806.     SetFirstOffspring(Root,Null);
  807.     SetDepth(Root,0);
  808.     nnodes:=1;
  809.     lastnum:=2;
  810.     {Set up root: no parents, no offspring.  Using program must set character-
  811.     istic and value. }
  812.   for i:=2 to maxnode do NodeList[i]:=nil;
  813. end;
  814.  
  815. Function Nextnum:integer;
  816.  
  817. (* Finds first open space in NODELIST.  Returns -1 if network full.
  818.    Remember not to use the MAXNODEth node. *)
  819.  
  820. var i:integer;
  821.  
  822. Begin
  823. {Just keep counting up to the top.}
  824.   i:=lastnum;
  825.   if i>=maxnode then
  826.   begin
  827.     nextnum:=null;
  828.     if (debug[3] or debug[1] or debug[2]) then
  829.     writeln(outfile,'NEXTNUM: >>> Tree Overflow <<');
  830.   end
  831.   else nextnum:=lastnum;
  832.   lastnum:=lastnum+1;
  833. End;
  834.  
  835. Procedure Createnode(pnode:integer;charn:ctype;var node:integer);
  836.  
  837. (* Creates a node with parent pnode and characteristic charn.  Node
  838.    number of new node is output unless no nodes are left: if so, node
  839.    = null *)
  840.  
  841. var tempnode,n:integer;
  842.     temprec:NodeType;
  843.  
  844. begin
  845.  
  846. (* Find a node to start from *)
  847.  
  848.   node:=nextnum;
  849.   if node<>null then
  850.   begin
  851.  
  852.     (* Register the value in nodelist *)
  853.     GetMem(Nodelist[Node],SizeOf(temprec));
  854.     nnodes:=nnodes+1;
  855.     if debug[2] then if (nnodes mod 1000) = 0 then writeln(outfile,
  856.                       'CREATENODE: Total Nodes: ',nnodes);
  857.     (* Set attributes *)
  858.     setcharacteristic(node,charn);
  859.     setParent(node,pnode);
  860.     setDepth(node,Depth(pnode)+1);
  861.     setRSibling(node,null);
  862.     setFirstOffspring(node,null);
  863.     SetValue(node,null);
  864.  
  865.     (* Set pointer into node.  Either from parent (if only child) or
  866.        sibling to the left. *)
  867.  
  868.     if FirstOffspring(pnode)=null then setFirstOffspring(pnode,node)
  869.     else begin
  870.       (* Find closest sibling *)
  871.       tempnode:=FirstOffspring(pnode);
  872.       repeat
  873.         n:=RSibling(tempnode);
  874.         if n=null then setRSibling(tempnode,node) else tempnode:=n;
  875.       until n=null;
  876.     end;
  877.   end else
  878.   begin
  879.     {overflow}
  880.     node:=null;
  881.   end;
  882.   if debug[1] then writeln(outfile, 'CREATENODE: Created node for parent '
  883.                                ,pnode,'.  Node =',node);
  884. end;
  885.  
  886. {Search routines}
  887.  
  888. Function Nextnode(node:integer):integer;
  889.  
  890. (* Finds node number of the next node from the input, for a depthwise
  891.    search.  Returns null if tree exhausted *)
  892.  
  893. Var n:integer;
  894.  
  895. Begin
  896.  
  897. if node=null then nextnode:=null else
  898. if FirstOffspring(node) <> null then nextnode:=FirstOffspring(node)
  899.    else if RSibling(node) <> null then nextnode:=RSibling(node)
  900.         else if Depth(node)<=1 then nextnode:=null
  901.                      { if no offspring, no sibs, and level=1, exhausted }
  902.             else begin
  903.                n:=node;
  904.                repeat
  905.                  n:=Parent(n)
  906.                until (Parent(n)=root) or (RSibling(n) <> null);
  907.                nextnode:=RSibling(n)
  908.              end;
  909. End;
  910.  
  911.  
  912. Function NNTL(node:integer):integer;
  913.  
  914. (* Finds the next node on the same level as input node. Good for level-wise
  915.    search.  Again, returns null if exhausted at this level. *)
  916.  
  917. Var basedepth,n,d:integer;
  918.  
  919. Begin
  920.  
  921.    basedepth:=Depth(node);
  922.    n:=node;
  923.    repeat
  924.      n:=nextnode(n);
  925.      if n<>null then d:=Depth(n) else d:=basedepth;
  926.    until d=basedepth;
  927.    nntl:=n;
  928. if debug[1] then writeln(outfile,' Next node after ',node,' is ',n);
  929. End;
  930.  
  931. Function NTOL(node:integer):integer;
  932. (* Finds first node after input node that either (1) is the same depth as
  933.    the input node or (2) is terminal (no offspring) and closer to the root
  934.    than the input node.  Returns null if none. *)
  935.  
  936. Var BaseDepth,N,TempNode,D: integer;
  937. Begin
  938.   BaseDepth:=Depth(node);
  939.   n:=node;
  940.   repeat
  941.     n:=nextnode(n);
  942.     if n<>null then begin
  943.                       d:=Depth(n);
  944.                       TempNode:=FirstOffspring(n);
  945.                     end else begin
  946.                       d:=basedepth;
  947.                       TempNode:=1; (*Any non null value*)
  948.                     end;
  949.  
  950.   until (d=basedepth) or (TempNode=Null);
  951.   NTOL:=n;
  952. End;
  953.  
  954.  
  955. Function Firlevel(level:integer):integer;
  956.  
  957. (* Finds leftmost node at depth or level = input level *)
  958.  
  959. var tempnode,dtemp:integer;
  960. begin
  961. if level=0 then tempnode:=root else
  962.  
  963.   begin
  964.     tempnode:=root;dtemp:=Depth(tempnode);
  965.     while (dtemp<>level) and (tempnode<>null) do
  966.     begin
  967.       tempnode:=nextnode(tempnode);
  968.       if tempnode=null then dtemp:=10000 else dtemp:=Depth(tempnode);
  969.     end;
  970.   end;
  971. firlevel:=tempnode;
  972. if debug[1] then writeln(outfile,'First node at level ',level,' is ',tempnode);
  973. end;
  974.  
  975. Function BFNextNode(node:integer):integer;
  976. (* Returns next node in breadth first search *)
  977. Var
  978.   n,n1,level:integer;
  979. Begin
  980.   if debug[1] then writeln('Looking for next node after ',node);
  981.   If (NNodes=1) or (node=Null) then BFNextNode:=Null else
  982.   Begin
  983.     n:=nntl(node); (* next node this level *)
  984.     if debug[1] then begin
  985.     write('  NNTL returned ');if n=null then writeln('null.') else
  986.                       writeln(n,'.');
  987.                      end;
  988.     If n<>Null then BFNextNode:=n else
  989.     Begin
  990.       level:=Depth(node) + 1;
  991.       if debug[1] then writeln('  Searching level ',level);
  992.       BFNextNode := FirLevel(level);
  993.     End;
  994.   End;
  995. End;
  996.  
  997. Procedure PrintTree;
  998. const col = 5;
  999.       spaces:string[40] = '                                        ';
  1000. var n,l:integer;
  1001.  
  1002. Begin
  1003.  
  1004. writeln(outfile);
  1005. writeln(outfile,'Number   Charac.  Depth  Value GValue');
  1006.  
  1007. n:=root;
  1008. while n<>null do
  1009. begin
  1010.   l:=Depth(n);
  1011.   write(outfile,copy(spaces,1,l*2));
  1012.   writeln(outfile,n:col,'Chrctc',Depth(n):col,NodeValue(n):col,
  1013.           GValue(n):col);
  1014.  
  1015.   n:=NextNode(n);
  1016. end;
  1017. writeln(outfile,nnodes,' total nodes.');
  1018. end;
  1019.  
  1020.  
  1021.