Bitboard database code samples

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Bitboard database code samples

Post by sje »

Bitboard database code samples

For those with an interest in bitboard database coding, I'm presenting some code snippets from Symbolic. The source is all original other than the theft of some identifiers from the Slate and Atkin report of Chess 4.x written some forty years ago.

Symbolic's code is different from some other programs in that it maintains the full attack-to-square bitboard vector at each node, and that it makes use of pre-computed tables as much as possible for the various square scanning loops.

First, from the Bitboard class definition in the bitboard database header file:

Code: Select all

  void AttackAdd(const Sq sq, const Color color, const Man man);
  void AttackDel(const Sq sq, const Color color);
  void AttackPro(const Sq sq);
  void AttackCut(const Sq sq);

  Bitboard merge;             // All men merged
  Bitboard sweep;             // Sweeper men merged (bishops, rooks, queens)
  Bitboard locbm[ManRCLen];   // Locus by man
  Bitboard locbc[ColorRCLen]; // Locus by color
  Bitboard atkbc[ColorRCLen]; // Attacked by color
  Bitboard atkfs[SqLen];      // Attacks from a square
  Bitboard atkts[SqLen];      // Attacks to a square
The Reset() routine:

Code: Select all

void BBDB::Reset(void)
{
  merge.Reset();
  sweep.Reset();

  ForEachManRC(man)
    locbm[man].Reset();

  ForEachColorRC(color)
  {
    locbc[color].Reset();
    atkbc[color].Reset();
  };

  ForEachSq(sq)
  {
    atkfs[sq].Reset();
    atkts[sq].Reset();
  };
}
The AttackAdd() routine:

Code: Select all

