Pre-announcement: Oscar Chess Library

Discussion of chess software programming and technical issues.

Moderator: Ras

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

Example internal routine: PositionExecute()

Post by sje »

Example internal routine: PositionExecute()

Code: Select all

static void PositionExecute(Position * const positionptr, const Move move)
{
  // This routine executes the given move for the given position.

  // Save the move

  if (positionptr->freemovelistptr->count == 0)
    MoveListAppendNode(positionptr->freemovelistptr, MoveNodeNew());

  MoveNode * const movenodeptr = MoveListDetachTail(positionptr->freemovelistptr);

  movenodeptr->move = move;
  MoveListAppendNode(positionptr->usedmovelistptr, movenodeptr);

  // Save the preserved position data

  if (positionptr->freeppdlistptr->count == 0)
    PpdListAppendNode(positionptr->freeppdlistptr, PpdNodeNew());

  PpdNode * const ppdnodeptr = PpdListDetachTail(positionptr->freeppdlistptr);

  ppdnodeptr->ppd.fss = positionptr->fss;
  ppdnodeptr->ppd.apd = positionptr->apd;
  ppdnodeptr->ppd.msig = positionptr->msig;
  PpdListAppendNode(positionptr->usedppdlistptr, ppdnodeptr);

  // Set up locals: pointer abbreviations

  Tracker * const trackerptr = &positionptr->tracker;
  Man * const manvec = positionptr->board.manvec;
  Hash * const msigptr = &positionptr->msig;

  // Set up locals: move components

  const bool isnotnull = (move != NullMove);
  const Sq frsq = GetFrSq(move), tosq = GetToSq(move);
  const Man frman = GetFrMan(move), toman = GetToMan(move);
  const Mc mc = GetMc(move);

  // Set up locals: FEN scalars

  const Color good = positionptr->fss.good, evil = positionptr->fss.evil;
  const Cabs cabs = positionptr->fss.cabs;
  const Sq epsq = positionptr->fss.epsq;

  // Process motion according to move special case

  if (isnotnull)
  {
    switch (mc)
    {
      case McReg:  // Regular move, no special handling
        if (IsManNotVacant(toman))
        {
          TrackerCapture(trackerptr, tosq);
          HashFoldManSq(msigptr, toman, tosq);
        };
        TrackerMovement(trackerptr, frsq, tosq);
        manvec[frsq] = ManVacant;
        manvec[tosq] = frman;
        HashFoldManSqSq(msigptr, frman, frsq, tosq);
        break;

      case McEPC:  // En passant capture
        {
          const Sq vpsq = NextSq[CvColorToAdvDir[evil]][epsq];

          TrackerCapture(trackerptr, vpsq);
          HashFoldManSq(msigptr, CvColorToPawn[evil], vpsq);
          TrackerMovement(trackerptr, frsq, tosq);
          manvec[vpsq] = ManVacant;
          manvec[frsq] = ManVacant;
          manvec[tosq] = frman;
          HashFoldManSqSq(msigptr, frman, frsq, tosq);
        };
        break;

      case McCQS:  // Castle queenside
      case McCKS:  // Castle kingside
        {
          const Castling castling = CvColorMcToCastling[good][mc];
          const Sq r0sq = CastInfo[castling].r0sq;
          const Sq r1sq = CastInfo[castling].r1sq;
          const Man rook = CastInfo[castling].rook;

          TrackerMovement(trackerptr, frsq, tosq);
          TrackerMovement(trackerptr, r0sq, r1sq);
          manvec[frsq] = ManVacant;
          manvec[tosq] = frman;
          HashFoldManSqSq(msigptr, frman, frsq, tosq);
          manvec[r0sq] = ManVacant;
          manvec[r1sq] = rook;
          HashFoldManSqSq(msigptr, rook, r0sq, r1sq);
        };
        break;

      case McPPN:  // Pawn promotes to knight
      case McPPB:  // Pawn promotes to bishop
      case McPPR:  // Pawn promotes to rook
      case McPPQ:  // Pawn promotes to queen
        {
          const Man newman = CvColorMcToMan[good][mc];

          if (IsManNotVacant(toman))
          {
            TrackerCapture(trackerptr, tosq);
            HashFoldManSq(msigptr, toman, tosq);
          };
          TrackerMovement(trackerptr, frsq, tosq);
          TrackerPromote(trackerptr, tosq, newman);
          manvec[frsq] = ManVacant;
          manvec[tosq] = newman;
          HashFoldManSq(msigptr, frman, frsq);
          HashFoldManSq(msigptr, newman, tosq);
        };
        break;

      default:
        break;
    };
  };

  // Update: Colors

  positionptr->fss.good = evil;
  positionptr->fss.evil = good;

  // Update: Castling availability bits

  if (isnotnull && (cabs != CabsNone))
  {
    positionptr->fss.cabs = (cabs & CabsPreservation[frsq] & CabsPreservation[tosq]);
    if (cabs != positionptr->fss.cabs)
    {
      HashFoldCabs(msigptr, cabs);
      HashFoldCabs(msigptr, positionptr->fss.cabs);
    };
  };

  // Update: En passant target square

  if (IsSqNotNil(epsq))
    HashFoldEpFile(msigptr, MapSqToFile(epsq));
  positionptr->fss.epsq = SqNil;

  if (isnotnull && IsManPawn(frman) && ((frsq ^ tosq) == (FileLen * 2)))
  {
    const Sq candepsq = NextSq[CvColorToAdvDir[good]][frsq];

    if (PositionTestEpSqCandPhase1(positionptr, candepsq))
    {
      positionptr->fss.epsq = candepsq;
      HashFoldEpFile(msigptr, MapSqToFile(positionptr->fss.epsq));
    };
  };

  // Update: Half move counter

  if (isnotnull && (IsManPawn(frman) || IsManNotVacant(toman)))
    positionptr->fss.hmvc = 0;
  else
    positionptr->fss.hmvc++;

  // Update: Full move number

  if (IsColorWhite(positionptr->fss.good))
    positionptr->fss.fmvn++;

  // Regenerate auxiliary position data

  PositionRegenerateApd(positionptr);
}
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Example internal routine: PositionRetract()

