home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Pascal / BPASCAL.700 / D11 / CHESSDLL.ZIP / LMOVEGEN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-01  |  19.7 KB  |  619 lines

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