home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l040 / 10.ddi / CHESS.ZIP / TVBOARD.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-27  |  29.5 KB  |  1,089 lines

  1. {************************************************}
  2. {                                                }
  3. {   Turbo Vision Chess Demo                      }
  4. {   Copyright (c) 1992 by Borland International  }
  5. {                                                }
  6. {************************************************}
  7.  
  8. unit TVBoard;
  9.  
  10. interface
  11.  
  12. {$IFDEF DPMI}
  13. uses Views, Objects, Dialogs, Validate, TVChsCmd, ChessDLL, MoveList,
  14.   Drivers, StdDlg, Dos, CTimers;
  15. {$ELSE}
  16. uses Views, Objects, Dialogs, Validate, TVChsCmd, ChessInf, MoveList,
  17.   Drivers, StdDlg, Dos, CTimers;
  18. {$ENDIF}
  19.  
  20. const
  21.   ChessSignature : array [0..33] of Char = 'Borland Pascal Chess saved game'#26#0;
  22.  
  23. type
  24.   { TChessSurface }
  25.  
  26.   PChessSurface = ^TChessSurface;
  27.   TChessSurface = object(TView)
  28.     ValidMoves: array[1..8, 1..8] of Boolean;
  29.     Squares: array[1..8, 1..8] of PView;
  30.     procedure Clear;
  31.     procedure Draw; virtual;
  32.   end;
  33.  
  34.   { TValidateMove }
  35.  
  36.   PValidateMove = ^TValidateMove;
  37.   TValidateMove = object(TValidator)
  38.     function IsValid(const S: String): Boolean; virtual;
  39.     procedure Error; virtual;
  40.     function Transfer(var S: String; Buffer: Pointer; Flag: TVTransfer):
  41.       Word; virtual;
  42.   end;
  43.  
  44.   { Palette layout }
  45.   { 1 = Border }
  46.   { 2 = White square }
  47.   { 3 = Black sqaure }
  48.   { 4 = Black piece }
  49.   { 5 = White piece }
  50.   { 6 = Black in jeopardy }
  51.   { 7 = White in jeopardy }
  52.   { 8 = White hint }
  53.   { 9 = Black hint }
  54.  
  55.   PChessBoard = ^TChessBoard;
  56.   TChessBoard = object(TGroup)
  57.     Surface: PChessSurface;
  58.     Game: HChess;
  59.     Computer, Player: TColor;
  60.     GameMode: Word;
  61.     GameOver: Boolean;
  62.     MoveHistory: PMoveList;
  63.     GameName: PathStr;
  64.     ThinkState: TSearchStatus;
  65.     ChessTimers: array[TColor] of PChessTimer;
  66.     ValidMoves,
  67.     OpponentMoves: array [0..(28*16+1)] of TMove;
  68.     QSquare: TLocation;
  69.     constructor Init(var Bounds: TRect);
  70.     destructor Done; virtual;
  71.     procedure AddToHistory(const AMove: TMove);
  72.     procedure AcceptComputerMove;
  73.     function CanMovePiece(Color: TColor): Boolean;
  74.     function CheckActiveGame: Word;
  75.     procedure ClearBoard;
  76.     procedure DoThink;
  77.     procedure DrawSurface;
  78.     procedure DrawValidMoves(Empty: Boolean);
  79.     function GetComputerTime: Longint;
  80.     function GetPalette: PPalette; virtual;
  81.     procedure HandleEvent(var Event: TEvent); virtual;
  82.     procedure InitGameBoard;
  83.     procedure InsertPiece(Piece: PView; Location: TLocation);
  84.     procedure MovePiece(Piece: PView; FromLoc, ToLoc: TLocation);
  85.     procedure Process;
  86.     procedure ReadGame;
  87.     procedure Redo;
  88.     procedure RemovePiece(Piece: PView; Location: TLocation);
  89.     procedure ResetCurrentPlayer;
  90.     procedure ResetValidMoves;
  91.     function SaveGame: Word;
  92.     function SaveGameAs: Word;
  93.     procedure SetGameBoard(const ABoard: TBoard);
  94.     procedure SetupNewGameBoard;
  95.     procedure ShowEndGame(Reason: Integer);
  96.     procedure StartComputerMove;
  97.     procedure Undo;
  98.     function ValidateMove(var C: TChange): TChessError;
  99.     function Valid(Command: Word): Boolean; virtual;
  100.     procedure Update;
  101.     procedure UpdateCommands;
  102.   end;
  103.  
  104. const
  105.   ChessBoard: PChessBoard = nil;
  106.  
  107. function Opponent(Color: TColor): TColor;
  108.  
  109. implementation
  110.  
  111. uses TVPieces, TVChsUtl, TVStatus, App, MsgBox, Strings, TVChstat;
  112.  
  113. function Opponent(Color: TColor): TColor;
  114. begin
  115.   if Color = cWhite then
  116.     Opponent := cBlack
  117.   else Opponent := cWhite;
  118. end;
  119.  
  120. { TValidateMove }
  121.  
  122. function TValidateMove.IsValid(const S: String): Boolean;
  123. var
  124.   Change: TChange;
  125.   MoveStr: array[0..20] of Char;
  126. begin
  127.   StrPCopy(MoveStr, S);
  128.   IsValid := ParseMove(MoveStr, Change) = ceOK;
  129. end;
  130.  
  131. procedure TValidateMove.Error;
  132. begin
  133.   MessageBox(^C'Invalid move syntax', nil, mfError + mfOKButton +
  134.     mfInsertInApp);
  135. end;
  136.  
  137. function TValidateMove.Transfer(var S: String; Buffer: Pointer;
  138.   Flag: TVTransfer): Word;
  139. var
  140.   Change: TChange;
  141.   MoveStr: array[0..20] of Char;
  142. begin
  143.   case Flag of
  144.     vtGetData:
  145.     begin
  146.       FillChar(Change, SizeOf(Change), 0);
  147.       StrPCopy(MoveStr, S);
  148.       if ParseMove(MoveStr, Change) = ceOK then
  149.         PChange(Buffer)^ := Change
  150.       else FillChar(Buffer^, SizeOf(TChange), 0);
  151.     end;
  152.   end;
  153.   Transfer := SizeOf(TChange);
  154. end;
  155.  
  156. { TChessSurface }
  157.  
  158. procedure TChessSurface.Clear;
  159. begin
  160.   FillChar(Squares, SizeOf(Squares), 0);
  161. end;
  162.  
  163. procedure TChessSurface.Draw;
  164. var
  165.   Border, White, Black, Color: Word;
  166.   B: TDrawBuffer;
  167.   I, J, K, Line: Integer;
  168.   DrawChr: Char;
  169.   XOfs, XLen: Integer;
  170.  
  171.   procedure TellPieceToDraw(P: PView);
  172.   begin
  173.     if (P <> nil) and (P^.State and sfDragging = 0) then
  174.       PChessPiece(P)^.RawDraw(B, 2 + 6 * K, I, XOfs, XLen);
  175.   end;
  176.  
  177. begin
  178.   Border := GetColor($0101);
  179.   White := GetColor($0802);
  180.   Black := GetColor($0903);
  181.   Line := 0;
  182.   for J := 7 downto 0 do
  183.   begin
  184.     if J = 7 then
  185.     begin
  186.       MoveChar(B, ' ', Border, 2);
  187.       for I := 0 to 7 do
  188.       begin
  189.         MoveChar(B[2 + 6 * I], ' ', Border, 3);
  190.         MoveChar(B[5 + 6 * I], Chr($41 + I), Border, 1);
  191.         MoveChar(B[6 + 6 * I], ' ', Border, 2);
  192.       end;
  193.       MoveChar(B[Size.X - 2], ' ', Border, 2);
  194.       WriteBuf(0, Line, Size.X, 1, B);
  195.       Inc(Line);
  196.     end;
  197.     for I := 0 to 2 do
  198.     begin
  199.       MoveChar(B, ' ', Border, 2);
  200.       if I = 1 then
  201.         MoveChar(B, Chr($31 + J), Border, 1);
  202.       for K := 0 to 7 do
  203.       begin
  204.         if (K + J) and 1 = 0 then
  205.         begin
  206.           DrawChr := '░';
  207.           Color := Black
  208.         end
  209.         else
  210.         begin
  211.           DrawChr := '▓';
  212.           Color := White;
  213.         end;
  214.         if ValidMoves[K + 1, J + 1] then
  215.         begin
  216.           DrawChr := ' ';
  217.           Color := Swap(Color);
  218.         end;
  219.         MoveChar(B[2 + 6 * K], DrawChr, Color, 6);
  220.         TellPieceToDraw(Squares[K + 1, J + 1])
  221.       end;
  222.       MoveChar(B[Size.X - 2], ' ', Border, 2);
  223.       WriteBuf(0, Line, Size.X, 1, B);
  224.       Inc(Line);
  225.     end;
  226.   end;
  227. end;
  228.  
  229. constructor TChessBoard.Init(var Bounds: TRect);
  230. var
  231.   Color: TColor;
  232. begin
  233.   inherited Init(Bounds);
  234.   EventMask := EventMask or evMove;
  235.   Options := Options or ofPostprocess;
  236.   Surface := New(PChessSurface, Init(Bounds));
  237.   Insert(Surface);
  238.   Computer := cBlack;
  239.   Player := cWhite;
  240.   GameMode := Settings.Players;
  241.   for Color := cWhite to cBlack do
  242.     ChessTimers[Color] := New(PChessTimer, Init);
  243.   MoveHistory := New(PMoveList, Init(20, 10));
  244.   if NewGame(Game) <> ceOK then
  245.     Game := 0
  246.   else SetupNewGameBoard;
  247.   if (StatusDialog <> nil) and (Game <> 0) then
  248.     StatusDialog^.Update(Game, ChessTimers, 0, 0, GameMode);
  249. end;
  250.  
  251. destructor TChessBoard.Done;
  252. begin
  253.   if Game <> 0 then DisposeGame(Game);
  254.   inherited Done;
  255. end;
  256.  
  257. procedure TChessBoard.AcceptComputerMove;
  258. var
  259.   Move: TMove;
  260. begin
  261.   if GetLastMove(Game, Move) = ceOK then
  262.   begin
  263.     AddToHistory(Move);
  264.     Message(@Self, evMove, cmMovePiece, @Move);
  265.     Wait(5);
  266.     Message(@Self, evMove, cmUndoMove, @Move);
  267.     Wait(5);
  268.     Message(@Self, evMove, cmMovePiece, @Move);
  269.     if (GameMode and gmDemo <> 0) and not GameOver then
  270.       StartComputerMove
  271.     else
  272.     begin
  273.       if Settings.Hints and hoThinkAhead <> 0 then
  274.         ThinkAhead(Game);
  275.       ChessTimers[GetPlayer(Game)]^.Mark;
  276.       ChessTimers[GetPlayer(Game)]^.Start;
  277.       ThinkState := GetSearchStatus(Game);
  278.     end;
  279.     ResetValidMoves;
  280.     Update;
  281.   end;
  282. end;
  283.  
  284. procedure TChessBoard.AddToHistory(const AMove: TMove);
  285. var
  286.   ChessPiece: PChessPiece;
  287. begin
  288.   if MoveHistory <> nil then MoveHistory^.AddMove(AMove);
  289.   if StatusDialog <> nil then StatusDialog^.UpdateList(MoveHistory);
  290. end;
  291.  
  292. function TChessBoard.CanMovePiece(Color: TColor): Boolean;
  293. begin
  294.   CanMovePiece := False;
  295.   if (Game <> 0) and (GetSearchStatus(Game) in [ssComplete, ssThinkAhead]) and
  296.     (Color = GetPlayer(Game)) then CanMovePiece := True;
  297. end;
  298.  
  299. function TChessBoard.CheckActiveGame: Word;
  300. var
  301.   Result: Word;
  302. begin
  303.   if (MoveHistory <> nil) and (MoveHistory^.Count <> 0) then
  304.   begin
  305.     Result := MessageBox('Save currently active game?', nil,
  306.       mfError + mfYesNoCancel + mfInsertInApp);
  307.     if Result = cmYes then
  308.       Result := SaveGame;
  309.   end else Result := cmOK;
  310.   CheckActiveGame := Result;
  311. end;
  312.  
  313. procedure TChessBoard.ClearBoard;
  314. var
  315.   KillCollection: TCollection;
  316. begin
  317.   Surface^.Clear;
  318.   KillCollection.Init(32, 0);
  319.   Message(@Self, evBroadcast, cmRegisterSave, @KillCollection);
  320.   KillCollection.Done;
  321. end;
  322.  
  323. procedure TChessBoard.DoThink;
  324. begin
  325.   if (Game <> 0) and not GameOver and
  326.     not (ThinkState in [ssComplete, ssGameOver]) then
  327.     Process;
  328.   if not GameOver then Update;
  329. end;
  330.  
  331. procedure TChessBoard.DrawSurface;
  332. begin
  333.   Surface^.DrawView;
  334. end;
  335.  
  336. procedure TChessBoard.DrawValidMoves(Empty: Boolean);
  337.  
  338.   procedure TestAndInvert(Test, Show: TLocation);
  339.   var
  340.     R: TRect;
  341.   begin
  342.     if Word(Test) = Word(QSquare) then
  343.       Surface^.ValidMoves[Show.X, Show.Y] := True;
  344.   end;
  345.  
  346. var
  347.   X : Integer;
  348. begin
  349.   FillChar(Surface^.ValidMoves, SizeOf(Surface^.ValidMoves), 0);
  350.   if not Empty then
  351.     { Show where this piece can move to }
  352.   begin
  353.     X := 0;
  354.     while (X <= High(ValidMoves)) and
  355.           (ValidMoves[X].Change.Piece <> pEmpty) do
  356.     begin
  357.       with ValidMoves[X].Change do
  358.         TestAndInvert(Source, Dest);
  359.       Inc(X);
  360.     end;
  361.     X := 0;
  362.     while (X <= High(ValidMoves)) and
  363.           (OpponentMoves[X].Change.Piece <> pEmpty) do
  364.     begin
  365.       with OpponentMoves[X].Change do
  366.         TestAndInvert(Source, Dest);
  367.       Inc(X);
  368.     end;
  369.   end
  370.   else
  371.     { Show what pieces can move to this square }
  372.   begin
  373.     X := 0;
  374.     while (X <= High(ValidMoves)) and
  375.           (ValidMoves[X].Change.Piece <> pEmpty) do
  376.     begin
  377.       with ValidMoves[X].Change do
  378.         TestAndInvert(Dest, Source);
  379.       Inc(X);
  380.     end;
  381.     X := 0;
  382.     while (X <= High(OpponentMoves)) and
  383.           (OpponentMoves[X].Change.Piece <> pEmpty) do
  384.     begin
  385.       with OpponentMoves[X].Change do
  386.         TestAndInvert(Dest, Source);
  387.       Inc(X);
  388.     end;
  389.   end;
  390. end;
  391.  
  392. function TChessBoard.GetComputerTime: Longint;
  393. var
  394.   MarkTime: Longint;
  395. begin
  396.   case Settings.TimeMode of
  397.     tmGameLimit:
  398.       begin
  399.         MarkTime := (Settings.GameTime * 1092 -
  400.           ChessTimers[Computer]^.GetCurrentTicks) div 44;
  401.         if MoveHistory^.UndoPos shr 1 <= 40 then
  402.           MarkTime := 91 + (MarkTime - 91) *
  403.             ((80 - MoveHistory^.UndoPos shr 1) div 40);
  404.         GetComputerTime := MarkTime;
  405.       end;
  406.     tmTurnLimit: GetComputerTime := Settings.TurnTime * 18;
  407.     tmMatchUser:
  408.       begin
  409.         MarkTime := ChessTimers[Opponent(GetPlayer(Game))]^.GetMarkTime;
  410.         if MarkTime > 0 then
  411.           GetComputerTime := MarkTime
  412.         else GetComputerTime := 5 * 18;
  413.       end;
  414.     tmInfinite:  GetComputerTime := High(Longint);
  415.   end;
  416. end;
  417.  
  418. function TChessBoard.GetPalette: PPalette;
  419. const
  420.   P: string[Length(CChessBoard)] = CChessBoard;
  421. begin
  422.   GetPalette := @P;
  423. end;
  424.  
  425. procedure TChessBoard.HandleEvent(var Event: TEvent);
  426. var
  427.   Move: TMove;
  428.   LastSquare: TLocation;
  429.  
  430. { The chess board holds the board surface and all pieces on that surface.
  431.   Pieces are "hidden" until asked to drag themselves.  Since the views are
  432.   hidden they don't recieve mouse events.  The following procedures will
  433.   insure that the pieces get mouse events so they can drag themselves. }
  434.  
  435.   procedure HandEventToPiece(P: PView);
  436.   begin
  437.     if Event.What and P^.EventMask <> 0 then P^.HandleEvent(Event);
  438.   end;
  439.  
  440.   function ContainsMouse(P: PView): Boolean; far;
  441.   begin
  442.     ContainsMouse := P^.MouseInView(Event.Where);
  443.   end;
  444.  
  445.   procedure EnterMove;
  446.   var
  447.     Dlg: PDialog;
  448.     R: TRect;
  449.     Control: PView;
  450.     Change: TChange;
  451.     P: PChessPiece;
  452.     Result: TChessError;
  453.   Begin
  454.     R.Assign(0,0,39,7);
  455.     New(Dlg, Init(R, 'Enter Move'));
  456.  
  457.     with Dlg^ do
  458.     begin
  459.       Options := Options or ofCentered;
  460.  
  461.       R.Assign(14, 2, 36, 3);
  462.       Control := New(PInputLine, Init(R, 20));
  463.       PInputLine(Control)^.SetValidator(New(PValidateMove, Init));
  464.       PInputLine(Control)^.Validator^.Options := voTransfer;
  465.       Insert(Control);
  466.       R.Assign(2, 2, 14, 3);
  467.       Insert(New(PLabel, Init(R, '~E~nter move ', Control)));
  468.  
  469.       R.Assign(8, 4, 18, 6);
  470.       Insert(New(PButton, Init(R, 'O~K~', cmOK, bfDefault)));
  471.  
  472.       Inc(R.A.X, 14); Inc(R.B.X, 14);
  473.       Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
  474.  
  475.       SelectNext(False);
  476.     end;
  477.  
  478.     if Application^.ExecView(Dlg) <> cmCancel then
  479.     begin
  480.       Dlg^.GetData(Change);
  481.       Result := ValidateMove(Change);
  482.       if Result in [ceOK, ceAmbiguousMove] then
  483.       begin
  484.         P := Message(@Self, evMove, cmFindPiece, Ptr(0, Word(Change.Source)));
  485.         if P = nil then
  486.           MessageBox(^C'Piece not located there.', nil, mfError + mfOKButton +
  487.             mfInsertInApp)
  488.         else
  489.         begin
  490.           if Result = ceAmbiguousMove then
  491.             Change.Piece := P^.GetPromotionPiece;
  492.           Message(@Self, evMove, cmSubmitMove, @Change);
  493.         end;
  494.       end
  495.       else MessageBox(^C'Invalid move.', nil,
  496.         mfError + mfOKButton + mfInsertInApp);
  497.     end;
  498.  
  499.     Dispose(Dlg, Done);
  500.   end;
  501.  
  502.   procedure ShowHint;
  503.   var
  504.     Move: TMove;
  505.   begin
  506.     GetHintMove(Game, Move);
  507.     if VerifyMove(Game, Move.Change) = ceOK then
  508.     begin
  509.       Message(@Self, evMove, cmMovePiece, @Move);
  510.       Wait(5);
  511.       Message(@Self, evMove, cmUndoMove, @Move);
  512.       Wait(5);
  513.       Message(@Self, evMove, cmMovePiece, @Move);
  514.       Wait(5);
  515.       Message(@Self, evMove, cmUndoMove, @Move);
  516.       Wait(5);
  517.     end
  518.     else MessageBox(^C'No hint available', nil, mfInformation + mfOKButton +
  519.       mfInsertInApp);
  520.   end;
  521.  
  522. begin
  523.   case Event.What of
  524.     evMove:
  525.       case Event.Command of
  526.         cmSubmitMove:
  527.           begin
  528.             ChessTimers[GetPlayer(Game)]^.Stop;
  529.             if SubmitMove(Game, TChange(Event.InfoPtr^)) = ceOK then
  530.             begin
  531.               if GetLastMove(Game, Move) = ceOK then
  532.               begin
  533.                 AddToHistory(Move);
  534.                 Message(@Self, evMove, cmMovePiece, @Move);
  535.                 ResetValidMoves;
  536.               end;
  537.               if GameMode and gmTwoPlay <> gmTwoPlay then
  538.                 StartComputerMove
  539.               else
  540.               begin
  541.                 ResetCurrentPlayer;
  542.                 ChessTimers[Player]^.Mark;
  543.                 ChessTimers[Player]^.Start;
  544.               end;
  545.               Exit;
  546.             end;
  547.           end;
  548.       end;
  549.     evMouseDown:
  550.       begin
  551.  
  552.         { The board handles all right mouse actions }
  553.  
  554.         if Event.Buttons = mbRightButton then
  555.         begin
  556.           if Settings.Hints and hoRtClickHints <> 0 then
  557.           begin
  558.             LastSquare := QSquare;
  559.             repeat
  560.               PointInSquare(Event.Where, QSquare);
  561.               if Word(QSquare) <> Word(LastSquare) then
  562.               begin
  563.                 DrawValidMoves(Message(@Self, evMove, cmFindPiece, Ptr(0, Word(QSquare))) = nil);
  564.                 Surface^.DrawView;
  565.                 LastSquare := QSquare;
  566.               end;
  567.             until not MouseEvent(Event, evMouseMove + evMouseAuto);
  568.             Word(Qsquare) := 0;
  569.             DrawValidMoves(False);
  570.             Surface^.DrawView;
  571.           end;
  572.           ClearEvent(Event);
  573.           Exit;
  574.         end;
  575.  
  576.         { All other mouse actions go to the appropriate piece }
  577.  
  578.         HandEventToPiece(FirstThat(@ContainsMouse));
  579.       end;
  580.   end;
  581.   inherited HandleEvent(Event);
  582.   case Event.What of
  583.     evCommand:
  584.       begin
  585.         case Event.Command of
  586.           cmNew:
  587.             begin
  588.               if GameOver or (CheckActiveGame <> cmCancel) then
  589.               begin
  590.                 DisposeGame(Game);
  591.                 if NewGame(Game) <> ceOK then
  592.                   Game := 0;
  593.                 SetupNewGameBoard;
  594.                 if Game <> 0 then StatusDialog^.Update(Game, ChessTimers,
  595.                   0, 0, GameMode);
  596.               end;
  597.             end;
  598.           cmComputerMove:
  599.             if ThinkState in [ssComplete, ssThinkAhead] then
  600.               StartComputerMove;
  601.           cmRunDemo:
  602.             if GameMode and gmDemo = 0 then
  603.             begin
  604.               GameMode := GameMode or gmDemo;
  605.               if ThinkState in [ssComplete, ssThinkAhead] then
  606.                 StartComputerMove;
  607.             end;
  608.           cmStop:
  609.             if (Game <> 0) and (ThinkState = ssMoveSearch) then
  610.             begin
  611.               ForceMove(Game);
  612.               Computer := GetPlayer(Game);
  613.               Player := Opponent(Computer);
  614.               GameMode := GameMode and not gmDemo;
  615.             end;
  616.           cmUndo: Undo;
  617.           cmRedo: Redo;
  618.           cmGameOver: ShowEndGame(Event.InfoInt);
  619.           cmSave: SaveGame;
  620.           cmSaveAs: SaveGameAs;
  621.           cmOpen: ReadGame;
  622.           cmEnterMove: EnterMove;
  623.           cmShowHint: ShowHint;
  624.         else
  625.           Exit
  626.         end;
  627.         ClearEvent(Event);
  628.       end;
  629.     evKeyDown:
  630.       if ThinkState in [ssComplete, ssThinkAhead] then
  631.       begin
  632.         PutEvent(Event);
  633.         EnterMove;
  634.         ClearEvent(Event);
  635.       end;
  636.   end;
  637. end;
  638.  
  639. procedure TChessBoard.InitGameBoard;
  640. var
  641.   I, J: Integer;
  642.   P: PChessPiece;
  643.   R: TRect;
  644.   Board: TBoard;
  645.   Location: TLocation;
  646.   ChessStatus: TChessStatus;
  647. begin
  648.   if Game <> 0 then
  649.   begin
  650.     if GetBoard(Game, Board) = ceOK then
  651.       for J := 1 to 8 do
  652.         for I := 1 to 8 do
  653.           if Board[I, J].Piece <> pEmpty then
  654.           begin
  655.             Location.X := I; Location.Y := J;
  656.             SquareToLocal(Location, R.A, Size.Y);
  657.             R.Assign(R.A.X, R.A.Y, R.A.X + 6, R.A.Y + 3);
  658.             P := New(PChessPiece, Init(R, Board[I, J], Location));
  659.             P^.Hide;
  660.             InsertPiece(P, P^.Location);
  661.           end;
  662.     Player := cWhite;
  663.     Computer := cBlack;
  664.     GameMode := GameMode and not gmDemo;
  665.     GameOver := False;
  666.     ThinkState := GetSearchStatus(Game);
  667.     Update;
  668.   end;
  669. end;
  670.  
  671. procedure TChessBoard.InsertPiece(Piece: PView; Location: TLocation);
  672. begin
  673.   Insert(Piece);
  674.   Surface^.Squares[Location.X, Location.Y] := Piece;
  675. end;
  676.  
  677. procedure TChessBoard.MovePiece(Piece: PView; FromLoc, ToLoc: TLocation);
  678. begin
  679.   Surface^.Squares[FromLoc.X, FromLoc.Y] := nil;
  680.   Surface^.Squares[ToLoc.X, ToLoc.Y] := Piece;
  681.   DrawSurface;
  682. end;
  683.  
  684. procedure TChessBoard.Process;
  685. var
  686.   OldState: TSearchStatus;
  687.   ChessStatus: TChessStatus;
  688.   Move: TMove;
  689.   Event: TEvent;
  690.   ComputerPlayer: TColor;
  691.   I: Integer;
  692. begin
  693.   OldState := ThinkState;
  694.   if (GetPlayer(Game) = Computer) or (GameMode and gmDemo <> 0) then
  695.   begin
  696.     ComputerPlayer := GetPlayer(Game);
  697.     ChessTimers[ComputerPlayer]^.Start;
  698.     Think(Game, 4, ThinkState);
  699.     ChessTimers[ComputerPlayer]^.Stop;
  700.   end else Think(Game, 2, ThinkState);
  701.   if (OldState = ssMoveSearch) and (ThinkState = ssComplete) then
  702.     AcceptComputerMove;
  703. end;
  704.  
  705. procedure TChessBoard.ReadGame;
  706. var
  707.   S: PBufStream;
  708.   Test: array [0..SizeOf(ChessSignature)] of Char;
  709.   NewMoveList : PMoveList;
  710.   FileDialog: PFileDialog;
  711.   AGameName: PathStr;
  712.   X: Integer;
  713.  
  714.   function ReplayMoves(P: PMove): Boolean; far;
  715.   begin
  716.     SubmitMove(Game, P^.Change);
  717.     ReplayMoves := (X >= MoveHistory^.UndoPos);
  718.     Inc(X);
  719.   end;
  720.  
  721. begin
  722.   if CheckActiveGame <> cmCancel then
  723.   begin
  724.     FileDialog := New(PFileDialog, Init('*.CHS', 'Open a Game', '~G~ame',
  725.       fdOpenButton, 100));
  726.     if Application^.ExecView(FileDialog) <> cmCancel then
  727.     begin
  728.       FileDialog^.GetFileName(AGameName);
  729.       S := New(PBufStream, Init(AGameName, stOpenRead, 1024));
  730.       S^.Read(Test, SizeOf(ChessSignature));
  731.       if S^.Status <> stOK then
  732.         {!!} MessageBox('Error reading file', nil, mfError + mfOKButton + mfInsertInApp)
  733.       else
  734.       if StrLComp(ChessSignature, Test, SizeOf(ChessSignature)) <> 0 then
  735.         {!!} MessageBox('This is not a chess game file', nil, mfError + mfOKButton + mfInsertInApp)
  736.       else
  737.       begin
  738.         NewMoveList := PMoveList(S^.Get);
  739.         if S^.Status <> stOK then
  740.           {!!} MessageBox('Error reading file', nil, mfError + mfOKButton + mfInsertInApp)
  741.         else
  742.         begin
  743.           ClearBoard;
  744.           DisposeGame(Game);
  745.           if NewGame(Game) <> ceOK then
  746.             Game := 0
  747.           else
  748.           begin
  749.             Dispose(MoveHistory, Done);
  750.             MoveHistory := NewMoveList;
  751.             X := 0;
  752.             MoveHistory^.FirstThat(@ReplayMoves);
  753.             if StatusDialog <> nil then
  754.               StatusDialog^.UpdateList(MoveHistory);
  755.             InitGameBoard;
  756.             ResetValidMoves;
  757.             Update;
  758.           end;
  759.         end;
  760.       end;
  761.       Dispose(S, Done);
  762.     end;
  763.   end;
  764. end;
  765.  
  766. procedure TChessBoard.Redo;
  767. var
  768.   Move: TMove;
  769. begin
  770.   AbortSearch(Game);
  771.   ChessTimers[Player]^.Stop;
  772.   MoveHistory^.Redo(Move);
  773.   if SubmitMove(Game, Move.Change) = ceOK then
  774.     if GetLastMove(Game, Move) = ceOK then
  775.       Message(@Self, evMove, cmMovePiece, @Move);
  776.   if (GameMode = gmOnePlay) and (MoveHistory^.RedoAvail) and
  777.     (Player <> GetPlayer(Game)) then
  778.   begin
  779.     MoveHistory^.Redo(Move);
  780.     if SubmitMove(Game, Move.Change) = ceOK then
  781.       if GetLastMove(Game, Move) = ceOK then
  782.         Message(@Self, evMove, cmMovePiece, @Move);
  783.   end
  784.   else ResetCurrentPlayer;
  785.   ResetValidMoves;
  786.   ChessTimers[Player]^.Mark;
  787.   ChessTimers[Player]^.Start;
  788.   if StatusDialog <> nil then StatusDialog^.UpdateList(MoveHistory);
  789.   Update;
  790. end;
  791.  
  792. procedure TChessBoard.RemovePiece(Piece: PView; Location: TLocation);
  793. begin
  794.   if Surface^.Squares[Location.X, Location.Y] = Piece then
  795.     Surface^.Squares[Location.X, Location.Y] := nil;
  796. end;
  797.  
  798. procedure TChessBoard.ResetCurrentPlayer;
  799. begin
  800.   Player := GetPlayer(Game);
  801.   Computer := Opponent(Player);
  802. end;
  803.  
  804. procedure TChessBoard.ResetValidMoves;
  805. var
  806.   Chg: TChange;
  807.   PlayerColor: TColor;
  808.   EmptyMove: TMove;
  809.  
  810.   procedure DoAttacks(P: PView); far;
  811.   begin
  812.     if TypeOf(P^) = TypeOf(TChessPiece) then
  813.       if PChessPiece(P)^.PieceType.Color <> PlayerColor then
  814.         if Settings.Hints and hoAttacks <> 0 then
  815.           PChessPiece(P)^.CheckJeopardy(ValidMoves)
  816.         else PChessPiece(P)^.CheckJeopardy(EmptyMove);
  817.   end;
  818.  
  819.   procedure DoJeopardies(P : PView); far;
  820.   begin
  821.     if TypeOf(P^) = TypeOf(TChessPiece) then
  822.       if (PChessPiece(P)^.PieceType.Color = PlayerColor) then
  823.         PChessPiece(P)^.CheckJeopardy(OpponentMoves)
  824.       else PChessPiece(P)^.CheckJeopardy(EmptyMove);
  825.   end;
  826.  
  827. begin
  828.   Chg.Piece := pEmpty;
  829.   Word(Chg.Source) := 0;
  830.   Word(Chg.Dest) := 0;
  831.   FillChar(EmptyMove, SizeOf(EmptyMove), 0);
  832.   PlayerColor := GetPlayer(Game);
  833.   if Settings.Hints and hoJeopardies <> 0 then
  834.   begin
  835.     { Switch players to see which opponent pieces attack ours }
  836.     SetPlayer(Game, Opponent(PlayerColor));
  837.     GetValidMoves(Game, Chg, OpponentMoves);
  838.     SetPlayer(Game, PlayerColor);
  839.     if Settings.Hints and hoJeopardies <> 0 then
  840.       ForEach(@DoJeopardies);
  841.   end
  842.   else
  843.   begin
  844.     OpponentMoves[0] := EmptyMove;    { Clear the jeopardy lists }
  845.     ForEach(@DoJeopardies);
  846.   end;
  847.   GetValidMoves(Game, Chg, ValidMoves);
  848.   ForEach(@DoAttacks);
  849.   DrawSurface;
  850. end;
  851.  
  852. function TChessBoard.SaveGame: Word;
  853. var
  854.   S: PBufStream;
  855. begin
  856.   SaveGame := cmCancel;
  857.   if GameName = '' then
  858.   begin
  859.     SaveGame := SaveGameAs;
  860.     Exit;
  861.   end
  862.   else if Game <> 0 then
  863.   begin
  864.     S := New(PBufStream, Init(GameName, stCreate, 1024));
  865.     S^.Write(ChessSignature, SizeOf(ChessSignature));
  866.     S^.Put(MoveHistory);
  867.     if S^.Status <> stOK then
  868.       {!!} MessageBox('Error writing file', nil, mfError + mfOKButton + mfInsertInApp);
  869.     Dispose(S, Done);
  870.     SaveGame := cmOK;
  871.   end;
  872. end;
  873.  
  874. function TChessBoard.SaveGameAs: Word;
  875. var
  876.   FileDialog: PFileDialog;
  877. begin
  878.   SaveGameAs := cmCancel;
  879.   FileDialog := New(PFileDialog, Init('*.CHS', 'Save Game As',
  880.     '~S~ave game as', fdOKButton, 101));
  881.   if Application^.ExecView(FileDialog) <> cmCancel then
  882.   begin
  883.     FileDialog^.GetFileName(GameName);
  884.     SaveGameAs := SaveGame;
  885.   end;
  886.   Dispose(FileDialog, Done);
  887. end;
  888.  
  889. procedure TChessBoard.SetGameBoard(const ABoard: TBoard);
  890. begin
  891.   if Game <> 0 then
  892.     if SetBoard(Game, ABoard) <> ceOK then
  893.       MessageBox('Error setting game board', nil,
  894.         mfError + mfOKButton + mfInsertInApp);
  895. end;
  896.  
  897. procedure TChessBoard.SetupNewGameBoard;
  898. var
  899.   Color: TColor;
  900. begin
  901.   ClearBoard;
  902.   if MoveHistory <> nil then MoveHistory^.Purge;
  903.   GameName := '';
  904.   for Color := cWhite to cBlack do
  905.     if ChessTimers[Color] <> nil then
  906.       ChessTimers[Color]^.Clear;
  907.   InitGameBoard;
  908.   ResetValidMoves;
  909. end;
  910.  
  911. procedure TChessBoard.ShowEndGame(Reason: Integer);
  912. type
  913.   TWinRec = record
  914.     Winner, Loser: PString;
  915.   end;
  916. var
  917.   ReasonStr: String;
  918.   Winner: TColor;
  919.   WinRec: TWinRec;
  920.   BStr, WStr: String[5];
  921. begin
  922.   Winner := Opponent(GetPlayer(Game));
  923.   BStr := 'Black';
  924.   WStr := 'White';
  925.   if Winner = cBlack then
  926.   begin
  927.     WinRec.Winner := @BStr;
  928.     WinREc.Loser := @WStr;
  929.   end
  930.   else
  931.   begin
  932.     WinRec.Winner := @WStr;
  933.     WinREc.Loser := @BStr;
  934.   end;
  935.   case TChessStatus(Reason) of
  936.     csCheckMate: ReasonStr := ^C'Checkmate!'#13^C'%s wins!';
  937.     csStaleMate: ReasonStr := ^C'Stalemate!';
  938.     csResigns:   ReasonStr := ^C'%s Resigns!'#13^C'%s wins!';
  939.     csFiftyMoveRule:
  940.       ReasonStr := ^C'Stalemate!'#13^C'Fifty move rule.';
  941.     csRepetitionRule:
  942.       ReasonStr := ^C'Stalemate!'#13^C'Repitition rule.';
  943.   end;
  944.   MessageBox(ReasonStr, @WinRec, mfInformation + mfOKButton + mfInsertInApp);
  945. end;
  946.  
  947. procedure TChessBoard.StartComputerMove;
  948. var
  949.   ComputerTime: Longint;
  950. begin
  951.   AbortSearch(Game);
  952.   Computer := GetPlayer(Game);
  953.   Player := Opponent(Computer);
  954.   ComputerTime := GetComputerTime;
  955.   ChessTimers[GetPlayer(Game)]^.Mark;
  956.   ComputerMove(Game, ComputerTime);
  957.   ThinkState := GetSearchStatus(Game);
  958.   if StatusDialog <> nil then
  959.     StatusDialog^.LastNodes := 0;
  960. end;
  961.  
  962. procedure TChessBoard.Undo;
  963. var
  964.   Move: TMove;
  965.   R: TRect;
  966.   P: PChessPiece;
  967. begin
  968.   AbortSearch(Game);
  969.   GameOver := False;
  970.   ChessTimers[Player]^.Stop;
  971.   MoveHistory^.Undo(Move);
  972.   if RetractMove(Game, Move) = ceOK then
  973.     Message(@Self, evMove, cmUndoMove, @Move);
  974.   if (GameMode = gmOnePlay) and (MoveHistory^.UndoAvail) and
  975.     (Player <> GetPlayer(Game)) then
  976.   begin
  977.     MoveHistory^.Undo(Move);
  978.     if RetractMove(Game, Move) = ceOK then
  979.       Message(@Self, evMove, cmUndoMove, @Move);
  980.   end
  981.   else ResetCurrentPlayer;
  982.   ResetValidMoves;
  983.   ChessTimers[Player]^.Mark;
  984.   ChessTimers[Player]^.Start;
  985.   if StatusDialog <> nil then StatusDialog^.UpdateList(MoveHistory);
  986.   Update;
  987. end;
  988.  
  989. procedure TChessBoard.Update;
  990. var
  991.   ChessStatus: TChessStatus;
  992.   MateInMoves: Integer;
  993.   Event: TEvent;
  994. begin
  995.   UpdateCommands;
  996.   ChessStatus := GetChessStatus(Game, MateInMoves);
  997.   if StatusLine <> nil then
  998.     PChessStatusLine(StatusLine)^.SetStatus(ChessStatus, MateInMoves);
  999.   if not (ChessStatus in [csNormal, csCheck, csMateFound]) then
  1000.   begin
  1001.     ChessTimers[cWhite]^.Stop;
  1002.     ChessTimers[cBlack]^.Stop;
  1003.     GameOver := True;
  1004.     AbortSearch(Game);
  1005.     GameMode := GameMode and not gmDemo;
  1006.     ThinkState := GetSearchStatus(Game);
  1007.     UpdateCommands;
  1008.     Event.What := evCommand;
  1009.     Event.Command := cmGameOver;
  1010.     Event.InfoInt := Integer(ChessStatus);
  1011.     PutEvent(Event);
  1012.   end;
  1013. end;
  1014.  
  1015. procedure TChessBoard.UpdateCommands;
  1016. begin
  1017.   if MoveHistory <> nil then
  1018.   begin
  1019.     if StatusDialog <> nil then
  1020.       StatusDialog^.Update(Game, ChessTimers, GetNodes(Game),
  1021.         MoveHistory^.GetNumMoves, GameMode);
  1022.     SetCmdState([cmRedo], (MoveHistory^.RedoAvail) and
  1023.       (ThinkState <> ssMoveSearch));
  1024.     SetCmdState([cmUndo], (MoveHistory^.UndoAvail) and
  1025.       (ThinkState <> ssMoveSearch));
  1026.     SetCmdState([cmComputerMove, cmEnterMove, cmShowHint],
  1027.       (ThinkState <> ssMoveSearch) and not GameOver);
  1028.     SetCmdState([cmStop], (GameMode and gmDemo <> 0) or
  1029.       (ThinkState = ssMoveSearch));
  1030.     SetCmdState([cmRunDemo], (GameMode and gmDemo = 0) and not GameOver);
  1031.   end;
  1032. end;
  1033.  
  1034. function TChessBoard.ValidateMove(var C: TChange): TChessError;
  1035. var
  1036.   X: Integer;
  1037.   ValidMove: TMove;
  1038.   CurMove: TMove;
  1039. begin
  1040.   ValidateMove := ceInvalidMove;
  1041.   if (ThinkState in [ssComplete, ssThinkAhead]) then
  1042.   begin
  1043.     X := 0;
  1044.     FillChar(CurMove, SizeOf(CurMove), 0);
  1045.     while (X <= High(ValidMoves)) and
  1046.       (ValidMoves[X].Change.Piece <> pEmpty) do
  1047.     begin
  1048.       ValidMove := ValidMoves[X];
  1049.       with ValidMove do
  1050.         if ((Change.Piece = C.Piece) or (C.Piece = pEmpty)) and
  1051.           ((Word(Change.Dest) = Word(C.Dest)) or (Word(C.Dest) = 0)) and
  1052.           ((Word(Change.Source) = Word(C.Source)) or (Word(C.Source) = 0)) then
  1053.         begin
  1054.           if CurMove.Change.Piece = pEmpty then
  1055.             CurMove := ValidMove
  1056.           else
  1057.           begin
  1058.             if (ValidMove.Change.Piece = pPawn) and
  1059.               (CurMove.Change.Piece <> pPawn) then
  1060.               CurMove := ValidMove
  1061.             else if (ValidMove.Change.Piece <> pPawn) and
  1062.               (CurMove.Change.Piece = pPawn) then
  1063.             else
  1064.             begin
  1065.               C := CurMove.Change;
  1066.               ValidateMove := ceAmbiguousMove;
  1067.               Exit;
  1068.             end;
  1069.           end;
  1070.         end;
  1071.       Inc(X);
  1072.     end;
  1073.     if CurMove.Change.Piece <> pEmpty then
  1074.     begin
  1075.       C := CurMove.Change;
  1076.       ValidateMove := ceOK;
  1077.     end;
  1078.   end;
  1079. end;
  1080.  
  1081. function TChessBoard.Valid(Command: Word): Boolean;
  1082. begin
  1083.   Valid := True;
  1084.   if Command = cmQuit then
  1085.     Valid := CheckActiveGame <> cmCancel;
  1086. end;
  1087.  
  1088. end.
  1089.