home *** CD-ROM | disk | FTP | other *** search
/ ftp.gamers.org / ftp.gamers.org.zip / ftp.gamers.org / pub / games / uwp-uml / misc / gomoku.exe / GO-MOKU.BAK < prev    next >
Text File  |  1987-12-21  |  21KB  |  695 lines

  1. {                             GO-MOKU 4.0
  2.             Copyright (c) 1985, 87 by Borland International, Inc.
  3.  
  4.   This program plays a very old Japanese game called GO-MOKU,
  5.   perhaps better known as  5-in-line.   The game is played on
  6.   a board with 19 x 19 squares, and the object of the game is
  7.   to get 5 stones in a row.
  8.  
  9.   System requirements:  IBM PC and true compatibles
  10.                         TURBO PASCAL 4.0
  11.                         DOS 2.0 or later
  12.                         128 K-bytes system memory (minimum)
  13.  
  14.   List of include modules:
  15.     GO-HELP.INC
  16.  
  17.   List of data files:
  18.     GO-MOKU.HLP   - Help text
  19. }
  20. program Gomoku;
  21. {$R-}
  22.  
  23.  
  24. Uses
  25.   Crt;
  26.  
  27. const
  28.   N            =  19;                            { Size of the board }
  29.   Esc          = #27;
  30.   NullKey      = #0;
  31.   CtrlC        =  #3;
  32.   Return       = #13;
  33.   Space        = #32;
  34.   AttackFactor =   4;                 { Importance of attack (1..16) }
  35.                     { Value of having 0, 1,2,3,4 or 5 pieces in line }
  36.   Weight       : array[0..6] of integer = (0, 0, 4, 20, 100, 500, 0);
  37.   NormalColor  : integer = White;
  38.   BorderColor  : integer = Yellow;
  39.   BoardColor   : integer = Cyan;
  40.   HeadingColor : integer = Brown;
  41.  
  42. type
  43.   TypeOfWin  = (Null, Horiz, DownLeft, DownRight, Vert);
  44.   BoardType  = (Empty, Cross, Nought);        { Contents of a square }
  45.   ColorType  = Cross..Nought;                      { The two players }
  46.   IndexType  = 1..N;                            { Index to the board }
  47.   NumberType = 0..5;                    { Number of pieces in a line }
  48.   LineType   = array[ColorType] of NumberType;
  49.                                    { Value of square for each player }
  50.   ValueType  = array[ColorType] of integer;
  51.   MaxString  = string[255];     { Used only as a procedure parameter }
  52.  
  53. var
  54.   Board      : array[IndexType, IndexType] of BoardType; { The board }
  55.   Player     : ColorType;            { The player whose move is next }
  56.   TotalLines : integer;             { The number of empty lines left }
  57.   GameWon    : boolean;          { Set if one of the players has won }
  58.   FileRead   : boolean;        { Help file read? ... Help system ... }
  59.                     { Number of pieces in each of all possible lines }
  60.   Line       :  array[0..3, IndexType, IndexType] of LineType;
  61.                               { Value of each square for each player }
  62.   Value      : array[IndexType, IndexType] of ValueType;
  63.   X, Y       : IndexType;                         { Move coordinates }
  64.   Command    : char;                         { Command from keyboard }
  65.   AutoPlay   : boolean;           { The program plays against itself }
  66.  
  67. procedure Abort;
  68. { Exit from the program }
  69. begin
  70.   TextColor(White);
  71.   TextBackground(Black);
  72.   LowVideo;
  73.   Window(1, 1, 80, 25);
  74.   GotoXY(1, 24);
  75.   Halt;
  76. end; { Abort }
  77.  
  78. procedure SetUpScreen;
  79. { Sets up the screen with an empty board }
  80. type
  81.   Str5=string[5];
  82.  
  83. procedure WriteBoard(N : integer; Top, Middle, Bottom : Str5);
  84. { Print the empty board and the border }
  85. var
  86.   I, J : IndexType;
  87.  
  88. procedure WriteLetters;
  89. { Write the letters }
  90. var
  91.   i : IndexType;
  92. begin
  93.   TextColor(BorderColor);
  94.   Write('  ');
  95.   for I := 1 to N do
  96.     Write(Chr(Ord('A') + I - 1):2);
  97.   WriteLn;
  98. end; { WriteLetters }
  99.  
  100. procedure WriteBoardLine(J : integer; S : Str5);
  101. { Write one line of the board }
  102. var
  103.   i : IndexType;
  104. begin
  105.   TextColor(BorderColor);
  106.   Write(J:2, ' ');
  107.   TextColor(BoardColor);
  108.   Write(s[1]);
  109.   for i := 2 to N - 1 do
  110.     Write(S[2], S[3]);
  111.   Write(S[4], S[5]);
  112.   TextColor(BorderColor);
  113.   WriteLn(' ', J:2);
  114. end; { WriteBoardLine }
  115.  
  116. begin { WriteBoard }
  117.   GotoXY(1, 1);
  118.   WriteLetters;
  119.   WriteBoardLine(N, Top);
  120.   for J := N - 1 downto 2 do
  121.     WriteBoardLine(J, Middle);
  122.   WriteBoardLine(1, Bottom);
  123.   WriteLetters;
  124. end; { WriteBoard }
  125.  
  126. begin { SetUpScreen }
  127.   WriteBoard(N, '┌─┬─┐',
  128.                 '├─┼─┤',
  129.                 '└─┴─┘');
  130.   TextColor(NormalColor);
  131. end; { SetUpScreen }
  132.  
  133. procedure GotoSquare(X, Y : IndexType);
  134. begin
  135.   GotoXY(2 + X * 2, N + 2 - Y);
  136. end; { GotoSquare }
  137.  
  138. procedure PrintMove(Piece : ColorType; X, Y : IndexType);
  139. { Prints a move }
  140. const
  141.   PieceChar  : array[ColorType] of char = ('X', '0');
  142.   PieceColor : array[ColorType] of byte = (White, LightGreen);
  143. begin
  144.   TextColor(PieceColor[Piece]);
  145.   GotoXY(49, 9);
  146.   Write(PieceChar[Piece], Chr(Ord('A') + X - 1):2, Y);
  147.   ClrEOL;
  148.   GotoSquare(X, Y);
  149.   Write(PieceChar[Piece]);
  150.   GotoSquare(X, Y);
  151.   TextColor(NormalColor);
  152. end; { PrintMove }
  153.  
  154. procedure ClearMove;
  155. { Clears the line where a move is displayed }
  156. begin
  157.   GotoXY(49, 9);
  158.   ClrEOL;
  159. end; { ClearMove }
  160.  
  161. procedure PrintMsg(Str : MaxString);
  162. { Prints a message }
  163. begin
  164.   TextColor(NormalColor);
  165.   GotoXY(1, 23);
  166.   Write(Str);
  167. end; { Print }
  168.  
  169. procedure ClearMsg;
  170. { Clears the message about the winner }
  171. begin
  172.   GotoXY(1,23);
  173.   ClrEOL;
  174. end; { ClearMsg }
  175.  
  176. procedure WriteHelp(S : MaxString; HiLen : byte);
  177. { Use one video background for HiLen bytes of
  178.   string, use other for HiLen + 1 to Length(s) }
  179. begin
  180.   TextBackground(NormalColor);
  181.   TextColor(Black);
  182.   Write(Copy(S, 1, HiLen));
  183.   TextBackground(Black);
  184.   TextColor(NormalColor);
  185.   Write(Copy(S, HiLen + 1, Length(s) - HiLen));
  186. end; { WriteHelp }
  187.  
  188. {
  189.   Please note that the help system is modular and may be easily
  190.   removed or incorporated into other programs.
  191.  
  192.   To remove the help system:
  193.     1.  Delete all lines with the comment ... Help system ...
  194.     2.  Delete the line that includes the HELP.INC file
  195.  
  196.   To incorporate the help system:
  197.     1.  Declare a global type:  MaxString = string[255]
  198.     2.  Include all lines with the comment ... Help system ...
  199.     3.  Include the HELP.INC file
  200. }
  201. {$I GO-HELP.INC           ... Help system ... }
  202.  
  203. procedure WriteCommand(S : MaxString);
  204. { Highlights the first letter of S }
  205. begin
  206.   TextColor(NormalColor);
  207.   Write(S[1]);
  208.   TextColor(NormalColor - 8);
  209.   Write(Copy(S, 2, Length(s) - 1));
  210. end; { WriteCommand }
  211.  
  212. procedure ResetGame(FirstGame : boolean);
  213. { Resets global variables to start a new game }
  214. var
  215.   I, J : IndexType;
  216.   D    : 0..3;
  217.   C    : ColorType;
  218. begin
  219.   SetUpScreen;
  220.   if FirstGame then
  221.   begin
  222.     TextColor(HeadingColor);
  223.     GotoXY(49, 1);
  224.     Write('T U R B O - G O M O K U');
  225.     GotoXY(49, 3);
  226.     WriteCommand('Newgame ');
  227.     WriteCommand('Quit ');
  228.     WriteCommand('Auto ');
  229.     WriteCommand('Play ');
  230.     WriteCommand('Hint');
  231.     GotoXY(49, 5);                                    { ... Help system ... }
  232.     WriteHelp('?-for Help    ', 1);                   { ... Help system ... }
  233.     FirstGame := false;
  234.   end
  235.   else
  236.   begin
  237.     ClearMsg;
  238.     ClearMove;
  239.   end;
  240.   for I := 1 to N do
  241.     for J := 1 to N do
  242.     begin                          { Clear tables }
  243.       Board[I, J] := Empty;
  244.       for C := Cross to Nought do
  245.       begin
  246.         Value[I, J, C] := 0;
  247.         for D := 0 to 3 do
  248.           Line[D, I, J, C] := 0;
  249.       end;
  250.     end; { for }
  251.   Player := Cross;               { Cross starts }
  252.   TotalLines := 2 * 2 * (N * (N - 4) + (N - 4) * (N - 4)); { Total number }
  253.   GameWon := false;                                        { of lines     }
  254. end; { ResetGame }
  255.  
  256. function OpponentColor(Player : ColorType) : ColorType;
  257. begin
  258.   if Player = Cross then
  259.     OpponentColor := Nought
  260.   else
  261.     OpponentColor := Cross;
  262. end; { OpponentColor }
  263.  
  264. procedure BlinkWinner(Piece : ColorType;
  265.                        X, Y : IndexType;
  266.                 WinningLine : TypeOfWin);
  267. { Prints the 5 winning stones in blinking color }
  268. const
  269.   PieceChar  : array[ColorType] of char = ('X', '0');
  270.   PieceColor : array[ColorType] of byte = (White, LightGreen);
  271.  
  272. var
  273.   XHold, YHold : integer; { Used to store the position of the winning move }
  274.   Dx, Dy       : integer; { Change in X and Y }
  275.  
  276. procedure BlinkRow(X, Y, Dx, Dy : integer);
  277. { Blink the row of 5 stones }
  278. var
  279.   I : integer;
  280. begin
  281.   TextColor(PieceColor[Piece] + blink);
  282.   for I := 1 to 5 do
  283.   begin
  284.     GotoSquare(X, Y);
  285.     Write(PieceChar[Piece]);
  286.     X := X - Dx;
  287.     Y := Y - Dy;
  288.   end;
  289. end; { BlinkRow }
  290.  
  291. begin { BlinkRow }
  292.   TextColor(PieceColor[Piece]);
  293.   GotoXY(49, 9);
  294.   Write(PieceChar[Piece],
  295.         Chr(Ord('A') + X - 1):2, Y);          { display winning move }
  296.   ClrEOL;
  297.   XHold := X;                            { preserve winning position }
  298.   YHold := Y;
  299.   case WinningLine of
  300.     Horiz : begin
  301.               Dx := 1;
  302.               Dy := 0;
  303.             end;
  304.     DownLeft : begin
  305.                   Dx := 1;
  306.                   Dy := 1;
  307.                 end;
  308.     Vert : begin
  309.              Dx := 0;
  310.              Dy := 1;
  311.            end;
  312.     DownRight : begin
  313.                   Dx := -1;
  314.                   Dy := 1;
  315.                 end;
  316.   end; { case }
  317.   while ((X + Dx) >= 1) and ((X + Dx) <= n) and
  318.         ((Y + DY) >= 1) and ((Y + DY) <= n) and
  319.         (Board[X + Dx, Y + Dy] <> Empty)   and { go to topmost, leftmost }
  320.         (Board[X + Dx, Y + Dy] = Piece ) do
  321.   begin
  322.      X := X + Dx;
  323.      Y := Y + Dy;
  324.   end;
  325.   BlinkRow(X, Y, Dx, Dy);
  326.   X := XHold;                             { restore winning position }
  327.   Y := YHold;
  328.   GotoSquare(X, Y);                      { go back to winning square }
  329.   TextColor(NormalColor);
  330. end; { BlinkWinner }
  331.  
  332. procedure MakeMove(X, Y : IndexType);
  333. { Performs the move X,Y for player, and updates the global variables
  334.   (Board, Line, Value, Player, GameWon, TotalLines and the screen)   }
  335.  
  336. var
  337.   Opponent : ColorType;
  338.   X1 ,Y1   : integer;
  339.   K, L     : NumberType;
  340.   WinningLine : TypeOfWin;
  341.  
  342. procedure Add(var Num : NumberType);
  343. { Adds one to the number of pieces in a line }
  344. begin
  345.   Num := Num + 1;                  { Adds one to the number.     }
  346.   if Num = 1 then                  { If it is the first piece in }
  347.     TotalLines := TotalLines - 1;  { the line, then the opponent }
  348.                                    { cannot use it any more.     }
  349.   if Num = 5 then                  { The game is won if there    }
  350.     GameWon := true;               { are 5 in line.              }
  351. end; { Add }
  352.  
  353. procedure Update(Lin : LineType; var Valu : ValueType);
  354. { Updates the value of a square for each player, taking into
  355.   account that player has placed an extra piece in the square.
  356.   The value of a square in a usable line is Weight[Lin[Player]+1]
  357.   where Lin[Player] is the number of pieces already placed
  358.   in the line }
  359. begin
  360.   { If the opponent has no pieces in the line, then simply
  361.     update the value for player }
  362.   if Lin[Opponent] = 0 then
  363.     Valu[Player] := Valu[Player] +
  364.                        Weight[Lin[Player] + 1] - Weight[Lin[Player]]
  365.   else
  366.     { If it is the first piece in the line, then the line is
  367.       spoiled for the opponent }
  368.     if Lin[Player] = 1 then
  369.       Valu[Opponent] := Valu[Opponent] - Weight[Lin[Opponent] + 1];
  370. end; { Update }
  371.  
  372. begin  { MakeMove }
  373.   WinningLine := Null;
  374.   Opponent := OpponentColor(Player);
  375.   GameWon := false;
  376.  
  377.   { Each square of the board is part of 20 different lines.
  378.     The procedure adds one to the number of pieces in each
  379.     of these lines. Then it updates the value for each of the 5
  380.     squares in each of the 20 lines. Finally Board is updated, and
  381.     the move is printed on the screen. }
  382.  
  383.   for K := 0 to 4 do           { Horizontal lines, from left to right }
  384.   begin
  385.     X1 := X - K;                           { Calculate starting point }
  386.     Y1 := Y;
  387.     if (1 <= X1) and (X1 <= N - 4) then        { Check starting point }
  388.     begin
  389.       Add(Line[0, X1, Y1, Player]);                 { Add one to line }
  390.       if GameWon and (WinningLine = Null) then    { Save winning line }
  391.         WinningLine := Horiz;
  392.       for L := 0 to 4 do { Update value for the 5 squares in the line }
  393.         Update(Line[0, X1, Y1], Value[X1 + L, Y1]);
  394.     end;
  395.   end; { for }
  396.  
  397.   for K := 0 to 4 do { Diagonal lines, from lower left to upper right }
  398.   begin
  399.     X1 := X - K;
  400.     Y1 := Y - K;
  401.     if (1 <= X1) and (X1 <= N - 4) and
  402.        (1 <= Y1) and (Y1 <= N - 4) then
  403.     begin
  404.       Add(Line[1, X1, Y1, Player]);
  405.       if GameWon and (WinningLine = Null) then    { Save winning line }
  406.         WinningLine := DownLeft;
  407.       for L := 0 to 4 do
  408.         Update(Line[1, X1, Y1], Value[X1 + L, Y1 + L]);
  409.     end;
  410.   end; { for }
  411.  
  412.   for K := 0 to 4 do       { Diagonal lines, down right to upper left }
  413.   begin
  414.     X1 := X + K;
  415.     Y1 := Y - K;
  416.     if (5 <= X1) and (X1 <= N) and
  417.        (1 <= Y1) and (Y1 <= N - 4) then
  418.     begin
  419.       Add(Line[3, X1, Y1, Player]);
  420.       if GameWon and (WinningLine = Null) then    { Save winning line }
  421.         WinningLine := DownRight;
  422.       for L := 0 to 4 do
  423.         Update(Line[3, X1, Y1], Value[X1 - L, Y1 + L]);
  424.     end;
  425.   end; { for }
  426.  
  427.   for K := 0 to 4 do                { Vertical lines, from down to up }
  428.   begin
  429.     X1 := X;
  430.     Y1 := Y - K;
  431.     if (1 <= Y1) and (Y1 <= N - 4) then
  432.     begin
  433.       Add(Line[2, X1, Y1, Player]);
  434.       if GameWon and (WinningLine = Null) then    { Save winning line }
  435.         WinningLine := Vert;
  436.       for L := 0 to 4 do
  437.         Update(Line[2, X1, Y1], Value[X1, Y1 + L]);
  438.     end;
  439.   end; { for }
  440.  
  441.   Board[X, Y] := Player;             { Place piece in board }
  442.   if GameWon then
  443.     BlinkWinner(Player, X, Y, WinningLine)
  444.   else
  445.     PrintMove(Player, X, Y);         { Print move on screen }
  446.   Player := Opponent;        { The opponent is next to move }
  447. end; { MakeMove }
  448.  
  449. function GameOver : boolean;
  450. { A game is over if one of the players have
  451.   won, or if there are no more empty lines }
  452. begin
  453.   GameOver := GameWon or (TotalLines <= 0);
  454. end; { GameOver }
  455.  
  456. procedure FindMove(var X, Y : IndexType);
  457. { Finds a move X,Y for player, simply by
  458.   picking the one with the highest value }
  459. var
  460.   Opponent  : ColorType;
  461.   I, J      : IndexType;
  462.   Max, Valu : integer;
  463. begin
  464.   Opponent := OpponentColor(Player);
  465.   Max := -MaxInt;
  466.   { If no square has a high value then pick the one in the middle }
  467.   X := (N + 1) DIV 2;
  468.   Y := (N + 1) DIV 2;
  469.   if Board[X, Y] = Empty then Max := 4;
  470.   { The evaluation for a square is simply the value of the square
  471.     for the player (attack points) plus the value for the opponent
  472.     (defense points). Attack is more important than defense, since
  473.     it is better to get 5 in line yourself than to prevent the op-
  474.     ponent from getting it. }
  475.  
  476.    for I := 1 to N do { For all empty squares }
  477.      for J := 1 to N do
  478.        if Board[I, J] = Empty then
  479.        begin
  480.          { Calculate evaluation }
  481.          Valu := Value[I, J, Player] * (16 + AttackFactor) DIV
  482.                  16 + Value[I, J, Opponent] + Random(4);
  483.          if Valu > Max then { Pick move with highest value }
  484.          begin
  485.            X := I;
  486.            Y := J;
  487.            Max := Valu;
  488.          end;
  489.        end; { if }
  490. end; { FindMove }
  491.  
  492. procedure ClearBuffer;
  493. { Clear the keyboard buffer }
  494. var
  495.   Ch : char;
  496. begin
  497.   While KeyPressed do
  498.     Ch := ReadKey;
  499. end; { ClearBuffer }
  500.  
  501. procedure GetChar(var Ch : char);
  502. { Get a character from the keyboard }
  503. begin
  504.   Ch := UpCase(ReadKey);
  505. end; { GetChar }
  506.  
  507. procedure ReadCommand(X, Y : IndexType; var Command : char);
  508. { Reads in a valid command character }
  509. var
  510.   ValidCommand : boolean;
  511.  
  512. begin
  513.   repeat
  514.     ValidCommand := true;
  515.     GotoSquare(X, Y);                                    { Goto square }
  516.     GetChar(Command);                             { Read from keyboard }
  517.     case Command of
  518.       '?'      : Help;                           { ... Help system ... }
  519.       CtrlC    : Command := 'Q';                   { Ctrl-C means quit }
  520.       Return,                          { Return or space means place a }
  521.       Space    : Command := 'E';       { stone at the cursor position  }
  522.       NullKey  : begin
  523.                    GetChar(Command);
  524.                    case Command of
  525.                      'K' : Command := 'L';             { Left arrow  }
  526.                      'M' : Command := 'R';             { Right arrow }
  527.                      'P' : Command := 'D';             { Down arrow  }
  528.                      'H' : Command := 'U';             { Up arrow    }
  529.                      'G' : Command := '7';             { Home key    }
  530.                      'I' : Command := '9';             { PgUp key    }
  531.                      'O' : Command := '1';             { End key     }
  532.                      'Q' : Command := '3';             { PgDn key    }
  533.                      else
  534.                      begin
  535.                        ValidCommand := false;
  536.                        ClearBuffer;
  537.                      end; { case else }
  538.                    end; { case }
  539.                  end;
  540.       Esc : if GameOver then command := 'P' { GameOver? treat Esc }
  541.             else                            { like any other key  }
  542.             begin
  543.               ValidCommand := false;     { ignore Esc during game }
  544.               ClearBuffer;
  545.             end; { ignore Esc }
  546.       'N','Q','A','P','H' : ;
  547.       else
  548.       begin
  549.         ValidCommand := false;
  550.         ClearBuffer;
  551.       end; { case else }
  552.     end; { case }
  553.   until ValidCommand;
  554. end; { ReadCommand }
  555.  
  556. procedure Initialize;
  557. begin
  558.   ClrScr;
  559.   Randomize;
  560.   AutoPlay := false;
  561.   FileRead := false; { Help file not read yet }
  562. end; { Initialize }
  563.  
  564. procedure InterpretCommand(Command : char);
  565. var
  566.   Temp : integer;
  567. begin
  568.   case Command of
  569.     'N': begin                                        { Start new game }
  570.            ResetGame(false);     { ResetGame but only redraw the board }
  571.            X := (N + 1) DIV 2;
  572.            Y := X;
  573.          end;
  574.     'H': FindMove(X, Y);               { Give the user a hint }
  575.     'L': X := (X + N - 2) MOD N + 1;                  { Left  }
  576.     'R': X := X MOD N + 1;                            { Right }
  577.     'D': Y := (Y + N - 2) MOD N + 1;                  { Down  }
  578.     'U': Y := Y MOD N + 1;                            { Up    }
  579.     '7': begin
  580.            if (X = 1) or (Y = N) then    { Move diagonally    }
  581.            begin                         { towards upper left }
  582.              Temp := X;
  583.              X := Y;
  584.              Y := Temp;
  585.            end
  586.            else
  587.            begin
  588.              X := X - 1;
  589.              Y := Y + 1;
  590.            end;
  591.          end;
  592.     '9': begin                           { Move diagonally    }
  593.            if X = N then                 { toward upper right }
  594.            begin
  595.              X := (N - Y) + 1;
  596.              Y := 1;
  597.            end
  598.            else if Y = N then
  599.            begin
  600.              Y := (N - X) + 1;
  601.              X := 1;
  602.            end
  603.            else
  604.            begin
  605.              X := X + 1;
  606.              Y := Y + 1;
  607.            end
  608.          end;
  609.     '1': begin                            { Move diagonally   }
  610.            if Y = 1 then                  { toward lower left }
  611.            begin
  612.              Y := (N - X) + 1;
  613.              X := N;
  614.            end
  615.            else if X = 1 then
  616.            begin
  617.              X := (N - Y) + 1;
  618.              Y := N;
  619.            end
  620.            else
  621.            begin
  622.              X := X - 1;
  623.              Y := Y - 1;
  624.            end;
  625.          end;
  626.     '3': begin                           { Move diagonally    }
  627.            if (X = N) or (Y = 1) then    { toward lower right }
  628.            begin
  629.              Temp := X;
  630.              X := Y;
  631.              Y := Temp;
  632.            end
  633.            else
  634.            begin
  635.              X := X + 1;
  636.              Y := Y - 1;
  637.            end;
  638.          end;
  639.     'A': AutoPlay := true;                   { Auto play mode }
  640.   end; { case }
  641. end; { InterpretCommand }
  642.  
  643. procedure PlayerMove;
  644. { Enter and make a move }
  645. begin
  646.   if Board[X, Y] = Empty then
  647.   begin
  648.     MakeMove(X, Y);
  649.     if GameWon then
  650.       PrintMsg('Congratulations, You won!');
  651.     Command := 'P';
  652.   end;
  653. end; { PlayerMove }
  654.  
  655. procedure ProgramMove;
  656. { Find and perform programs move }
  657. begin
  658.   repeat
  659.     if KeyPressed then
  660.       ClearBuffer;
  661.     if GameOver then
  662.     begin
  663.       AutoPlay := false;
  664.       if (Command <> 'Q') and (not GameWon) then
  665.         PrintMsg('Tie game!');
  666.     end
  667.     else
  668.     begin
  669.       FindMove(X, Y);
  670.       MakeMove(X, Y);
  671.       if GameWon then
  672.         PrintMsg('I won!');
  673.     end;
  674.   until AutoPlay = false;
  675. end; { ProgramMove }
  676.  
  677. begin { Program Body }
  678.   Initialize;
  679.   ResetGame(true);     { ResetGame and draw the entire screen }
  680.   X := (N + 1) DIV 2;              { Set starting position to }
  681.   Y := X;                          { the middle of the board  }
  682.   repeat
  683.     ReadCommand(X, Y, Command);
  684.     if GameOver then
  685.       if Command <> 'Q' then
  686.         Command := 'N';
  687.     InterpretCommand(Command);
  688.     if Command = 'E' then
  689.       PlayerMove;
  690.     if Command in ['P', 'A'] then
  691.       ProgramMove;
  692.   until Command in ['Q', CtrlC];
  693.   Abort;
  694. end.
  695.