home *** CD-ROM | disk | FTP | other *** search
/ Game-A-Roma (Doom Edition) / GAME_A_ROMA.iso / games / big2 / go-moku.pas < prev    next >
Pascal/Delphi Source File  |  1985-10-24  |  21KB  |  694 lines

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