2010-09-21 06:33:18 +00:00
|
|
|
unit chessgame;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2011-04-04 16:09:05 +00:00
|
|
|
Classes, SysUtils, fpimage, dateutils,
|
|
|
|
Forms, Controls, Graphics, Dialogs,
|
|
|
|
ExtCtrls, ComCtrls, StdCtrls, Buttons, Spin;
|
2010-09-21 06:33:18 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
colA = 1;
|
|
|
|
colB = 2;
|
|
|
|
colC = 3;
|
|
|
|
colD = 4;
|
|
|
|
colE = 5;
|
|
|
|
colF = 6;
|
|
|
|
colG = 7;
|
|
|
|
colH = 8;
|
|
|
|
|
2010-09-22 15:45:23 +00:00
|
|
|
INT_CHESSTILE_SIZE = 40;
|
|
|
|
INT_CHESSBOARD_SIZE = 40 * 8;
|
|
|
|
|
2010-09-23 07:31:28 +00:00
|
|
|
FPCOLOR_TRANSPARENT_TILE: TFPColor = (Red: $0000; Green: $8100; Blue: $8100; Alpha: alphaOpaque); //+/-colTeal
|
2010-09-21 06:33:18 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
2010-10-05 20:05:41 +00:00
|
|
|
TPacketKind = (pkConnect, pkStartGameClientAsWhite, pkStartGameClientAsBlack, pkMove);
|
2011-04-07 06:57:02 +00:00
|
|
|
BitBoard = array[1..8] of array [1..8] of boolean;// Map of attacked squares
|
2010-10-05 20:05:41 +00:00
|
|
|
|
|
|
|
{ TPacket }
|
|
|
|
|
|
|
|
TPacket = class
|
|
|
|
public
|
|
|
|
// Packet Data
|
|
|
|
ID: Cardinal;
|
|
|
|
Kind: TPacketKind;
|
|
|
|
MoveStartX, MoveStartY, MoveEndX, MoveEndY: Byte;
|
|
|
|
Next: TPacket; // To build a linked list
|
|
|
|
end;
|
|
|
|
|
2010-09-21 06:33:18 +00:00
|
|
|
TChessTile = (ctEmpty,
|
|
|
|
ctWPawn, ctWKnight, ctWBishop, ctWRook, ctWQueen, ctWKing,
|
|
|
|
ctBPawn, ctBKnight, ctBBishop, ctBRook, ctBQueen, ctBKing
|
|
|
|
);
|
|
|
|
|
2010-09-23 10:43:52 +00:00
|
|
|
const
|
|
|
|
WhitePieces = [ctWPawn, ctWKnight, ctWBishop, ctWRook, ctWQueen, ctWKing];
|
|
|
|
BlackPieces = [ctBPawn, ctBKnight, ctBBishop, ctBRook, ctBQueen, ctBKing];
|
2011-04-10 05:51:39 +00:00
|
|
|
WhitePiecesOrEmpty = [ctEmpty, ctWPawn, ctWKnight, ctWBishop, ctWRook, ctWQueen, ctWKing];
|
|
|
|
BlackPiecesOrEmpty = [ctEmpty, ctBPawn, ctBKnight, ctBBishop, ctBRook, ctBQueen, ctBKing];
|
2010-09-23 10:43:52 +00:00
|
|
|
|
|
|
|
type
|
2010-09-21 06:33:18 +00:00
|
|
|
{@@
|
|
|
|
The index [1][1] refers to the left-bottom corner of the table,
|
|
|
|
also known as A1.
|
|
|
|
The first index is the column, to follow the same standard used to
|
|
|
|
say coordinates, for example: C7 = [3][7]
|
|
|
|
}
|
|
|
|
TChessBoard = array[1..8] of array[1..8] of TChessTile;
|
|
|
|
|
2011-04-09 06:27:30 +00:00
|
|
|
TChessMove = record
|
|
|
|
From, To_: TPoint;
|
|
|
|
PieceMoved, PieceEaten: TChessTile;
|
|
|
|
end;
|
|
|
|
|
2010-09-21 06:33:18 +00:00
|
|
|
{ TChessGame }
|
|
|
|
|
|
|
|
TChessGame = class
|
2011-04-15 14:45:57 +00:00
|
|
|
private
|
2010-09-21 06:33:18 +00:00
|
|
|
public
|
|
|
|
Board: TChessBoard;
|
2011-04-04 16:09:05 +00:00
|
|
|
msg : String;
|
2010-09-23 10:43:52 +00:00
|
|
|
CurrentPlayerIsWhite: Boolean;
|
|
|
|
Dragging: Boolean;
|
|
|
|
DragStart, MouseMovePos: TPoint;
|
2010-09-23 15:40:21 +00:00
|
|
|
UseTimer: Boolean;
|
|
|
|
WhitePlayerTime: Integer; // milisseconds
|
|
|
|
BlackPlayerTime: Integer; // milisseconds
|
|
|
|
MoveStartTime: TDateTime;
|
2011-04-09 06:27:30 +00:00
|
|
|
// Last move (might in the future store all history)
|
|
|
|
PreviousMove: TChessMove;
|
|
|
|
// Data for Enpassant
|
2011-04-11 05:40:35 +00:00
|
|
|
EnpassantSquare: TPoint; // Negative coords indicate that it is not allowed
|
|
|
|
// Data for the Roque
|
|
|
|
IsWhiteLeftRoquePossible, IsWhiteRightRoquePossible: Boolean;
|
|
|
|
IsBlackLeftRoquePossible, IsBlackRightRoquePossible: Boolean;
|
2011-04-09 06:27:30 +00:00
|
|
|
//
|
2010-09-23 15:40:21 +00:00
|
|
|
constructor Create;
|
|
|
|
procedure StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer); overload;
|
|
|
|
procedure StartNewGame(APlayAsWhite: Integer; AUseTimer: Boolean; APlayerTime: Integer); overload;
|
2010-09-23 10:43:52 +00:00
|
|
|
function ClientToBoardCoords(AClientCoords: TPoint): TPoint;
|
|
|
|
function CheckStartMove(AFrom: TPoint): Boolean;
|
2011-04-10 05:51:39 +00:00
|
|
|
function CheckEndMove(ATo: TPoint): Boolean;
|
2011-04-15 14:45:57 +00:00
|
|
|
function WillKingBeInCheck(AFrom, ATo, AEnpassantToClear: TPoint): Boolean;
|
|
|
|
function IsKingInCheck(AKingPos: TPoint): Boolean;
|
|
|
|
function FindKing(): TPoint;
|
2010-09-23 10:43:52 +00:00
|
|
|
function MovePiece(AFrom, ATo: TPoint): Boolean;
|
2011-04-15 14:45:57 +00:00
|
|
|
procedure DoMovePiece(AFrom, ATo, AEnpassantToClear: TPoint);
|
2011-04-04 16:09:05 +00:00
|
|
|
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;
|
2011-04-09 06:27:30 +00:00
|
|
|
function ValidatePawnMove(AFrom, ATo: TPoint;
|
2011-04-11 05:40:35 +00:00
|
|
|
var AEnpassantSquare, AEnpassantSquareToClear: TPoint) : boolean;
|
2011-04-04 16:30:23 +00:00
|
|
|
function IsSquareOccupied(ASquare: TPoint): Boolean;
|
2010-09-23 15:40:21 +00:00
|
|
|
procedure UpdateTimes();
|
2010-09-21 06:33:18 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
vChessGame: TChessGame;
|
|
|
|
|
2011-04-09 06:27:30 +00:00
|
|
|
operator = (A, B: TPoint): Boolean;
|
|
|
|
|
2010-09-21 06:33:18 +00:00
|
|
|
implementation
|
|
|
|
|
2011-04-09 06:27:30 +00:00
|
|
|
operator=(A, B: TPoint): Boolean;
|
|
|
|
begin
|
|
|
|
Result := (A.X = B.X) and (A.Y = B.Y);
|
|
|
|
end;
|
|
|
|
|
2010-09-21 06:33:18 +00:00
|
|
|
{ TChessGame }
|
|
|
|
|
2010-09-23 15:40:21 +00:00
|
|
|
constructor TChessGame.Create;
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TChessGame.StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer);
|
2010-09-21 06:33:18 +00:00
|
|
|
var
|
|
|
|
lWPawnRow, lWMainRow, lBPawnRow, lBMainRow: Byte;
|
|
|
|
i: Integer;
|
|
|
|
j: Integer;
|
|
|
|
begin
|
2010-09-23 15:40:21 +00:00
|
|
|
UseTimer := AUseTimer;
|
2010-09-23 10:43:52 +00:00
|
|
|
CurrentPlayerIsWhite := True;
|
2010-09-23 15:40:21 +00:00
|
|
|
WhitePlayerTime := APlayerTime * 60 * 1000; // minutes to milisseconds
|
|
|
|
BlackPlayerTime := APlayerTime * 60 * 1000; // minutes to milisseconds
|
|
|
|
MoveStartTime := Now;
|
2010-09-23 10:43:52 +00:00
|
|
|
|
2011-04-11 05:40:35 +00:00
|
|
|
EnpassantSquare := Point(-1, -1); // Negative coords indicate that it is not allowed
|
|
|
|
IsWhiteLeftRoquePossible := True;
|
|
|
|
IsWhiteRightRoquePossible := True;
|
|
|
|
IsBlackLeftRoquePossible := True;
|
|
|
|
IsBlackRightRoquePossible := True;
|
|
|
|
|
2010-09-21 06:33:18 +00:00
|
|
|
//
|
|
|
|
if APlayAsWhite then
|
|
|
|
begin
|
|
|
|
lWPawnRow := 2;
|
|
|
|
lWMainRow := 1;
|
|
|
|
lBPawnRow := 7;
|
|
|
|
lBMainRow := 8;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
lWPawnRow := 7;
|
|
|
|
lWMainRow := 8;
|
|
|
|
lBPawnRow := 2;
|
|
|
|
lBMainRow := 1;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// First, clear the board
|
|
|
|
for i := 1 to 8 do
|
|
|
|
for j := 1 to 8 do
|
|
|
|
Board[i][j] := ctEmpty;
|
|
|
|
|
|
|
|
// White pawns
|
|
|
|
for i := 1 to 8 do
|
|
|
|
Board[i][lWPawnRow] := ctWPawn;
|
|
|
|
|
|
|
|
// White main row
|
|
|
|
Board[1][lWMainRow] := ctWRook;
|
|
|
|
Board[2][lWMainRow] := ctWKnight;
|
|
|
|
Board[3][lWMainRow] := ctWBishop;
|
|
|
|
Board[4][lWMainRow] := ctWQueen;
|
|
|
|
Board[5][lWMainRow] := ctWKing;
|
|
|
|
Board[6][lWMainRow] := ctWBishop;
|
|
|
|
Board[7][lWMainRow] := ctWKnight;
|
|
|
|
Board[8][lWMainRow] := ctWRook;
|
|
|
|
|
|
|
|
// White pawns
|
|
|
|
for i := 1 to 8 do
|
|
|
|
Board[i][lBPawnRow] := ctBPawn;
|
|
|
|
|
|
|
|
// Black main row
|
|
|
|
Board[1][lBMainRow] := ctBRook;
|
|
|
|
Board[2][lBMainRow] := ctBKnight;
|
|
|
|
Board[3][lBMainRow] := ctBBishop;
|
|
|
|
Board[4][lBMainRow] := ctBQueen;
|
|
|
|
Board[5][lBMainRow] := ctBKing;
|
|
|
|
Board[6][lBMainRow] := ctBBishop;
|
|
|
|
Board[7][lBMainRow] := ctBKnight;
|
|
|
|
Board[8][lBMainRow] := ctBRook;
|
|
|
|
end;
|
|
|
|
|
2010-09-23 15:40:21 +00:00
|
|
|
procedure TChessGame.StartNewGame(APlayAsWhite: Integer; AUseTimer: Boolean; APlayerTime: Integer);
|
2010-09-21 06:33:18 +00:00
|
|
|
begin
|
2010-09-23 15:40:21 +00:00
|
|
|
StartNewGame(APlayAsWhite = 0, AUseTimer, APlayerTime);
|
2010-09-21 06:33:18 +00:00
|
|
|
end;
|
|
|
|
|
2010-09-23 10:43:52 +00:00
|
|
|
{
|
|
|
|
Returns: If the move is valid and was executed
|
|
|
|
}
|
|
|
|
function TChessGame.MovePiece(AFrom, ATo: TPoint): Boolean;
|
2011-04-09 06:27:30 +00:00
|
|
|
var
|
|
|
|
i : integer;
|
|
|
|
LEnpassantSquare, LEnpassantToClear: TPoint;
|
2010-09-23 10:43:52 +00:00
|
|
|
begin
|
2011-04-11 05:40:35 +00:00
|
|
|
LEnpassantSquare := Point(-1, -1);
|
|
|
|
LEnpassantToClear := Point(-1, -1);
|
2011-04-09 06:27:30 +00:00
|
|
|
Result := False;
|
|
|
|
|
2011-04-10 05:51:39 +00:00
|
|
|
// Verify what is in the start and destination squares
|
2011-04-07 06:27:40 +00:00
|
|
|
if not CheckStartMove(AFrom) then Exit;
|
2011-04-10 05:51:39 +00:00
|
|
|
if not CheckEndMove(ATo) then Exit;
|
2011-04-04 16:09:05 +00:00
|
|
|
|
2011-04-09 06:27:30 +00:00
|
|
|
// Verify if the movement is in accordace to the rules for this piece
|
2011-04-11 05:40:35 +00:00
|
|
|
if Board[AFrom.X][AFrom.Y] in [ctWPawn, ctBPawn] then result := ValidatePawnMove(AFrom,ATo, LEnpassantSquare, LEnpassantToClear)
|
2011-04-09 06:27:30 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
if not Result then Exit;
|
|
|
|
|
|
|
|
// Check if the king will be left in check by this move
|
2011-04-15 14:45:57 +00:00
|
|
|
if WillKingBeInCheck(AFrom, ATo, LEnpassantToClear) then Exit;
|
2011-04-09 06:27:30 +00:00
|
|
|
|
|
|
|
// 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];
|
|
|
|
EnpassantSquare := LEnpassantSquare;
|
|
|
|
|
|
|
|
// Now we will execute the move
|
2011-04-15 14:45:57 +00:00
|
|
|
DoMovePiece(AFrom, ATo, LEnpassantToClear);
|
2011-04-09 06:27:30 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
UpdateTimes();
|
|
|
|
|
|
|
|
// Change player
|
|
|
|
CurrentPlayerIsWhite := not CurrentPlayerIsWhite;
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
|
2011-04-15 14:45:57 +00:00
|
|
|
{ Really moves the piece without doing any check }
|
|
|
|
procedure TChessGame.DoMovePiece(AFrom, ATo, AEnpassantToClear: TPoint);
|
|
|
|
begin
|
|
|
|
// 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 AEnpassantToClear.X <> -1 then
|
|
|
|
Board[AEnpassantToClear.X][AEnpassantToClear.Y] := ctEmpty;
|
|
|
|
end;
|
|
|
|
|
2011-04-04 16:09:05 +00:00
|
|
|
//return true if the move of a Rook is valid.
|
|
|
|
function TChessGame.ValidateRookMove(AFrom, ATo: TPoint): boolean;
|
2011-04-09 06:27:30 +00:00
|
|
|
var
|
2011-04-10 05:51:39 +00:00
|
|
|
i: Integer;
|
2011-04-04 16:09:05 +00:00
|
|
|
begin
|
2011-04-10 05:51:39 +00:00
|
|
|
Result := False;
|
|
|
|
|
|
|
|
//////////////////////////////////////UP////////////////////////////////////////
|
|
|
|
if (AFrom.X = ATo.X) and (AFrom.Y < ATo.Y) then
|
|
|
|
begin
|
|
|
|
// Check if there are pieces in the middle of the way
|
|
|
|
for i := AFrom.Y + 1 to ATo.Y - 1 do
|
|
|
|
if Board[AFrom.X][i] <> ctEmpty then Exit;
|
|
|
|
|
|
|
|
Exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
///////////////////////////////////DOWN/////////////////////////////////////////
|
2011-04-10 05:51:39 +00:00
|
|
|
if (AFrom.X = ATo.X) and (AFrom.Y > ATo.Y) then
|
|
|
|
begin
|
|
|
|
// Check if there are pieces in the middle of the way
|
|
|
|
for i := AFrom.Y - 1 downto ATo.Y + 1 do
|
|
|
|
if Board[AFrom.X][i] <> ctEmpty then Exit;
|
|
|
|
|
|
|
|
Exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
////////////////////////////////////RIGHT////////////////////////////////////////
|
2011-04-10 05:51:39 +00:00
|
|
|
if (AFrom.X < ATo.X) and (AFrom.Y = ATo.Y) then
|
|
|
|
begin
|
|
|
|
// Check if there are pieces in the middle of the way
|
|
|
|
for i := AFrom.X + 1 to ATo.X - 1 do
|
|
|
|
if Board[i][AFrom.Y] <> ctEmpty then Exit;
|
|
|
|
|
|
|
|
Exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
///////////////////////////////////LEFT/////////////////////////////////////////
|
2011-04-10 05:51:39 +00:00
|
|
|
if (AFrom.X > ATo.X) and (AFrom.Y = ATo.Y) then
|
|
|
|
begin
|
|
|
|
// Check if there are pieces in the middle of the way
|
|
|
|
for i := AFrom.X - 1 downto ATo.X + 1 do
|
|
|
|
if Board[i][AFrom.Y] <> ctEmpty then Exit;
|
2010-09-23 10:43:52 +00:00
|
|
|
|
2011-04-10 05:51:39 +00:00
|
|
|
Exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
end;
|
2011-04-10 05:51:39 +00:00
|
|
|
|
2011-04-10 06:07:53 +00:00
|
|
|
{
|
|
|
|
The knight has 8 possible destinations only:
|
|
|
|
|
|
|
|
[X][ ][X]
|
|
|
|
[X][ ][ ][ ][X]
|
|
|
|
[ ][ ][K][ ][ ]
|
|
|
|
[X][ ][ ][ ][X]
|
|
|
|
[X] [X]
|
|
|
|
}
|
2011-04-04 16:09:05 +00:00
|
|
|
function TChessGame.ValidateKnightMove(AFrom, ATo: TPoint): Boolean;
|
|
|
|
begin
|
2011-04-10 06:07:53 +00:00
|
|
|
Result := (AFrom.X = ATo.X + 1) and (AFrom.Y + 2 = ATo.Y); // upper left corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X + 2) and (AFrom.Y + 1 = ATo.Y)); // upper left corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X + 2) and (AFrom.Y - 1 = ATo.Y)); // lower left corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X + 1) and (AFrom.Y - 2 = ATo.Y)); // lower left corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X - 1) and (AFrom.Y - 2 = ATo.Y)); // lower right corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X - 2) and (AFrom.Y - 1 = ATo.Y)); // lower right corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X - 2) and (AFrom.Y + 1 = ATo.Y)); // upper right corner
|
|
|
|
Result := Result or ((AFrom.X = ATo.X - 1) and (AFrom.Y + 2 = ATo.Y)); // upper right corner
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-07 06:57:02 +00:00
|
|
|
|
2011-04-04 16:09:05 +00:00
|
|
|
function TChessGame.ValidateBishopMove(AFrom, ATo: TPoint): Boolean;
|
2011-04-07 06:57:02 +00:00
|
|
|
var
|
|
|
|
i,j : Integer;
|
2011-04-04 16:09:05 +00:00
|
|
|
begin
|
2011-04-11 05:46:27 +00:00
|
|
|
result :=false;
|
|
|
|
//Up left
|
|
|
|
if (AFrom.X>ATo.X) and (AFrom.Y<ATo.Y) and (AFrom.X-ATo.X=ATo.Y-AFrom.Y)then
|
|
|
|
begin
|
|
|
|
i := AFrom.X-1;
|
|
|
|
j := AFrom.Y+1;
|
|
|
|
while (i>=ATo.X+1) and (j<=ATo.Y-1) do
|
|
|
|
begin
|
|
|
|
if Board[i][j] <> ctEmpty then Exit;
|
|
|
|
i := i - 1;
|
|
|
|
j := j + 1;
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
//Up right
|
|
|
|
if (AFrom.X<ATo.X) and (AFrom.Y<ATo.Y) and (ATo.X-AFrom.X=ATo.Y-AFrom.Y) then
|
|
|
|
begin
|
|
|
|
i := AFrom.X+1;
|
|
|
|
j := AFrom.Y+1;
|
|
|
|
while (i<=ATo.X-1) and (j<=ATo.Y-1) do
|
|
|
|
begin
|
|
|
|
if Board[i][j] <> ctEmpty then Exit;
|
|
|
|
i := i + 1;
|
|
|
|
j := j + 1;
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
//Down left
|
|
|
|
if (AFrom.X>ATo.X) and (AFrom.Y>ATo.Y) and (AFrom.X-ATo.X=AFrom.Y-ATo.Y) then
|
|
|
|
begin
|
|
|
|
i := AFrom.X-1;
|
|
|
|
j := AFrom.Y-1;
|
|
|
|
while (i>=ATo.X+1) and (j>=ATo.Y+1) do
|
|
|
|
begin
|
|
|
|
if Board[i][j] <> ctEmpty then Exit;
|
|
|
|
i := i - 1;
|
|
|
|
j := j - 1;
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
//Down right
|
|
|
|
if (AFrom.X<ATo.X) and (AFrom.Y>ATo.Y) and (ATo.X-AFrom.X=AFrom.Y-ATo.Y)then
|
|
|
|
begin
|
|
|
|
i := AFrom.X+1;
|
|
|
|
j := AFrom.Y-1;
|
|
|
|
while (i<=ATo.X-1) and (j>=ATo.Y+1) do
|
|
|
|
begin
|
|
|
|
if Board[i][j] <> ctEmpty then Exit;
|
|
|
|
i := i + 1;
|
|
|
|
j := j - 1;
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
2011-04-11 05:46:27 +00:00
|
|
|
exit(True);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
end;
|
2011-04-10 06:07:53 +00:00
|
|
|
|
2011-04-04 16:09:05 +00:00
|
|
|
function TChessGame.ValidateQueenMove(AFrom, ATo: TPoint): Boolean;
|
|
|
|
begin
|
2011-04-10 06:07:53 +00:00
|
|
|
Result := ValidateRookMove(AFrom, ATo) or ValidateBishopMove(AFrom, ATo);
|
2011-04-04 16:09:05 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TChessGame.ValidateKingMove(AFrom, ATo: TPoint): Boolean;
|
|
|
|
begin
|
2011-04-10 05:51:39 +00:00
|
|
|
Result := False;
|
2011-04-10 06:07:53 +00:00
|
|
|
|
|
|
|
// Verify the possibility of a Roque
|
2011-04-11 05:40:35 +00:00
|
|
|
if CurrentPlayerIsWhite then
|
|
|
|
begin
|
|
|
|
// Roque to the left
|
|
|
|
{ if IsWhiteLeftRoquePossible and (AFrom.X = 5) and (AFrom.Y = 1)
|
|
|
|
and (ATo.X = 7) and (ATo.Y = 1) then
|
|
|
|
begin
|
|
|
|
Board[ATo.X][ATo.Y] :=
|
|
|
|
Board[ATo.X][ATo.Y] :=
|
|
|
|
Board[ATo.X][ATo.Y] :=
|
|
|
|
WhitePieces = [ctWPawn, ctWKnight, ctWBishop, ctWRook, ctWQueen, ctWKing];
|
|
|
|
BlackPieces = [ctBPawn, ctBKnight, ctBBishop, ctBRook, ctBQueen, ctBKing];
|
|
|
|
end;}
|
|
|
|
// Roque to the right
|
|
|
|
if IsWhiteRightRoquePossible and (AFrom.X = 5) and (AFrom.Y = 1)
|
|
|
|
and (ATo.X = 7) and (ATo.Y = 1) then
|
|
|
|
begin
|
|
|
|
Board[AFrom.X][AFrom.Y] := ctEmpty;
|
|
|
|
Board[ATo.X][ATo.Y] := ctWKing;
|
|
|
|
Board[ATo.X + 1][ATo.Y] := ctEmpty;
|
|
|
|
Board[ATo.X - 1][ATo.Y] := ctWRook;
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Roque to the left
|
|
|
|
// IsBlackLeftRoquePossible
|
|
|
|
// Roque to the right
|
|
|
|
// IsBlackRightRoquePossible: Boolean;
|
|
|
|
end;
|
2011-04-10 06:07:53 +00:00
|
|
|
|
|
|
|
// Simple move
|
2011-04-10 05:51:39 +00:00
|
|
|
if (AFrom.X > ATo.X + 1) or (AFrom.X + 1 < ATo.X) then Exit;
|
|
|
|
if (AFrom.Y > ATo.Y + 1) or (AFrom.Y + 1 < ATo.Y) then Exit;
|
2011-04-04 16:09:05 +00:00
|
|
|
Result := True;
|
|
|
|
end;
|
|
|
|
|
2011-04-04 16:30:23 +00:00
|
|
|
{
|
|
|
|
The white is always in the bottom at the moment,
|
|
|
|
which means the smallest x,y values
|
2011-04-11 05:40:35 +00:00
|
|
|
|
|
|
|
If positive coords are feed to AEnpassantSquare, this means that
|
|
|
|
enpassant will be allowed in the next move
|
|
|
|
|
|
|
|
If positive coords are feed to AEnpassantSquareToClear, then we
|
|
|
|
made an enpassant capture and a square is to be cleared from the
|
|
|
|
captured pawn. This isn't done yet because the check verification
|
|
|
|
wasn't made yet, so it is not certain that the move will take place.
|
2011-04-04 16:30:23 +00:00
|
|
|
}
|
2011-04-09 06:27:30 +00:00
|
|
|
function TChessGame.ValidatePawnMove(AFrom, ATo: TPoint;
|
2011-04-11 05:40:35 +00:00
|
|
|
var AEnpassantSquare, AEnpassantSquareToClear: TPoint): Boolean;
|
2011-04-04 16:09:05 +00:00
|
|
|
begin
|
2011-04-11 05:40:35 +00:00
|
|
|
AEnpassantSquare := Point(-1, -1);
|
|
|
|
AEnpassantSquareToClear := Point(-1, -1);
|
2011-04-04 16:30:23 +00:00
|
|
|
Result := False;
|
|
|
|
|
|
|
|
if CurrentPlayerIsWhite then
|
|
|
|
begin
|
|
|
|
// Normal move forward
|
|
|
|
if (AFrom.X = ATo.X) and (AFrom.Y = ATo.Y - 1) then
|
|
|
|
begin
|
|
|
|
Result := not IsSquareOccupied(ATo);
|
|
|
|
end
|
|
|
|
// Initial double move forward
|
|
|
|
else if (AFrom.X = ATo.X) and (AFrom.Y = 2) and (AFrom.Y = ATo.Y - 2) then
|
|
|
|
begin
|
|
|
|
Result := not IsSquareOccupied(ATo);
|
2011-04-09 06:27:30 +00:00
|
|
|
AEnpassantSquare := Point(AFrom.X, ATo.Y - 1);
|
2011-04-07 06:27:40 +00:00
|
|
|
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
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
end
|
|
|
|
// Normal capture in the right
|
|
|
|
else if (ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y+1) and (Board[ATo.X][ATo.Y] in BlackPieces) then
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
end
|
|
|
|
// En Passant Capture in the left
|
2011-04-11 05:40:35 +00:00
|
|
|
else if (EnPassantSquare = ATo) and
|
2011-04-09 06:27:30 +00:00
|
|
|
(ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y+1) then
|
2011-04-07 06:27:40 +00:00
|
|
|
begin
|
|
|
|
Result := True;
|
2011-04-09 06:27:30 +00:00
|
|
|
AEnpassantSquareToClear := Point(ATo.X, ATo.Y-1);
|
2011-04-07 06:27:40 +00:00
|
|
|
end
|
|
|
|
// En Passant Capture in the right
|
2011-04-11 05:40:35 +00:00
|
|
|
else if (EnPassantSquare = ATo) and
|
2011-04-09 06:27:30 +00:00
|
|
|
(ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y+1) then
|
2011-04-07 06:27:40 +00:00
|
|
|
begin
|
|
|
|
Result := True;
|
2011-04-09 06:27:30 +00:00
|
|
|
AEnpassantSquareToClear := Point(ATo.X, ATo.Y-1);
|
2011-04-04 16:30:23 +00:00
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Normal move forward
|
|
|
|
if (AFrom.X = ATo.X) and (AFrom.Y = ATo.Y + 1) then
|
|
|
|
begin
|
|
|
|
Result := not IsSquareOccupied(ATo);
|
|
|
|
end
|
|
|
|
// Initial double move forward
|
|
|
|
else if (AFrom.X = ATo.X) and (AFrom.Y = 7) and (AFrom.Y = ATo.Y + 2) then
|
|
|
|
begin
|
|
|
|
Result := not IsSquareOccupied(ATo);
|
2011-04-09 06:27:30 +00:00
|
|
|
AEnpassantSquare := Point(AFrom.X, ATo.Y + 1);
|
2011-04-07 06:27:40 +00:00
|
|
|
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
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
end
|
|
|
|
// Capture a piece in the right
|
|
|
|
else if (ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y-1) and (Board[ATo.X][ATo.Y] in WhitePieces) then
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
end
|
|
|
|
// En Passant Capture in the left
|
2011-04-11 05:40:35 +00:00
|
|
|
else if (EnPassantSquare = ATo) and
|
2011-04-09 06:27:30 +00:00
|
|
|
(ATo.X = AFrom.X-1) and (ATo.Y = AFrom.Y-1) then
|
2011-04-07 06:27:40 +00:00
|
|
|
begin
|
|
|
|
Result := True;
|
2011-04-09 06:27:30 +00:00
|
|
|
AEnpassantSquareToClear := Point(ATo.X, ATo.Y+1);
|
2011-04-07 06:27:40 +00:00
|
|
|
end
|
|
|
|
// En Passant Capture in the right
|
2011-04-11 05:40:35 +00:00
|
|
|
else if (EnPassantSquare = ATo) and
|
2011-04-09 06:27:30 +00:00
|
|
|
(ATo.X = AFrom.X+1) and (ATo.Y = AFrom.Y-1) then
|
2011-04-07 06:27:40 +00:00
|
|
|
begin
|
|
|
|
Result := True;
|
2011-04-09 06:27:30 +00:00
|
|
|
// Don't clear immediately because we haven't yet checked for kind check
|
|
|
|
AEnpassantSquareToClear := Point(ATo.X, ATo.Y+1);
|
2011-04-04 16:30:23 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TChessGame.IsSquareOccupied(ASquare: TPoint): Boolean;
|
|
|
|
begin
|
|
|
|
Result := Board[ASquare.X][ASquare.Y] <> ctEmpty;
|
2010-09-23 10:43:52 +00:00
|
|
|
end;
|
|
|
|
|
2010-09-23 15:40:21 +00:00
|
|
|
procedure TChessGame.UpdateTimes();
|
|
|
|
var
|
|
|
|
lNow: TDateTime;
|
|
|
|
lTimeDelta: Integer;
|
|
|
|
begin
|
|
|
|
lNow := Now;
|
|
|
|
|
|
|
|
lTimeDelta := MilliSecondsBetween(lNow, MoveStartTime);
|
|
|
|
MoveStartTime := lNow;
|
|
|
|
|
|
|
|
if CurrentPlayerIsWhite then WhitePlayerTime := WhitePlayerTime - lTimeDelta
|
|
|
|
else BlackPlayerTime := BlackPlayerTime - lTimeDelta;
|
|
|
|
end;
|
|
|
|
|
2010-09-23 10:43:52 +00:00
|
|
|
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;
|
|
|
|
|
2011-04-10 05:51:39 +00:00
|
|
|
// Check if we are moving to either an empty space or to an enemy piece
|
|
|
|
function TChessGame.CheckEndMove(ATo: TPoint): Boolean;
|
|
|
|
begin
|
|
|
|
if CurrentPlayerIsWhite then
|
|
|
|
Result := Board[ATo.X][ATo.Y] in BlackPiecesOrEmpty
|
|
|
|
else
|
|
|
|
Result := Board[ATo.X][ATo.Y] in WhitePiecesOrEmpty;
|
|
|
|
end;
|
|
|
|
|
2010-09-23 10:43:52 +00:00
|
|
|
{@@
|
2011-04-10 05:51:39 +00:00
|
|
|
Check if we are moving one of our own pieces
|
|
|
|
|
2010-09-23 10:43:52 +00:00
|
|
|
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;
|
|
|
|
|
2011-04-15 14:45:57 +00:00
|
|
|
// True - The King will be in check
|
|
|
|
// False - The King will not be in check
|
|
|
|
function TChessGame.WillKingBeInCheck(AFrom, ATo, AEnpassantToClear: TPoint): Boolean;
|
|
|
|
var
|
|
|
|
kingPos: TPoint;
|
|
|
|
localBoard: TChessBoard;
|
2011-04-09 06:27:30 +00:00
|
|
|
begin
|
2011-04-15 14:45:57 +00:00
|
|
|
Result := false;
|
|
|
|
|
|
|
|
localBoard := Board;
|
|
|
|
|
|
|
|
DoMovePiece(AFrom, ATo, AEnpassantToClear);
|
|
|
|
|
|
|
|
kingPos := FindKing();
|
|
|
|
|
|
|
|
Result := IsKingInCheck(kingPos);
|
|
|
|
|
|
|
|
Board:=localBoard;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TChessGame.IsKingInCheck(AKingPos: TPoint): Boolean;
|
|
|
|
var
|
|
|
|
i,j : integer;
|
|
|
|
piecePos : TPoint;
|
|
|
|
begin
|
|
|
|
Result := False;
|
|
|
|
|
|
|
|
for i:=1 to 8 do
|
|
|
|
for j:=1 to 8 do
|
|
|
|
begin
|
|
|
|
piecePos := Point(i, j);
|
|
|
|
if not (CurrentPlayerIsWhite) then
|
|
|
|
begin
|
|
|
|
case Board[i][j] of
|
|
|
|
ctWRook: Result:= ValidateRookMove(piecePos,AKingPos);
|
|
|
|
ctWKnight: Result:= ValidateKnightMove(piecePos,AKingPos);
|
|
|
|
ctWBishop: Result:= ValidateBishopMove(piecePos,AKingPos);
|
|
|
|
ctWQueen: Result:= ValidateQueenMove(piecePos,AKingPos);
|
|
|
|
ctWKing: Result:= ValidateKingMove(piecePos,AKingPos);
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
case Board[i][j] of
|
|
|
|
ctBRook: Result:= ValidateRookMove(piecePos,AKingPos);
|
|
|
|
ctBKnight: Result:= ValidateKnightMove(piecePos,AKingPos);
|
|
|
|
ctBBishop: Result:= ValidateBishopMove(piecePos,AKingPos);
|
|
|
|
ctBQueen: Result:= ValidateQueenMove(piecePos,AKingPos);
|
|
|
|
ctBKing: Result:= ValidateKingMove(piecePos,AKingPos);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if (result) then exit();
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Negative coords indicate that the king is not in the game }
|
|
|
|
function TChessGame.FindKing(): TPoint;
|
|
|
|
var
|
|
|
|
i,j : integer;
|
|
|
|
begin
|
|
|
|
Result := Point(-1, -1);
|
|
|
|
|
|
|
|
for i:=1 to 8 do
|
|
|
|
for j:=1 to 8 do
|
|
|
|
if (CurrentPlayerIsWhite) and (Board[i][j]=ctWKing) then
|
|
|
|
begin
|
|
|
|
Result := Point(i, j);
|
|
|
|
Exit;
|
|
|
|
end
|
|
|
|
else if (not CurrentPlayerIsWhite) and (Board[i][j]=ctBKing) then
|
|
|
|
begin
|
|
|
|
Result := Point(i, j);
|
|
|
|
Exit;
|
|
|
|
end;
|
2011-04-09 06:27:30 +00:00
|
|
|
end;
|
|
|
|
|
2010-09-21 06:33:18 +00:00
|
|
|
initialization
|
|
|
|
|
|
|
|
vChessGame := TChessGame.Create;
|
|
|
|
|
|
|
|
finalization
|
|
|
|
|
|
|
|
vChessGame.Free;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|