home *** CD-ROM | disk | FTP | other *** search
- {************************************************}
- { }
- { Chess - Shared DLL Example }
- { CHESS.DLL Move generator/Position analysis }
- { Copyright (c) 1992 by Borland International }
- { }
- {************************************************}
-
- unit LMoveGen;
-
- {$R-,Q-,S-,W-}
-
- interface
-
- uses GameRec;
-
- procedure CalcAttackTab;
- function PieceAttacks(APiece: PieceType; AColor: ColorType;
- ASquare, Square: SquareType): Boolean;
- function Attacks(AColor: ColorType; Square: SquareType): Boolean;
-
- { Castling types }
- type
- CastDirType = (Long,Short);
- CastType = set of CastDirType;
-
- procedure CalcCastling(InColor: ColorType; var Cast: CastType);
- function RepeatMove(Move: MoveType): Boolean;
-
- type
- FiftyType = 0..150;
-
- function FiftyMoveCnt: FiftyType;
-
- type
- RepeatType = 1..4;
-
- function Repetition(Immediate: Boolean): RepeatType;
-
- function KillMovGen(Move: MoveType): Boolean;
- procedure InitMovGen;
- procedure MovGen;
-
- { Directions }
- type
- DirType = 0..7;
-
- const
- { Move directions used in the Move generation }
-
- { Rook, Bishop etc. }
- DirTab: array[DirType] of Integer =
- (1,-1,$10,-$10,$11,-$11,$F,-$F);
-
- { Knight moves }
- KnightDir: array[DirType] of Integer =
- ($E,-$E,$12,-$12,$1F,-$1F,$21,-$21);
-
- { Pawn Direction }
- PawnDir: array[ColorType] of Integer =
- ($10,-$10);
-
- { Castling moves }
- const
- CastMove: array[ColorType,CastDirType] of
- record
- CastNew,CastOld: SquareType;
- end =
- (((CastNew: 2; CastOld: 4),
- (CastNew: 6; CastOld: 4)),
- ((CastNew: $72; CastOld: $74),
- (CastNew: $76; CastOld: $74)));
-
- implementation
-
- { Tables for calculating whether a Piece Attacks a Square }
- type
- SetOfPiece = byte;
- const
- BitTab: array[King..Pawn] of SetOfPiece = (1,2,4,8,$10,$20);
-
- var
- { A constant, which is calculated in CalcAttackTab.
- Gives the squares which a Piece in the middle of the
- table can Move to.
-
- This is not modified during the game and can safely be
- made global in the chessdll, shared between game contexts.}
- AttackTab: array[-$77..$77] of
- record
- { A set of King..Pawn.
- Gives the Pieces, which can
- Move to the Square }
- PieceSet: SetOfPiece;
- Direction: Integer; { The Direction from the Piece to the
- Square }
- end;
-
- { Calculates AttackTab }
- procedure CalcAttackTab;
- var
- Dir: DirType;
- Sq: Integer;
- i: Byte;
- begin
- FillChar(AttackTab, sizeof(AttackTab), 0);
- for Dir:=7 downto 0 do
- begin
- for i:=1 to 7 do
- with AttackTab[DirTab[Dir]*i] do
- begin
- if Dir<4 then
- PieceSet:=BitTab[Queen]+BitTab[Rook]
- else
- PieceSet:=BitTab[Queen]+BitTab[Bishop];
- Direction:=DirTab[Dir];
- end;
- with AttackTab[DirTab[Dir]] do
- PieceSet:=PieceSet+BitTab[King];
- with AttackTab[KnightDir[Dir]] do
- begin
- PieceSet:=BitTab[Knight];
- Direction:=KnightDir[Dir];
- end;
- end;
- end; { CalcAttachTab }
-
- { Calculates whether APiece placed On ASquare Attacks the Square }
- function PieceAttacks(APiece: PieceType; AColor: ColorType;
- ASquare, Square: SquareType): Boolean;
- var
- Sq: EdgeSquareType;
- begin
- if APiece = Pawn then
- { Pawn Attacks }
- PieceAttacks := abs(Square - ASquare - PawnDir[AColor]) = 1
-
- else
- { Other Attacks: Can the Piece Move to the Square? }
- with AttackTab[Square - ASquare] do
- if (PieceSet and BitTab[APiece]) <> 0 then
- if (APiece = King) or (APiece = Knight) then
- PieceAttacks := true
- else
- begin
- { Are there any blocking Pieces in between? }
- Sq := ASquare;
- repeat
- Sq := Sq + Direction;
- until (Sq = Square) or (CC.Board[Sq].Piece <> Empty);
- PieceAttacks := Sq = Square;
- end
- else
- PieceAttacks := False;
- end; { PieceAttacks }
-
- { Calculates whether AColor Attacks the Square }
- function Attacks(AColor: ColorType; Square: SquareType): Boolean;
-
- { Calculates whether AColor Attacks the Square with a Pawn }
- function PawnAttacks(AColor: ColorType;
- Square: SquareType): Boolean;
- var Sq: EdgeSquareType;
- begin
- PawnAttacks:=true;
- Sq := Square - PawnDir[AColor] - 1; { Left Square }
- if (Sq and $88) = 0 then
- with CC.Board[Sq] do
- if (Piece = Pawn) and (Color = AColor) then Exit;
- Sq := Sq + 2; { Right Square }
- if (Sq and $88) = 0 then
- with CC.Board[Sq] do
- if (Piece = Pawn) and (Color = AColor) then Exit;
- PawnAttacks := False;
- end; { PawnAttacks }
-
-
- var
- i: IndexType;
-
- begin { Attacks }
- Attacks := True;
-
- { Pawn Attacks }
- if PawnAttacks(AColor,Square) then
- Exit;
-
- { Other Attacks: Try all Pieces, starting with the smallest }
- with CC do
- for i := OfficerNo[AColor] downto 0 do
- with PieceTab[AColor,i] do
- if IPiece <> Empty then
- if PieceAttacks(IPiece,AColor,ISquare,Square) then
- Exit;
-
- Attacks := False;
- end; { Attacks }
-
- { Calculates whether InColor can castle }
- procedure CalcCastling(InColor: ColorType; var Cast: CastType);
-
- function Check(Square: SquareType; InPiece: PieceType): Boolean;
- { Checks whether InPiece is placed On Square and has never moved }
- var
- Dep: DepthType;
- begin
- Check := False;
- with CC, Board[Square] do { Check Square }
- if (Piece = InPiece) and (Color = InColor) then
- begin
- Dep := Depth - 1; { Check all moves }
- while MovTab[Dep].MovPiece <> Empty do
- begin
- if MovTab[Dep].New1 = Square then Exit;
- Dep := Dep - 1;
- end;
- Check := True;
- end;
- end; { Check }
-
- var
- Square: SquareType;
- begin { CalcCastling }
- Square := 0;
- if InColor = Black then Square := $70;
- Cast :=[];
- if Check(Square + 4,King) then
- begin { Check King }
- if Check(Square ,Rook) then Cast := Cast +[Long]; { Check a-Rook }
- if Check(Square + 7,Rook) then Cast := Cast +[Short]; { Check h-Rook }
- end;
- end; { CalcCastling }
-
- { Check if Move is a Pawn Move or a capture }
- function RepeatMove(Move: MoveType): Boolean;
- begin
- with Move do
- RepeatMove := (MovPiece <> Empty) and (MovPiece <> Pawn)
- and (Content = Empty) and not Spe;
- end; { RepeatMove }
-
- { Counts the Number of moves since Last capture or Pawn Move.
- The game is a Draw when FiftyMoveCnt = 100 }
- function FiftyMoveCnt: FiftyType;
- var Cnt: FiftyType;
- begin
- Cnt := 0;
- with CC do
- while RepeatMove(MovTab[Depth - Cnt]) do
- Inc(Cnt);
- FiftyMoveCnt := Cnt;
- end;
-
- { Calculates how many times the position has occured before.
- The game is a Draw when Repetition = 3.
- MovTab[Back..Depth] contains the previous moves.
- When Immediate is set, only Immediate Repetition is checked }
- function Repetition(Immediate: Boolean): RepeatType;
- var
- LastDep,CompDep,TraceDep,CheckDep,SameDepth: DepthType;
- TraceSq,CheckSq: SquareType;
- RepeatCount: RepeatType;
- label 10;
- begin
- with CC do
- begin
- Repetition := 1;
- RepeatCount := 1;
- SameDepth := Depth + 1; { Current position }
- CompDep := SameDepth - 4; { First position to compare }
- LastDep := SameDepth;
-
- { MovTab[LastDep..Depth] contains previous relevant moves }
- while RepeatMove(MovTab[LastDep - 1]) and
- ((CompDep < LastDep) or not Immediate) do
- Dec(LastDep);
- if CompDep < LastDep then Exit; { No Repetition Possible }
- CheckDep := SameDepth;
- repeat
- Dec(CheckDep); { Get Next Move to test }
- CheckSq := MovTab[CheckDep].New1;
- TraceDep := CheckDep + 2; { Check if Move has been }
- while TraceDep < SameDepth do
- begin
- if MovTab[TraceDep].Old = CheckSq then goto 10;
- Inc(TraceDep, 2);
- end;
-
- { Trace the Move backward to see whether
- it has been 'undone' earlier }
- TraceDep := CheckDep;
- TraceSq := MovTab[TraceDep].Old;
- repeat
- if TraceDep - 2 < LastDep then Exit;
- Dec(TraceDep, 2);
- { Check if Piece has been moved before }
- with MovTab[TraceDep] do
- if TraceSq = New1 then
- TraceSq := Old;
- until (TraceSq = CheckSq) and (TraceDep <= CompDep + 1);
- if TraceDep < CompDep then { Adjust evt. CompDep }
- begin
- CompDep := TraceDep;
- if odd(SameDepth - CompDep) then
- begin
- if CompDep = LastDep then Exit;
- Dec(CompDep);
- end;
- CheckDep := SameDepth;
- end;
- { All moves between SAMEDEP and CompDep have been checked,
- so a Repetition is Found }
- 10: if CheckDep <= CompDep then
- begin
- Inc(RepeatCount);
- Repetition := RepeatCount;
- if CompDep - 2 < LastDep then Exit;
- SameDepth := CompDep; { Search for more repetitions }
- Dec(CompDep, 2);
- CheckDep := SameDepth;
- end;
- until False;
- end; { with CC^ }
- end { Repetition };
-
- { Tests whether a Move is Possible.
-
- On entry :
- Move contains a full description of a Move, which
- has been legally generated in a different position.
- MovTab[Depth - 1] contains Last performed Move.
-
- On Exit :
- KillMovGen indicates whether the Move is Possible }
- function KillMovGen(Move: MoveType): Boolean;
- var
- CastSq: SquareType;
- Promote: PieceType;
- CastDir: CastDirType;
- Cast: CastType;
- begin
- KillMovGen := False;
- with CC, Move do
- begin
- if Spe and (MovPiece = King) then
- begin
- { Castling }
- CalcCastling(Player,Cast);
- if New1 > Old then
- CastDir := Short
- else
- CastDir := Long;
-
- { Has King or Rook moved before? }
- if CastDir in Cast then
- begin
- CastSq := (New1 + Old) div 2;
- { Are the squares Empty? }
- if (Board[New1 ].Piece = Empty) then
- if (Board[CastSq].Piece = Empty) then
- if ((New1 > Old) or (Board[New1 - 1 ].Piece = Empty)) then
- { Are the squares unattacked? }
- if not Attacks(Opponent,Old) then
- if not Attacks(Opponent,New1) then
- if not Attacks(Opponent,CastSq) then
- KillMovGen := True;
- end;
- end
- else
- if Spe and (MovPiece = Pawn) then
- begin
- { E.p. capture }
- with MovTab[Depth - 1] do
- { Was the Opponent's Move a 2 Square Move }
- if MovPiece = Pawn then
- if abs(New1 - Old) >= $20 then
- { Is there a Piece On the Square? }
- with Board[Move.Old] do
- if (Piece = Pawn) and (Color = Player) then
- KillMovGen := Move.New1 = (New1 + Old) div 2;
- end { if }
- else
- begin
- if Spe then { Normal test }
- begin
- Promote := MovPiece; { Pawnpromotion }
- MovPiece := Pawn;
- end;
-
- { Is the Content of Old and New1 squares correct? }
- if (Board[Old].Piece = MovPiece) and
- (Board[Old].Color = Player) and
- (Board[New1].Piece = Content) and
- ((Content = Empty) or
- (Board[New1].Color = Opponent)) then
-
- { Is the Move Possible? }
- if MovPiece = Pawn then
- if Abs(New1 - Old) < $20 then
- KillMovGen := True
- else
- KillMovGen := Board[(New1 + Old) div 2].Piece = Empty
- else
- KillMovGen := PieceAttacks(MovPiece,Player,Old,New1);
- if Spe then
- MovPiece := Promote;
- end;
- end; { with }
- end; { KillMovGen }
-
- { Movegeneration variables }
-
- { The move generator.
- InitMovGen generates all Possible moves and places them
- in a Buffer. MovGen will then Generate the moves One by One and
- place them in Next.
-
- On entry :
- Player contains the Color to Move.
- MovTab[Depth - 1] the Last performed Move.
-
- On Exit :
- Buffer contains the generated moves.
-
- The moves are generated in the order :
- Captures
- Castlings
- Non captures
- E.p. captures }
- procedure InitMovGen;
-
- { Stores a Move in Buffer }
- procedure Generate;
- begin
- with CC do
- begin
- BufCount := BufCount + 1;
- Buffer[BufCount] := NextMove;
- end;
- end; { Generate }
-
- { Generates Pawnpromotion }
- procedure PawnPromotionGen;
- var
- Promote: PieceType;
- begin
- with CC.NextMove do
- begin
- Spe := True;
- for Promote := Queen to Knight do
- begin
- MovPiece := Promote;
- Generate;
- end;
- Spe := False;
- end;
- end; { PawnPromotionGen }
-
- { Generates captures of the Piece On New1 using PieceTab }
- procedure CapMovGen;
- var
- NextSq,Sq: EdgeSquareType;
- i: IndexType;
- begin
- with CC, NextMove do
- begin
- Spe := False;
- Content := Board[New1].Piece;
- MovPiece := Pawn; { Pawn captures }
- NextSq := New1 - PawnDir[Player];
- for Sq := NextSq - 1 to NextSq + 1 do if Sq <> NextSq then
- if (Sq and $88) = 0 then
- with Board[Sq] do
- if (Piece = Pawn) and (Color = Player) then
- begin
- Old := Sq;
- if (New1 < 8) or (New1 >= $70) then
- PawnPromotionGen
- else
- Generate;
- end;
-
- { Other captures, starting with the smallest Pieces }
- for i := OfficerNo[Player] downto 0 do
- with PieceTab[Player,i] do
- if (IPiece <> Empty) and (IPiece <> Pawn) then
- if PieceAttacks(IPiece,Player,ISquare,New1) then
- begin
- Old := ISquare;
- MovPiece := IPiece;
- Generate;
- end;
- end { with };
- end; { CapMovGen }
-
- { Generates non captures for the Piece On Old }
- procedure NonCapMovGen;
- var
- First,Last,Dir: DirType;
- Direction: Integer;
- NewSq: EdgeSquareType;
- begin
- with CC, NextMove do
- begin
- Spe := False;
- MovPiece := Board[Old].Piece;
- Content := Empty;
- case MovPiece of
- King:
- for Dir := 7 downto 0 do
- begin
- NewSq := Old + DirTab[Dir];
- if (NewSq and $88) = 0 then
- if Board[NewSq].Piece = Empty then
- begin
- New1 := NewSq;
- Generate;
- end;
- end;
- Knight:
- for Dir := 7 downto 0 do
- begin
- NewSq := Old + KnightDir[Dir];
- if (NewSq and $88) = 0 then
- if Board[NewSq].Piece = Empty then
- begin
- New1 := NewSq;
- Generate;
- end;
- end;
- Queen,
- Rook,
- Bishop:
- begin
- First := 7;
- Last := 0;
- if MovPiece = Rook then First := 3;
- if MovPiece = Bishop then Last := 4;
- for Dir := First downto Last do
- begin
- Direction := DirTab[Dir];
- NewSq := Old + Direction;
- { Generate all non captures in
- the Direction }
- while (NewSq and $88) = 0 do
- begin
- if Board[NewSq].Piece <> Empty then Break;
- New1 := NewSq;
- Generate;
- NewSq := New1 + Direction;
- end;
- end;
- end;
- Pawn:
- begin
- New1 := Old + PawnDir[Player]; { One Square forward }
- if Board[New1].Piece = Empty then
- if (New1 < 8) or (New1 >= $70) then
- PawnPromotionGen
- else
- begin
- Generate;
- if (Old < $18) or (Old >= $60) then
- begin
- New1 := New1 + (New1 - Old); { Two squares forward }
- if Board[New1].Piece = Empty then
- Generate;
- end;
- end;
- end;
- end; { case }
- end; { with }
- end; { NonCapMovGen }
-
- var
- CastDir: CastDirType;
- Sq: EdgeSquareType;
- Index: IndexType;
-
- begin { InitMovGen }
- { Reset the Buffer }
- with CC, NextMove do
- begin
- BufCount := 0;
- BufPnt := 0;
-
- { Generate all captures starting with captures of
- largest Pieces }
- for Index := 1 to PawnNo[Opponent] do
- with PieceTab[Opponent,Index] do
- if IPiece <> Empty then
- begin
- New1 := ISquare;
- CapMovGen;
- end;
-
- { Castling }
- Spe := True;
- MovPiece := King;
- Content := Empty;
- for CastDir := Short downto Long do
- with CastMove[Player,CastDir] do
- begin
- New1 := CastNew;
- Old := CastOld;
- if KillMovGen(NextMove) then Generate;
- end;
-
- { Generate non captures, starting with pawns }
- for Index := PawnNo[Player] downto 0 do
- with PieceTab[Player,Index] do
- if IPiece <> Empty then
- begin
- Old := ISquare;
- NonCapMovGen;
- end;
-
- { E.p. captures }
- with MovTab[Depth - 1] do
- if MovPiece = Pawn then
- if Abs(New1 - Old) >= $20 then
- begin
- NextMove.Spe := True;
- NextMove.MovPiece := Pawn;
- NextMove.Content := Empty;
- NextMove.New1 := (New1 + Old) div 2;
- for Sq := New1 - 1 to New1 + 1 do
- if Sq <> New1 then
- if (Sq and $88) = 0 then
- begin
- NextMove.Old := Sq;
- if KillMovGen(NextMove) then Generate;
- end;
- end;
- end; { with }
- end; { InitMovGen }
-
- { Place Next Move from the Buffer in Next.
- Generate ZeroMove when there is No more moves }
- procedure MovGen;
- begin
- with CC do
- begin
- if BufPnt >= BufCount then
- NextMove := ZeroMove
- else
- begin
- BufPnt := BufPnt + 1;
- NextMove := Buffer[BufPnt];
- end;
- end;
- end; { MovGen }
-
- end.