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