void BBDB::AttackAdd(const Sq sq, const Color color, const Man man)
{
  BitboardPtr const atkfsptr = &atkfs[sq];
  BitboardPtr const atkbcptr = &atkbc[color];

  switch (CvManToPiece[man])
  {
    case PiecePawn:
      {
        const Sq *scanptr = PawnRunPtrs[color][sq];
        Sq afssq;

        while (IsSqNotNil(afssq = *scanptr++))
        {
          atkts[afssq].SetSq(sq);
          atkfsptr->SetSq(afssq);
          atkbcptr->SetSq(afssq);
        };
      };
      break;

    case PieceKnight:
      {
        const Sq *scanptr = KnightRunPtrs[sq];
        Sq afssq;

        while (IsSqNotNil(afssq = *scanptr++))
        {
          atkts[afssq].SetSq(sq);
          atkfsptr->SetSq(afssq);
          atkbcptr->SetSq(afssq);
        };
      };
      break;

    case PieceBishop:
    case PieceRook:
    case PieceQueen:
      {
        const Dir dir1 = CvManToDir1[man];
        
        for &#40;Dir dir = CvManToDir0&#91;man&#93;; dir <= dir1; IncrDir&#40;dir&#41;)
          if &#40;Bitboard&#58;&#58;ShadowBBVec&#91;dir&#93;.IsSqSet&#40;sq&#41;)
          &#123;
            const Bitboard stopbb = merge | Bitboard&#58;&#58;EdgeBBVec&#91;dir&#93;;
            const si delta = CvDirToSqDelta&#91;dir&#93;;
            Sq afssq = sq;

            do
            &#123;
              ApplySqDelta&#40;afssq, delta&#41;;
              atkts&#91;afssq&#93;.SetSq&#40;sq&#41;;
              atkfsptr->SetSq&#40;afssq&#41;;
              atkbcptr->SetSq&#40;afssq&#41;;
            &#125; while &#40;stopbb.IsSqReset&#40;afssq&#41;);
          &#125;;
      &#125;;
      break;

    case PieceKing&#58;
      &#123;
        const Sq *scanptr = KingRunPtrs&#91;sq&#93;;
        Sq afssq;

        while &#40;IsSqNotNil&#40;afssq = *scanptr++))
        &#123;
          atkts&#91;afssq&#93;.SetSq&#40;sq&#41;;
          atkfsptr->SetSq&#40;afssq&#41;;
          atkbcptr->SetSq&#40;afssq&#41;;
        &#125;;
      &#125;;
      break;

    default&#58;
      SwitchFault&#40;"BBDB&#58;&#58;AttackAdd");
      break;
  &#125;;
&#125;
The AttackDel() routine:

Code: Select all

void BBDB&#58;&#58;AttackDel&#40;const Sq sq, const Color color&#41;
&#123;
  const Bitboard locbb = locbc&#91;color&#93;;
  BitboardPtr const atkbcptr = &atkbc&#91;color&#93;;
  Sq atssq;

  while &#40;IsSqNotNil&#40;atssq = atkfs&#91;sq&#93;.NextSq&#40;)))
  &#123;
    atkts&#91;atssq&#93;.ResetSq&#40;sq&#41;;
    if (&#40;atkts&#91;atssq&#93; & locbb&#41;.IsReset&#40;))
      atkbcptr->ResetSq&#40;atssq&#41;;
  &#125;;
&#125;
The AttackPro() routine:

Code: Select all

void BBDB&#58;&#58;AttackPro&#40;const Sq sq&#41;
&#123;
  const Bitboard atrbb = atkts&#91;sq&#93; & sweep;

  ForEachColorRC&#40;atsco&#41;
  &#123;
    BitboardPtr const atkbcptr = &atkbc&#91;atsco&#93;;
    Bitboard atsbb = atrbb & locbc&#91;atsco&#93;;
    Sq atssq;

    while &#40;IsSqNotNil&#40;atssq = atsbb.NextSq&#40;)))
    &#123;
      const Dir dir = SqSqDir&#91;atssq&#93;&#91;sq&#93;;

      if &#40;Bitboard&#58;&#58;ShadowBBVec&#91;dir&#93;.IsSqSet&#40;sq&#41;)
      &#123;
        BitboardPtr const atkfsptr = &atkfs&#91;atssq&#93;;
        const Bitboard stopbb = merge | Bitboard&#58;&#58;EdgeBBVec&#91;dir&#93;;
        const si delta = CvDirToSqDelta&#91;dir&#93;;
        Sq raysq = sq;

        do
        &#123;
          ApplySqDelta&#40;raysq, delta&#41;;
          atkts&#91;raysq&#93;.SetSq&#40;atssq&#41;;
          atkfsptr->SetSq&#40;raysq&#41;;
          atkbcptr->SetSq&#40;raysq&#41;;
        &#125;
        while &#40;stopbb.IsSqReset&#40;raysq&#41;);
      &#125;;
    &#125;;
  &#125;;
&#125;
The AttackCut() routine:

Code: Select all

void BBDB&#58;&#58;AttackPro&#40;const Sq sq&#41;
&#123;
  const Bitboard atrbb = atkts&#91;sq&#93; & sweep;

  ForEachColorRC&#40;atsco&#41;
  &#123;
    BitboardPtr const atkbcptr = &atkbc&#91;atsco&#93;;
    Bitboard atsbb = atrbb & locbc&#91;atsco&#93;;
    Sq atssq;

    while &#40;IsSqNotNil&#40;atssq = atsbb.NextSq&#40;)))
    &#123;
      const Dir dir = SqSqDir&#91;atssq&#93;&#91;sq&#93;;

      if &#40;Bitboard&#58;&#58;ShadowBBVec&#91;dir&#93;.IsSqSet&#40;sq&#41;)
      &#123;
        BitboardPtr const atkfsptr = &atkfs&#91;atssq&#93;;
        const Bitboard stopbb = merge | Bitboard&#58;&#58;EdgeBBVec&#91;dir&#93;;
        const si delta = CvDirToSqDelta&#91;dir&#93;;
        Sq raysq = sq;

        do
        &#123;
          ApplySqDelta&#40;raysq, delta&#41;;
          atkts&#91;raysq&#93;.SetSq&#40;atssq&#41;;
          atkfsptr->SetSq&#40;raysq&#41;;
          atkbcptr->SetSq&#40;raysq&#41;;
        &#125;
        while &#40;stopbb.IsSqReset&#40;raysq&#41;);
      &#125;;
    &#125;;
  &#125;;
&#125;
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Sorry, wrong cut-n-paste for AttackCut()

Post by sje »

Sorry, wrong cut-n-paste for AttackCut(). The Real thing:

Code: Select all

void BBDB&#58;&#58;AttackCut&#40;const Sq sq&#41;
&#123;
  const Bitboard atrbb = atkts&#91;sq&#93; & sweep;

  ForEachColorRC&#40;atsco&#41;
  &#123;
    const Bitboard locbb = locbc&#91;atsco&#93;;
    BitboardPtr const atkbcptr = &atkbc&#91;atsco&#93;;
    Bitboard atsbb = atrbb & locbb;
    Sq atssq;

    while &#40;IsSqNotNil&#40;atssq = atsbb.NextSq&#40;)))
    &#123;
      const Dir dir = SqSqDir&#91;atssq&#93;&#91;sq&#93;;

      if &#40;Bitboard&#58;&#58;ShadowBBVec&#91;dir&#93;.IsSqSet&#40;sq&#41;)
      &#123;
        BitboardPtr const atkfsptr = &atkfs&#91;atssq&#93;;
        const Bitboard stopbb = merge | Bitboard&#58;&#58;EdgeBBVec&#91;dir&#93;;
        const si delta = CvDirToSqDelta&#91;dir&#93;;
        Sq raysq = sq;

        do
        &#123;
          ApplySqDelta&#40;raysq, delta&#41;;
          atkts&#91;raysq&#93;.ResetSq&#40;atssq&#41;;
          atkfsptr->ResetSq&#40;raysq&#41;;
          if (&#40;atkts&#91;raysq&#93; & locbb&#41;.IsReset&#40;))
            atkbcptr->ResetSq&#40;raysq&#41;;
        &#125;
        while &#40;stopbb.IsSqReset&#40;raysq&#41;);
      &#125;;
    &#125;;
  &#125;;
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Some routines to move men in the bitboard database

Post by sje »

Some routines to move men in the bitboard database

These routines are all inline:

Code: Select all

  void LocusToggle&#40;const Sq sq, const Color color, const Man man&#41;
  &#123;
    const Bitboard bb&#40;SingleBitBB&#40;sq&#41;);

    merge ^= bb;
    locbc&#91;color&#93; ^= bb;
    locbm&#91;man&#93; ^= bb;
    if &#40;IsManSweeper&#40;man&#41;)
      sweep ^= bb;
  &#125;

  void LocusChange&#40;const Sq frsq, const Sq tosq, const Color color, const Man man&#41;
  &#123;
    const Bitboard bb&#40;SingleBitBB&#40;frsq&#41; | SingleBitBB&#40;tosq&#41;);

    merge ^= bb;
    locbc&#91;color&#93; ^= bb;
    locbm&#91;man&#93; ^= bb;
    if &#40;IsManSweeper&#40;man&#41;)
      sweep ^= bb;
  &#125;

  void AddMan&#40;const Sq sq, const Color color, const Man man&#41;
  &#123;
    LocusToggle&#40;sq, color, man&#41;;

    AttackCut&#40;sq&#41;;
    AttackAdd&#40;sq, color, man&#41;;
  &#125;

  void DelMan&#40;const Sq sq, const Color color, const Man man&#41;
  &#123;
    LocusToggle&#40;sq, color, man&#41;;

    AttackDel&#40;sq, color&#41;;
    AttackPro&#40;sq&#41;;
  &#125;

  void MoveMan&#40;const Sq frsq, const Sq tosq, const Color color, const Man man&#41;
  &#123;
    LocusChange&#40;frsq, tosq, color, man&#41;;

    AttackDel&#40;frsq, color&#41;;
    AttackPro&#40;frsq&#41;;
    AttackCut&#40;tosq&#41;;
    AttackAdd&#40;tosq, color, man&#41;;
  &#125;

  void Capture&#40;const Sq frsq, const Sq tosq,
               const Color frcolor, const Color tocolor,
               const Man frman, const Man toman&#41;
  &#123;
    LocusToggle&#40;tosq, tocolor, toman&#41;;
    LocusChange&#40;frsq, tosq, frcolor, frman&#41;;

    AttackDel&#40;tosq, tocolor&#41;;
    AttackDel&#40;frsq, frcolor&#41;;
    AttackPro&#40;frsq&#41;;
    AttackAdd&#40;tosq, frcolor, frman&#41;;
  &#125;

  void RevCapt&#40;const Sq frsq, const Sq tosq,
               const Color frcolor, const Color tocolor,
               const Man frman, const Man toman&#41;
  &#123;
    LocusChange&#40;tosq, frsq, frcolor, frman&#41;;
    LocusToggle&#40;tosq, tocolor, toman&#41;;

    AttackDel&#40;tosq, frcolor&#41;;
    AttackCut&#40;frsq&#41;;
    AttackAdd&#40;frsq, frcolor, frman&#41;;
    AttackAdd&#40;tosq, tocolor, toman&#41;;
  &#125;
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Re: Sorry, wrong cut-n-paste for AttackCut()

Post by sje »

Oops, left off a closing brace. Please append the following:

Code: Select all

&#125;
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

The database, the board, and the tracker

Post by sje »

The database, the board, and the tracker

The routines presented above are all part of the BBDB (Bitboard Database) class. They are wholly separate from the Board (chessboard) class and the Tracker (inverse chessboard with history) classes. All three classes are siblings and live inside the Position class.

The idea was to be able to develop and test the classes separately and not to have one be dependent on any other. A couple of opportunities for minor optimizations were lost, but the result is clearer code; when a move is made or unmade, there are no restrictions on the update order of the three class instances.

----

Not shown is the Bitboard class. It is mostly what you'd expect except that it uses absolutely no assembly language. There is a self-setting compile time switch which selects either plain C++ code or GNU intrinsic functions; the program works both ways.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Shadow squares and run pointers

Post by sje »

In the attack routines above, there are various references to ShadowSqBBVec. This is an array of constant bitboards, one for each direction. Each bitboard contains the squares which are shadows of a sweep attack along a the direction to the square. A shadow square is the first square (if any) past the given square along the direction. The basic idea is to detect the case where a square is on the edge of the board AND does or does not have a retreat square along the direction.

Knowing about shadow squares is helpful in the above code for simplifying inner scanning loops or eliminating their execution entirely. The knowledge is also useful for generating check evasion moves.

----

In the AttackAdd() routine, there are references to the arrays PawnRunPtrs, KnightRunPtrs, and KingRunPtrs. These arrays contain pointers to the contents of pre-calculated lists of squares which give the target squares sequences, each ending with a SqNil value. This saves time by taking care of all the target square generation and edge detection work during program initialization.