LMOVEGEN.PAS
Upload User: gengyihm
Upload Date: 2013-08-13
Package Size: 137k
Code Size: 18k
Development Platform:

Pascal

  1. {************************************************}
  2. {                                                }
  3. {   Chess - Shared DLL Example                   }
  4. {   CHESS.DLL Move generator/Position analysis   }
  5. {   Copyright (c) 1992 by Borland International  }
  6. {                                                }
  7. {************************************************}
  8. unit LMoveGen;
  9. {$R-,Q-,S-,W-}
  10. interface
  11. uses  GameRec;
  12. procedure CalcAttackTab;
  13. function PieceAttacks(APiece: PieceType; AColor: ColorType;
  14.   ASquare, Square:  SquareType): Boolean;
  15. function Attacks(AColor: ColorType; Square: SquareType): Boolean;
  16. { Castling types }
  17. type
  18.   CastDirType = (Long,Short);
  19.   CastType = set of CastDirType;
  20. procedure CalcCastling(InColor: ColorType; var Cast: CastType);
  21. function RepeatMove(Move: MoveType): Boolean;
  22. type
  23.   FiftyType = 0..150;
  24. function FiftyMoveCnt: FiftyType;
  25. type
  26.   RepeatType = 1..4;
  27. function Repetition(Immediate: Boolean): RepeatType;
  28. function KillMovGen(Move: MoveType): Boolean;
  29. procedure InitMovGen;
  30. procedure MovGen;
  31. { Directions }
  32. type
  33.   DirType   = 0..7;
  34. const
  35.   { Move directions used in the Move generation }
  36.   { Rook, Bishop etc. }
  37.   DirTab:    array[DirType] of Integer =
  38.     (1,-1,$10,-$10,$11,-$11,$F,-$F);
  39.   { Knight moves }
  40.   KnightDir: array[DirType] of Integer =
  41.     ($E,-$E,$12,-$12,$1F,-$1F,$21,-$21);
  42.   { Pawn Direction }
  43.   PawnDir:   array[ColorType] of Integer =
  44.     ($10,-$10);
  45. { Castling moves }
  46. const
  47.   CastMove: array[ColorType,CastDirType] of
  48.     record
  49.       CastNew,CastOld: SquareType;
  50.     end =
  51.     (((CastNew:   2;   CastOld:   4),
  52.       (CastNew:   6;   CastOld:   4)),
  53.      ((CastNew: $72;   CastOld: $74),
  54.       (CastNew: $76;   CastOld: $74)));
  55. implementation
  56. { Tables for calculating whether a Piece Attacks a Square }
  57. type
  58.   SetOfPiece = byte;
  59. const
  60.   BitTab: array[King..Pawn] of SetOfPiece = (1,2,4,8,$10,$20);
  61. var
  62.   { A constant, which is calculated in CalcAttackTab.
  63.     Gives the squares which a Piece in the middle of the
  64.     table can Move to.
  65.     This is not modified during the game and can safely be
  66.     made global in the chessdll, shared between game contexts.}
  67.   AttackTab: array[-$77..$77] of
  68.     record
  69.       { A set of King..Pawn.
  70.         Gives the Pieces, which can
  71.         Move to the Square }
  72.       PieceSet:  SetOfPiece;
  73.       Direction: Integer;  { The Direction from the Piece to the
  74.                              Square }
  75.     end;
  76. { Calculates AttackTab }
  77. procedure CalcAttackTab;
  78. var
  79.   Dir: DirType;
  80.   Sq: Integer;
  81.   i: Byte;
  82. begin
  83.    FillChar(AttackTab, sizeof(AttackTab), 0);
  84.    for Dir:=7 downto 0 do
  85.    begin
  86.      for i:=1 to 7 do
  87.        with AttackTab[DirTab[Dir]*i] do
  88.        begin
  89.          if Dir<4 then
  90.            PieceSet:=BitTab[Queen]+BitTab[Rook]
  91.          else
  92.            PieceSet:=BitTab[Queen]+BitTab[Bishop];
  93.          Direction:=DirTab[Dir];
  94.        end;
  95.      with AttackTab[DirTab[Dir]] do
  96.        PieceSet:=PieceSet+BitTab[King];
  97.      with AttackTab[KnightDir[Dir]] do
  98.      begin
  99.        PieceSet:=BitTab[Knight];
  100.        Direction:=KnightDir[Dir];
  101.      end;
  102.    end;
  103. end; { CalcAttachTab }
  104. { Calculates whether APiece placed On ASquare Attacks the Square }
  105. function PieceAttacks(APiece: PieceType; AColor: ColorType;
  106.   ASquare, Square:  SquareType): Boolean;
  107. var
  108.   Sq: EdgeSquareType;
  109. begin
  110.   if APiece = Pawn then
  111.     { Pawn Attacks }
  112.     PieceAttacks := abs(Square - ASquare - PawnDir[AColor]) = 1
  113.   else
  114.     { Other Attacks: Can the Piece Move to the Square? }
  115.     with AttackTab[Square - ASquare] do
  116.       if (PieceSet and BitTab[APiece]) <> 0 then
  117.         if (APiece = King) or (APiece = Knight) then
  118.           PieceAttacks := true
  119.         else
  120.         begin
  121.           { Are there any blocking Pieces in between? }
  122.           Sq := ASquare;
  123.           repeat
  124.             Sq := Sq + Direction;
  125.           until (Sq = Square) or (CC.Board[Sq].Piece <> Empty);
  126.           PieceAttacks := Sq = Square;
  127.         end
  128.       else
  129.          PieceAttacks := False;
  130. end; { PieceAttacks }
  131. { Calculates whether AColor Attacks the Square }
  132. function Attacks(AColor: ColorType; Square: SquareType): Boolean;
  133.   { Calculates whether AColor Attacks the Square with a Pawn }
  134.   function PawnAttacks(AColor: ColorType;
  135.     Square: SquareType): Boolean;
  136.   var   Sq: EdgeSquareType;
  137.   begin
  138.     PawnAttacks:=true;
  139.     Sq := Square - PawnDir[AColor] - 1;                    { Left Square }
  140.     if (Sq and $88) = 0 then
  141.       with CC.Board[Sq] do
  142.         if (Piece = Pawn) and (Color = AColor) then Exit;
  143.     Sq := Sq + 2;                                         { Right Square }
  144.     if (Sq and $88) = 0 then
  145.       with CC.Board[Sq] do
  146.         if (Piece = Pawn) and (Color = AColor) then Exit;
  147.     PawnAttacks := False;
  148.   end; { PawnAttacks }
  149. var
  150.   i: IndexType;
  151. begin { Attacks }
  152.    Attacks := True;
  153.    { Pawn Attacks }
  154.    if PawnAttacks(AColor,Square) then
  155.       Exit;
  156.    { Other Attacks:  Try all Pieces, starting with the smallest }
  157.    with CC do
  158.      for i := OfficerNo[AColor] downto 0 do
  159.        with PieceTab[AColor,i] do
  160.          if IPiece <> Empty then
  161.            if PieceAttacks(IPiece,AColor,ISquare,Square) then
  162.              Exit;
  163.    Attacks := False;
  164. end; { Attacks }
  165. { Calculates whether InColor can castle }
  166. procedure CalcCastling(InColor: ColorType; var Cast: CastType);
  167.   function Check(Square: SquareType; InPiece: PieceType): Boolean;
  168.   { Checks whether InPiece is placed On Square and has never moved }
  169.   var
  170.     Dep: DepthType;
  171.   begin
  172.     Check := False;
  173.     with CC, Board[Square] do                             { Check Square }
  174.       if (Piece = InPiece) and (Color = InColor) then
  175.       begin
  176.         Dep := Depth - 1;                              { Check all moves }
  177.         while MovTab[Dep].MovPiece <> Empty do
  178.         begin
  179.           if MovTab[Dep].New1 = Square then Exit;
  180.           Dep := Dep - 1;
  181.         end;
  182.         Check := True;
  183.       end;
  184.   end; { Check }
  185. var
  186.   Square: SquareType;
  187. begin { CalcCastling }
  188.   Square := 0;
  189.   if InColor = Black then Square := $70;
  190.   Cast :=[];
  191.   if Check(Square + 4,King) then
  192.   begin                                                     { Check King }
  193.     if Check(Square  ,Rook) then Cast := Cast +[Long];    { Check a-Rook }
  194.     if Check(Square + 7,Rook) then Cast := Cast +[Short]; { Check h-Rook }
  195.   end;
  196. end; { CalcCastling }
  197. { Check if Move is a Pawn Move or a capture }
  198. function RepeatMove(Move: MoveType): Boolean;
  199. begin
  200.   with Move do
  201.     RepeatMove := (MovPiece <> Empty) and (MovPiece <> Pawn)
  202.       and (Content = Empty) and not Spe;
  203. end; { RepeatMove }
  204. { Counts the Number of moves since Last capture or Pawn Move.
  205.   The game is a Draw when FiftyMoveCnt = 100 }
  206. function FiftyMoveCnt: FiftyType;
  207. var   Cnt: FiftyType;
  208. begin
  209.   Cnt := 0;
  210.   with CC do
  211.     while RepeatMove(MovTab[Depth - Cnt]) do
  212.       Inc(Cnt);
  213.   FiftyMoveCnt := Cnt;
  214. end;
  215. { Calculates how many times the position has occured before.
  216.   The game is a Draw when Repetition = 3.
  217.   MovTab[Back..Depth] contains the previous moves.
  218.   When Immediate is set, only Immediate Repetition is checked }
  219. function Repetition(Immediate: Boolean): RepeatType;
  220. var
  221.   LastDep,CompDep,TraceDep,CheckDep,SameDepth: DepthType;
  222.   TraceSq,CheckSq: SquareType;
  223.   RepeatCount: RepeatType;
  224. label 10;
  225. begin
  226.   with CC do
  227.   begin
  228.     Repetition := 1;
  229.     RepeatCount := 1;
  230.     SameDepth := Depth + 1;                           { Current position }
  231.     CompDep := SameDepth - 4;                { First position to compare }
  232.     LastDep := SameDepth;
  233.     { MovTab[LastDep..Depth] contains previous relevant moves  }
  234.     while RepeatMove(MovTab[LastDep - 1]) and
  235.         ((CompDep < LastDep) or not Immediate) do
  236.       Dec(LastDep);
  237.     if CompDep < LastDep then Exit;             { No Repetition Possible }
  238.     CheckDep := SameDepth;
  239.     repeat
  240.       Dec(CheckDep);                            { Get Next Move to test }
  241.       CheckSq := MovTab[CheckDep].New1;
  242.       TraceDep := CheckDep + 2;                { Check if Move has been }
  243.       while TraceDep < SameDepth do
  244.       begin
  245.         if MovTab[TraceDep].Old = CheckSq then goto 10;
  246.         Inc(TraceDep, 2);
  247.       end;
  248.       { Trace the Move backward to see whether
  249.         it has been 'undone' earlier }
  250.       TraceDep := CheckDep;
  251.       TraceSq := MovTab[TraceDep].Old;
  252.       repeat
  253.         if TraceDep - 2 < LastDep then Exit;
  254.         Dec(TraceDep, 2);
  255.         { Check if Piece has been moved before }
  256.         with MovTab[TraceDep] do
  257.           if TraceSq = New1 then
  258.             TraceSq := Old;
  259.       until (TraceSq = CheckSq) and (TraceDep <= CompDep + 1);
  260.       if TraceDep < CompDep then                   { Adjust evt. CompDep }
  261.       begin
  262.         CompDep := TraceDep;
  263.         if odd(SameDepth - CompDep) then
  264.         begin
  265.           if CompDep = LastDep then Exit;
  266.           Dec(CompDep);
  267.         end;
  268.         CheckDep := SameDepth;
  269.       end;
  270.       { All moves between SAMEDEP and CompDep have been checked,
  271.         so a Repetition is Found }
  272.   10: if CheckDep <= CompDep then
  273.       begin
  274.         Inc(RepeatCount);
  275.         Repetition := RepeatCount;
  276.         if CompDep - 2 < LastDep then Exit;
  277.         SameDepth := CompDep;              { Search for more repetitions }
  278.         Dec(CompDep, 2);
  279.         CheckDep := SameDepth;
  280.       end;
  281.     until False;
  282.   end;  { with CC^ }
  283. end { Repetition };
  284. { Tests whether a Move is Possible.
  285.    On entry :
  286.       Move contains a full description of a Move, which
  287.       has been legally generated in a different position.
  288.       MovTab[Depth - 1] contains Last performed Move.
  289.    On Exit :
  290.       KillMovGen indicates whether the Move is Possible }
  291. function KillMovGen(Move: MoveType): Boolean;
  292. var
  293.   CastSq: SquareType;
  294.   Promote: PieceType;
  295.   CastDir: CastDirType;
  296.   Cast: CastType;
  297. begin
  298.    KillMovGen := False;
  299.    with CC, Move do
  300.    begin
  301.      if Spe and (MovPiece = King) then
  302.      begin
  303.        { Castling }
  304.        CalcCastling(Player,Cast);
  305.        if New1 > Old then
  306.          CastDir := Short
  307.        else
  308.          CastDir := Long;
  309.        { Has King or Rook moved before? }
  310.        if CastDir in Cast then
  311.        begin
  312.          CastSq := (New1 + Old) div 2;
  313.          { Are the squares Empty? }
  314.          if (Board[New1   ].Piece = Empty) then
  315.            if (Board[CastSq].Piece = Empty) then
  316.              if ((New1 > Old) or (Board[New1 - 1 ].Piece = Empty)) then
  317.                { Are the squares unattacked? }
  318.                if not Attacks(Opponent,Old) then
  319.                  if not Attacks(Opponent,New1) then
  320.                    if not Attacks(Opponent,CastSq) then
  321.                      KillMovGen := True;
  322.        end;
  323.      end
  324.      else
  325.      if Spe and (MovPiece = Pawn) then
  326.      begin
  327.        { E.p. capture }
  328.        with MovTab[Depth - 1] do
  329.          { Was the Opponent's Move a 2 Square Move }
  330.          if MovPiece = Pawn then
  331.            if abs(New1 - Old) >= $20 then
  332.              { Is there a Piece On the Square? }
  333.              with Board[Move.Old] do
  334.                if (Piece = Pawn) and (Color = Player) then
  335.                  KillMovGen := Move.New1 = (New1 + Old) div 2;
  336.      end { if }
  337.      else
  338.      begin
  339.        if Spe then                                         { Normal test }
  340.        begin
  341.          Promote := MovPiece;                            { Pawnpromotion }
  342.          MovPiece := Pawn;
  343.        end;
  344.        { Is the Content of Old and New1 squares correct? }
  345.        if (Board[Old].Piece = MovPiece) and
  346.           (Board[Old].Color = Player) and
  347.           (Board[New1].Piece = Content) and
  348.          ((Content = Empty) or
  349.           (Board[New1].Color = Opponent)) then
  350.           { Is the Move Possible? }
  351.           if MovPiece = Pawn then
  352.             if Abs(New1 - Old) < $20 then
  353.               KillMovGen := True
  354.             else
  355.               KillMovGen := Board[(New1 + Old) div 2].Piece = Empty
  356.           else
  357.              KillMovGen := PieceAttacks(MovPiece,Player,Old,New1);
  358.        if Spe then
  359.          MovPiece := Promote;
  360.      end;
  361.   end; { with }
  362. end; { KillMovGen }
  363. { Movegeneration variables }
  364. { The move generator.
  365.   InitMovGen generates all Possible moves and places them
  366.   in a Buffer. MovGen will then Generate the moves One by One and
  367.   place them in Next.
  368.   On entry :
  369.      Player contains the Color to Move.
  370.      MovTab[Depth - 1] the Last performed Move.
  371.   On Exit :
  372.      Buffer contains the generated moves.
  373.      The moves are generated in the order :
  374.         Captures
  375.         Castlings
  376.         Non captures
  377.         E.p. captures }
  378. procedure InitMovGen;
  379.   { Stores a Move in Buffer }
  380.   procedure Generate;
  381.   begin
  382.     with CC do
  383.     begin
  384.       BufCount := BufCount + 1;
  385.       Buffer[BufCount] := NextMove;
  386.     end;
  387.   end; { Generate }
  388.   { Generates Pawnpromotion }
  389.   procedure PawnPromotionGen;
  390.   var
  391.     Promote: PieceType;
  392.   begin
  393.     with CC.NextMove do
  394.     begin
  395.       Spe := True;
  396.       for Promote := Queen to Knight do
  397.       begin
  398.         MovPiece := Promote;
  399.         Generate;
  400.       end;
  401.       Spe := False;
  402.     end;
  403.   end; { PawnPromotionGen }
  404.   { Generates captures of the Piece On New1 using PieceTab }
  405.   procedure CapMovGen;
  406.   var
  407.     NextSq,Sq: EdgeSquareType;
  408.     i:  IndexType;
  409.   begin
  410.     with CC, NextMove do
  411.     begin
  412.       Spe := False;
  413.       Content := Board[New1].Piece;
  414.       MovPiece := Pawn;                                  { Pawn captures }
  415.       NextSq := New1 - PawnDir[Player];
  416.       for Sq := NextSq - 1 to NextSq + 1 do if Sq <> NextSq then
  417.       if (Sq and $88) = 0 then
  418.         with Board[Sq] do
  419.           if (Piece = Pawn) and (Color = Player) then
  420.           begin
  421.             Old := Sq;
  422.             if (New1 < 8) or (New1 >= $70) then
  423.               PawnPromotionGen
  424.             else
  425.               Generate;
  426.           end;
  427.       { Other captures, starting with the smallest Pieces }
  428.       for i := OfficerNo[Player] downto 0 do
  429.         with PieceTab[Player,i] do
  430.           if (IPiece <> Empty) and (IPiece <> Pawn) then
  431.             if PieceAttacks(IPiece,Player,ISquare,New1) then
  432.             begin
  433.               Old := ISquare;
  434.               MovPiece := IPiece;
  435.               Generate;
  436.             end;
  437.         end { with };
  438.   end; { CapMovGen }
  439.   { Generates non captures for the Piece On Old }
  440.   procedure NonCapMovGen;
  441.   var
  442.     First,Last,Dir: DirType;
  443.     Direction: Integer;
  444.     NewSq: EdgeSquareType;
  445.   begin
  446.     with CC, NextMove do
  447.     begin
  448.       Spe := False;
  449.       MovPiece := Board[Old].Piece;
  450.       Content := Empty;
  451.       case MovPiece of
  452.         King:
  453.           for Dir := 7 downto 0 do
  454.           begin
  455.             NewSq := Old + DirTab[Dir];
  456.             if (NewSq and $88) = 0 then
  457.               if Board[NewSq].Piece = Empty then
  458.               begin
  459.                 New1 := NewSq;
  460.                 Generate;
  461.               end;
  462.           end;
  463.         Knight:
  464.           for Dir := 7 downto 0 do
  465.           begin
  466.             NewSq := Old + KnightDir[Dir];
  467.             if (NewSq and $88) = 0 then
  468.               if Board[NewSq].Piece = Empty then
  469.               begin
  470.                 New1 := NewSq;
  471.                 Generate;
  472.               end;
  473.           end;
  474.         Queen,
  475.         Rook,
  476.         Bishop:
  477.           begin
  478.             First := 7;
  479.             Last := 0;
  480.             if MovPiece = Rook   then First := 3;
  481.             if MovPiece = Bishop then Last := 4;
  482.             for Dir := First downto Last do
  483.             begin
  484.               Direction := DirTab[Dir];
  485.               NewSq := Old + Direction;
  486.               { Generate all non captures in
  487.                     the Direction }
  488.               while (NewSq and $88) = 0 do
  489.               begin
  490.                 if Board[NewSq].Piece <> Empty then Break;
  491.                 New1 := NewSq;
  492.                 Generate;
  493.                 NewSq := New1 + Direction;
  494.               end;
  495.             end;
  496.           end;
  497.         Pawn:
  498.           begin
  499.             New1 := Old + PawnDir[Player];          { One Square forward }
  500.             if Board[New1].Piece = Empty then
  501.               if (New1 < 8) or (New1 >= $70) then
  502.                 PawnPromotionGen
  503.               else
  504.               begin
  505.                 Generate;
  506.                 if (Old < $18) or (Old >= $60) then
  507.                 begin
  508.                   New1 := New1 + (New1 - Old);     { Two squares forward }
  509.                   if Board[New1].Piece = Empty then
  510.                     Generate;
  511.                 end;
  512.               end;
  513.           end;
  514.       end; { case }
  515.     end; { with }
  516.   end; { NonCapMovGen }
  517. var
  518.   CastDir: CastDirType;
  519.   Sq: EdgeSquareType;
  520.   Index: IndexType;
  521. begin { InitMovGen }
  522.   { Reset the Buffer }
  523.   with CC, NextMove do
  524.   begin
  525.     BufCount := 0;
  526.     BufPnt := 0;
  527.     { Generate all captures starting with captures of
  528.       largest Pieces }
  529.     for Index := 1 to PawnNo[Opponent] do
  530.       with PieceTab[Opponent,Index] do
  531.         if IPiece <> Empty then
  532.         begin
  533.           New1 := ISquare;
  534.           CapMovGen;
  535.         end;
  536.     { Castling }
  537.     Spe := True;
  538.     MovPiece := King;
  539.     Content := Empty;
  540.     for CastDir := Short downto Long do
  541.       with CastMove[Player,CastDir] do
  542.       begin
  543.         New1 := CastNew;
  544.         Old := CastOld;
  545.         if KillMovGen(NextMove) then Generate;
  546.       end;
  547.     { Generate non captures, starting with pawns }
  548.     for Index := PawnNo[Player] downto 0 do
  549.       with PieceTab[Player,Index] do
  550.         if IPiece <> Empty then
  551.         begin
  552.           Old := ISquare;
  553.           NonCapMovGen;
  554.         end;
  555.     { E.p. captures }
  556.     with MovTab[Depth - 1] do
  557.       if MovPiece = Pawn then
  558.         if Abs(New1 - Old) >= $20 then
  559.         begin
  560.           NextMove.Spe := True;
  561.           NextMove.MovPiece := Pawn;
  562.           NextMove.Content := Empty;
  563.           NextMove.New1 := (New1 + Old) div 2;
  564.           for Sq := New1 - 1 to New1 + 1 do
  565.             if Sq <> New1 then
  566.               if (Sq and $88) = 0 then
  567.               begin
  568.                 NextMove.Old := Sq;
  569.                 if KillMovGen(NextMove) then Generate;
  570.               end;
  571.         end;
  572.   end; { with }
  573. end; { InitMovGen }
  574. { Place Next Move from the Buffer in Next.
  575.   Generate ZeroMove when there is No more moves }
  576. procedure MovGen;
  577. begin
  578.   with CC do
  579.   begin
  580.     if BufPnt >= BufCount then
  581.        NextMove := ZeroMove
  582.     else
  583.     begin
  584.        BufPnt := BufPnt + 1;
  585.        NextMove := Buffer[BufPnt];
  586.     end;
  587.   end;
  588. end; { MovGen }
  589. end.