home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / AIE8909.ZIP / APPRENTI next >
Encoding:
Text File  |  1989-09-01  |  14.6 KB  |  499 lines

  1.  
  2.  
  3. MAXITH CODE:
  4.  
  5.  
  6. {$R+}    {Range checking off}
  7. {$B+}    {Boolean complete evaluation on}
  8. {$S+}    {Stack checking on}
  9. {$I+}    {I/O checking on}
  10. {$N-}    {No numeric coprocessor}
  11.  
  12. Program Maxit;
  13. {This demonstrates a straightforward approach to computer game playing: an
  14.  N level search followed by minimax-based back pruning}
  15.  
  16. {White moves first and horizontal.  Black moves second and vertical.}
  17.  
  18. Uses
  19.   Dos;
  20.  
  21. Const
  22.  
  23. {$I tconh.ins}
  24.  
  25.   Taken = -100;
  26.   Black = 'B';
  27.   White = 'W';
  28.   Neither = '[';
  29.   Expanded = 1;
  30.   Unexpanded = 2;
  31.   BigWin = 32000;
  32.   BigLoss = -32000;
  33.   UnEvaluated = -Maxint;
  34.   {Change the next two things to alter the dimension of the board}
  35.   BD = 8; {Board Dimension}
  36.  
  37. Type
  38.      bry = array[1..bd,1..bd] of integer;
  39.      ctype = record
  40.               whosemove:char;         {whose move brought us here}
  41.                                       {It changes on the human side in
  42.  GetInputMove.  The current board is input, and so whosemove shows the
  43.  computer at that point, as the computer got us to the current state.
  44.  Once inside GetInputMove, the board changes and whosemove changes too.}
  45.               icoord:integer;         {current cursor location}
  46.               jcoord:integer;
  47.               bscore:integer;         {black points and white points,}
  48.               wscore:integer;         {both including current item}
  49.               brd: bry;
  50.                                       {-100 indicates taken}
  51.             end;
  52.  
  53. {$i ttyph.ins}
  54.  
  55. Var
  56.  
  57. {$I tvarh.ins}
  58. EmptyCType:                            ctype;
  59. Human, Computer:                       char;
  60. Winner:                                char;
  61. CurrentBoard:                          ctype;
  62. Nlevels:                               Integer; {Number of levels to search}
  63. TreeFull:                              Boolean;
  64. T1,T2:                                 Real; {for timekeeping}
  65.  
  66. {$I tprch.ins}
  67. {$I time.ins}
  68.  
  69. Procedure Init(var Board:Ctype);
  70. Var I,J:Integer;
  71. Begin
  72. with board do begin
  73.   {Set up Board}
  74.   for i:=1 to bd do for j:=1 to bd do brd[i,j]:=random(40);
  75.   for i:=1 to bd do for j:=1 to bd do brd[i,j]:=brd[i,j]-20;
  76.   whosemove:=black;{starts off white}
  77.   bscore:=0;wscore:=0;
  78.   icoord:=2;jcoord:=1;
  79.   brd[icoord,jcoord]:=taken;
  80.   Assign(outfile,'');
  81.   Rewrite(outfile);
  82. end;
  83. End;
  84.  
  85. Function Opposite(Side:char):char;
  86. Begin
  87.   If Side=White then Opposite:=Black
  88.   Else If Side=Black then Opposite:=White
  89.   Else Writeln('OPPOSITE:  Requested opposite for side=',side);
  90. End;
  91.  
  92. Function Won(TestBoard:Ctype):char;
  93. {Examines board TestBoard to see, given that WhosMove just moved, whether or
  94.  not WhosMove just won.}
  95.  
  96. Var
  97.   Whosmove:char;
  98.   SoFar : char;
  99.   I,J,K,L,BSum,WSum,DI: Integer;
  100.   CheckedColor:char;
  101.   AllBlocked:               boolean;
  102. Begin
  103.   With TestBoard Do
  104.   Begin
  105.     Whosmove := Whosemove;
  106.     Allblocked:=true;
  107.     If whosemove=black then  {black got us here, so check if white is now
  108.                               blocked}
  109.     begin
  110.       for j:=1 to bd do
  111.       if brd[icoord,j]<>taken then allblocked:=false;
  112.     end else
  113.     if whosemove=white then
  114.     begin
  115.       for i:=1 to bd do
  116.       if brd[i,jcoord]<>taken then allblocked:=false;
  117.     end else
  118.     writeln('WON: side not black or white=',whosemove);
  119.  
  120.     if not allblocked then won:=neither else
  121.     if bscore > wscore then won:=black else won:=white;
  122.   end;
  123. End; {Procedure}
  124.  
  125. Procedure Expand(N:integer;OriginalSide:char;var TreeFilled:boolean;
  126.                  Nlevels:integer);
  127.  
  128. label panic;
  129. { This expands node N, producing all of its offspring nodes.  Any control
  130.   strategies are implemented here.
  131.  
  132.   Output is entirely in the tree structure.
  133.  
  134.   When done, sets node N to "expanded."
  135.  
  136.   N, upon start of the procedure, is the previous (human) move.
  137.  
  138.   Generated offspring are set to "unexpanded" unless they are terminal nodes or
  139.   at max depth, in which case they are set to "expanded".
  140. }
  141.  
  142. Var
  143.    Od,Nw:Ctype;  {parent and child states}
  144.    brdnow,newbrd: bry;
  145.    whonow,WhoWon:char;
  146.    junk,inow,jnow,bnow,wnow,i0,j0,k,i,j,di,dj,NN:integer;
  147.    WinFlag:boolean;
  148.  
  149. begin
  150.   TreeFilled:=False;
  151.   Characteristic(N,Od);
  152.   With Od do
  153.   begin  brdnow:=Od.Brd; whonow:=Opposite(Od.whosemove);
  154.          inow:=Od.icoord; jnow:=Od.jcoord;bnow:=Od.Bscore;wnow:=Od.Wscore;
  155.   end;
  156.   {Preliminary setups for Nw, new state}
  157.   {Remember that if we're about to move for black, Whosemove is white.}
  158.   Nw:=Od;
  159.   Nw.Whosemove:=whonow;
  160.   if whonow=white then begin di:=0; dj:=1; i0:=inow; j0:=0; end;
  161.   if whonow=black then begin di:=1; dj:=0; i0:=0; j0:=jnow; end;
  162.   {scan the row/column}
  163.   For k:=1 to bd do
  164.   begin      {For each possible move on this level}
  165.     i:=i0+k*di; j:= j0+k*dj;
  166.     {check to see if it's available}
  167.     if brdnow[i,j]<>taken then
  168.     begin  {this block creates a new node}
  169.       {first create the new space}
  170.       Nw.Brd:=brdnow;
  171.       if whonow=white then Nw.wscore:=Od.wscore+brdnow[i,j]
  172.                       else Nw.bscore:=Od.bscore+brdnow[i,j];
  173.       Nw.Brd[i,j]:=taken;Nw.icoord:=i;Nw.jcoord:=j;
  174.       {now create the node}
  175.       CreateNode(N,Nw,NN);
  176.       If (NN=null) then
  177.       begin
  178.         if (debug[2] or debug[3])then
  179.                  writeln('EXPAND: Tree space filled!');
  180.         TreeFilled:=True;
  181.         goto panic;
  182.       {Is it a win?}
  183.       end;
  184.       WhoWon:=Won(Nw);
  185.       WinFlag:=(WhoWon<>Neither);
  186.       {If a win, it's expanded and evaluated}
  187.       If WinFlag Then
  188.       begin   {handle game over position}
  189.         If WhoWon=OriginalSide Then
  190.         SetGValue(NN,BigWin) Else SetGValue(NN,BigLoss);
  191.         SetValue(NN,Expanded);
  192.       end     {handle game over position}
  193.       else
  194.       begin   {this block handles terminal or normal node}
  195.         {if terminal, expanded/unevaluated}
  196.         if (Depth(NN)>=Nlevels) Then
  197.         Begin
  198.           SetGValue(NN,UnEvaluated); SetValue(NN,Expanded);
  199.         end else
  200.         Begin {this block handles normal nodes--unevaluated, unexpanded}
  201.           SetGValue(NN,UnEvaluated);SetValue(NN,UnExpanded);
  202.         End   {this block handles normal nodes}
  203.       end     {this block handles terminal or normal node}
  204.     end {this block creates a new node}
  205.   end;
  206.   {Now that we've generated all offspring, set parent to Expanded}
  207.   panic:
  208.   SetValue(N,Expanded);
  209. End;
  210.  
  211. Procedure ShowBoard(Board:Ctype);
  212. Var I,J:Integer;
  213. {Prints out the selected board string}
  214. Begin
  215.   with board do
  216.   begin
  217.     for i:=1 to bd do
  218.     begin
  219.       for j:=1 to bd do
  220.       if ((i=icoord) and (j=jcoord)) then write(outfile,'*':4)
  221.       else if (brd[i,j]=taken) then write(outfile,'X':4)
  222.       else write(outfile,brd[i,j]:4);
  223.       writeln(outfile);
  224.     end;
  225.     Write('Player that lead to this move:',whosemove);
  226.     Writeln(' White Score: ',wscore,' Black Score: ',bscore);
  227.   end;
  228.   writeln;
  229. End;
  230.  
  231.  
  232. Procedure ChooseColor;
  233. Var Answer:char;
  234. Begin
  235.   Write('Would you like to go first? ');
  236.   Readln(Answer);Answer:=UpCase(Answer);
  237.   If Answer='Y' then Begin
  238.                        Human:=White;
  239.                        Computer:=Black;
  240.                      End Else
  241.                      Begin
  242.                        Human:=Black;
  243.                        Computer:=White;
  244.                      End;
  245. End;
  246.  
  247.  
  248. Function Evaluate(Tester:ctype;OriginalSide:char):Integer;
  249. {Returns the integer evaluation function value for a given board position
  250.  and a given side.}
  251. Var
  252.   Enemy:                 char;
  253.   Us,Them,I:               Integer;
  254.   Board:bry;
  255. Begin
  256.   Board:=Tester.Brd;
  257.   if OriginalSide=white then evaluate:=tester.wscore-tester.bscore
  258.   else if OriginalSide=black then evaluate:=tester.bscore-tester.wscore
  259.   else writeln('EVALUATE: Side not black or white found.  OriginalSide=',
  260.                 OriginalSide);
  261. end;
  262.  
  263.  
  264. Procedure FindFirst(Var N:integer);
  265. Var
  266.   M:         Integer;
  267.   Alldone:   Boolean;
  268. { Locates first unexpanded node.  This version uses a depth-first search.}
  269. Begin
  270.   M:=Root;
  271.   Alldone:=False;
  272.   While not Alldone do
  273.   Begin
  274.     If M=Null then Alldone:=True else
  275.     Begin
  276.       If NodeValue(M) = Unexpanded then Alldone:=True else M:=NextNode(M);
  277.     End;
  278.   End;
  279.   N:=M;
  280. End;
  281.  
  282. Procedure Gentree(var Nlevels:Integer;var CurrentBoard:Ctype);
  283. {Generates a game move tree Nlevels deep from state CurrentBoard}
  284. {Is allowed to modify Nlevels and inform Minimax if necessary}
  285. Var
  286.   N:Integer;
  287.   CompleteTree,AllDone,TreeFilled:Boolean;
  288.  
  289. Begin
  290.   CompleteTree:=False;
  291.   While Not CompleteTree do
  292.   begin
  293.     Inittree;
  294.     SetCharacteristic(Root,CurrentBoard);
  295.     SetValue(Root,Unexpanded);
  296.     SetGValue(Root,Unevaluated);
  297.     Alldone:=False;
  298.     While not Alldone Do
  299.     Begin
  300.       {Find first unexpanded node.}
  301.       FindFirst(N);
  302.       IF N=Null then Alldone:=True Else
  303.       Expand(N,Opposite(CurrentBoard.WhoseMove),TreeFilled,Nlevels);
  304.       if TreeFilled Then
  305.       begin
  306.         Nlevels:=Nlevels-1;
  307.         writeln('GENTREE:Tree was filled.  Only ',nlevels,
  308.         ' plies will be examined.');
  309.         CompleteTree:=False;
  310.         Alldone:=true;
  311.       end else CompleteTree:=true;
  312.     end;
  313.   End;
  314. End;
  315.  
  316.  
  317. Procedure GetInputMove(var Current:ctype);
  318. {Gets latest move from human player.  Checks for validity of moves}
  319. {ASSUMES that the current board is passed in, new board returned}
  320. Var Valid:boolean;
  321.     inval,Row1,Col1:Integer;
  322.     EN:                       Integer;
  323. Begin
  324.   EN := 0; {No errors to start}
  325.   Current.Whosemove:=Opposite(Current.Whosemove); {Next side moves now}
  326.   Repeat
  327.     Valid:=True;     {if white, we're moving horizontal}
  328.     Write(current.whosemove,', which ');
  329.     If Current.Whosemove=black then Write('row to take? ')
  330.                                else Write('column to take? ');
  331.     Readln(inval);
  332.     if Current.Whosemove=black then
  333.     begin
  334.       Row1:=inval; Col1:=Current.Jcoord;
  335.     end else
  336.     begin
  337.       Row1:=Current.Icoord; Col1:=inval;
  338.     end;
  339.     if Current.Brd[Row1,Col1]=Taken Then
  340.     begin
  341.       Writeln('That''s already taken.');
  342.       Valid:=false;
  343.     end;
  344.   Until Valid;
  345.   Writeln('Valid Move.');
  346.   With Current Do
  347.   begin
  348.     icoord:=Row1;
  349.     jcoord:=Col1;
  350.     if whosemove=white then wscore:=wscore+brd[Row1,Col1] else
  351.                             bscore:=bscore+brd[Row1,Col1];
  352.     brd[Row1,Col1]:=Taken;
  353.   End;
  354. End; {Procedure}
  355.  
  356. Procedure Minimax(Var Board:ctype;OriginalSide:char;BottomLevel:Integer);
  357. Const
  358.  
  359.   Maximum = 10;
  360.   Minimum = 11;
  361.  
  362. Var
  363.   N,Lev,F,NN:          Integer;
  364.   Temp:                ctype;
  365.   movesfound:          boolean;
  366.  
  367.   Procedure FindOptOffspr(Node:Integer;Var BestValue,BestNode:Integer;
  368.                           Direction:Integer);
  369.   {Searches all offspring of node Node to find optimal (minimum or maximum)
  370.    G values.  Best G value and node where it was found are stored in BestValue
  371.    and BestNode.  Direction indicates whether to find minimum or maximum.}
  372.   Var
  373.     N,X,BestSoFar,BestNodeSoFar,Factor:            Integer;
  374.   Begin
  375.     N:=FirstOffspring(Node);
  376.     If Direction=Maximum Then BestSoFar :=-Maxint Else BestSoFar := Maxint;
  377.     BestNodeSoFar := Null;
  378.     While N<>Null Do    {Search all offspring}
  379.     Begin
  380.       X := GValue(N) ;
  381.       If ((X > BestSoFar) and (Direction=Maximum)) Or ((X < BestSoFar) and
  382.                                         (Direction=Minimum)) Then
  383.       Begin
  384.         BestSoFar := X;
  385.         BestNodeSoFar := N;
  386.       End;
  387.       N:=RSibling(N);
  388.     End; {Search all offspring}
  389.     BestNode := BestNodeSoFar;
  390.     BestValue := BestSoFar;
  391.     If BestNode = Null then Writeln('FINDOPTOFFSPR: Warning: No moves found.');
  392.   End; {Procedure}
  393.  
  394. Begin
  395.   Board := emptyctype;
  396.   movesfound:=false;
  397.   {First, evaluate the bottom level}
  398.   N := FirLevel(BottomLevel);
  399.   If N=Null Then Writeln('MINIMAX:No bottom level (',bottomlevel,') found in ',
  400.                           'Minimax.');
  401.   While N<>Null Do
  402.   Begin
  403.     If GValue(N)=Unevaluated Then
  404.     begin
  405.       Characteristic(N,Temp);
  406.       SetGValue(N,Evaluate(Temp,OriginalSide));
  407.     end;
  408.     N := NNTL(N);
  409.   End;
  410.   {Now do the folding/pruning process}
  411.   Lev := Bottomlevel - 1;
  412.   While Lev >= 0 Do
  413.   Begin
  414.     {For each node on this level that is not already evaluated (nonterminal
  415.      nodes), fold the offspring values into the node.}
  416.     N := FirLevel(Lev);
  417.     While N<>Null Do
  418.     Begin
  419.       If Gvalue(N)=Unevaluated Then
  420.       Begin
  421.         {Alternate looking for maxima and minima}
  422.         If Odd(Lev) Then FindOptOffspr(N,F,NN,Minimum)
  423.                     Else FindOptOffspr(N,F,NN,Maximum);
  424.         {In case this isn't clear, recall (for instance) that the root is
  425.          at depth = 0.  Here, we examine the descendants of the root and
  426.          look for the maximum.}
  427.         SetGValue(N,F);
  428.         {If level = 0, this is the final pass.  Save the result.}
  429.         If Lev=0 Then begin Characteristic(NN,Board); movesfound:=true;end;
  430.       End;
  431.       N:=NNTL(N); {Get next node on this level}
  432.     End;          {Until finished with this level}
  433.     Lev := Lev - 1;
  434.   End; {Get next level}
  435.   If not movesfound then writeln('No move found in Minimax.');
  436. End; {Procedure}
  437.  
  438.  
  439. Procedure ComputeAMove(var Board:ctype;Nlevels:integer);
  440. Begin
  441.   T1 := Time;
  442.   Gentree(Nlevels,CurrentBoard); {Generates move tree}
  443.                                  {May modify Nlevels temporarily,
  444.                                   but that will not affect the actual
  445.                                   Nlevels}
  446.   Minimax(Board,Opposite(CurrentBoard.WhoseMove),Nlevels);
  447.   {Returns new board position, modifies CurrentBoard}
  448.   T2 := Time;
  449.   Writeln('Computed value of move: ',GValue(Root),'.  ',NNodes,
  450.   ' examined.  Required ',(t2-t1):7:2,' seconds.');
  451. End;
  452.  
  453. Procedure MoveSide(var CurrentBoard:Ctype;Nlevels:integer;var Winner:char);
  454. Var
  455.   NewBoard:                    ctype;
  456.   Side:                        char;
  457.  
  458. Begin
  459.   Side:=opposite(currentboard.whosemove);
  460.   If Human=Side then
  461.   Begin
  462.     NewBoard:=CurrentBoard;
  463.     GetInputMove(NewBoard);
  464.     CurrentBoard := NewBoard;
  465.   End
  466.    Else
  467.   Begin
  468.     NewBoard:=CurrentBoard;
  469.     ComputeAMove(NewBoard,Nlevels);
  470.     CurrentBoard := NewBoard;
  471.   End;
  472.   Winner:= Won(CurrentBoard);
  473. End;
  474.  
  475.  
  476. Begin
  477.   debug[1]:=false;debug[2]:=false;debug[3]:=false;
  478.   Init(CurrentBoard);  {sets up CurrentBoard}
  479.   Write('How many plies to examine? ');Readln(Nlevels);
  480.   ChooseColor;  {Play white or black?}
  481.   ShowBoard(CurrentBoard);
  482.   Repeat
  483.     MoveSide(CurrentBoard,Nlevels,Winner);  {Changes CurrentBoard}
  484.     if CurrentBoard.Whosemove=Computer Then Writeln('>> Computer Move:');
  485.     ShowBoard(CurrentBoard);
  486.   Until Winner<>Neither;
  487.   If Winner=Computer then Writeln('I have won.')
  488.                  else Writeln('You have won.');
  489. End.
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496.  
  497.  
  498. her;
  499.   If Winner=Computer th