home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l040 / 10.ddi / CHESS.ZIP / LMOVEGEN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-27  |  18.4 KB  |  654 lines

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