internal Position(Position other, Thread thread) { Array.Copy(other.board, board, other.board.Length); Array.Copy(other.byColorBB, byColorBB, other.byColorBB.Length); Array.Copy(other.byTypeBB, byTypeBB, other.byTypeBB.Length); Array.Copy(other.castlingPath, castlingPath, other.castlingPath.Length); Array.Copy(other.castlingRightsMask, castlingRightsMask, other.castlingRightsMask.Length); Array.Copy(other.castlingRookSquare, castlingRookSquare, other.castlingRookSquare.Length); Array.Copy(other.index, index, other.index.Length); Array.Copy(other.pieceCount, pieceCount, other.pieceCount.Length); Array.Copy(other.pieceList, pieceList, other.pieceList.Length); chess960 = other.chess960; gamePly = other.gamePly; sideToMove = other.sideToMove; thisThread = thread; startState = new StateInfo(); startState.copyFrom(other.st); st = startState; nodes = 0; Debug.Assert(pos_is_ok()); }
/// Position::do_move() makes a move, and saves all information necessary /// to a StateInfo object. The move is assumed to be legal. Pseudo-legal /// moves should be filtered out before this function is called. internal void do_move(MoveT m, StateInfo newSt, bool givesCheck) { Debug.Assert(Move.is_ok(m)); Debug.Assert(newSt != st); ++nodes; var k = st.key ^ Zobrist.side; // Copy some fields of the old state to our new StateInfo object except the // ones which are going to be recalculated from scratch anyway and then switch // our state pointer to point to the new (ready to be updated) state. newSt.copyFrom(st); newSt.previous = st; st = newSt; // Increment ply counters. In particular, rule50 will be reset to zero later on // in case of a capture or a pawn move. ++gamePly; ++st.rule50; ++st.pliesFromNull; var us = sideToMove; var them = Color.opposite(us); var from = Move.from_sq(m); var to = Move.to_sq(m); var pt = (int)Piece.type_of(piece_on(from)); var captured = Move.type_of(m) == MoveType.ENPASSANT ? PieceType.PAWN : Piece.type_of(piece_on(to)); Debug.Assert(Piece.color_of(piece_on(from)) == us); Debug.Assert( piece_on(to) == Piece.NO_PIECE || Piece.color_of(piece_on(to)) == (Move.type_of(m) != MoveType.CASTLING ? them : us)); Debug.Assert(captured != PieceType.KING); if (Move.type_of(m) == MoveType.CASTLING) { Debug.Assert(pt == PieceType.KING); SquareT rfrom, rto; do_castling(true, us, from, ref to, out rfrom, out rto); captured = PieceType.NO_PIECE_TYPE; st.psq += PSQT.psq[us, PieceType.ROOK, rto] - PSQT.psq[us, PieceType.ROOK, rfrom]; k ^= Zobrist.psq[us, PieceType.ROOK, rfrom] ^ Zobrist.psq[us, PieceType.ROOK, rto]; } if (captured != 0) { var capsq = to; // If the captured piece is a pawn, update pawn hash key, otherwise // update non-pawn material. if (captured == PieceType.PAWN) { if (Move.type_of(m) == MoveType.ENPASSANT) { capsq -= Square.pawn_push(us); Debug.Assert(pt == PieceType.PAWN); Debug.Assert(to == st.epSquare); Debug.Assert(Rank.relative_rank_CtSt(us, to) == Rank.RANK_6); Debug.Assert(piece_on(to) == Piece.NO_PIECE); Debug.Assert(piece_on(capsq) == Piece.make_piece(them, PieceType.PAWN)); board[capsq] = Piece.NO_PIECE; // Not done by remove_piece() } st.pawnKey ^= Zobrist.psq[them, PieceType.PAWN, capsq]; } else { st.nonPawnMaterial[them] -= Value.PieceValue[(int) Phase.MG][captured]; } // Update board and piece lists remove_piece(them, PieceType.Create(captured), capsq); // Update material hash key and prefetch access to materialTable k ^= Zobrist.psq[them, captured, capsq]; st.materialKey ^= Zobrist.psq[them, captured, pieceCount[them, captured]]; // Update incremental scores st.psq -= PSQT.psq[them, captured, capsq]; // Reset rule 50 counter st.rule50 = 0; } // Update hash key k ^= Zobrist.psq[us, pt, @from] ^ Zobrist.psq[us, pt, to]; // Reset en passant square if (st.epSquare != Square.SQ_NONE) { k ^= Zobrist.enpassant[Square.file_of(st.epSquare)]; st.epSquare = Square.SQ_NONE; } // Update castling rights if needed if (st.castlingRights != 0 && ((castlingRightsMask[@from] | castlingRightsMask[to]) != 0)) { var cr = castlingRightsMask[@from] | castlingRightsMask[to]; k ^= Zobrist.castling[st.castlingRights & cr]; st.castlingRights &= ~cr; } // Move the piece. The tricky Chess960 castling is handled earlier if (Move.type_of(m) != MoveType.CASTLING) { move_piece(us, PieceType.Create(pt), from, to); } // If the moving piece is a pawn do some special extra work if (pt == PieceType.PAWN) { // Set en-passant square if the moved pawn can be captured if ((to ^ from) == 16 && (attacks_from_PS(PieceType.PAWN, to - Square.pawn_push(us), us) & pieces_CtPt(them, PieceType.PAWN)) != 0) { st.epSquare = (from + to)/2; k ^= Zobrist.enpassant[Square.file_of(st.epSquare)]; } else if (Move.type_of(m) == MoveType.PROMOTION) { var promotion = (int)Move.promotion_type(m); Debug.Assert(Rank.relative_rank_CtSt(us, to) == Rank.RANK_8); Debug.Assert(promotion >= PieceType.KNIGHT && promotion <= PieceType.QUEEN); remove_piece(us, PieceType.PAWN, to); put_piece(us, PieceType.Create(promotion), to); // Update hash keys k ^= Zobrist.psq[us, PieceType.PAWN, to] ^ Zobrist.psq[us, promotion, to]; st.pawnKey ^= Zobrist.psq[us, PieceType.PAWN, to]; st.materialKey ^= Zobrist.psq[us, promotion, pieceCount[us, promotion] - 1] ^ Zobrist.psq[us, PieceType.PAWN, pieceCount[us, PieceType.PAWN]]; // Update incremental score st.psq += PSQT.psq[us, promotion, to] - PSQT.psq[us, PieceType.PAWN, to]; // Update material st.nonPawnMaterial[us] += Value.PieceValue[(int) Phase.MG][promotion]; } // Update pawn hash key and prefetch access to pawnsTable st.pawnKey ^= Zobrist.psq[us, PieceType.PAWN, from] ^ Zobrist.psq[us, PieceType.PAWN, to]; // Reset rule 50 draw counter st.rule50 = 0; } // Update incremental scores st.psq += PSQT.psq[us, pt, to] - PSQT.psq[us, pt, @from]; // Set capture piece st.capturedType = PieceType.Create(captured); // Update the key with the final value st.key = k; // Calculate checkers bitboard (if move gives check) st.checkersBB = givesCheck ? attackers_to(square(PieceType.KING, them)) & pieces_Ct(us) : Bitboard.Create(0); sideToMove = Color.opposite(sideToMove); Debug.Assert(pos_is_ok()); }
/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips /// the side to move without executing any move on the board. internal void do_null_move(StateInfo newSt) { Debug.Assert(checkers() == 0); Debug.Assert(newSt != st); newSt.copyFrom(st); newSt.previous = st; st = newSt; if (st.epSquare != Square.SQ_NONE) { st.key ^= Zobrist.enpassant[Square.file_of(st.epSquare)]; st.epSquare = Square.SQ_NONE; } st.key ^= Zobrist.side; ++st.rule50; st.pliesFromNull = 0; sideToMove = Color.opposite(sideToMove); Debug.Assert(pos_is_ok()); }