Date: Thu, 27 Jul 2023 21:16:01 -0600
Earlier we found ourselves writing code for a bitmap based chess engine
which when finding pseudo-legal moves required bit shifting in different
directions based on the color of the pawn.
#include <cstdint>
void getLegalChessMoves(bool isForWhite, Board* board) {
uint64_t attacks, pushes;
for (uint64_t mask = /*...*/) {
if (isForWhite) {
attacks = ((mask << 9) | (mask << 7));
pushes = (mask << 8) & board->emptySquares;
// ...
} else {
attacks = ((mask >> 9) | (mask >> 7));
pushes = (mask >> 8) & board->emptySquares;
// ...
}
}
}
One way of deduplicating this would be to treat the bit shifts as function
objects and switch the used function depending on the isForWhite condition,
like so:
#include <cstdint>
#include <functional>
void getLegalChessMoves(bool isForWhite, Board* board) {
uint64_t attacks, pushes;
auto shift = isForWhite ? std::function<uint64_t(uint64_t,
uint64_t)>(std::shift_left<>()) : std::function<uint64_t(uint64_t,
uint64_t)>(std::shift_right<>());
for (uint64_t mask = /*...*/) {
attacks = (shift(mask, 9) | shift(mask, 7));
pushes = shift(mask, 8) & board->emptySquares;
// ...
}
}
However, we were surprised to find that there is no functional object for
left and right bit shifts in <functional>, even though every other binary
operator in C++ has a corresponding functional object! A workaround for
this is of course to define the function ourselves:
auto shift = isForWhite ? [](uint64_t a, uint64_t b) { return a << b; } :
[](uint64_t a, uint64_t b) { return a >>b; };
…but such a lambda can already be used in place of any binary operator
functor, and the <functional> header feels incomplete without an
implementation of the std::shift_left and std::shift_right functors. We
propose adding the aforementioned functors with a similar implementation to
the currently defined binary operator functors.
Proposed Changes:
To 22.10.11 [function.objects], Function objects, paragraph 2, add to the
header <functional> synopsis:
template <class T = void> struct shift_left;
template <class T = void> struct shift_right;
template<> struct shift_left<void>;
template<> struct shift_right<void>;
The library provides basic function object classes for all of the bitwise
operators in the language ([expr.shift] 7.6.7)
template <class T> struct shift_left {
T operator()(const T&x , const T&y ) const;
};
constexpr T operator()(const T& x, const T& y) const;
operator() returns x << y
template <class T> struct shift_right {
T operator()(const T&x , const T&y ) const;
};
constexpr T operator()(const T& x, const T& y) const;
operator() returns x >> y
Cheers,
~ Andrew and Leon
which when finding pseudo-legal moves required bit shifting in different
directions based on the color of the pawn.
#include <cstdint>
void getLegalChessMoves(bool isForWhite, Board* board) {
uint64_t attacks, pushes;
for (uint64_t mask = /*...*/) {
if (isForWhite) {
attacks = ((mask << 9) | (mask << 7));
pushes = (mask << 8) & board->emptySquares;
// ...
} else {
attacks = ((mask >> 9) | (mask >> 7));
pushes = (mask >> 8) & board->emptySquares;
// ...
}
}
}
One way of deduplicating this would be to treat the bit shifts as function
objects and switch the used function depending on the isForWhite condition,
like so:
#include <cstdint>
#include <functional>
void getLegalChessMoves(bool isForWhite, Board* board) {
uint64_t attacks, pushes;
auto shift = isForWhite ? std::function<uint64_t(uint64_t,
uint64_t)>(std::shift_left<>()) : std::function<uint64_t(uint64_t,
uint64_t)>(std::shift_right<>());
for (uint64_t mask = /*...*/) {
attacks = (shift(mask, 9) | shift(mask, 7));
pushes = shift(mask, 8) & board->emptySquares;
// ...
}
}
However, we were surprised to find that there is no functional object for
left and right bit shifts in <functional>, even though every other binary
operator in C++ has a corresponding functional object! A workaround for
this is of course to define the function ourselves:
auto shift = isForWhite ? [](uint64_t a, uint64_t b) { return a << b; } :
[](uint64_t a, uint64_t b) { return a >>b; };
…but such a lambda can already be used in place of any binary operator
functor, and the <functional> header feels incomplete without an
implementation of the std::shift_left and std::shift_right functors. We
propose adding the aforementioned functors with a similar implementation to
the currently defined binary operator functors.
Proposed Changes:
To 22.10.11 [function.objects], Function objects, paragraph 2, add to the
header <functional> synopsis:
template <class T = void> struct shift_left;
template <class T = void> struct shift_right;
template<> struct shift_left<void>;
template<> struct shift_right<void>;
The library provides basic function object classes for all of the bitwise
operators in the language ([expr.shift] 7.6.7)
template <class T> struct shift_left {
T operator()(const T&x , const T&y ) const;
};
constexpr T operator()(const T& x, const T& y) const;
operator() returns x << y
template <class T> struct shift_right {
T operator()(const T&x , const T&y ) const;
};
constexpr T operator()(const T& x, const T& y) const;
operator() returns x >> y
Cheers,
~ Andrew and Leon
Received on 2023-07-28 03:16:13