extern void makeMove(Board_t self, int move) { int to = to(move), from = from(move); pushList(self->hashHistory, self->hash); pushList(self->pkHashHistory, self->pawnKingHash); pushList(self->materialHistory, self->materialKey); preparePushList(self->undoStack, maxMoveUndo); signed char *sp = &self->undoStack.v[self->undoStack.len]; *sp++ = sentinel; #define push(offset, value) Statement( \ *sp++ = (value); \ *sp++ = (offset); \ ) #define makeSimpleMove(from, to) Statement( \ int _piece = self->squares[from]; \ int _victim = self->squares[to]; \ \ /* Update the undo stack */ \ push(from, _piece); \ push(to, _victim); /* last for recaptureSquare */ \ \ /* Make the simple move */ \ self->squares[to] = _piece; \ self->squares[from] = empty; \ \ /* Update the incremental hash */ \ self->hash ^= zobristPiece[_piece][from] \ ^ zobristPiece[_piece][to] \ ^ zobristPiece[_victim][to]; \ \ /* And the pawn/king/etc hash */ \ self->pawnKingHash ^= subHash[_piece][from] \ ^ subHash[_piece][to] \ ^ subHash[_victim][to]; \ \ /* Update the material key */ \ self->materialKey -= materialKeys[_victim][squareColor(to)];\ ) // Always clear en passant info if (self->enPassantPawn) { push(offsetof_enPassantPawn, self->enPassantPawn); self->hash ^= hashEnPassant(self->enPassantPawn); self->enPassantPawn = 0; } // Handle special moves first if (move & specialMoveFlag) { switch (rank(from)) { case rank8: // Black castles. Insert the corresponding rook move if (to == g8) makeSimpleMove(h8, f8); else makeSimpleMove(a8, d8); break; case rank7: if (self->squares[from] == blackPawn) { // Set en passant flag push(offsetof_enPassantPawn, 0); self->enPassantPawn = to; self->hash ^= hashEnPassant(to); } else { push(from, self->squares[from]); // White promotes int promoPiece = whiteQueen + ((move >> promotionBits) & 3); self->squares[from] = promoPiece; self->hash ^= zobristPiece[whitePawn][from] ^ zobristPiece[promoPiece][from]; self->pawnKingHash ^= zobristPiece[whitePawn][from]; self->materialKey += materialKeys[promoPiece][squareColor(to)] - materialKeys[whitePawn][0]; } break; case rank5: // White captures en passant case rank4: { // Black captures en passant int square = square(file(to), rank(from)); int victim = self->squares[square]; push(square, victim); self->squares[square] = empty; self->hash ^= zobristPiece[victim][square]; self->pawnKingHash ^= zobristPiece[victim][square]; self->materialKey -= materialKeys[victim][0]; break; } case rank2: if (self->squares[from] == whitePawn) { // Set en passant flag push(offsetof_enPassantPawn, 0); self->enPassantPawn = to; self->hash ^= hashEnPassant(to); } else { push(from, self->squares[from]); // Black promotes int promoPiece = blackQueen + ((move >> promotionBits) & 3); self->squares[from] = promoPiece; self->hash ^= zobristPiece[blackPawn][from] ^ zobristPiece[promoPiece][from]; self->pawnKingHash ^= zobristPiece[blackPawn][from]; self->materialKey += materialKeys[promoPiece][squareColor(to)] - materialKeys[blackPawn][0]; } break; case rank1: // White castles. Insert the corresponding rook move if (to == g1) makeSimpleMove(h1, f1); else makeSimpleMove(a1, d1); break; default: break; } } self->plyNumber++; self->hash ^= zobristTurn[0]; if (self->squares[to] != empty || self->squares[from] == whitePawn || self->squares[from] == blackPawn) { push(offsetof_halfmoveClock, self->halfmoveClock); // This is why it is a byte self->halfmoveClock = 0; } else self->halfmoveClock++; // Can overflow the byte, but don't worry int flagsToClear = (castleFlagsClear[from] | castleFlagsClear[to]) & self->castleFlags; if (flagsToClear) { push(offsetof_castleFlags, self->castleFlags); self->castleFlags ^= flagsToClear; uint64_t deltaHash = hashCastleFlags(flagsToClear); self->hash ^= deltaHash; self->pawnKingHash ^= deltaHash; } // The real move always as last makeSimpleMove(from, to); self->undoStack.len = sp - self->undoStack.v; // Finalize en passant (this is only safe after the update of self->undoStack.len) if (self->enPassantPawn) normalizeEnPassantStatus(self); }
extern void boardToFen(Board_t self, char *fen) { /* * Squares */ for (int rank=rank8; rank!=rank1-rankStep; rank-=rankStep) { int emptySquares = 0; for (int file=fileA; file!=fileH+fileStep; file+=fileStep) { int square = square(file, rank); int piece = self->squares[square]; if (piece != empty) { if (emptySquares > 0) *fen++ = '0' + emptySquares; *fen++ = pieceToChar[piece]; emptySquares = 0; } else emptySquares++; } if (emptySquares > 0) *fen++ = '0' + emptySquares; if (rank != rank1) *fen++ = '/'; } /* * Side to move */ *fen++ = ' '; *fen++ = (sideToMove(self) == white) ? 'w' : 'b'; /* * Castling flags */ *fen++ = ' '; if (self->castleFlags) { if (self->castleFlags & castleFlagWhiteKside) *fen++ = 'K'; if (self->castleFlags & castleFlagWhiteQside) *fen++ = 'Q'; if (self->castleFlags & castleFlagBlackKside) *fen++ = 'k'; if (self->castleFlags & castleFlagBlackQside) *fen++ = 'q'; } else *fen++ = '-'; /* * En-passant square */ *fen++ = ' '; normalizeEnPassantStatus(self); if (self->enPassantPawn) { *fen++ = fileToChar(file(self->enPassantPawn)); *fen++ = rankToChar((sideToMove(self) == white) ? rank6 : rank3); } else *fen++ = '-'; /* * Move number (TODO) */ /* * Halfmove clock (TODO) */ *fen = '\0'; }
extern int setupBoard(Board_t self, const char *fen) { int ix = 0; /* * Squares */ while (isspace(fen[ix])) ix++; int file = fileA, rank = rank8; int nrWhiteKings = 0, nrBlackKings = 0; memset(self->squares, empty, boardSize); self->materialKey = 0; while (rank != rank1 || file != fileH + fileStep) { int piece = empty; int count = 1; switch (fen[ix]) { case 'K': piece = whiteKing; nrWhiteKings++; break; case 'Q': piece = whiteQueen; break; case 'R': piece = whiteRook; break; case 'B': piece = whiteBishop; break; case 'N': piece = whiteKnight; break; case 'P': piece = whitePawn; break; case 'k': piece = blackKing; nrBlackKings++; break; case 'r': piece = blackRook; break; case 'q': piece = blackQueen; break; case 'b': piece = blackBishop; break; case 'n': piece = blackKnight; break; case 'p': piece = blackPawn; break; case '/': rank -= rankStep; file = fileA; ix++; continue; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': count = fen[ix] - '0'; break; default: return 0; // FEN error } int squareColor = squareColor(square(file,rank)); self->materialKey += materialKeys[piece][squareColor]; do { self->squares[square(file,rank)] = piece; file += fileStep; } while (--count && file != fileH + fileStep); ix++; } if (nrWhiteKings != 1 || nrBlackKings != 1) return 0; /* * Side to move */ self->plyNumber = 2 + (fen[ix+1] == 'b'); // 2 means full move number starts at 1 //self->lastZeroing = self->plyNumber; ix += 2; /* * Castling flags */ while (isspace(fen[ix])) ix++; self->castleFlags = 0; for (;; ix++) { switch (fen[ix]) { case 'K': self->castleFlags |= castleFlagWhiteKside; continue; case 'Q': self->castleFlags |= castleFlagWhiteQside; continue; case 'k': self->castleFlags |= castleFlagBlackKside; continue; case 'q': self->castleFlags |= castleFlagBlackQside; continue; case '-': ix++; break; default: break; } break; } /* * En passant square */ while (isspace(fen[ix])) ix++; if ('a' <= fen[ix] && fen[ix] <= 'h') { file = charToFile(fen[ix]); ix++; rank = (sideToMove(self) == white) ? rank5 : rank4; if (isdigit(fen[ix])) ix++; // ignore what it says self->enPassantPawn = square(file, rank); } else { self->enPassantPawn = 0; if (fen[ix] == '-') ix++; } // Eat move number and halfmove clock. TODO: process this properly while (isspace(fen[ix])) ix++; while (isdigit(fen[ix])) ix++; while (isspace(fen[ix])) ix++; while (isdigit(fen[ix])) ix++; self->sideInfoPlyNumber = -1; // side info is invalid now // Reset the undo stack self->undoStack.len = 0; // Initialize hash and its history self->hash = hash(self); self->hashHistory.len = 0; self->pawnKingHash = pawnKingHash(self); self->pkHashHistory.len = 0; normalizeEnPassantStatus(self); // Only safe after update of hash self->eloDiff = 0; return ix; }