Post by sje »

Example internal routine: PositionRetract():

Code: Select all

static void PositionRetract(Position * const positionptr)
{
  // This routine retracts the played move for the given position.

  // Restore the move

  MoveNode * const movenodeptr = MoveListDetachTail(positionptr->usedmovelistptr);
  const Move move = movenodeptr->move;

  MoveListAppendNode(positionptr->freemovelistptr, movenodeptr);

  // Restore the preserved position data

  PpdNode * const ppdnodeptr = PpdListDetachTail(positionptr->usedppdlistptr);

  positionptr->fss = ppdnodeptr->ppd.fss;
  positionptr->apd = ppdnodeptr->ppd.apd;
  positionptr->msig = ppdnodeptr->ppd.msig;
  PpdListAppendNode(positionptr->freeppdlistptr, ppdnodeptr);

  // Set up locals: pointer abbreviations

  Tracker * const trackerptr = &positionptr->tracker;
  Man * const manvec = positionptr->board.manvec;

  // Set up locals: move components

  const bool isnotnull = (move != NullMove);
  const Sq frsq = GetFrSq(move), tosq = GetToSq(move);
  const Man frman = GetFrMan(move), toman = GetToMan(move);
  const Mc mc = GetMc(move);

  // Set up locals: FEN scalars

  const Color good = positionptr->fss.good, evil = positionptr->fss.evil;
  const Sq epsq = positionptr->fss.epsq;

  // Process motion according to move special case

  if (isnotnull)
  {
    switch (mc)
    {
      case McReg:  // Regular move, no special handling
        TrackerMovement(trackerptr, tosq, frsq);
        if (IsManNotVacant(toman))
          TrackerRestore(trackerptr);
        manvec[frsq] = frman;
        manvec[tosq] = toman;
        break;

      case McEPC:  // En passant capture
        {
          const Sq vpsq = NextSq[CvColorToAdvDir[evil]][epsq];

          TrackerMovement(trackerptr, tosq, frsq);
          TrackerRestore(trackerptr);
          manvec[vpsq] = CvColorToPawn[evil];
          manvec[frsq] = frman;
          manvec[tosq] = ManVacant;
        };
        break;

      case McCQS:  // Castle queenside
      case McCKS:  // Castle kingside
        {
          const Castling castling = CvColorMcToCastling[good][mc];
          const Sq r0sq = CastInfo[castling].r0sq;
          const Sq r1sq = CastInfo[castling].r1sq;

          TrackerMovement(trackerptr, r1sq, r0sq);
          TrackerMovement(trackerptr, tosq, frsq);
          manvec[r0sq] = CastInfo[castling].rook;
          manvec[r1sq] = ManVacant;
          manvec[frsq] = frman;
          manvec[tosq] = ManVacant;
        };
        break;

      case McPPN:  // Pawn promotes to knight
      case McPPB:  // Pawn promotes to bishop
      case McPPR:  // Pawn promotes to rook
      case McPPQ:  // Pawn promotes to queen
        {
          const Man newman = CvColorMcToMan[good][mc];

          TrackerUnpromote(trackerptr, tosq, newman);
          TrackerMovement(trackerptr, tosq, frsq);
          if (IsManNotVacant(toman))
            TrackerRestore(trackerptr);
          manvec[frsq] = frman;
          manvec[tosq] = toman;
        };
        break;

      default:
        break;
    };
  };
}
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

The time-saving SqSqInfo array of InfoBits

Post by sje »

Oscar has an array SqSqInfo array of InfoBits:

Code: Select all

static InfoBits SqSqInfo[SqLen][SqLen];  // For each from-square, for each to-square: properties
Where InfoBits is an unsigned 32 bit integer with each bit giving the value of a some boolean function PropertyN(from-square, to-square). The contents of SqSqInfo are assigned during initialization and remain constant thereafter. Oscar gets a big speed improvement by avoiding the need to calculate simple property values more than once.

