From 58986098919c51a874b7a5943348e518dce7dd65 Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Thu, 23 Sep 2010 10:43:52 +0000 Subject: [PATCH] Implements support for piece moving in fpchess git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1326 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- applications/fpchess/chessdrawer.pas | 18 +++ applications/fpchess/chessgame.pas | 52 +++++++ applications/fpchess/fpchess.lpi | 148 +++++++++++--------- applications/fpchess/mainform.lfm | 26 ++-- applications/fpchess/mainform.pas | 75 +++++++++- applications/fpchess/skins/classic/base.png | Bin 12671 -> 6489 bytes 6 files changed, 242 insertions(+), 77 deletions(-) diff --git a/applications/fpchess/chessdrawer.pas b/applications/fpchess/chessdrawer.pas index 4da457dd2..9e875edb2 100644 --- a/applications/fpchess/chessdrawer.pas +++ b/applications/fpchess/chessdrawer.pas @@ -11,6 +11,15 @@ uses type + TChessDrawerDelegate = class + public + procedure HandleMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); virtual; abstract; + procedure HandleMouseUp(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); virtual; abstract; + procedure HandleMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); virtual; abstract; + end; + { TChessDrawer } TChessDrawer = class(TCustomControl) @@ -31,6 +40,7 @@ type AImage: TFPImageBitmap); function GetChessTileImage(ATile: TChessTile): TPortableNetworkGraphic; procedure LoadImages(); + procedure SetDelegate(ADelegate: TChessDrawerDelegate); end; var @@ -224,5 +234,13 @@ begin bmpWKnight.Assign(imgBKing); } end; +procedure TChessDrawer.SetDelegate(ADelegate: TChessDrawerDelegate); +begin + // Events + OnMouseMove := @ADelegate.HandleMouseMove; + OnMouseUp := @ADelegate.HandleMouseUp; + OnMouseDown := @ADelegate.HandleMouseDown; +end; + end. diff --git a/applications/fpchess/chessgame.pas b/applications/fpchess/chessgame.pas index 841ce54a2..08e109a0e 100644 --- a/applications/fpchess/chessgame.pas +++ b/applications/fpchess/chessgame.pas @@ -29,6 +29,11 @@ type ctBPawn, ctBKnight, ctBBishop, ctBRook, ctBQueen, ctBKing ); +const + WhitePieces = [ctWPawn, ctWKnight, ctWBishop, ctWRook, ctWQueen, ctWKing]; + BlackPieces = [ctBPawn, ctBKnight, ctBBishop, ctBRook, ctBQueen, ctBKing]; + +type {@@ The index [1][1] refers to the left-bottom corner of the table, also known as A1. @@ -42,8 +47,14 @@ type TChessGame = class public Board: TChessBoard; + CurrentPlayerIsWhite: Boolean; + Dragging: Boolean; + DragStart, MouseMovePos: TPoint; procedure StartNewGame(APlayAsWhite: Boolean); overload; procedure StartNewGame(APlayAsWhite: Integer); overload; + function ClientToBoardCoords(AClientCoords: TPoint): TPoint; + function CheckStartMove(AFrom: TPoint): Boolean; + function MovePiece(AFrom, ATo: TPoint): Boolean; end; var @@ -59,6 +70,8 @@ var i: Integer; j: Integer; begin + CurrentPlayerIsWhite := True; + // if APlayAsWhite then begin @@ -114,6 +127,45 @@ begin StartNewGame(APlayAsWhite = 0); end; +{ + Returns: If the move is valid and was executed +} +function TChessGame.MovePiece(AFrom, ATo: TPoint): Boolean; +begin + Result := False; + + if not CheckStartMove(AFrom) then Exit; + + // Parameter checking + if (AFrom.X < 1) or (AFrom.X > 8) or (ATo.X < 1) or (ATo.X > 8) then Exit; + if (AFrom.Y < 1) or (AFrom.Y > 8) or (ATo.Y < 1) or (ATo.Y > 8) then Exit; + + // col, row + Board[ATo.X][ATo.Y] := Board[AFrom.X][AFrom.Y]; + Board[AFrom.X][AFrom.Y] := ctEmpty; + + CurrentPlayerIsWhite := not CurrentPlayerIsWhite; + + Result := True; +end; + +function TChessGame.ClientToBoardCoords(AClientCoords: TPoint): TPoint; +begin + Result.X := 1 + AClientCoords.X div INT_CHESSTILE_SIZE; + Result.Y := 1 + (INT_CHESSBOARD_SIZE - AClientCoords.Y) div INT_CHESSTILE_SIZE; +end; + +{@@ + AFrom - The start move position in board coordinates +} +function TChessGame.CheckStartMove(AFrom: TPoint): Boolean; +begin + if CurrentPlayerIsWhite then + Result := Board[AFrom.X][AFrom.Y] in WhitePieces + else + Result := Board[AFrom.X][AFrom.Y] in BlackPieces; +end; + initialization vChessGame := TChessGame.Create; diff --git a/applications/fpchess/fpchess.lpi b/applications/fpchess/fpchess.lpi index cf3c18874..54b3de982 100644 --- a/applications/fpchess/fpchess.lpi +++ b/applications/fpchess/fpchess.lpi @@ -36,7 +36,7 @@ - + @@ -46,9 +46,9 @@ - - - + + + @@ -123,10 +123,12 @@ + - - - + + + + @@ -163,9 +165,9 @@ - - - + + + @@ -222,9 +224,9 @@ - - - + + + @@ -235,7 +237,7 @@ - + @@ -280,119 +282,133 @@ - - - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + diff --git a/applications/fpchess/mainform.lfm b/applications/fpchess/mainform.lfm index e127f5898..43c973f30 100644 --- a/applications/fpchess/mainform.lfm +++ b/applications/fpchess/mainform.lfm @@ -1,19 +1,19 @@ object formChess: TformChess Left = 181 - Height = 300 + Height = 433 Top = 209 Width = 240 Caption = 'FP Chess 0.1' - ClientHeight = 300 + ClientHeight = 433 ClientWidth = 240 OnCreate = FormCreate LCLVersion = '0.9.29' object notebookMain: TUntabbedNotebook Left = 0 - Height = 300 + Height = 433 Top = 0 Width = 240 - PageIndex = 0 + PageIndex = 3 Align = alClient TabOrder = 0 TabStop = True @@ -111,8 +111,8 @@ object formChess: TformChess end end object pageConfigConnection: TUNBPage - ClientWidth = 240 - ClientHeight = 300 + ClientWidth = 480 + ClientHeight = 600 object Label3: TLabel Left = 0 Height = 32 @@ -176,8 +176,8 @@ object formChess: TformChess end end object pageConnecting: TUNBPage - ClientWidth = 240 - ClientHeight = 300 + ClientWidth = 480 + ClientHeight = 600 object Label4: TLabel Left = 0 Height = 32 @@ -200,7 +200,7 @@ object formChess: TformChess end object pageGame: TUNBPage ClientWidth = 240 - ClientHeight = 300 + ClientHeight = 433 object Label5: TLabel Left = 0 Height = 32 @@ -213,6 +213,14 @@ object formChess: TformChess ParentColor = False ParentFont = False end + object labelPos: TLabel + Left = 8 + Height = 14 + Top = 392 + Width = 40 + Caption = 'labelPos' + ParentColor = False + end end end end diff --git a/applications/fpchess/mainform.pas b/applications/fpchess/mainform.pas index 4cbcf13c0..54b09c157 100644 --- a/applications/fpchess/mainform.pas +++ b/applications/fpchess/mainform.pas @@ -5,13 +5,24 @@ unit mainform; interface uses - Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, - ComCtrls, StdCtrls, Buttons, + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, + ExtCtrls, ComCtrls, StdCtrls, Buttons, // chessdrawer, chessgame, chessconfig; type + { TFormDrawerDelegate } + + TFormDrawerDelegate = class(TChessDrawerDelegate) + public + procedure HandleMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); override; + procedure HandleMouseUp(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); override; + procedure HandleMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); override; + end; + { TformChess } TformChess = class(TForm) @@ -26,6 +37,7 @@ type Label4: TLabel; Label5: TLabel; Label6: TLabel; + labelPos: TLabel; LabeledEdit1: TLabeledEdit; LabeledEdit2: TLabeledEdit; editPlayerName: TLabeledEdit; @@ -41,10 +53,12 @@ type { private declarations } public { public declarations } + procedure UpdateCaptions; end; var formChess: TformChess; + vFormDrawerDelegate: TFormDrawerDelegate; implementation @@ -62,6 +76,17 @@ begin else if Sender = btnDirectComm then notebookMain.PageIndex := 1; end; +procedure TformChess.UpdateCaptions; +var + CurPlayerStr: string; +begin + if vChessGame.CurrentPlayerIsWhite then CurPlayerStr := 'White playing' + else CurPlayerStr := 'Black playing'; + + formChess.labelPos.Caption := Format(CurPlayerStr + ' X: %d Y: %d', + [vChessGame.MouseMovePos.X, vChessGame.MouseMovePos.Y]); +end; + procedure TformChess.FormCreate(Sender: TObject); begin // Creation of internal components @@ -71,10 +96,56 @@ begin vChessDrawer.Left := 20; vChessDrawer.Height := INT_CHESSBOARD_SIZE; vChessDrawer.Width := INT_CHESSBOARD_SIZE; + vChessDrawer.SetDelegate(vFormDrawerDelegate); // Loading of resources vChessDrawer.LoadImages(); end; +{ TFormDrawerDelegate } + +procedure TFormDrawerDelegate.HandleMouseMove(Sender: TObject; + Shift: TShiftState; X, Y: Integer); +begin + vChessGame.MouseMovePos := vChessGame.ClientToBoardCoords(Point(X, Y)); + formChess.UpdateCaptions; +end; + +procedure TFormDrawerDelegate.HandleMouseUp(Sender: TObject; + Button: TMouseButton; Shift: TShiftState; X, Y: Integer); +var + lCoords: TPoint; +begin + vChessGame.Dragging := False; + + lCoords := vChessGame.ClientToBoardCoords(Point(X, Y)); + if not vChessGame.MovePiece(vChessGame.DragStart, lCoords) then Exit; + + vChessDrawer.Invalidate; + formChess.UpdateCaptions; +end; + +procedure TFormDrawerDelegate.HandleMouseDown(Sender: TObject; + Button: TMouseButton; Shift: TShiftState; X, Y: Integer); +var + lCoords: TPoint; +begin + lCoords := vChessGame.ClientToBoardCoords(Point(X, Y)); + if not vChessGame.CheckStartMove(lCoords) then Exit; + + vChessGame.Dragging := True; + vChessGame.DragStart := lCoords; + vChessDrawer.Invalidate; + formChess.UpdateCaptions; +end; + +initialization + + vFormDrawerDelegate := TFormDrawerDelegate.Create; + +finalization + + vFormDrawerDelegate.Free; + end. diff --git a/applications/fpchess/skins/classic/base.png b/applications/fpchess/skins/classic/base.png index 3d11fe471f89907296b52e7a794f2bcb699a8930..11a7c2464eb2b28dc6614cabb4dba80ec77ba980 100644 GIT binary patch literal 6489 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 Ii~s4r0Jgdzp8x;= literal 12671 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