home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Pascal / BPASCAL.700 / D12 / CHESSOWL.ZIP / BOARD.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-01  |  17.2 KB  |  622 lines

  1. unit board;
  2.  
  3. interface
  4.  
  5. uses Winprocs, Wintypes, Objects, OWindows, ChPieces, Chessdll,
  6.      chconst;
  7.  
  8. type
  9.   PChessBoard = ^TChessBoard;
  10.   TChessBoard = object(TWindow)
  11.     Game: HChess;
  12.     Squares: array [1..8,1..8] of PChessPiece;
  13.     Pieces: TCollection;
  14.     BoardBitmap: HBitmap;
  15.     WhiteColor: TColorRef;
  16.     BlackColor: TColorRef;
  17.     WhiteBrush: HBrush;
  18.     BlackBrush: HBrush;
  19.     SquareWidth: Word;
  20.     Dragger: PChessPiece;    { if <> nil, we're dragging it }
  21.     BoardDC, DragDC: HDC;
  22.     BoardOldBM, DragBM : HBitmap;
  23.     ValidMoves,
  24.     OpponentMoves: array [0..(28*16+1)] of TMove;
  25.     { Setup and shutdown }
  26.     constructor Init(AParent: PWindowsObject; GH: HChess);
  27.     destructor Done; virtual;
  28.     function  GetClassName: PChar; virtual;
  29.     procedure GetWindowClass(var WC: TWndClass); virtual;
  30.     procedure ResetBoard(GH: HChess);
  31.  
  32.     { Board display }
  33.     function  IdealWidth: Word;
  34.     procedure InitBoardBitmap;
  35.     procedure DrawBoard;
  36.     procedure Paint(DC: HDC; var PS: TPaintStruct); virtual;
  37.     procedure WMEraseBkgnd(var Msg: TMessage);
  38.       virtual wm_First + wm_EraseBkgnd;
  39.     procedure WMSize(var Msg: TMessage);
  40.       virtual wm_First + wm_Size;
  41.  
  42.     { Conversions }
  43.     function  PieceFromPoint(Pt: TPoint): PChessPiece;
  44.     procedure SquareFromPoint(Pt: TPoint; var Sq: TLocation);
  45.     procedure SquareToRect(Sq: TLocation; var R: TRect);
  46.  
  47.     { Piece management }
  48.     procedure InsertPiece(Sq: TLocation; P: PChessPiece);
  49.     function  RemovePiece(Sq: TLocation): PChessPiece;
  50.     procedure ExecuteMove(const Move: TMove);
  51.     procedure RetractMove(const Move: TMove);
  52.     procedure ResetValidMoves;
  53.     procedure FreshenPiece(P : PChessPiece);
  54.  
  55.     { Piece dragging routines }
  56.     procedure WMSetCursor(var Msg: TMessage);
  57.       virtual wm_First + wm_SetCursor;
  58.     procedure WMLButtonDown(var Msg: TMessage);
  59.       virtual wm_First + wm_LButtonDown;
  60.     procedure WMMouseMove(var Msg: TMessage);
  61.       virtual wm_First + wm_MouseMove;
  62.     procedure WMLButtonUp(var Msg: TMessage);
  63.       virtual wm_First + wm_LButtonUp;
  64.   end;
  65.  
  66.   function OtherPlayer(Color: TColor): TColor;
  67.  
  68. implementation
  69.  
  70. uses AppUtils, ChessDlg;
  71.  
  72. function OtherPlayer(Color: TColor): TColor;
  73. begin
  74.   if Color = cWhite then
  75.     OtherPlayer := cBlack
  76.   else
  77.     OtherPlayer := cWhite;
  78. end;
  79.  
  80. constructor TChessBoard.Init(AParent: PWindowsObject; GH: HChess);
  81. begin
  82.   inherited Init(AParent, nil);
  83.   with Attr do
  84.   begin
  85.     X := 0;
  86.     Y := 0;
  87.     W := 200;
  88.     H := 200;
  89.     Style := ws_Child {or ws_Border};
  90.         { NOT ws_Visible - the parent window will resize us  }
  91.         { to the ideal width and then show us.               }
  92.   end;
  93.   BoardBitmap := 0;
  94.   DragDC := 0;
  95.   BoardDC := CreateMemoryDC;
  96.   Dragger := nil;
  97.   WhiteColor := XApp^.GetAppProfileRGB(
  98.                        'Board','WhiteColor',RGB(255,255,255));
  99.   WhiteBrush := CreateSolidBrush(WhiteColor);
  100.   BlackColor := XApp^.GetAppProfileRGB(
  101.                        'Board','BlackColor',RGB(255,0,0));
  102.   BlackBrush := CreateSolidBrush(BlackColor);
  103.   Pieces.Init(32, 4);  { Growth allows for edited boards with > 32 pieces }
  104.   ResetBoard(GH);
  105. end;
  106.  
  107. destructor TChessBoard.Done;
  108. var
  109.   Temp: array [0..15] of Char;
  110. begin
  111.   inherited Done;
  112.   Pieces.Done;
  113.   if BoardDC <> 0 then
  114.   begin
  115.     SelectObject(BoardDC, BoardOldBM);
  116.     DeleteDC(BoardDC);
  117.   end;
  118.   if DragDC <> 0 then
  119.   begin
  120.     DeleteObject(SelectObject(DragDC, DragBM));
  121.     DeleteDC(DragDC);
  122.   end;
  123.   if BoardBitmap <> 0 then
  124.     DeleteObject(BoardBitmap);
  125.   DeleteObject(WhiteBrush);
  126.   DeleteObject(BlackBrush);
  127.   XApp^.WriteAppProfileRGB('Board','WhiteColor',WhiteColor);
  128.   XApp^.WriteAppProfileRGB('Board','BlackColor',BlackColor);
  129. end;
  130.  
  131. function  TChessBoard.GetClassName: PChar;
  132. begin
  133.   GetClassName := 'TPWOWLChessBoard';
  134. end;
  135.  
  136. procedure TChessBoard.GetWindowClass(var WC: TWndClass);
  137. begin
  138.   inherited GetWindowClass(WC);
  139.   WC.Style := cs_ByteAlignWindow;
  140.   WC.hCursor := 0;
  141. end;
  142.  
  143. procedure TChessBoard.ResetBoard(GH: HChess);
  144.   procedure DoResize(P : PChessPiece); far;
  145.   var
  146.     R: TRect;
  147.     S: TLocation;
  148.   begin
  149.     P^.GetSquare(S);
  150.     SquareToRect(S, R);
  151.     P^.SetRect(R);
  152.   end;
  153. var
  154.   TempBoard: TBoard;
  155.   Square: TLocation;
  156. begin
  157.   Game := GH;
  158.   Pieces.FreeAll;
  159.   FillChar(Squares, SizeOf(Squares), 0);
  160.  
  161.   GetBoard(Game, TempBoard);
  162.  
  163.   SquareWidth := Attr.W div 8;
  164.   for Square.X := 1 to 8 do
  165.     for Square.Y := 1 to 8 do
  166.       if (TempBoard[Square.X, Square.Y].Piece <> pEmpty) then
  167.       begin
  168.         Squares[Square.X,Square.Y] := New(PChessPiece,
  169.              Init(@Self, TempBoard[Square.X, Square.Y], Square));
  170.         Pieces.Insert(Squares[Square.X,Square.Y]);
  171.       end;
  172.   ResetValidMoves;
  173.   if HWindow <> 0 then
  174.   begin
  175.     Pieces.ForEach(@DoResize);
  176.     DrawBoard;
  177.     InvalidateRect(HWindow, nil, False);
  178.   end;
  179. end;
  180.  
  181. function TChessBoard.IdealWidth: Word;
  182. var
  183.   Best: Word;
  184.   procedure CheckBitmapSize(P: PChessPiece); far;
  185.   begin
  186.     if Best < P^.BitSize.X then Best := P^.BitSize.X;
  187.     if Best < P^.BitSize.Y then Best := P^.BitSize.Y;
  188.   end;
  189. begin
  190.   Best := 0;
  191.   Pieces.ForEach(@CheckBitmapSize);
  192.   IdealWidth := (Best + 4) * 8;
  193. end;
  194.  
  195. procedure TChessBoard.InitBoardBitmap;
  196. var
  197.   DC: HDC;
  198. begin
  199.   if BoardBitmap <> 0 then
  200.   begin
  201.     SelectObject(BoardDC, BoardOldBM);
  202.     DeleteObject(BoardBitmap);
  203.   end;
  204.  
  205.   DC := GetDC(HWindow);
  206.   BoardBitmap := CreateCompatibleBitmap(DC, Attr.W, Attr.H);
  207.   ReleaseDC(HWindow, DC);
  208.   BoardOldBM := SelectObject(BoardDC, BoardBitmap);
  209.   SquareWidth := Attr.W div 8;
  210. end;
  211.  
  212. procedure TChessBoard.DrawBoard;
  213. var
  214.   OldBrush, SquareBrush : HBrush;
  215.   X, Y: Integer;
  216.  
  217.   procedure DoPaint(P: PChessPiece); far;
  218.   begin
  219.     P^.Paint(BoardDC);
  220.   end;
  221.  
  222. begin
  223.   OldBrush := SelectObject(BoardDC, CreateSolidBrush(WhiteColor));
  224.   PatBlt(BoardDC, 0, 0, Attr.W, Attr.H, PatCopy);
  225.  
  226.   DeleteObject(SelectObject(BoardDC, CreateSolidBrush(BlackColor)));
  227.   for Y := 0 to 7 do
  228.     for X := 0 to 7 do
  229.       if Odd(X + Y) then
  230.         PatBlt(BoardDC, X * SquareWidth, Y * SquareWidth,
  231.                       SquareWidth, SquareWidth, PatCopy);
  232.  
  233.   DeleteObject(SelectObject(BoardDC, OldBrush));
  234.   Pieces.ForEach(@DoPaint);
  235. end;
  236.  
  237. { Because of the way the board paints from a memory bitmap, we don't
  238.   need the window to erase the background before we paint.  }
  239. procedure TChessBoard.WMEraseBkgnd(var Msg: TMessage);
  240. begin
  241.   Msg.Result := 1;
  242. end;
  243.  
  244. procedure TChessBoard.Paint(DC: HDC; var PS: TPaintStruct);
  245.   procedure CheckPieces(P: PChessPiece); far;
  246.   var
  247.     Sq: TLocation;
  248.     OldBrush: HBrush;
  249.   begin
  250.     if P^.NeedRedraw then
  251.     begin
  252.       P^.GetSquare(Sq);
  253.       if Odd(Sq.X + Sq.Y) then
  254.         OldBrush := SelectObject(BoardDC, WhiteBrush)
  255.       else
  256.         OldBrush := SelectObject(BoardDC, BlackBrush);
  257.       with P^.Rect do
  258.         PatBlt(BoardDC, Left, Top, Right - Left, Bottom - Top, PatCopy);
  259.       SelectObject(BoardDC, OldBrush);
  260.       P^.Paint(BoardDC);
  261.     end;
  262.   end;
  263. begin
  264.   Pieces.ForEach(@CheckPieces);
  265.   with PS.rcPaint do
  266.     BitBlt(DC, Left, Top, Right - Left, Bottom - Top,
  267.            BoardDC, Left, Top, SrcCopy);
  268.   if Dragger <> nil then
  269.     Dragger^.Paint(DC);
  270. end;
  271.  
  272. procedure TChessBoard.WMSize(var Msg: TMessage);
  273.  
  274.   procedure DoResize(P : PChessPiece); far;
  275.   var
  276.     R: TRect;
  277.     S: TLocation;
  278.   begin
  279.     P^.GetSquare(S);
  280.     SquareToRect(S, R);
  281.     P^.SetRect(R);
  282.   end;
  283.  
  284. begin
  285.   inherited WMSize(Msg);
  286.   SquareWidth := Attr.W div 8;
  287.   InitBoardBitmap;
  288.   Pieces.ForEach(@DoResize);
  289.   DrawBoard;
  290. end;
  291.  
  292. function TChessBoard.PieceFromPoint(Pt: TPoint): PChessPiece;
  293.   function DoHitTest(P: PChessPiece): Boolean; far;
  294.   begin
  295.     DoHitTest := P^.HitTest(Pt);
  296.   end;
  297. begin
  298.   PieceFromPoint := PChessPiece(Pieces.FirstThat(@DoHitTest));
  299. end;
  300.  
  301. procedure TChessBoard.SquareFromPoint(Pt: TPoint; var Sq: TLocation);
  302. begin
  303.   Sq.X := (Pt.X div SquareWidth) + 1;
  304.   Sq.Y := (Attr.H - Pt.Y) div SquareWidth + 1;
  305. end;
  306.  
  307. procedure TChessBoard.SquareToRect(Sq: TLocation; var R: TRect);
  308. begin
  309.   R.Left   := (Sq.X - 1) * SquareWidth;
  310.   R.Right  := R.Left + SquareWidth;
  311.   R.Top    := Attr.H - (Sq.Y * SquareWidth);
  312.   R.Bottom := R.Top + SquareWidth;
  313. end;
  314.  
  315. procedure TChessBoard.ExecuteMove(const Move: TMove);
  316.  
  317.   function  CreatePromote(P: TPiece; Dest: TLocation): PChessPiece;
  318.   var                    { This function creates the piece specified by }
  319.     X: TSquare;          { P using color info from the piece already on }
  320.   begin                  { on the board at Dest.  This is for           }
  321.     X.Piece := P;        { Pawn Promotion moves only.                   }
  322.     X.Color := Squares[Dest.X, Dest.Y]^.Color;
  323.     InsertPiece(Dest, New(PChessPiece, Init(@Self, X, Dest)));
  324.   end;
  325.  
  326. begin
  327.   if Move.Change.Piece = pEmpty then Exit;
  328.   with Move, Move.Change do
  329.   begin
  330.     InsertPiece(Dest, RemovePiece(Source)); { Also deletes what's at dest }
  331.     case Move.Kind of
  332.       kEnPassant  : Dispose(RemovePiece(EPCapture), Done);
  333.       kCastling   : InsertPiece(RookDest, RemovePiece(RookSource));
  334.       kPawnPromote: CreatePromote(Piece, Dest);
  335.     end;
  336.   end;
  337. end;
  338.  
  339. procedure TChessBoard.RetractMove(const Move: TMove);
  340.   procedure CreatePiece(P: TPiece; Color: Boolean; Dest: TLocation);
  341.   var
  342.     X: TSquare;
  343.   begin
  344.     X.Piece := P;
  345.     X.Color := TColor(Color);
  346.     InsertPiece(Dest, New(PChessPiece, Init(@Self, X, Dest)));
  347.   end;
  348. var
  349.   Color: Boolean;   { Color of opponent }
  350. begin
  351.   if Move.Change.Piece = pEmpty then Exit;
  352.   with Move, Move.Change do
  353.   begin
  354.     Color := not Boolean(Squares[Dest.X, Dest.Y]^.Color);
  355.     InsertPiece(Source, RemovePiece(Dest)); {Back out of destination }
  356.     case Move.Kind of
  357.       kNormal     : if Capture then CreatePiece(Contents, Color, Dest);
  358.       kEnPassant  : CreatePiece(Contents, Color, EPCapture);
  359.       kCastling   : InsertPiece(RookSource, RemovePiece(RookDest));
  360.       kPawnPromote:
  361.         begin
  362.           if Capture then CreatePiece(Contents, not Color, Dest);
  363.           CreatePiece(pPawn, Color, Source);
  364.         end;
  365.     end;
  366.   end;
  367. end;
  368.  
  369.  
  370. procedure TChessBoard.ResetValidMoves;
  371. var
  372.   Chg: TChange;
  373.   PlayerColor: TColor;
  374.   EmptyMove: TMove;
  375.  
  376.   procedure DoValids(P : PChessPiece); far;
  377.   begin
  378.     if P^.Color = PlayerColor then
  379.       P^.ResetValidMoves(ValidMoves)  { piece gets its moves from list }
  380.     else
  381.     begin
  382.       P^.ResetValidMoves(EmptyMove);  { clear opponent's move lists }
  383.       if ChessSettings.ShowAttacks then
  384.         P^.CheckJeopardy(ValidMoves);
  385.     end;
  386.   end;
  387.  
  388.   procedure DoJeopardies(P : PChessPiece); far;
  389.   begin
  390.     if P^.Color = PlayerColor then
  391.       P^.CheckJeopardy(OpponentMoves);
  392.   end;
  393.  
  394. begin
  395.   Chg.Piece := pEmpty;
  396.   Word(Chg.Source) := 0;
  397.   Word(Chg.Dest) := 0;
  398.   FillChar(EmptyMove, SizeOf(EmptyMove), 0);
  399.   PlayerColor := GetPlayer(Game);
  400.   if ChessSettings.ShowJeopardies then
  401.   begin
  402.     { Switch players to see which opponent pieces attack ours }
  403.     SetPlayer(Game, OtherPlayer(PlayerColor));
  404.     GetValidMoves(Game, Chg, OpponentMoves);
  405.     SetPlayer(Game, PlayerColor);
  406.     Pieces.ForEach(@DoJeopardies);
  407.   end;
  408.   { Now see what moves our pieces can make }
  409.   GetValidMoves(Game, Chg, ValidMoves);
  410.   Pieces.ForEach(@DoValids);
  411. end;
  412.  
  413. procedure TChessBoard.FreshenPiece(P:PChessPiece);
  414. var
  415.   TempMoves: array [0..28] of TMove;
  416.   PlayerColor: TColor;
  417.   Chg: TChange;
  418.   EmptyMove: TMove;
  419. begin
  420.   Chg.Piece := pEmpty;
  421.   Word(Chg.Source) := 0;
  422.   Chg.Dest := P^.Square;
  423.   FillChar(EmptyMove, SizeOf(EmptyMove), 0);
  424.   PlayerColor := GetPlayer(Game);
  425.   if ChessSettings.ShowJeopardies then
  426.   begin
  427.     { Switch players to see which opponent pieces attack ours }
  428.     SetPlayer(Game, OtherPlayer(PlayerColor));
  429.     GetValidMoves(Game, Chg, TempMoves);
  430.     SetPlayer(Game, PlayerColor);
  431.     P^.CheckJeopardy(TempMoves);
  432.   end;
  433. end;
  434.  
  435. procedure TChessBoard.WMSetCursor(var Msg: TMessage);
  436. var
  437.   P: TPoint;
  438.   X: PChessPiece;
  439. begin
  440.   DefWndProc(Msg);
  441.   if Msg.Result = 0 then
  442.   begin
  443.     GetCursorPos(P);
  444.     ScreenToClient(HWindow, P);
  445.     X := PieceFromPoint(P);
  446.     if (X <> nil) and X^.CanDrag then
  447.       SetCursor(X^.GetCursor)
  448.     else
  449.       SetCursor(LoadCursor(0, PChar(idc_Arrow)));
  450.   end;
  451. end;
  452.  
  453. procedure TChessBoard.WMLButtonDown(var Msg: TMessage);
  454. var
  455.    R: TRect;
  456.   Sq: TLocation;
  457.   DC: HDC;
  458. begin
  459.   if Dragger = nil then
  460.   begin
  461.     Dragger := PieceFromPoint(TPoint(Msg.LParam));
  462.     if Dragger <> nil then
  463.       if Dragger^.CanDrag then
  464.       begin
  465.         Dragger^.GetSquare(Sq);
  466.         RemovePiece(Sq);
  467.         SetCapture(HWindow);
  468.         DC := GetDC(HWindow);
  469.         DragDC := CreateCompatibleDC(DC);
  470.         DragBM := SelectObject(DragDC,
  471.                     CreateCompatibleBitmap(DC, Attr.W, Attr.H));
  472.         BitBlt(DragDC, 0, 0, Attr.W, Attr.H, BoardDC, 0, 0, SrcCopy);
  473.         R := Dragger^.Rect;
  474.         Dragger^.DragBegin(DragDC, TPoint(Msg.LParam));
  475.         UnionRect(R, R, Dragger^.Rect);
  476.         with R do
  477.           BitBlt(DC, Left, Top, Right - Left, Bottom - Top,
  478.                  DragDC, Left, Top, SrcCopy);
  479.         ReleaseDC(HWindow, DC);
  480.       end
  481.       else
  482.       begin
  483.         Dragger := nil;
  484.         MessageBeep(0);
  485.       end;
  486.   end;
  487.   DefWndProc(Msg);
  488. end;
  489.  
  490. procedure TChessBoard.WMMouseMove(var Msg: TMessage);
  491. var
  492.    R: TRect;
  493.   Sq: TLocation;
  494.   DC: HDC; 
  495. begin
  496.   if Dragger <> nil then
  497.   begin
  498.     GetClientRect(HWindow, R);
  499.     if PtInRect(R, TPoint(Msg.LParam)) then
  500.     begin
  501.       SquareFromPoint(TPoint(Msg.LParam), Sq);
  502.       with Dragger^.Rect do
  503.         BitBlt(DragDC, Left, Top, Right - Left, Bottom - Top,
  504.                BoardDC, Left, Top, SrcCopy);
  505.       R := Dragger^.Rect;
  506.       Dragger^.DragContinue(DragDC, TPoint(Msg.LParam), Sq);
  507.       UnionRect(R, R, Dragger^.Rect);
  508.       DC := GetDC(HWindow);
  509.       with R do
  510.         BitBlt(DC, Left, Top, Right - Left, Bottom - Top,
  511.                DragDC, Left, Top, SrcCopy);
  512.       ReleaseDC(HWindow, DC);
  513.     end
  514.     else
  515.     begin
  516.       Dragger^.DragHide;
  517.       InvalidateRect(HWindow, @Dragger^.Rect, False);
  518.       SetCursor(LoadCursor(GetModuleHandle('User'), PChar(idc_No)));
  519.     end;
  520.   end;
  521.   DefWndProc(Msg);
  522. end;
  523.  
  524. procedure TChessBoard.WMLButtonUp(var Msg: TMessage);
  525. var
  526.   NewSq, OldSq: TLocation;
  527.   R: TRect;
  528.   Chg: TChange;
  529.   ValidMove: Boolean;
  530.   PlayerColor : TColor;
  531. begin
  532.   if Dragger <> nil then
  533.   begin
  534.     GetClientRect(HWindow, R);
  535.     with Dragger^.Rect do
  536.       BitBlt(DragDC, Left, Top, Right - Left, Bottom - Top,
  537.              BoardDC, Left, Top, SrcCopy);
  538.     if PtInRect(R, TPoint(Msg.LParam)) then
  539.       SquareFromPoint(TPoint(Msg.LParam), NewSq)
  540.     else
  541.     begin
  542.       NewSq.X := 0;     { 0 = off board or invalid }
  543.       NewSq.Y := 0;
  544.     end;
  545.     R := Dragger^.Rect;
  546.     Dragger^.GetSquare(OldSq);
  547.     ValidMove := Dragger^.DragEnd(DragDC, TPoint(Msg.LParam), NewSq, Chg);
  548.     InvalidateRect(HWindow, @R, False);
  549.     InsertPiece(OldSq, Dragger);  { Go back to original square }
  550.     Dragger := nil;
  551.     ReleaseCapture;
  552.     DeleteObject(SelectObject(DragDC, DragBM));
  553.     DeleteDC(DragDC);
  554.     DragDC := 0;
  555.     if (Chg.Piece = pPawn) and (VerifyMove(Game, Chg) = ceAmbiguousMove) then
  556.       Chg.Piece := pQueen;
  557.             { am_SubmitMove will return a boolean accept/reject response }
  558.     if ValidMove and
  559.        LongBool(SendMessage(Parent^.HWindow, am_SubmitMove, 0, Longint(@Chg))) then
  560.     begin
  561.         { After Submitmove, player color has switched.  We need to temporarily
  562.           switch it back to our color for the following operations. }
  563.       PlayerColor := GetPlayer(Game);
  564.       SetPlayer(Game, OtherPlayer(PlayerColor));
  565.       if ChessSettings.ShowAttacks then
  566.         { Reset all pieces' valid moves, so that opponents that were attacked by
  567.           the moved piece's former position can be cleared as well as note what
  568.           opponent pieces are now attacked in the new position. }
  569.         ResetValidMoves
  570.       else
  571.       if ChessSettings.ShowJeopardies then
  572.         { For jeopardies, we just need to see who attacks the new square.
  573.           FreshenPiece is must faster and simpler than ResetValidMoves. }
  574.         FreshenPiece(Squares[NewSq.X,NewSq.Y]);
  575.       SetPlayer(Game, PlayerColor);
  576.     end;
  577.     UpdateWindow(HWindow);
  578.   end;
  579.   DefWndProc(Msg);
  580. end;
  581.  
  582. procedure TChessBoard.InsertPiece(Sq: TLocation; P: PChessPiece);
  583. var
  584.   R: TRect;
  585. begin
  586.   if Squares[Sq.X,Sq.Y] = P then Exit;
  587.   if (Squares[Sq.X,Sq.Y] <> nil) then
  588.     Dispose(RemovePiece(Sq), Done);
  589.   Pieces.Insert(P);
  590.   P^.SetSquare(Sq);
  591.   Squares[Sq.X, Sq.Y] := P;
  592.   SquareToRect(Sq, R);
  593.   P^.SetRect(R);
  594.   P^.Paint(BoardDC);
  595.   InvalidateRect(HWindow, @R, False);
  596. end;
  597.  
  598. function TChessBoard.RemovePiece(Sq: TLocation): PChessPiece;
  599. var
  600.   OldBrush: HBrush;
  601.   R: TRect;
  602. begin
  603.   RemovePiece := nil;
  604.   if Squares[Sq.X,Sq.Y] <> nil then
  605.   begin
  606.     RemovePiece := Squares[Sq.X,Sq.Y];
  607.     Pieces.Delete(Squares[Sq.X,Sq.Y]);
  608.     Squares[Sq.X,Sq.Y] := nil;
  609.  
  610.     if Odd(Sq.X + Sq.Y) then
  611.       OldBrush := SelectObject(BoardDC, WhiteBrush)
  612.     else
  613.       OldBrush := SelectObject(BoardDC, BlackBrush);
  614.     SquareToRect(Sq, R);
  615.     with R do
  616.       PatBlt(BoardDC, Left, Top, Right - Left, Bottom - Top, PatCopy);
  617.     SelectObject(BoardDC, OldBrush);
  618.     InvalidateRect(HWindow, @R, False);
  619.   end;
  620. end;
  621.  
  622. end.