board.pas
来自「用于开发税务票据管理的软件」· PAS 代码 · 共 1,705 行 · 第 1/5 页
PAS
1,705 行
// and possibly move the piece
if FDragging then
begin
if FHighlightStartSquare then
EraseSquare(FDraggedPiece.Location, FDraggedPiece);
// Remove any previous highlights by calling EraseSquare
if FHighlightSquare.Count > 0 then
repeat
FindPiece(CurrPiecePtr, TPoint(FHighlightSquare.First^));
EraseSquare(TPoint(FHighlightSquare.First^), CurrPiecePtr);
Dispose(FHighlightSquare.First);
FHighlightSquare.Delete(0);
until FHighlightSquare.Count = 0;
{ If in position mode, call PositionDrop instead }
if FPositionMode then
begin
PositionDrop(FindNewLocation(Msg.XPos, Msg.YPos));
inherited;
Exit;
end;
// If the move is valid...
if Screen.Cursor = crYesMove then
begin
Curr := FDraggedPiece.Location;
// Get board square from message x & y positions
GetPos := FindNewLocation(Msg.XPos, Msg.YPos);
if MovePiece(FDraggedPiece, GetPos) then
SendEndTurnEvent;
{ Reset all information variables }
FCaptureOn := False;
FEnPassant := point(0,0);
FCastling := csNone;
end;
{ Reset all drag-drop information variables }
Screen.Cursor := crDefault;
FDragging := False;
FDraggedPiece := nil;
{ Remember that you need to implement this for all the move functions if
you intend to use this component elsewhere (I think) }
// if Assigned(OnEndDrag) then OnEndDrag(FDraggedPiece^, Self, Msg.XPos, Msg.YPos);
// etc for all other drag functions - TELL ME IF THIS IS NOT CORRECT
end;
CheckForGameEnd;
inherited;
end;
{ Check for game ending positions - i.e. Checkmate, Stalemate and draw-by-
repetition. If these are found, finish the current game }
function TChessboard.CheckForGameEnd: Boolean;
var
Finished,
ResetGame: Boolean;
begin
ResetGame := False;
Finished := False;
// Check for checkmate, stalemate and repetition
if FCheck then
begin
if CheckForCheckmate then // Is player in checkmate?
begin
Finished := True; // prevents undo/redo until reset
FGameRunning := False;
if Assigned(FOnCheckmate) then FOnCheckMate(TColour(FTurn), ResetGame);
end;
end
else
if CheckForStalemate then // Is player in stalemate?
begin
Finished := True;
FGameRunning := False;
if Assigned(FOnDraw) then FOnDraw(True, False, ResetGame);
end
else
if CheckForRepetition then // 3 move repetition draw?
begin
Finished := True;
FGameRunning := False;
if Assigned(FOnDraw) then FOnDraw(False, True, ResetGame);
end;
if Finished then
begin
if ResetGame then
ResetBoard
else
begin
FGameRunning := True; // so finishcurrentgame does something
FinishCurrentGame;
end;
end;
Result := Finished;
end;
// TODO possibly make this procedure into several, to improve readability
{ Handle WM_MOUSEMOVE, testing whether to respond by dragging pieces }
procedure TChessboard.WMMouseMove(var Msg: TWMMouseMove);
var
PieceAtMouse, // Piece in current square, if any
FindPiecePtr: TPiece;
i: Integer;
AtStartSquare, // Is location over FDraggedPiece's start square?
MoveIsValid: Boolean;
NewLoc, // Square mouse is currently over
OldHighlight: TPoint; // Used to store old highlights & then erase them
AddLoc: ^TPoint; // create a new point for adding to highlight list
AddHighlightHere: Boolean; // add highlight outline at current square?
oldColour: TColor; // used for temp. swapping highlight colours
TempOldLoc: TPoint; // change this later...
begin
if FGameRunning then
begin
NewLoc := FindNewLocation(Msg.XPos, Msg.YPos);
if FDragging then
begin
// Check if moving over the same square as last time - if so, nothing
// has changed. This check gives a nice boost in efficiency. Just
// look at the amount of code this can ignore!
if ComparePoints(NewLoc, FOldDragLoc) then
begin
inherited;
Exit;
end;
TempOldLoc := FOldDragLoc;
// Update this to be the most recent square dragged over
FOldDragLoc := NewLoc;
{ If in position mode, call PositionMove instead }
if FPositionMode then
begin
PositionMove(NewLoc);
inherited;
Exit;
end;
if (InBounds(NewLoc)) then
AddHighlightHere := not FSquaresHighlighted[NewLoc.X shl 3 + NewLoc.Y - 9]
else
AddHighlightHere := False;
AtStartSquare := ComparePoints(NewLoc, FDraggedPiece.Location);
if AtStartSquare and not FHighlightStartSquare then
begin
inherited;
Exit;
end;
MoveIsValid := ValidMove(FDraggedPiece.Location, NewLoc);
// The following block is used for ShowValidMoves mode. It checks to
// see if the new square is a valid one by checking the highlight list.
// If it is, it sets the previously highlighted square back to the
// default highlight colour, changes the current square and sets the
// current square to be the last highlighted square to be changed.
if FShowValidMoves then
begin
if MoveIsValid then
begin
// Unhighlight the previously "current" square
if not AtStartSquare then
if ComparePoints(FLastValidMoveLoc, FDraggedPiece.Location) = False then
HighlightBoardSquare(FLastValidMoveLoc, Ord(FDraggedPiece.Colour) = Turn);
// Highlight the new location with "current" move colour
for i := 0 to FHighlightSquare.Count - 1 do
begin
if ComparePoints(NewLoc, TPoint(FHighlightSquare[i]^)) then
begin
OldColour := FHighlightColour;
FHighlightColour := FValidMoveDragColour;
HighlightBoardSquare(NewLoc, Ord(FDraggedPiece.Colour) = Turn);
FHighlightColour := OldColour;
Break;
end;
end;
FLastValidMoveLoc := NewLoc;
end
else
// If not a valid move, unhighlight the last valid square
if ComparePoints(FLastValidMoveLoc, FDraggedPiece.Location) = False then
HighlightBoardSquare(FLastValidMoveLoc, Ord(FDraggedPiece.Colour) = FTurn);
AddHighlightHere := False;
end;
// This block is for the default type of highlighting, when a piece is
// dragged over a new location.
if AddHighlightHere then
begin
// Create a new square for the highlight list
New(AddLoc);
AddLoc^ := NewLoc;
// Clear any old highlights on the board - note that this does not
// affect the original dragged location, which will be cleared when
// the drag-drop has finished
if FHighlightSquare.Count > 0 then
repeat
OldHighlight := TPoint(FHighlightSquare.First^);
FindPiece(FindPiecePtr, OldHighlight);
// Don't unhighlight the original square!
if FindPiecePtr <> FDraggedPiece then
EraseSquare(OldHighlight, FindPiecePtr);
FSquaresHighlighted[ OldHighlight.X shl 3 + OldHighlight.Y - 9] := False;
Dispose(FHighlightSquare.First);
FHighlightSquare.Delete(0);
until FHighlightSquare.Count = 0;
// Adding it to the list so it can be cleared either by a later
// mouse move or when the drag-drop has finished
FHighlightSquare.Insert(0, AddLoc);
// NOW to highlight the square (finally!). If the current move is
// valid, or highlighting the original square, then display using
// the "valid move" colour (default of clYellow). Otherwise, display
// using the "ememy/invalid" colour (default of clRed). Note that
// HighlightBoardSquare may decide to display the square using the
// "emeny capture" colour (clLime) if there is an enemy piece to
// capture. See also HighlightBoardSquare
OldColour := FHighlightColour;
if (MoveIsValid) or ( AtStartSquare and
(Ord(FDraggedPiece.Colour) <> Turn) ) then
begin
if AtStartSquare then
FHighlightColour := FStartDragColour;
HighlightBoardSquare(AddLoc^, False);
end
else
begin
FHighlightColour := FHighlightEnemyColour;
HighlightBoardSquare(AddLoc^, True);
end;
FHighlightColour := OldColour;
FSquaresHighlighted[AddLoc^.X shl 3 + AddLoc^.Y - 9] := True;
// Dispose(AddLoc);
// FHighlightSquare.Delete(0);
end;
// Change screen cursor to either accept or decline piece
if MoveIsValid then
Screen.Cursor := crYesMove
else
Screen.Cursor := crNoMove;
end
else
// Not dragging the piece, so show the piece hint instead
if FShowHints then
begin
if FindPiece(PieceAtMouse, NewLoc) then
Hint := PieceAtMouse.Hint
else
Hint := '';
end;
end;
inherited;
end;
{ This procedure calculates all of the relevant details for the set of special
cases that the OnEndTurn event needs. Once this is complete, it then calls
the OnEndTurn event, passing in the new parameters. MAKE SURE TO CALL THIS
AFTER A CALL TO MOVEPIECE IF YOU WANT THE EVENT "OnEndTurn" TO BE CALLED! }
procedure TChessboard.SendEndTurnEvent;
var
MoveDetails: TSetOfSpecials;
r: TUndoRecordPtr;
PiecePtr: TPiece;
begin
if Assigned(FOnEndTurn) then
begin
MoveDetails := [];
if FCheck then Include(MoveDetails, spCheck);
if FCastling <> csNone then
begin
if FCastling = csQueenSideCastling then
Include(MoveDetails, spQueenSideCastling)
else
Include(MoveDetails, spKingSideCastling);
end
else
if FCaptureOn then Include(MoveDetails, spCapture);
if (FEnPassant.X <> 0) then Include(MoveDetails, spEnPassant);
R := UndoList[FUndoCount];
if R^.PromotedPiece <> nil then
begin
Include(MoveDetails, spPromoting);
PiecePtr := R^.PromotedPiece;
end
else
PiecePtr := R^.MovedPiece;
OnEndTurn(PiecePtr, R.From, R.WhereTo, MoveDetails);
end;
end;
{ Return whether the current position is checkmate or not }
function TChessboard.CheckforCheckmate: Boolean;
var
x,y: Integer;
KingPtr,
P, // meaningful variable name :) Used for temp. checking each piece
EnemyPtr: TPiece; // if 1 piece is attacking the king, it's stored in here
KingColour: TColour; // The colour of the king under attack
i: Integer;
StartPt, // Used for calculating blocking positions
EndPt: TPoint; // Same here
begin
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?