You've already forked lazarus-ccr
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:
@@ -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,58 +192,71 @@ 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
|
|
||||||
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;
|
|
||||||
|
|
||||||
UpdateTimes();
|
if not Result then Exit;
|
||||||
CurrentPlayerIsWhite := not CurrentPlayerIsWhite;
|
|
||||||
end;
|
// 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;
|
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
|
||||||
i,j : Integer;
|
AttackedSquares: BitBoard;
|
||||||
l : integer = 0;
|
i,j: Integer;
|
||||||
haveCaptured: boolean = false; //already have captured a piece
|
l: integer = 0;
|
||||||
willBeACapture : boolean = false;// the movement will be a capture
|
haveCaptured: boolean = false; //already have captured a piece
|
||||||
validMove : boolean = false; //if the piece in the 'to' square is not of the same color of the player
|
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;
|
// 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,20 +571,18 @@ 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
|
||||||
i,j : Integer;
|
AttackedSquares : BitBoard;
|
||||||
x,y,l : integer; //l it's the same of the y or x, just an index.
|
i,j : Integer;
|
||||||
haveCaptured: boolean = false; //already have captured a piece
|
x,y,l : integer; //l it's the same of the y or x, just an index.
|
||||||
willBeACapture : boolean = false;// the movement will be a capture
|
haveCaptured: boolean = false; //already have captured a piece
|
||||||
validMove : boolean = false; //if the piece in the 'to' square is not of the same color of the player
|
willBeACapture : boolean = false;// the movement will be a capture
|
||||||
mensagem : String;
|
validMove : boolean = false; //if the piece in the 'to' square is not of the same color of the player
|
||||||
|
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;
|
||||||
|
Reference in New Issue
Block a user