BitBaseResultFull bitbaseComputeStaticResult(Sq pawnSq, Sq wKingSq, Colour stm, Sq bKingSq) { // Sanity checks. assert(sqIsValid(pawnSq) && sqFile(pawnSq)<=FileD); assert(sqIsValid(wKingSq)); assert(colourIsValid(stm)); assert(sqIsValid(bKingSq)); // Init. BB wKingAtks=attacksKing(wKingSq); BB bKingAtks=attacksKing(bKingSq); BB pawnAtks=attacksPawn(pawnSq, ColourWhite); BB wKingBB=bbSq(wKingSq); BB bKingBB=bbSq(bKingSq); BB pawnBB=bbSq(pawnSq); BB occ=(pawnBB | wKingBB | bKingBB); // If any pieces occupy the same square, or the side not on move is in check, invalid position. if (pawnSq==wKingSq || pawnSq==bKingSq || wKingSq==bKingSq || // Pieces overlap. sqRank(pawnSq)==Rank1 || // Pawn on rank 1. (wKingAtks & bKingBB)!=BBNone || // Kings are adjacent. (stm==ColourWhite && (pawnAtks & bKingBB)!=BBNone)) // White to move, black king attacked by pawn. return BitBaseResultFullInvalid; // If pawn can promote without capture, win. if (sqRank(pawnSq)==Rank7 && stm==ColourWhite) { Sq promoSq=sqNorth(pawnSq,1); if (promoSq!=wKingSq && promoSq!=bKingSq && // Promotion square empty. ((bbSq(promoSq) & bKingAtks)==BBNone || (bbSq(promoSq) & wKingAtks)!=BBNone)) // Promotion square is safe. return BitBaseResultFullWin; } // If black can capture pawn, draw. bool pawnAttacked=((bKingAtks & pawnBB)!=BBNone); bool pawnDefended=((wKingAtks & pawnBB)!=BBNone); if (stm==ColourBlack && pawnAttacked && !pawnDefended) return BitBaseResultFullDraw; // If 'pawn' is on 8th rank, win (we have already shown that it cannot be captured). if (sqRank(pawnSq)==Rank8) return BitBaseResultFullWin; // If no moves available for stm, draw (stalemate). if (stm==ColourWhite) { BB safe=~(bKingAtks | occ); if ((wKingAtks & safe)==BBNone && // No king moves. (sqNorth(pawnSq,1)==wKingSq || sqNorth(pawnSq,1)==bKingSq)) // No pawn moves. return BitBaseResultFullDraw; } else { BB safe=~(wKingAtks | pawnAtks | occ); // We already check if black can capture pawn, hence assume defended. if ((bKingAtks & safe)==BBNone) return BitBaseResultFullDraw; } // Unable to statically evaluate the position. return BitBaseResultFullUnknown; }
void posDraw(const Pos *pos) { // Header. uciWrite("Position:\n"); // Board with pieces. int file, rank; for(rank=Rank8;rank>=Rank1;--rank) { uciWrite("%i|", (rank+8)-Rank8); for(file=FileA;file<=FileH;++file) uciWrite(" %c", pieceToChar(posGetPieceOnSq(pos, sqMake(file,rank)))); uciWrite("\n"); } uciWrite(" ----------------\n"); uciWrite(" "); for(file=FileA;file<=FileH;++file) uciWrite(" %c", (file+'a')-FileA); uciWrite("\n"); // Other information. uciWrite("STM: %s\n", colourToStr(posGetSTM(pos))); uciWrite("Castling rights: %s\n", posCastRightsToStr(posGetCastRights(pos))); if (pos->data->epSq!=SqInvalid) uciWrite("EP-sq: %c%c\n", fileToChar(sqFile(pos->data->epSq)), rankToChar(sqRank(pos->data->epSq))); else uciWrite("EP-sq: -\n"); uciWrite("Half move number: %u\n", pos->data->halfMoveNumber); uciWrite("Full move number: %u\n", pos->fullMoveNumber); uciWrite("Base hash key: %016"PRIxKey"\n", posGetKey(pos)); uciWrite("Pawn hash key: %016"PRIxKey"\n", posGetPawnKey(pos)); uciWrite("Material hash key: %016"PRIxKey"\n", posGetMatKey(pos)); }
BitBaseResult bitbaseProbeRaw(Sq pawnSq, Sq wKingSq, Sq bKingSq, Colour stm) { // Sanity checks. assert(sqIsValid(pawnSq)); assert(sqIsValid(wKingSq)); assert(sqIsValid(bKingSq)); assert(colourIsValid(stm)); // Adjust so pawn is in left half of board (on files A, B, C or D). File pawnFile=sqFile(pawnSq); Rank pawnRank=sqRank(pawnSq); if (pawnFile>FileD) { pawnFile=fileMirror(pawnFile); wKingSq=sqMirror(wKingSq); bKingSq=sqMirror(bKingSq); } // Probe. unsigned int index=bitbaseIndex(pawnFile, pawnRank, wKingSq, stm); return (bitbase[index]>>bKingSq)&1; }
BitBaseResultFull bitbaseComputeDynamicResult(const BitBaseResultFull *array, Sq pawnSq, Sq wKingSq, Colour stm, Sq bKingSq) { // Sanity checks. assert(sqIsValid(pawnSq) && sqFile(pawnSq)<=FileD && sqRank(pawnSq)>=Rank2 && sqRank(pawnSq)<=Rank7); assert(sqIsValid(wKingSq)); assert(colourIsValid(stm)); assert(sqIsValid(bKingSq)); // If white to move and any children are wins, win. If white to move and // all children are draws, draw. // If black to move and any children are draws, draw. If black to move // and all children are wins, win. File pawnFile=sqFile(pawnSq); Rank pawnRank=sqRank(pawnSq); Colour xstm=colourSwap(stm); if (stm==ColourWhite) { bool allDraws=true; // King moves. BB set=attacksKing(wKingSq); while(set) { unsigned int newIndex=bitbaseIndexFull(pawnFile, pawnRank, bbScanReset(&set), xstm, bKingSq); switch(array[newIndex]) { case BitBaseResultFullInvalid: break; // Invalid move. case BitBaseResultFullUnknown: allDraws=false; break; case BitBaseResultFullDraw: break; case BitBaseResultFullWin: return BitBaseResultFullWin; break; } } // Standard pawn move forward. // We do not need to test for moving to 8th rank or into either of the kings as such positions will be marked already. unsigned int newIndex=bitbaseIndexFull(pawnFile, pawnRank+1, wKingSq, xstm, bKingSq); bool singlePushOk=true; switch(array[newIndex]) { case BitBaseResultFullInvalid: singlePushOk=false; break; // Invalid move. case BitBaseResultFullUnknown: allDraws=false; break; case BitBaseResultFullDraw: break; case BitBaseResultFullWin: return BitBaseResultFullWin; break; } // Double pawn move. // Again we do not need to check for moving into either of the kings. if (pawnRank==Rank2 && singlePushOk) { unsigned int newIndex=bitbaseIndexFull(pawnFile, pawnRank+2, wKingSq, xstm, bKingSq); switch(array[newIndex]) { case BitBaseResultFullInvalid: break; // Invalid move. case BitBaseResultFullUnknown: allDraws=false; break; case BitBaseResultFullDraw: break; case BitBaseResultFullWin: return BitBaseResultFullWin; break; } } return (allDraws ? BitBaseResultFullDraw : BitBaseResultFullUnknown); } else { // King moves. bool allWins=true; BB set=attacksKing(bKingSq); while(set) { unsigned int newIndex=bitbaseIndexFull(pawnFile, pawnRank, wKingSq, xstm, bbScanReset(&set)); switch(array[newIndex]) { case BitBaseResultFullInvalid: break; // Invalid move. case BitBaseResultFullUnknown: allWins=false; break; case BitBaseResultFullDraw: return BitBaseResultFullDraw; break; case BitBaseResultFullWin: break; } } return (allWins ? BitBaseResultFullWin : BitBaseResultFullUnknown); } assert(false); return BitBaseResultFullInvalid; }
bool posLegalMoveExists(const Pos *pos, MoveType type) { Colour stm=posGetSTM(pos); BB occ=posGetBBAll(pos); BB opp=posGetBBColour(pos, colourSwap(stm)); BB allowed=BBNone; // Squares pieces can move to. if (type & MoveTypeQuiet) allowed|=~occ; if (type & MoveTypeCapture) allowed|=opp; // Pieces (ordered for speed). if (posLegalMoveExistsPiece(pos, PieceTypeKing, allowed) || posLegalMoveExistsPiece(pos, PieceTypeKnight, allowed) || posLegalMoveExistsPiece(pos, PieceTypeQueen, allowed) || posLegalMoveExistsPiece(pos, PieceTypeRook, allowed) || posLegalMoveExistsPiece(pos, PieceTypeBishopL, allowed) || posLegalMoveExistsPiece(pos, PieceTypeBishopD, allowed)) goto success; // Pawns. Piece piece=pieceMake(PieceTypePawn, stm); BB pawns=posGetBBPiece(pos, piece); BB forwardPawns=(bbForwardOne(pawns, stm) & ~occ); int delta; BB set; if (type & MoveTypeQuiet) { // Pawns - standard move forward. delta=(stm==ColourWhite ? 8 : -8); set=(forwardPawns & ~(stm==ColourWhite ? bbRank(Rank8) : bbRank(Rank1))); while(set) { Sq toSq=bbScanReset(&set); Sq fromSq=toSq-delta; Move move=moveMake(fromSq, toSq, piece); if (posCanMakeMove(pos, move)) goto success; } // Pawns - double first move. delta=(stm==ColourWhite ? 16 : -16); set=(bbForwardOne(forwardPawns, stm) & ~occ); set&=(stm==ColourWhite ? bbRank(Rank4) : bbRank(Rank5)); while(set) { Sq toSq=bbScanReset(&set); Sq fromSq=toSq-delta; Move move=moveMake(fromSq, toSq, piece); if (posCanMakeMove(pos, move)) goto success; } } if (type & MoveTypeCapture) { // Pawns - west captures. delta=(stm==ColourWhite ? 7 : -9); set=(bbWestOne(forwardPawns) & opp); while(set) { Sq toSq=bbScanReset(&set); Sq fromSq=toSq-delta; Piece toPiece=((sqRank(toSq)==Rank1 || sqRank(toSq)==Rank8) ? pieceMake(PieceTypeQueen, stm) : piece); Move move=moveMake(fromSq, toSq, toPiece); if (posCanMakeMove(pos, move)) goto success; } // Pawns - east captures. delta=(stm==ColourWhite ? 9 : -7); set=(bbEastOne(forwardPawns) & opp); while(set) { Sq toSq=bbScanReset(&set); Sq fromSq=toSq-delta; Piece toPiece=((sqRank(toSq)==Rank1 || sqRank(toSq)==Rank8) ? pieceMake(PieceTypeQueen, stm) : piece); Move move=moveMake(fromSq, toSq, toPiece); if (posCanMakeMove(pos, move)) goto success; } // Pawns - en-passent captures. if (pos->data->epSq!=SqInvalid) { Sq toSq=pos->data->epSq, fromSq; // Left capture. if (sqFile(pos->data->epSq)!=FileH && posGetPieceOnSq(pos, fromSq=sqEastOne(sqBackwardOne(toSq, stm)))==piece) { Move move=moveMake(fromSq, toSq, piece); if (posCanMakeMove(pos, move)) goto success; } // Right capture. if (sqFile(pos->data->epSq)!=FileA && posGetPieceOnSq(pos, fromSq=sqWestOne(sqBackwardOne(toSq, stm)))==piece) { Move move=moveMake(fromSq, toSq, piece); if (posCanMakeMove(pos, move)) goto success; } } } // No moves available. assert(posGenLegalMove(pos, type)==MoveInvalid); return false; // At least one move available. success: assert(posGenLegalMove(pos, type)!=MoveInvalid); return true; }
bool posMakeMove(Pos *pos, Move move) { assert(moveIsValid(move) || move==MoveNone); # ifndef NDEBUG bool canMakeMoveResult=posCanMakeMove(pos, move); # endif // Use next data entry. if (pos->data+1>=pos->dataEnd) { // We need more space. size_t size=2*(pos->dataEnd-pos->dataStart); PosData *ptr=realloc(pos->dataStart, size*sizeof(PosData)); if (ptr==NULL) return false; unsigned int dataOffset=pos->data-pos->dataStart; pos->dataStart=ptr; pos->dataEnd=ptr+size; pos->data=ptr+dataOffset; } ++pos->data; // Update generic fields. Sq fromSq=moveGetFromSq(move); Sq toSq=moveGetToSq(move); pos->data->lastMove=move; pos->data->halfMoveNumber=(pos->data-1)->halfMoveNumber+1; pos->data->epSq=SqInvalid; pos->data->key=(pos->data-1)->key^posKeySTM^posKeyEP[(pos->data-1)->epSq]; pos->data->cast=(pos->data-1)->cast; pos->data->movePiece=PieceNone; pos->data->capPiece=posGetPieceOnSq(pos, toSq); pos->data->capSq=toSq; pos->fullMoveNumber+=(pos->stm==ColourBlack); // Inc after black's move. pos->stm=colourSwap(pos->stm); if (move!=MoveNone) { Piece fromPiece=posGetPieceOnSq(pos, fromSq); pos->data->movePiece=fromPiece; // Update castling rights pos->data->cast&=posCastlingUpdate[fromSq] & posCastlingUpdate[toSq]; switch(pieceGetType(fromPiece)) { case PieceTypePawn: { // Pawns are complicated so deserve a special case. // En-passent capture? bool isEP=(sqFile(fromSq)!=sqFile(toSq) && pos->data->capPiece==PieceNone); if (isEP) { pos->data->capSq^=8; pos->data->capPiece=pieceMake(PieceTypePawn, pos->stm); assert(posGetPieceOnSq(pos, pos->data->capSq)==pos->data->capPiece); } // Capture? if (pos->data->capPiece!=PieceNone) // Remove piece. posPieceRemove(pos, pos->data->capSq); // Move the pawn, potentially promoting. Piece toPiece=moveGetToPiece(move); if (toPiece!=fromPiece) posPieceMoveChange(pos, fromSq, toSq, toPiece); else posPieceMove(pos, fromSq, toSq); // Pawn moves reset 50 move counter. pos->data->halfMoveNumber=0; // If double pawn move check set EP capture square (for next move). if (abs(sqRank(toSq)-sqRank(fromSq))==2) { Sq epSq=toSq^8; if (posIsEPCap(pos, epSq)) pos->data->epSq=epSq; } } break; case PieceTypeKing: // Castling. if (sqFile(toSq)==sqFile(fromSq)+2) posPieceMove(pos, toSq+1, toSq-1); // Kingside. else if (sqFile(toSq)==sqFile(fromSq)-2) posPieceMove(pos, toSq-2, toSq+1); // Queenside. // Fall through to move king. default: // Capture? if (pos->data->capPiece!=PieceNone) { // Remove piece. posPieceRemove(pos, toSq); // Captures reset 50 move counter. pos->data->halfMoveNumber=0; } // Move non-pawn piece (i.e. no promotion to worry about). posPieceMove(pos, fromSq, toSq); break; } // Does move leave STM in check? if (posIsXSTMInCheck(pos)) { posUndoMove(pos); assert(!canMakeMoveResult); return false; } // Update key. pos->data->key^=posKeyCastling[pos->data->cast^(pos->data-1)->cast]^posKeyEP[pos->data->epSq]; } assert(posIsConsistent(pos)); assert(canMakeMoveResult); return true; }
bool moveIsDP(Move move) { assert(moveIsValid(move)); int toRank=sqRank(moveGetToSqRaw(move)); int fromRank=sqRank(moveGetFromSq(move)); return (moveGetToPieceType(move)==PieceTypePawn && abs(toRank-fromRank)==2); }