Currently, only 24 of the bits have a property function assignment; eight bits are just spare entries for now.

The property list:

Code: Select all

// SqSqInfo[frsq][tosq] array element coding

#define InfoHasDir BX(0x1f)  // Has a directional relationship; if 0 then all bits are 0
#define InfoAdjSqs BX(0x1e)  // Squares are adjacent
#define InfoResB1d BX(0x1d)  // Reserved bit 0x1d
#define InfoResB1c BX(0x1c)  // Reserved bit 0x1c
#define InfoResB1b BX(0x1b)  // Reserved bit 0x1b
#define InfoResB1a BX(0x1a)  // Reserved bit 0x1a
#define InfoResB19 BX(0x19)  // Reserved bit 0x19
#define InfoResB18 BX(0x18)  // Reserved bit 0x18
#define InfoResB17 BX(0x17)  // Reserved bit 0x17
#define InfoResB16 BX(0x16)  // Reserved bit 0x16
#define InfoSmRank BX(0x15)  // Squares are on same rank
#define InfoSmFile BX(0x14)  // Squares are on same file
#define InfoPromBP BX(0x13)  // Promotion: black pawn
#define InfoPromWP BX(0x12)  // Promotion: white pawn
#define InfoDAdvBP BX(0x11)  // Double square advance: black pawn
#define InfoDAdvWP BX(0x10)  // Double square advance: white pawn
#define InfoSAdvBP BX(0x0f)  // Single square advance: black pawn
#define InfoSAdvWP BX(0x0e)  // Single square advance: white pawn
#define InfoPathBP BX(0x0d)  // Forward path of black pawn on from square includes to square
#define InfoPathWP BX(0x0c)  // Forward path of white pawn on from square includes to square
#define InfoAtkfBP BX(0x0b)  // From square has a black pawn attack on the to square
#define InfoAtkfWP BX(0x0a)  // From square has a white pawn attack on the to square
#define InfoBdrBt1 BX(0x09)  // Bidirection bit 1
#define InfoBdrBt0 BX(0x08)  // Bidirection bit 0
#define InfoDOrtho BX(0x07)  // Direction is ortho
#define InfoDDiago BX(0x06)  // Direction is diago
#define InfoDCrook BX(0x05)  // Direction is crook
#define InfoDSweep BX(0x04)  // Direction is sweep
#define InfoDirBt3 BX(0x03)  // Direction bit 3
#define InfoDirBt2 BX(0x02)  // Direction bit 2
#define InfoDirBt1 BX(0x01)  // Direction bit 1
#define InfoDirBt0 BX(0x00)  // Direction bit 0
A few helpful macros:

Code: Select all

#define MapInfoBitsToDir(infobits)   ((Dir)   (((infobits) >> 0) & MX(4)))
#define MapInfoBitsToBidir(infobits) ((Bidir) (((infobits) >> 8) & MX(2)))

#define InfoIpMaskWP (InfoSAdvWP | InfoDAdvWP)
#define InfoIpMaskBP (InfoSAdvBP | InfoDAdvBP)

#define InfoSAdvMask (InfoSAdvWP | InfoSAdvBP)
#define InfoDAdvMask (InfoDAdvWP | InfoDAdvBP)
#define InfoPromMask (InfoPromWP | InfoPromBP)
Example usage:

Code: Select all

static bool PositionSqAttacksSq(const Position * const positionptr, const Sq frsq, const Sq tosq)
{
  // This routine tests for an attack from one square to another in the given position.

  bool result = false;
  const InfoBits infobits = SqSqInfo[frsq][tosq];

  if (infobits)
  {
    switch (positionptr->board.manvec[frsq])
    {
      case ManWhitePawn:
        if (infobits & InfoAtkfWP)
          result = true;
        break;

      case ManBlackPawn:
        if (infobits & InfoAtkfBP)
          result = true;
        break;

      case ManWhiteKnight:
      case ManBlackKnight:
        if (infobits & InfoDCrook)
          result = true;
        break;

      case ManWhiteBishop:
      case ManBlackBishop:
        if ((infobits & InfoDDiago) &&
            ((infobits & InfoAdjSqs) ||
             BoardTestClearPath(&positionptr->board, MapInfoBitsToDir(infobits), frsq, tosq)))
          result = true;
        break;

      case ManWhiteRook:
      case ManBlackRook:
        if ((infobits & InfoDOrtho) &&
            ((infobits & InfoAdjSqs) ||
             BoardTestClearPath(&positionptr->board, MapInfoBitsToDir(infobits), frsq, tosq)))
          result = true;
        break;

      case ManWhiteQueen:
      case ManBlackQueen:
        if ((infobits & InfoDSweep) &&
            ((infobits & InfoAdjSqs) ||
             BoardTestClearPath(&positionptr->board, MapInfoBitsToDir(infobits), frsq, tosq)))
          result = true;
        break;

      case ManWhiteKing:
      case ManBlackKing:
        if (infobits & InfoAdjSqs)
          result = true;
        break;

      default:
        break;
    };
  };
  return result;
}