// Initialize board with given string // NOTE: Last two fields of FEN (half-move count and move number) are optional. This allows // Function to work with a string consisting of the first four fields of an EPD record fenErr_t setBoard(board_t *brd, const char *FEN) { // Local copy of a board upon which to do all operations... board_t b; // Index into the 64 squares of the board... int index = 0; setBoardEmpty(&b); if (FEN == NULL) FEN = startString; // parse piece placement field... while(index < 64) { switch(*FEN) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': // Skip over the number of squares index += (*FEN - '0'); break; // For each possible piece, add it at the given offset... case 'B': addPiece(&b, index++, BISHOP, WHITE); break; case 'K': addPiece(&b, index++, KING, WHITE); break; case 'N': addPiece(&b, index++, KNIGHT, WHITE); break; case 'P': addPiece(&b, index++, PAWN, WHITE); break; case 'Q': addPiece(&b, index++, QUEEN, WHITE); break; case 'R': addPiece(&b, index++, ROOK, WHITE); break; case 'b': addPiece(&b, index++, BISHOP, BLACK); break; case 'k': addPiece(&b, index++, KING, BLACK); break; case 'n': addPiece(&b, index++, KNIGHT, BLACK); break; case 'p': addPiece(&b, index++, PAWN, BLACK); break; case 'q': addPiece(&b, index++, QUEEN, BLACK); break; case 'r': addPiece(&b, index++, ROOK, BLACK); break; // This is just to make the string more readable... just ignore. case '/': break; // Any other characters denote an error default: return FEN_ILLEGAL_CHARACTER_FIELD_1; break; } FEN++; // move to next character. } // Make sure we counted 64 positions if(index != 64) return FEN_PARSE_ERR_FIELD_1; // Make sure we have no more characters in FIELD 1 if(*FEN != ' ') return FEN_PARSE_ERR_FIELD_1; // Move pointer to start of field 2 FEN++; // Should be either w or b if(*FEN == 'w') { // Update the position hash b.toMove = WHITE; b.hash ^= Z_WHITE_TURN_KEY; } else if (*FEN == 'b') { b.toMove = BLACK; } else if (*FEN == ' ') { // too many spaces between.... return FEN_BAD_SEPARATOR; } else { // found an unexpected character return FEN_INVALID_FIELD_2; } // Move to separator between field 2 and 3 FEN++; // If not a space, we have an error because field 2 is longer than 1 byte if(*FEN != ' ') return FEN_INVALID_FIELD_2; // Move to start of field 3 (castling squares) FEN++; // check for too many spaces.... if(*FEN == ' ') { return FEN_BAD_SEPARATOR; } // if a dash, all castle bits should be zero if(*FEN == '-') { FEN++; // make sure we have a proper separator. if (*FEN != ' ') { return FEN_INVALID_FIELD_3; } } // otherwise, check for valid castle bits // NOTE: the following allows bits to occur in any order. May not be // strictly allowable by FEN specification. // TODO CHESS960 else { while(*FEN != ' ') { if(*FEN == 'K' && !(b.castleBits & WHITE_CASTLE_SHORT) ) { b.castleBits |= WHITE_CASTLE_SHORT; b.hash ^= Z_WHITE_SHORT_KEY; } else if(*FEN == 'Q' && !(b.castleBits & WHITE_CASTLE_LONG) ) { b.castleBits |= WHITE_CASTLE_LONG; b.hash ^= Z_WHITE_LONG_KEY; } else if(*FEN == 'k' && !(b.castleBits & BLACK_CASTLE_SHORT) ) { b.castleBits |= BLACK_CASTLE_SHORT; b.hash ^= Z_BLACK_SHORT_KEY; } else if(*FEN == 'q' && !(b.castleBits & BLACK_CASTLE_LONG) ) { b.castleBits |= BLACK_CASTLE_LONG; b.hash ^= Z_BLACK_LONG_KEY; } else { return FEN_INVALID_FIELD_3; } FEN++; } } // Separator already confirmed at this point FEN++; // Check for no enPassant... if(*FEN == '-') { FEN++; } // else should be a-f else if(*FEN >='a' && *FEN <= 'h') { int targetSquare; b.enPassantCol = *FEN - 'a'; targetSquare = b.enPassantCol + (b.toMove == WHITE ? 40 : 16); if( (shiftE(squareMask[targetSquare]) & b.pieces[PAWN] & b.colors[b.toMove == WHITE ? BLACK : WHITE]) || (shiftW(squareMask[targetSquare]) & b.pieces[PAWN] & b.colors[b.toMove == WHITE ? BLACK : WHITE])) { b.zobristEnPassantCol = *FEN - 'a'; b.hash ^= Z_ENPASSANT_COL_KEY(b.enPassantCol); } FEN++; if( !(*FEN == '3' && b.toMove == BLACK) && !(*FEN == '6' && b.toMove == WHITE) ) { return FEN_INVALID_FIELD_4; } else { FEN++; } } else if (*FEN == ' ') { return FEN_BAD_SEPARATOR; } else { return FEN_INVALID_FIELD_4; } // At this point, and EPD record will end. What follows then is optional.... // If string ends here, leave defaults... if(*FEN != '\0') { b.moveNumber = 0; // Since string didn't end, assume moveNumber will be included. // Set to zero so logic below will work correctly... // Move to next character FEN++; // Should be a separator if(*FEN == ' ') { return FEN_BAD_SEPARATOR; } // This is the half-move counter... should be a digit... if(*FEN <'0' && *FEN >'9') { return FEN_INVALID_FIELD_5; } while(1) { b.halfMoves *= 10; b.halfMoves += *FEN - '0'; FEN++; if(*FEN == ' ') break; if(*FEN <'0' || *FEN >'9') { return FEN_INVALID_FIELD_5; } } // We hit a separator, so move to next character FEN++; // This is the first digit of the move number... should be a non-zero digit... if(*FEN <'1' || *FEN >'9') { return FEN_INVALID_FIELD_6; } while(1) { b.moveNumber *= 10; b.moveNumber += *FEN - '0'; FEN++; if(*FEN == '\0') break; if(*FEN <'0' || *FEN >'9') { return FEN_INVALID_FIELD_6; } } } memcpy(brd, &b, sizeof(board_t)); positionHistory[0] = brd->hash; positionIndex = 1; return FEN_OK; }
U64 knightSEE(){ return shiftSE(shiftE(pieces[KNIGHT])); }
// Move assumed valid for this board. // Add/remove functions will ASSERT adds are to empty squares and removes have expected piece there... // Castling moves assumed legal (if to/from square and piece moved match a castle for king), rook is also moved with king revMove_t move(board_t *b, const move_t m) { unsigned char to = m.to; unsigned char from = m.from; int offset = 0; revMove_t retValue; // A place holder for the side to move (saves repeated pointer access) color_t toMove = b->toMove; // A place to hold the "opposite color" calculation color_t oppositeColor = (toMove == WHITE ? BLACK : WHITE); // promotion piece (if any) piece_t promote = (piece_t)(m.promote); // handy marker for piece moved piece_t pieceMoved = ( (b->pieces[PAWN] & squareMask[from]) ? PAWN : ( (b->pieces[KNIGHT] & squareMask[from]) ? KNIGHT : ( (b->pieces[BISHOP] & squareMask[from]) ? BISHOP : ( (b->pieces[ROOK] & squareMask[from]) ? ROOK : ( (b->pieces[QUEEN] & squareMask[from]) ? QUEEN : KING))))); // Test that side to move has proper value ASSERT( (toMove == WHITE) || (toMove == BLACK) ); // Check that a piece of the side to move exists at the origin ASSERT( (b->colors[toMove] & squareMask[from]) != 0); // Prepare the return value... retValue.move.from = from; retValue.move.to = to; retValue.move.promote = promote; retValue.priorCastleBits = b->castleBits; retValue.priorEnPassant = b->enPassantCol; retValue.priorHalfMoveCnt = b->halfMoves; retValue.priorZobristEnPassantCol = b->zobristEnPassantCol; retValue.captured = b->colors[oppositeColor] & squareMask[to] ? ( (b->pieces[PAWN] & squareMask[to]) ? PAWN : ( (b->pieces[KNIGHT] & squareMask[to]) ? KNIGHT : ( (b->pieces[BISHOP] & squareMask[to]) ? BISHOP : ( (b->pieces[ROOK] & squareMask[to]) ? ROOK : QUEEN)))) : PIECE_NONE; // Check for capture enPassant... if( (pieceMoved == PAWN) && // moved a pawn ( (to % 8) != (from % 8) ) && // to and from files are different ( (squareMask[to] & b->colors[oppositeColor]) == 0) // target square doesn't have enemy piece ) { offset = (toMove == WHITE ? 8 : -8); retValue.captured = PAWN; } // First remove any captured piece from the board if(retValue.captured != PIECE_NONE) { removePiece(b, to + offset, retValue.captured, oppositeColor ); } if(promote == PIECE_NONE) { // Simply move the piece back movePiece(b, from, to, pieceMoved, toMove); } else { ASSERT( ( ( (to < 8) && toMove) == WHITE) || ( (to > 55) && toMove == BLACK ) ); ASSERT( pieceMoved == PAWN); // Remove pawn removePiece(b, from, pieceMoved, toMove); // Add promoted piece addPiece(b, to, promote, toMove); } // If this was a castling move, move the rook too... if(pieceMoved == KING) { if(from == E1) { if(to == C1) { movePiece(b, A1, D1, ROOK, toMove); } else if(to == G1) { movePiece(b, H1, F1, ROOK, toMove); } } else if(from == E8) { if(to == C8) { movePiece(b, A8, D8, ROOK, toMove); } else if(to == G8) { movePiece(b, H8, F8, ROOK, toMove); } } } // TODO CHESS960 // Adjust Castling possibilities if needed if( pieceMoved == KING ) { if( toMove == WHITE) { if(b->castleBits & WHITE_CASTLE_LONG) { b->hash ^= Z_WHITE_LONG_KEY; } if(b->castleBits & WHITE_CASTLE_SHORT) { b->hash ^= Z_WHITE_SHORT_KEY; } b->castleBits &= ~(WHITE_CASTLE_LONG | WHITE_CASTLE_SHORT); } else { if(b->castleBits & BLACK_CASTLE_LONG) { b->hash ^= Z_BLACK_LONG_KEY; } if(b->castleBits & BLACK_CASTLE_SHORT) { b->hash ^= Z_BLACK_SHORT_KEY; } b->castleBits &= ~(BLACK_CASTLE_LONG | BLACK_CASTLE_SHORT); } } if( pieceMoved == ROOK ) { if(from == A1 && b->castleBits & WHITE_CASTLE_LONG) { b->hash ^= Z_WHITE_LONG_KEY; b->castleBits &= ~WHITE_CASTLE_LONG; } else if (from == H1 && b->castleBits & WHITE_CASTLE_SHORT) { b->hash ^= Z_WHITE_SHORT_KEY; b->castleBits &= ~WHITE_CASTLE_SHORT; } else if(from == A8 && b->castleBits & BLACK_CASTLE_LONG) { b->hash ^= Z_BLACK_LONG_KEY; b->castleBits &= ~BLACK_CASTLE_LONG; } else if (from == H8 && b->castleBits & BLACK_CASTLE_SHORT) { b->hash ^= Z_BLACK_SHORT_KEY; b->castleBits &= ~BLACK_CASTLE_SHORT; } } // If a rook was captured, that affects castling too... if( retValue.captured == ROOK) { if( (to == A1) && (b->castleBits & WHITE_CASTLE_LONG) ) { b->hash ^= Z_WHITE_LONG_KEY; b->castleBits &= ~WHITE_CASTLE_LONG; } else if( (to == H1) && (b->castleBits & WHITE_CASTLE_SHORT) ) { b->hash ^= Z_WHITE_SHORT_KEY; b->castleBits &= ~WHITE_CASTLE_SHORT; } else if( (to == A8) && (b->castleBits & BLACK_CASTLE_LONG) ) { b->hash ^= Z_BLACK_LONG_KEY; b->castleBits &= ~BLACK_CASTLE_LONG; } else if( (to == H8) && (b->castleBits & BLACK_CASTLE_SHORT) ) { b->hash ^= Z_BLACK_SHORT_KEY; b->castleBits &= ~BLACK_CASTLE_SHORT; } } // Adjust halfmove count if( (pieceMoved == PAWN) || (retValue.captured != PIECE_NONE) ) { b->halfMoves = 0; } else { b->halfMoves++; } // Adjust move number if black just moved if(toMove == BLACK) { b->moveNumber++; } // Adjust side to move b->toMove = oppositeColor; b->hash ^= Z_WHITE_TURN_KEY; // Either side to move will trigger this toggle. // Adjust enPassant col if we moved a pawn forward two if ( (pieceMoved == PAWN) && (abs(from - to) == 16) ) { b->enPassantCol = to & 0x07; if( (shiftE(squareMask[to]) & b->pieces[PAWN] & b->colors[oppositeColor]) || (shiftW(squareMask[to]) & b->pieces[PAWN] & b->colors[oppositeColor])) { b->zobristEnPassantCol = to & 0x07; } else { b->zobristEnPassantCol = 8; } } else { b->enPassantCol = 8; b->zobristEnPassantCol = 8; } if(retValue.priorZobristEnPassantCol != b->zobristEnPassantCol) { if(retValue.priorZobristEnPassantCol != 8) { b->hash ^= Z_ENPASSANT_COL_KEY(retValue.priorZobristEnPassantCol); } if( b->zobristEnPassantCol != 8) { b->hash ^= Z_ENPASSANT_COL_KEY(b->zobristEnPassantCol); } } ASSERT(positionIndex < POSITION_HISTORY_SIZE); ASSERT(positionIndex >= 0); positionHistory[positionIndex++] = b->hash; if(positionIndex == POSITION_HISTORY_SIZE) { positionIndex = 0; } return retValue; }