fpchess: some cleanup and enpassant refactoring

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1557 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat
2011-04-09 06:27:30 +00:00
parent dfce58e752
commit 498b0efc99

View File

@@ -58,6 +58,11 @@ type
} }
TChessBoard = array[1..8] of array[1..8] of TChessTile; TChessBoard = array[1..8] of array[1..8] of TChessTile;
TChessMove = record
From, To_: TPoint;
PieceMoved, PieceEaten: TChessTile;
end;
{ TChessGame } { TChessGame }
TChessGame = class TChessGame = class
@@ -65,25 +70,32 @@ type
Board: TChessBoard; Board: TChessBoard;
msg : String; msg : String;
CurrentPlayerIsWhite: Boolean; CurrentPlayerIsWhite: Boolean;
EnPassant : array[1..2] of array[1..8] of boolean; // Storage a flag to determine en passant captures, 1 is for white pawns.
Dragging: Boolean; Dragging: Boolean;
DragStart, MouseMovePos: TPoint; DragStart, MouseMovePos: TPoint;
UseTimer: Boolean; UseTimer: Boolean;
WhitePlayerTime: Integer; // milisseconds WhitePlayerTime: Integer; // milisseconds
BlackPlayerTime: Integer; // milisseconds BlackPlayerTime: Integer; // milisseconds
MoveStartTime: TDateTime; MoveStartTime: TDateTime;
// Last move (might in the future store all history)
PreviousMove: TChessMove;
// Data for Enpassant
EnpassantAllowed: Boolean;
EnpassantSquare: TPoint;
//
constructor Create; constructor Create;
procedure StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer); overload; procedure StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer); overload;
procedure StartNewGame(APlayAsWhite: Integer; AUseTimer: Boolean; APlayerTime: Integer); overload; procedure StartNewGame(APlayAsWhite: Integer; AUseTimer: Boolean; APlayerTime: Integer); overload;
function ClientToBoardCoords(AClientCoords: TPoint): TPoint; function ClientToBoardCoords(AClientCoords: TPoint): TPoint;
function CheckStartMove(AFrom: TPoint): Boolean; function CheckStartMove(AFrom: TPoint): Boolean;
function CheckKingInCheck(AFrom, ATo, AEnpassantSquareToClear: TPoint): Boolean;
function MovePiece(AFrom, ATo: TPoint): Boolean; function MovePiece(AFrom, ATo: TPoint): Boolean;
function ValidateRookMove(AFrom, ATo: TPoint) : boolean; function ValidateRookMove(AFrom, ATo: TPoint) : boolean;
function ValidateKnightMove(AFrom, ATo: TPoint) : boolean; function ValidateKnightMove(AFrom, ATo: TPoint) : boolean;
function ValidateBishopMove(AFrom, ATo: TPoint) : boolean; function ValidateBishopMove(AFrom, ATo: TPoint) : boolean;
function ValidateQueenMove(AFrom, ATo: TPoint) : boolean; function ValidateQueenMove(AFrom, ATo: TPoint) : boolean;
function ValidateKingMove(AFrom, ATo: TPoint) : boolean; function ValidateKingMove(AFrom, ATo: TPoint) : boolean;
function ValidatePawnMove(AFrom, ATo: TPoint) : boolean; function ValidatePawnMove(AFrom, ATo: TPoint;
var AEnpassantAllowed: Boolean; var AEnpassantSquare, AEnpassantSquareToClear: TPoint) : boolean;
function IsSquareOccupied(ASquare: TPoint): Boolean; function IsSquareOccupied(ASquare: TPoint): Boolean;
procedure UpdateTimes(); procedure UpdateTimes();
@@ -92,15 +104,21 @@ type
var var
vChessGame: TChessGame; vChessGame: TChessGame;
operator = (A, B: TPoint): Boolean;
implementation implementation
operator=(A, B: TPoint): Boolean;
begin
Result := (A.X = B.X) and (A.Y = B.Y);
end;
{ TChessGame } { TChessGame }
constructor TChessGame.Create; constructor TChessGame.Create;
begin begin
inherited Create; inherited Create;
end; end;
procedure TChessGame.StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer); procedure TChessGame.StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer);
@@ -174,45 +192,61 @@ end;
Returns: If the move is valid and was executed Returns: If the move is valid and was executed
} }
function TChessGame.MovePiece(AFrom, ATo: TPoint): Boolean; function TChessGame.MovePiece(AFrom, ATo: TPoint): Boolean;
var i : integer; var
i : integer;
LEnpassantAllowed: Boolean;
LEnpassantSquare, LEnpassantToClear: TPoint;
begin begin
result := false; LEnpassantAllowed := False;
//AFrom.x:=AFrom.x; LEnpassantToClear.X := -1;
//AFrom.y:=AFrom.y+2; Result := False;
// Verify if the starting square contains a valid piece
if not CheckStartMove(AFrom) then Exit; if not CheckStartMove(AFrom) then Exit;
if ( (Board[AFrom.X][AFrom.Y]) in WhitePieces ) then begin // Verify if the movement is in accordace to the rules for this piece
if Board[AFrom.X][AFrom.Y] = ctWRook then result:=(ValidateRookMove(AFrom,ATo));; if Board[AFrom.X][AFrom.Y] in [ctWPawn, ctBPawn] then result := ValidatePawnMove(AFrom,ATo, LEnpassantAllowed, LEnpassantSquare, LEnpassantToClear)
if Board[AFrom.X][AFrom.Y] = ctWKnight then result :=(ValidateKnightMove(AFrom,ATo)); else if Board[AFrom.X][AFrom.Y] in [ctWRook, ctBRook] then result := ValidateRookMove(AFrom,ATo)
if Board[AFrom.X][AFrom.Y] = ctWBishop then result :=(ValidateBishopMove(AFrom,ATo)); else if Board[AFrom.X][AFrom.Y] in [ctWKnight, ctBKnight] then result := ValidateKnightMove(AFrom,ATo)
if Board[AFrom.X][AFrom.Y] = ctWQueen then result :=(ValidateQueenMove(AFrom,ATo)); else if Board[AFrom.X][AFrom.Y] in [ctWBishop, ctBBishop] then result := ValidateBishopMove(AFrom,ATo)
if Board[AFrom.X][AFrom.Y] = ctWKing then result :=(ValidateKingMove(AFrom,ATo)); else if Board[AFrom.X][AFrom.Y] in [ctWQueen, ctBQueen] then result := ValidateQueenMove(AFrom,ATo)
if Board[AFrom.X][AFrom.Y] = ctWPawn then result :=(ValidatePawnMove(AFrom,ATo)); else if Board[AFrom.X][AFrom.Y] in [ctWKing, ctBKing] then result := ValidateKingMove(AFrom,ATo);
if (result) then for i := 0 to 8 do EnPassant[2][i]:=false;
end if not Result then Exit;
else begin
if Board[AFrom.X][AFrom.Y] = ctBRook then result :=(ValidateRookMove(AFrom,ATo)); // Check if the king will be left in check by this move
if Board[AFrom.X][AFrom.Y] = ctBKnight then result :=(ValidateKnightMove(AFrom,ATo)); if not CheckKingInCheck(AFrom, ATo, LEnpassantToClear) then Exit;
if Board[AFrom.X][AFrom.Y] = ctBBishop then result :=(ValidateBishopMove(AFrom,ATo));
if Board[AFrom.X][AFrom.Y] = ctBQueen then result :=(ValidateQueenMove(AFrom,ATo)); // If we arrived here, this means that the move will be really executed
if Board[AFrom.X][AFrom.Y] = ctBKing then result :=(ValidateKingMove(AFrom,ATo));
if Board[AFrom.X][AFrom.Y] = ctBPawn then result :=(ValidatePawnMove(AFrom,ATo)); // Store this move as the previously executed one
if (result) then for i := 0 to 8 do EnPassant[1][i]:=false; PreviousMove.From := AFrom;
end; PreviousMove.To_ := ATo;
// ShowMessage('Resultado := ' + BoolToStr(result,true)); PreviousMove.PieceMoved := Board[AFrom.X][AFrom.Y];
if (result) then begin PreviousMove.PieceEaten := Board[ATo.X][ATo.Y];
EnpassantAllowed := LEnpassantAllowed;
EnpassantSquare := LEnpassantSquare;
// Now we will execute the move
// col, row // col, row
Board[ATo.X][ATo.Y] := Board[AFrom.X][AFrom.Y]; Board[ATo.X][ATo.Y] := Board[AFrom.X][AFrom.Y];
Board[AFrom.X][AFrom.Y] := ctEmpty; Board[AFrom.X][AFrom.Y] := ctEmpty;
// If Enpassant, clear the remaining pawn
if LEnpassantToClear.X <> -1 then
Board[LEnpassantToClear.X][LEnpassantToClear.Y] := ctEmpty;
//
UpdateTimes(); UpdateTimes();
// Change player
CurrentPlayerIsWhite := not CurrentPlayerIsWhite; CurrentPlayerIsWhite := not CurrentPlayerIsWhite;
end; end;
end;
//return true if the move of a Rook is valid. //return true if the move of a Rook is valid.
function TChessGame.ValidateRookMove(AFrom, ATo: TPoint): boolean; function TChessGame.ValidateRookMove(AFrom, ATo: TPoint): boolean;
var AttackedSquares : BitBoard; var
AttackedSquares: BitBoard;
i,j: Integer; i,j: Integer;
l: integer = 0; l: integer = 0;
haveCaptured: boolean = false; //already have captured a piece haveCaptured: boolean = false; //already have captured a piece
@@ -220,12 +254,9 @@ var AttackedSquares : BitBoard;
validMove: boolean = false; //if the piece in the 'to' square is not of the same color of the player validMove: boolean = false; //if the piece in the 'to' square is not of the same color of the player
// mensagem : String; // mensagem : String;
begin begin
for i:=1 to 8 do // initialize the bitboard of attacked pieces. for i:=1 to 8 do // initialize the bitboard of attacked pieces.
for j:=1 to 8 do for j:=1 to 8 do
AttackedSquares[i][j]:= false; AttackedSquares[i][j]:= false;
// ShowMessage('vai passar pelo up');
//////////////////////////////////////UP//////////////////////////////////////// //////////////////////////////////////UP////////////////////////////////////////
l := AFrom.y+1; l := AFrom.y+1;
if (l<=8) then begin if (l<=8) then begin
@@ -387,7 +418,6 @@ begin
for i:=1 to 8 do // initialize the bitboard of attacked pieces. for i:=1 to 8 do // initialize the bitboard of attacked pieces.
for j:=1 to 8 do for j:=1 to 8 do
AttackedSquares[i][j]:= false; AttackedSquares[i][j]:= false;
// ShowMessage('vai passar pelo up left');
//////////////////////////////////////UP LEFT/////////////////////////////////// //////////////////////////////////////UP LEFT///////////////////////////////////
y := AFrom.y+1; y := AFrom.y+1;
x := AFrom.x-1; x := AFrom.x-1;
@@ -541,7 +571,8 @@ result := (AttackedSquares[Ato.X][Ato.y]);
end; end;
function TChessGame.ValidateQueenMove(AFrom, ATo: TPoint): Boolean; function TChessGame.ValidateQueenMove(AFrom, ATo: TPoint): Boolean;
var AttackedSquares : BitBoard; var
AttackedSquares : BitBoard;
i,j : Integer; i,j : Integer;
x,y,l : integer; //l it's the same of the y or x, just an index. x,y,l : integer; //l it's the same of the y or x, just an index.
haveCaptured: boolean = false; //already have captured a piece haveCaptured: boolean = false; //already have captured a piece
@@ -549,12 +580,9 @@ var AttackedSquares : BitBoard;
validMove : boolean = false; //if the piece in the 'to' square is not of the same color of the player validMove : boolean = false; //if the piece in the 'to' square is not of the same color of the player
mensagem : String; mensagem : String;
begin begin
for i:=1 to 8 do // initialize the bitboard of attacked pieces. for i:=1 to 8 do // initialize the bitboard of attacked pieces.
for j:=1 to 8 do for j:=1 to 8 do
AttackedSquares[i][j]:= false; AttackedSquares[i][j]:= false;
// ShowMessage('vai passar pelo up');
//////////////////////////////////////UP//////////////////////////////////////// //////////////////////////////////////UP////////////////////////////////////////
l := AFrom.y+1; l := AFrom.y+1;
if (l<=8) then begin if (l<=8) then begin
@@ -847,10 +875,10 @@ end;
The white is always in the bottom at the moment, The white is always in the bottom at the moment,
which means the smallest x,y values which means the smallest x,y values
} }
function TChessGame.ValidatePawnMove(AFrom, ATo: TPoint): Boolean; function TChessGame.ValidatePawnMove(AFrom, ATo: TPoint;
var AEnpassantAllowed: Boolean; var AEnpassantSquare, AEnpassantSquareToClear: TPoint): Boolean;
begin begin
// ToDo: Verify if the kind will be in check AEnpassantAllowed := False;
Result := False; Result := False;
if CurrentPlayerIsWhite then if CurrentPlayerIsWhite then
@@ -864,7 +892,8 @@ begin
else if (AFrom.X = ATo.X) and (AFrom.Y = 2) and (AFrom.Y = ATo.Y - 2) then else if (AFrom.X = ATo.X) and (AFrom.Y = 2) and (AFrom.Y = ATo.Y - 2) then
begin begin
Result := not IsSquareOccupied(ATo); Result := not IsSquareOccupied(ATo);
EnPassant[1][AFrom.X]:=True; AEnpassantAllowed := True;
AEnpassantSquare := Point(AFrom.X, ATo.Y - 1);
end end
// Normal capture in the left // Normal capture in the left
else if (ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y+1) and (Board[ATo.X][ATo.Y] in BlackPieces) then else if (ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y+1) and (Board[ATo.X][ATo.Y] in BlackPieces) then
@@ -877,16 +906,18 @@ begin
Result := True; Result := True;
end end
// En Passant Capture in the left // En Passant Capture in the left
else if (EnPassant[2][AFrom.X-1]= true) and (AFrom.Y = 5) and (ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y+1) and (Board[ATo.X][ATo.Y-1] in BlackPieces) then else if EnPassantAllowed and (EnPassantSquare = ATo) and
(ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y+1) then
begin begin
Result := True; Result := True;
Board[ATo.X][ATo.Y-1]:=ctEmpty; AEnpassantSquareToClear := Point(ATo.X, ATo.Y-1);
end end
// En Passant Capture in the right // En Passant Capture in the right
else if (EnPassant[2][AFrom.X+1]= true) and (AFrom.Y = 5) and (ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y+1) and (Board[ATo.X][ATo.Y-1] in BlackPieces) then else if EnPassantAllowed and (EnPassantSquare = ATo) and
(ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y+1) then
begin begin
Result := True; Result := True;
Board[ATo.X][ATo.Y-1]:=ctEmpty; AEnpassantSquareToClear := Point(ATo.X, ATo.Y-1);
end; end;
end end
else else
@@ -900,7 +931,8 @@ begin
else if (AFrom.X = ATo.X) and (AFrom.Y = 7) and (AFrom.Y = ATo.Y + 2) then else if (AFrom.X = ATo.X) and (AFrom.Y = 7) and (AFrom.Y = ATo.Y + 2) then
begin begin
Result := not IsSquareOccupied(ATo); Result := not IsSquareOccupied(ATo);
EnPassant[2][AFrom.X]:=True; AEnpassantAllowed := True;
AEnpassantSquare := Point(AFrom.X, ATo.Y + 1);
end end
// Capture a piece in the left // Capture a piece in the left
else if (ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y-1) and (Board[ATo.X][ATo.Y] in WhitePieces) then else if (ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y-1) and (Board[ATo.X][ATo.Y] in WhitePieces) then
@@ -913,16 +945,19 @@ begin
Result := True; Result := True;
end end
// En Passant Capture in the left // En Passant Capture in the left
else if (EnPassant[1][AFrom.X-1]= true) and (AFrom.Y = 4) and (ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y-1) and (Board[ATo.X][ATo.Y+1] in WhitePieces) then else if EnPassantAllowed and (EnPassantSquare = ATo) and
(ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y-1) then
begin begin
Result := True; Result := True;
Board[ATo.X][ATo.Y+1]:=ctEmpty; AEnpassantSquareToClear := Point(ATo.X, ATo.Y+1);
end end
// En Passant Capture in the right // En Passant Capture in the right
else if (EnPassant[1][AFrom.X+1]= true) and (AFrom.Y = 4) and (ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y-1) and (Board[ATo.X][ATo.Y+1] in WhitePieces) then else if EnPassantAllowed and (EnPassantSquare = ATo) and
(ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y-1) then
begin begin
Result := True; Result := True;
Board[ATo.X][ATo.Y+1]:=ctEmpty; // Don't clear immediately because we haven't yet checked for kind check
AEnpassantSquareToClear := Point(ATo.X, ATo.Y+1);
end; end;
end; end;
end; end;
@@ -963,6 +998,16 @@ begin
Result := Board[AFrom.X][AFrom.Y] in BlackPieces; Result := Board[AFrom.X][AFrom.Y] in BlackPieces;
end; end;
// True - The King will not be in check
// False - The King will be in check
//
// if AEnpassantSquareToClear.X = -1 then it is not an enpassant attack
function TChessGame.CheckKingInCheck(AFrom, ATo, AEnpassantSquareToClear: TPoint
): Boolean;
begin
Result := True;
end;
initialization initialization
vChessGame := TChessGame.Create; vChessGame := TChessGame.Create;