/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if (defined(_MSC_VER) || defined(__INTEL_COMPILER)) && !defined(__clang__)
#include
#endif
namespace chess
{
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#define FORCEINLINE __attribute__((always_inline))
#elif defined(_MSC_VER)
// NOTE: for some reason it breaks the profiler a little
// keep it on only when not profiling.
//#define FORCEINLINE __forceinline
#define FORCEINLINE
#else
#define FORCEINLINE inline
#endif
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE
#endif
namespace intrin
{
[[nodiscard]] constexpr int popcount_constexpr(std::uint64_t value)
{
int r = 0;
while (value)
{
value &= value - 1;
++r;
}
return r;
}
[[nodiscard]] constexpr int lsb_constexpr(std::uint64_t value)
{
int c = 0;
value &= ~value + 1; // leave only the lsb
if ((value & 0x00000000FFFFFFFFull) == 0) c += 32;
if ((value & 0x0000FFFF0000FFFFull) == 0) c += 16;
if ((value & 0x00FF00FF00FF00FFull) == 0) c += 8;
if ((value & 0x0F0F0F0F0F0F0F0Full) == 0) c += 4;
if ((value & 0x3333333333333333ull) == 0) c += 2;
if ((value & 0x5555555555555555ull) == 0) c += 1;
return c;
}
[[nodiscard]] constexpr int msb_constexpr(std::uint64_t value)
{
int c = 63;
if ((value & 0xFFFFFFFF00000000ull) == 0) { c -= 32; value <<= 32; }
if ((value & 0xFFFF000000000000ull) == 0) { c -= 16; value <<= 16; }
if ((value & 0xFF00000000000000ull) == 0) { c -= 8; value <<= 8; }
if ((value & 0xF000000000000000ull) == 0) { c -= 4; value <<= 4; }
if ((value & 0xC000000000000000ull) == 0) { c -= 2; value <<= 2; }
if ((value & 0x8000000000000000ull) == 0) { c -= 1; }
return c;
}
}
namespace intrin
{
[[nodiscard]] inline int popcount(std::uint64_t b)
{
#if (defined(_MSC_VER) || defined(__INTEL_COMPILER)) && !defined(__clang__)
return static_cast(_mm_popcnt_u64(b));
#else
return static_cast(__builtin_popcountll(b));
#endif
}
#if defined(_MSC_VER) && !defined(__clang__)
[[nodiscard]] inline int lsb(std::uint64_t value)
{
assert(value != 0);
unsigned long idx;
_BitScanForward64(&idx, value);
return static_cast(idx);
}
[[nodiscard]] inline int msb(std::uint64_t value)
{
assert(value != 0);
unsigned long idx;
_BitScanReverse64(&idx, value);
return static_cast(idx);
}
#else
[[nodiscard]] inline int lsb(std::uint64_t value)
{
assert(value != 0);
return __builtin_ctzll(value);
}
[[nodiscard]] inline int msb(std::uint64_t value)
{
assert(value != 0);
return 63 ^ __builtin_clzll(value);
}
#endif
}
template
[[nodiscard]] constexpr IntT floorLog2(IntT value)
{
return intrin::msb_constexpr(value);
}
template
constexpr auto computeMasks()
{
static_assert(std::is_unsigned_v);
constexpr std::size_t numBits = sizeof(IntT) * CHAR_BIT;
std::array nbitmasks{};
for (std::size_t i = 0; i < numBits; ++i)
{
nbitmasks[i] = (static_cast(1u) << i) - 1u;
}
nbitmasks[numBits] = ~static_cast(0u);
return nbitmasks;
}
template
constexpr auto nbitmask = computeMasks();
template >
inline ToT signExtend(FromT value)
{
static_assert(std::is_signed_v);
static_assert(std::is_unsigned_v);
static_assert(sizeof(ToT) == sizeof(FromT));
constexpr std::size_t totalBits = sizeof(FromT) * CHAR_BIT;
static_assert(N > 0 && N <= totalBits);
constexpr std::size_t unusedBits = totalBits - N;
if constexpr (ToT(~FromT(0)) >> 1 == ToT(~FromT(0)))
{
return ToT(value << unusedBits) >> ToT(unusedBits);
}
else
{
constexpr FromT mask = (~FromT(0)) >> unusedBits;
value &= mask;
if (value & (FromT(1) << (N - 1)))
{
value |= ~mask;
}
return static_cast(value);
}
}
namespace lookup
{
constexpr int nthSetBitIndexNaive(std::uint64_t value, int n)
{
for (int i = 0; i < n; ++i)
{
value &= value - 1;
}
return intrin::lsb_constexpr(value);
}
constexpr std::array, 256> nthSetBitIndex = []()
{
std::array, 256> t{};
for (int i = 0; i < 256; ++i)
{
for (int j = 0; j < 8; ++j)
{
t[i][j] = nthSetBitIndexNaive(i, j);
}
}
return t;
}();
}
inline int nthSetBitIndex(std::uint64_t v, std::uint64_t n)
{
std::uint64_t shift = 0;
std::uint64_t p = intrin::popcount(v & 0xFFFFFFFFull);
std::uint64_t pmask = static_cast(p > n) - 1ull;
v >>= 32 & pmask;
shift += 32 & pmask;
n -= p & pmask;
p = intrin::popcount(v & 0xFFFFull);
pmask = static_cast(p > n) - 1ull;
v >>= 16 & pmask;
shift += 16 & pmask;
n -= p & pmask;
p = intrin::popcount(v & 0xFFull);
pmask = static_cast(p > n) - 1ull;
shift += 8 & pmask;
v >>= 8 & pmask;
n -= p & pmask;
return static_cast(lookup::nthSetBitIndex[v & 0xFFull][n] + shift);
}
namespace util
{
inline std::size_t usedBits(std::size_t value)
{
if (value == 0) return 0;
return intrin::msb(value) + 1;
}
}
template
struct EnumTraits;
template
[[nodiscard]] constexpr auto hasEnumTraits() -> decltype(EnumTraits::cardinaliy, bool{})
{
return true;
}
template
[[nodiscard]] constexpr bool hasEnumTraits(...)
{
return false;
}
template
[[nodiscard]] constexpr bool isNaturalIndex() noexcept
{
return EnumTraits::isNaturalIndex;
}
template
[[nodiscard]] constexpr int cardinality() noexcept
{
return EnumTraits::cardinality;
}
template
[[nodiscard]] constexpr const std::array()>& values() noexcept
{
return EnumTraits::values;
}
template
[[nodiscard]] constexpr EnumT fromOrdinal(int id) noexcept
{
assert(!EnumTraits::isNaturalIndex || (id >= 0 && id < EnumTraits::cardinality));
return EnumTraits::fromOrdinal(id);
}
template
[[nodiscard]] constexpr typename EnumTraits::IdType ordinal(EnumT v) noexcept
{
return EnumTraits::ordinal(v);
}
template ()>>
[[nodiscard]] constexpr decltype(auto) toString(EnumT v, ArgsTs&&... args)
{
return EnumTraits::toString(v, std::forward(args)...);
}
template
[[nodiscard]] constexpr decltype(auto) toString(EnumT v)
{
return EnumTraits::toString(v);
}
template ()>>
[[nodiscard]] constexpr decltype(auto) toString(FormatT&& f, EnumT v)
{
return EnumTraits::toString(std::forward(f), v);
}
template
[[nodiscard]] constexpr decltype(auto) toChar(EnumT v)
{
return EnumTraits::toChar(v);
}
template
[[nodiscard]] constexpr decltype(auto) toChar(FormatT&& f, EnumT v)
{
return EnumTraits::toChar(std::forward(f), v);
}
template
[[nodiscard]] constexpr decltype(auto) fromString(ArgsTs&& ... args)
{
return EnumTraits::fromString(std::forward(args)...);
}
template
[[nodiscard]] constexpr decltype(auto) fromChar(ArgsTs&& ... args)
{
return EnumTraits::fromChar(std::forward(args)...);
}
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = bool;
static constexpr int cardinality = 2;
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
false,
true
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
return static_cast(id);
}
};
template ()>
struct EnumArray
{
static_assert(isNaturalIndex(), "Enum must start with 0 and end with cardinality-1.");
using value_type = ValueT;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = ValueT *;
using const_pointer = const ValueT*;
using reference = ValueT &;
using const_reference = const ValueT &;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator;
using const_reverse_iterator = std::reverse_iterator;
using KeyType = EnumT;
using ValueType = ValueT;
constexpr void fill(const ValueType& init)
{
for (auto& v : elements)
{
v = init;
}
}
[[nodiscard]] constexpr ValueType& operator[](const KeyType& dir)
{
assert(static_cast(ordinal(dir)) < static_cast(SizeV));
return elements[ordinal(dir)];
}
[[nodiscard]] constexpr const ValueType& operator[](const KeyType& dir) const
{
assert(static_cast(ordinal(dir)) < static_cast(SizeV));
return elements[ordinal(dir)];
}
[[nodiscard]] constexpr ValueType& front()
{
return elements[0];
}
[[nodiscard]] constexpr const ValueType& front() const
{
return elements[0];
}
[[nodiscard]] constexpr ValueType& back()
{
return elements[SizeV - 1];
}
[[nodiscard]] constexpr const ValueType& back() const
{
return elements[SizeV - 1];
}
[[nodiscard]] constexpr pointer data()
{
return elements;
}
[[nodiscard]] constexpr const_pointer data() const
{
return elements;
}
[[nodiscard]] constexpr iterator begin() noexcept
{
return elements;
}
[[nodiscard]] constexpr const_iterator begin() const noexcept
{
return elements;
}
[[nodiscard]] constexpr iterator end() noexcept
{
return elements + SizeV;
}
[[nodiscard]] constexpr const_iterator end() const noexcept
{
return elements + SizeV;
}
[[nodiscard]] constexpr reverse_iterator rbegin() noexcept
{
return reverse_iterator(end());
}
[[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
[[nodiscard]] constexpr reverse_iterator rend() noexcept
{
return reverse_iterator(begin());
}
[[nodiscard]] constexpr const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
[[nodiscard]] constexpr const_iterator cbegin() const noexcept
{
return begin();
}
[[nodiscard]] constexpr const_iterator cend() const noexcept
{
return end();
}
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept
{
return rbegin();
}
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept
{
return rend();
}
[[nodiscard]] constexpr size_type size() const noexcept
{
return SizeV;
}
ValueT elements[SizeV];
};
template (), std::size_t Size2V = cardinality()>
using EnumArray2 = EnumArray, Size1V>;
enum struct Color : std::uint8_t
{
White,
Black
};
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = Color;
static constexpr int cardinality = 2;
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
Color::White,
Color::Black
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality);
return static_cast(id);
}
[[nodiscard]] static constexpr std::string_view toString(EnumType c) noexcept
{
return std::string_view("wb" + ordinal(c), 1);
}
[[nodiscard]] static constexpr char toChar(EnumType c) noexcept
{
return "wb"[ordinal(c)];
}
[[nodiscard]] static constexpr std::optional fromChar(char c) noexcept
{
if (c == 'w') return Color::White;
if (c == 'b') return Color::Black;
return {};
}
[[nodiscard]] static constexpr std::optional fromString(std::string_view sv) noexcept
{
if (sv.size() != 1) return {};
return fromChar(sv[0]);
}
};
constexpr Color operator!(Color c)
{
return fromOrdinal(ordinal(c) ^ 1);
}
enum struct PieceType : std::uint8_t
{
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
None
};
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = PieceType;
static constexpr int cardinality = 7;
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
PieceType::Pawn,
PieceType::Knight,
PieceType::Bishop,
PieceType::Rook,
PieceType::Queen,
PieceType::King,
PieceType::None
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality);
return static_cast(id);
}
[[nodiscard]] static constexpr std::string_view toString(EnumType p, Color c) noexcept
{
return std::string_view("PpNnBbRrQqKk " + (chess::ordinal(p) * 2 + chess::ordinal(c)), 1);
}
[[nodiscard]] static constexpr char toChar(EnumType p, Color c) noexcept
{
return "PpNnBbRrQqKk "[chess::ordinal(p) * 2 + chess::ordinal(c)];
}
[[nodiscard]] static constexpr std::optional fromChar(char c) noexcept
{
auto it = std::string_view("PpNnBbRrQqKk ").find(c);
if (it == std::string::npos) return {};
else return static_cast(it/2);
}
[[nodiscard]] static constexpr std::optional fromString(std::string_view sv) noexcept
{
if (sv.size() != 1) return {};
return fromChar(sv[0]);
}
};
struct Piece
{
[[nodiscard]] static constexpr Piece fromId(int id)
{
return Piece(id);
}
[[nodiscard]] static constexpr Piece none()
{
return Piece(PieceType::None, Color::White);
}
constexpr Piece() noexcept :
Piece(PieceType::None, Color::White)
{
}
constexpr Piece(PieceType type, Color color) noexcept :
m_id((ordinal(type) << 1) | ordinal(color))
{
assert(type != PieceType::None || color == Color::White);
}
constexpr Piece& operator=(const Piece& other) = default;
[[nodiscard]] constexpr friend bool operator==(Piece lhs, Piece rhs) noexcept
{
return lhs.m_id == rhs.m_id;
}
[[nodiscard]] constexpr friend bool operator!=(Piece lhs, Piece rhs) noexcept
{
return !(lhs == rhs);
}
[[nodiscard]] constexpr PieceType type() const
{
return fromOrdinal(m_id >> 1);
}
[[nodiscard]] constexpr Color color() const
{
return fromOrdinal(m_id & 1);
}
[[nodiscard]] constexpr std::pair parts() const
{
return std::make_pair(type(), color());
}
[[nodiscard]] constexpr explicit operator int() const
{
return static_cast(m_id);
}
private:
constexpr Piece(int id) :
m_id(id)
{
}
std::uint8_t m_id; // lowest bit is a color, 7 highest bits are a piece type
};
[[nodiscard]] constexpr Piece operator|(PieceType type, Color color) noexcept
{
return Piece(type, color);
}
[[nodiscard]] constexpr Piece operator|(Color color, PieceType type) noexcept
{
return Piece(type, color);
}
constexpr Piece whitePawn = Piece(PieceType::Pawn, Color::White);
constexpr Piece whiteKnight = Piece(PieceType::Knight, Color::White);
constexpr Piece whiteBishop = Piece(PieceType::Bishop, Color::White);
constexpr Piece whiteRook = Piece(PieceType::Rook, Color::White);
constexpr Piece whiteQueen = Piece(PieceType::Queen, Color::White);
constexpr Piece whiteKing = Piece(PieceType::King, Color::White);
constexpr Piece blackPawn = Piece(PieceType::Pawn, Color::Black);
constexpr Piece blackKnight = Piece(PieceType::Knight, Color::Black);
constexpr Piece blackBishop = Piece(PieceType::Bishop, Color::Black);
constexpr Piece blackRook = Piece(PieceType::Rook, Color::Black);
constexpr Piece blackQueen = Piece(PieceType::Queen, Color::Black);
constexpr Piece blackKing = Piece(PieceType::King, Color::Black);
static_assert(Piece::none().type() == PieceType::None);
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = Piece;
static constexpr int cardinality = 13;
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
whitePawn,
blackPawn,
whiteKnight,
blackKnight,
whiteBishop,
blackBishop,
whiteRook,
blackRook,
whiteQueen,
blackQueen,
whiteKing,
blackKing,
Piece::none()
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(int id) noexcept
{
assert(id >= 0 && id < cardinality);
return Piece::fromId(id);
}
[[nodiscard]] static constexpr std::string_view toString(EnumType p) noexcept
{
return std::string_view("PpNnBbRrQqKk " + ordinal(p), 1);
}
[[nodiscard]] static constexpr char toChar(EnumType p) noexcept
{
return "PpNnBbRrQqKk "[ordinal(p)];
}
[[nodiscard]] static constexpr std::optional fromChar(char c) noexcept
{
auto it = std::string_view("PpNnBbRrQqKk ").find(c);
if (it == std::string::npos) return {};
else return Piece::fromId(static_cast(it));
}
[[nodiscard]] static constexpr std::optional fromString(std::string_view sv) noexcept
{
if (sv.size() != 1) return {};
return fromChar(sv[0]);
}
};
template
struct Coord
{
constexpr Coord() noexcept :
m_i(0)
{
}
constexpr explicit Coord(int i) noexcept :
m_i(i)
{
}
[[nodiscard]] constexpr explicit operator int() const
{
return static_cast(m_i);
}
constexpr friend Coord& operator++(Coord& c)
{
++c.m_i;
return c;
}
constexpr friend Coord& operator--(Coord& c)
{
--c.m_i;
return c;
}
constexpr friend Coord& operator+=(Coord& c, int d)
{
c.m_i += d;
return c;
}
constexpr friend Coord& operator-=(Coord& c, int d)
{
c.m_i -= d;
return c;
}
constexpr friend Coord operator+(const Coord& c, int d)
{
Coord cpy(c);
cpy += d;
return cpy;
}
constexpr friend Coord operator-(const Coord& c, int d)
{
Coord cpy(c);
cpy -= d;
return cpy;
}
constexpr friend int operator-(const Coord& c1, const Coord& c2)
{
return c1.m_i - c2.m_i;
}
[[nodiscard]] constexpr friend bool operator==(const Coord& c1, const Coord& c2) noexcept
{
return c1.m_i == c2.m_i;
}
[[nodiscard]] constexpr friend bool operator!=(const Coord& c1, const Coord& c2) noexcept
{
return c1.m_i != c2.m_i;
}
[[nodiscard]] constexpr friend bool operator<(const Coord& c1, const Coord& c2) noexcept
{
return c1.m_i < c2.m_i;
}
[[nodiscard]] constexpr friend bool operator<=(const Coord& c1, const Coord& c2) noexcept
{
return c1.m_i <= c2.m_i;
}
[[nodiscard]] constexpr friend bool operator>(const Coord& c1, const Coord& c2) noexcept
{
return c1.m_i > c2.m_i;
}
[[nodiscard]] constexpr friend bool operator>=(const Coord& c1, const Coord& c2) noexcept
{
return c1.m_i >= c2.m_i;
}
private:
std::int8_t m_i;
};
struct FileTag;
struct RankTag;
using File = Coord;
using Rank = Coord;
constexpr File fileA = File(0);
constexpr File fileB = File(1);
constexpr File fileC = File(2);
constexpr File fileD = File(3);
constexpr File fileE = File(4);
constexpr File fileF = File(5);
constexpr File fileG = File(6);
constexpr File fileH = File(7);
constexpr Rank rank1 = Rank(0);
constexpr Rank rank2 = Rank(1);
constexpr Rank rank3 = Rank(2);
constexpr Rank rank4 = Rank(3);
constexpr Rank rank5 = Rank(4);
constexpr Rank rank6 = Rank(5);
constexpr Rank rank7 = Rank(6);
constexpr Rank rank8 = Rank(7);
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = File;
static constexpr int cardinality = 8;
static constexpr bool isNaturalIndex = true;
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality);
return static_cast(id);
}
[[nodiscard]] static constexpr std::string_view toString(EnumType c) noexcept
{
assert(ordinal(c) >= 0 && ordinal(c) < 8);
return std::string_view("abcdefgh" + ordinal(c), 1);
}
[[nodiscard]] static constexpr std::optional fromChar(char c) noexcept
{
if (c < 'a' || c > 'h') return {};
return static_cast(c - 'a');
}
[[nodiscard]] static constexpr std::optional fromString(std::string_view sv) noexcept
{
if (sv.size() != 1) return {};
return fromChar(sv[0]);
}
};
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = Rank;
static constexpr int cardinality = 8;
static constexpr bool isNaturalIndex = true;
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality);
return static_cast(id);
}
[[nodiscard]] static constexpr std::string_view toString(EnumType c) noexcept
{
assert(ordinal(c) >= 0 && ordinal(c) < 8);
return std::string_view("12345678" + ordinal(c), 1);
}
[[nodiscard]] static constexpr std::optional fromChar(char c) noexcept
{
if (c < '1' || c > '8') return {};
return static_cast(c - '1');
}
[[nodiscard]] static constexpr std::optional fromString(std::string_view sv) noexcept
{
if (sv.size() != 1) return {};
return fromChar(sv[0]);
}
};
// files east
// ranks north
struct FlatSquareOffset
{
std::int8_t value;
constexpr FlatSquareOffset() noexcept :
value(0)
{
}
constexpr FlatSquareOffset(int files, int ranks) noexcept :
value(files + ranks * cardinality())
{
assert(files + ranks * cardinality() >= std::numeric_limits::min());
assert(files + ranks * cardinality() <= std::numeric_limits::max());
}
constexpr FlatSquareOffset operator-() const noexcept
{
return FlatSquareOffset(-value);
}
private:
constexpr FlatSquareOffset(int v) noexcept :
value(v)
{
}
};
struct Offset
{
std::int8_t files;
std::int8_t ranks;
constexpr Offset() :
files(0),
ranks(0)
{
}
constexpr Offset(int files_, int ranks_) :
files(files_),
ranks(ranks_)
{
}
[[nodiscard]] constexpr FlatSquareOffset flat() const
{
return { files, ranks };
}
[[nodiscard]] constexpr Offset operator-() const
{
return { -files, -ranks };
}
};
struct SquareCoords
{
File file;
Rank rank;
constexpr SquareCoords() noexcept :
file{},
rank{}
{
}
constexpr SquareCoords(File f, Rank r) noexcept :
file(f),
rank(r)
{
}
constexpr friend SquareCoords& operator+=(SquareCoords& c, Offset offset)
{
c.file += offset.files;
c.rank += offset.ranks;
return c;
}
[[nodiscard]] constexpr friend SquareCoords operator+(const SquareCoords& c, Offset offset)
{
SquareCoords cpy(c);
cpy.file += offset.files;
cpy.rank += offset.ranks;
return cpy;
}
[[nodiscard]] constexpr bool isOk() const
{
return file >= fileA && file <= fileH && rank >= rank1 && rank <= rank8;
}
};
struct Square
{
private:
static constexpr std::int8_t m_noneId = cardinality() * cardinality();
static constexpr std::uint8_t fileMask = 0b111;
static constexpr std::uint8_t rankMask = 0b111000;
static constexpr std::uint8_t rankShift = 3;
public:
[[nodiscard]] static constexpr Square none()
{
return Square(m_noneId);
}
constexpr Square() noexcept :
m_id(0)
{
}
constexpr explicit Square(int idx) noexcept :
m_id(idx)
{
assert(isOk() || m_id == m_noneId);
}
constexpr Square(File file, Rank rank) noexcept :
m_id(ordinal(file) + ordinal(rank) * cardinality())
{
assert(isOk());
}
constexpr explicit Square(SquareCoords coords) noexcept :
Square(coords.file, coords.rank)
{
}
[[nodiscard]] constexpr friend bool operator<(Square lhs, Square rhs) noexcept
{
return lhs.m_id < rhs.m_id;
}
[[nodiscard]] constexpr friend bool operator>(Square lhs, Square rhs) noexcept
{
return lhs.m_id > rhs.m_id;
}
[[nodiscard]] constexpr friend bool operator<=(Square lhs, Square rhs) noexcept
{
return lhs.m_id <= rhs.m_id;
}
[[nodiscard]] constexpr friend bool operator>=(Square lhs, Square rhs) noexcept
{
return lhs.m_id >= rhs.m_id;
}
[[nodiscard]] constexpr friend bool operator==(Square lhs, Square rhs) noexcept
{
return lhs.m_id == rhs.m_id;
}
[[nodiscard]] constexpr friend bool operator!=(Square lhs, Square rhs) noexcept
{
return !(lhs == rhs);
}
constexpr friend Square& operator++(Square& sq)
{
++sq.m_id;
return sq;
}
constexpr friend Square& operator--(Square& sq)
{
--sq.m_id;
return sq;
}
[[nodiscard]] constexpr friend Square operator+(Square sq, FlatSquareOffset offset)
{
Square sqCpy = sq;
sqCpy += offset;
return sqCpy;
}
constexpr friend Square& operator+=(Square& sq, FlatSquareOffset offset)
{
assert(sq.m_id + offset.value >= 0 && sq.m_id + offset.value < Square::m_noneId);
sq.m_id += offset.value;
return sq;
}
[[nodiscard]] constexpr friend Square operator+(Square sq, Offset offset)
{
assert(sq.file() + offset.files >= fileA);
assert(sq.file() + offset.files <= fileH);
assert(sq.rank() + offset.ranks >= rank1);
assert(sq.rank() + offset.ranks <= rank8);
return operator+(sq, offset.flat());
}
constexpr friend Square& operator+=(Square& sq, Offset offset)
{
return operator+=(sq, offset.flat());
}
[[nodiscard]] constexpr explicit operator int() const
{
return m_id;
}
[[nodiscard]] constexpr File file() const
{
assert(isOk());
return File(static_cast(m_id) & fileMask);
}
[[nodiscard]] constexpr Rank rank() const
{
assert(isOk());
return Rank(static_cast(m_id) >> rankShift);
}
[[nodiscard]] constexpr SquareCoords coords() const
{
return { file(), rank() };
}
[[nodiscard]] constexpr Color color() const
{
assert(isOk());
return !fromOrdinal((ordinal(rank()) + ordinal(file())) & 1);
}
constexpr void flipVertically()
{
m_id ^= rankMask;
}
constexpr void flipHorizontally()
{
m_id ^= fileMask;
}
constexpr Square flippedVertically() const
{
return Square(m_id ^ rankMask);
}
constexpr Square flippedHorizontally() const
{
return Square(m_id ^ fileMask);
}
[[nodiscard]] constexpr bool isOk() const
{
return m_id >= 0 && m_id < m_noneId;
}
private:
std::int8_t m_id;
};
constexpr Square a1(fileA, rank1);
constexpr Square a2(fileA, rank2);
constexpr Square a3(fileA, rank3);
constexpr Square a4(fileA, rank4);
constexpr Square a5(fileA, rank5);
constexpr Square a6(fileA, rank6);
constexpr Square a7(fileA, rank7);
constexpr Square a8(fileA, rank8);
constexpr Square b1(fileB, rank1);
constexpr Square b2(fileB, rank2);
constexpr Square b3(fileB, rank3);
constexpr Square b4(fileB, rank4);
constexpr Square b5(fileB, rank5);
constexpr Square b6(fileB, rank6);
constexpr Square b7(fileB, rank7);
constexpr Square b8(fileB, rank8);
constexpr Square c1(fileC, rank1);
constexpr Square c2(fileC, rank2);
constexpr Square c3(fileC, rank3);
constexpr Square c4(fileC, rank4);
constexpr Square c5(fileC, rank5);
constexpr Square c6(fileC, rank6);
constexpr Square c7(fileC, rank7);
constexpr Square c8(fileC, rank8);
constexpr Square d1(fileD, rank1);
constexpr Square d2(fileD, rank2);
constexpr Square d3(fileD, rank3);
constexpr Square d4(fileD, rank4);
constexpr Square d5(fileD, rank5);
constexpr Square d6(fileD, rank6);
constexpr Square d7(fileD, rank7);
constexpr Square d8(fileD, rank8);
constexpr Square e1(fileE, rank1);
constexpr Square e2(fileE, rank2);
constexpr Square e3(fileE, rank3);
constexpr Square e4(fileE, rank4);
constexpr Square e5(fileE, rank5);
constexpr Square e6(fileE, rank6);
constexpr Square e7(fileE, rank7);
constexpr Square e8(fileE, rank8);
constexpr Square f1(fileF, rank1);
constexpr Square f2(fileF, rank2);
constexpr Square f3(fileF, rank3);
constexpr Square f4(fileF, rank4);
constexpr Square f5(fileF, rank5);
constexpr Square f6(fileF, rank6);
constexpr Square f7(fileF, rank7);
constexpr Square f8(fileF, rank8);
constexpr Square g1(fileG, rank1);
constexpr Square g2(fileG, rank2);
constexpr Square g3(fileG, rank3);
constexpr Square g4(fileG, rank4);
constexpr Square g5(fileG, rank5);
constexpr Square g6(fileG, rank6);
constexpr Square g7(fileG, rank7);
constexpr Square g8(fileG, rank8);
constexpr Square h1(fileH, rank1);
constexpr Square h2(fileH, rank2);
constexpr Square h3(fileH, rank3);
constexpr Square h4(fileH, rank4);
constexpr Square h5(fileH, rank5);
constexpr Square h6(fileH, rank6);
constexpr Square h7(fileH, rank7);
constexpr Square h8(fileH, rank8);
static_assert(e1.color() == Color::Black);
static_assert(e8.color() == Color::White);
static_assert(e1.file() == fileE);
static_assert(e1.rank() == rank1);
static_assert(e1.flippedHorizontally() == d1);
static_assert(e1.flippedVertically() == e8);
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = Square;
static constexpr int cardinality = chess::cardinality() * chess::cardinality();
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
a1, b1, c1, d1, e1, f1, g1, h1,
a2, b2, c2, d2, e2, f2, g2, h2,
a3, b3, c3, d3, e3, f3, g3, h3,
a4, b4, c4, d4, e4, f4, g4, h4,
a5, b5, c5, d5, e5, f5, g5, h5,
a6, b6, c6, d6, e6, f6, g6, h6,
a7, b7, c7, d7, e7, f7, g7, h7,
a8, b8, c8, d8, e8, f8, g8, h8
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality + 1);
return static_cast(id);
}
[[nodiscard]] static constexpr std::string_view toString(Square sq)
{
assert(sq.isOk());
return
std::string_view(
"a1b1c1d1e1f1g1h1"
"a2b2c2d2e2f2g2h2"
"a3b3c3d3e3f3g3h3"
"a4b4c4d4e4f4g4h4"
"a5b5c5d5e5f5g5h5"
"a6b6c6d6e6f6g6h6"
"a7b7c7d7e7f7g7h7"
"a8b8c8d8e8f8g8h8"
+ (ordinal(sq) * 2),
2
);
}
[[nodiscard]] static constexpr std::optional fromString(std::string_view sv) noexcept
{
if (sv.size() != 2) return {};
const char f = sv[0];
const char r = sv[1];
if (f < 'a' || f > 'h') return {};
if (r < '1' || r > '8') return {};
return Square(static_cast(f - 'a'), static_cast(r - '1'));
}
};
static_assert(toString(d1) == std::string_view("d1"));
static_assert(values()[29] == f4);
enum struct MoveType : std::uint8_t
{
Normal,
Promotion,
Castle,
EnPassant
};
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = MoveType;
static constexpr int cardinality = 4;
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
MoveType::Normal,
MoveType::Promotion,
MoveType::Castle,
MoveType::EnPassant
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality);
return static_cast(id);
}
};
enum struct CastleType : std::uint8_t
{
Short,
Long
};
[[nodiscard]] constexpr CastleType operator!(CastleType ct)
{
return static_cast(static_cast(ct) ^ 1);
}
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = CastleType;
static constexpr int cardinality = 2;
static constexpr bool isNaturalIndex = true;
static constexpr std::array values{
CastleType::Short,
CastleType::Long
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
assert(id >= 0 && id < cardinality);
return static_cast(id);
}
};
struct CompressedMove;
// castling is encoded as a king capturing rook
// ep is encoded as a normal pawn capture (move.to is empty on the board)
struct Move
{
Square from;
Square to;
MoveType type = MoveType::Normal;
Piece promotedPiece = Piece::none();
[[nodiscard]] constexpr friend bool operator==(const Move& lhs, const Move& rhs) noexcept
{
return lhs.from == rhs.from
&& lhs.to == rhs.to
&& lhs.type == rhs.type
&& lhs.promotedPiece == rhs.promotedPiece;
}
[[nodiscard]] constexpr friend bool operator!=(const Move& lhs, const Move& rhs) noexcept
{
return !(lhs == rhs);
}
[[nodiscard]] constexpr CompressedMove compress() const noexcept;
[[nodiscard]] constexpr static Move null()
{
return Move{ Square::none(), Square::none() };
}
[[nodiscard]] constexpr static Move castle(CastleType ct, Color c);
[[nodiscard]] constexpr static Move normal(Square from, Square to)
{
return Move{ from, to, MoveType::Normal, Piece::none() };
}
[[nodiscard]] constexpr static Move enPassant(Square from, Square to)
{
return Move{ from, to, MoveType::EnPassant, Piece::none() };
}
[[nodiscard]] constexpr static Move promotion(Square from, Square to, Piece piece)
{
return Move{ from, to, MoveType::Promotion, piece };
}
};
namespace detail::castle
{
constexpr EnumArray2 moves = { {
{{ { e1, h1, MoveType::Castle }, { e8, h8, MoveType::Castle } }},
{{ { e1, a1, MoveType::Castle }, { e8, a8, MoveType::Castle } }}
} };
}
[[nodiscard]] constexpr Move Move::castle(CastleType ct, Color c)
{
return detail::castle::moves[ct][c];
}
static_assert(sizeof(Move) == 4);
struct CompressedMove
{
private:
// from most significant bits
// 2 bits for move type
// 6 bits for from square
// 6 bits for to square
// 2 bits for promoted piece type
// 0 if not a promotion
static constexpr std::uint16_t squareMask = 0b111111u;
static constexpr std::uint16_t promotedPieceTypeMask = 0b11u;
static constexpr std::uint16_t moveTypeMask = 0b11u;
public:
[[nodiscard]] constexpr static CompressedMove readFromBigEndian(const unsigned char* data)
{
CompressedMove move{};
move.m_packed = (data[0] << 8) | data[1];
return move;
}
constexpr CompressedMove() noexcept :
m_packed(0)
{
}
// move must be either valid or a null move
constexpr CompressedMove(Move move) noexcept :
m_packed(0)
{
// else null move
if (move.from != move.to)
{
assert(move.from != Square::none());
assert(move.to != Square::none());
m_packed =
(static_cast(ordinal(move.type)) << (16 - 2))
| (static_cast(ordinal(move.from)) << (16 - 2 - 6))
| (static_cast(ordinal(move.to)) << (16 - 2 - 6 - 6));
if (move.type == MoveType::Promotion)
{
assert(move.promotedPiece != Piece::none());
m_packed |= ordinal(move.promotedPiece.type()) - ordinal(PieceType::Knight);
}
else
{
assert(move.promotedPiece == Piece::none());
}
}
}
void writeToBigEndian(unsigned char* data) const
{
*data++ = m_packed >> 8;
*data++ = m_packed & 0xFF;
}
[[nodiscard]] constexpr std::uint16_t packed() const
{
return m_packed;
}
[[nodiscard]] constexpr MoveType type() const
{
return fromOrdinal(m_packed >> (16 - 2));
}
[[nodiscard]] constexpr Square from() const
{
return fromOrdinal((m_packed >> (16 - 2 - 6)) & squareMask);
}
[[nodiscard]] constexpr Square to() const
{
return fromOrdinal((m_packed >> (16 - 2 - 6 - 6)) & squareMask);
}
[[nodiscard]] constexpr Piece promotedPiece() const
{
if (type() == MoveType::Promotion)
{
const Color color =
(to().rank() == rank1)
? Color::Black
: Color::White;
const PieceType pt = fromOrdinal((m_packed & promotedPieceTypeMask) + ordinal(PieceType::Knight));
return color | pt;
}
else
{
return Piece::none();
}
}
[[nodiscard]] constexpr Move decompress() const noexcept
{
if (m_packed == 0)
{
return Move::null();
}
else
{
const MoveType type = fromOrdinal(m_packed >> (16 - 2));
const Square from = fromOrdinal((m_packed >> (16 - 2 - 6)) & squareMask);
const Square to = fromOrdinal((m_packed >> (16 - 2 - 6 - 6)) & squareMask);
const Piece promotedPiece = [&]() {
if (type == MoveType::Promotion)
{
const Color color =
(to.rank() == rank1)
? Color::Black
: Color::White;
const PieceType pt = fromOrdinal((m_packed & promotedPieceTypeMask) + ordinal(PieceType::Knight));
return color | pt;
}
else
{
return Piece::none();
}
}();
return Move{ from, to, type, promotedPiece };
}
}
private:
std::uint16_t m_packed;
};
static_assert(sizeof(CompressedMove) == 2);
[[nodiscard]] constexpr CompressedMove Move::compress() const noexcept
{
return CompressedMove(*this);
}
static_assert(a4 + Offset{ 0, 1 } == a5);
static_assert(a4 + Offset{ 0, 2 } == a6);
static_assert(a4 + Offset{ 0, -2 } == a2);
static_assert(a4 + Offset{ 0, -1 } == a3);
static_assert(e4 + Offset{ 1, 0 } == f4);
static_assert(e4 + Offset{ 2, 0 } == g4);
static_assert(e4 + Offset{ -1, 0 } == d4);
static_assert(e4 + Offset{ -2, 0 } == c4);
enum struct CastlingRights : std::uint8_t
{
None = 0x0,
WhiteKingSide = 0x1,
WhiteQueenSide = 0x2,
BlackKingSide = 0x4,
BlackQueenSide = 0x8,
White = WhiteKingSide | WhiteQueenSide,
Black = BlackKingSide | BlackQueenSide,
All = WhiteKingSide | WhiteQueenSide | BlackKingSide | BlackQueenSide
};
[[nodiscard]] constexpr CastlingRights operator|(CastlingRights lhs, CastlingRights rhs)
{
return static_cast(static_cast(lhs) | static_cast(rhs));
}
[[nodiscard]] constexpr CastlingRights operator&(CastlingRights lhs, CastlingRights rhs)
{
return static_cast(static_cast(lhs) & static_cast(rhs));
}
[[nodiscard]] constexpr CastlingRights operator~(CastlingRights lhs)
{
return static_cast(~static_cast(lhs) & static_cast(CastlingRights::All));
}
constexpr CastlingRights& operator|=(CastlingRights& lhs, CastlingRights rhs)
{
lhs = static_cast(static_cast(lhs) | static_cast(rhs));
return lhs;
}
constexpr CastlingRights& operator&=(CastlingRights& lhs, CastlingRights rhs)
{
lhs = static_cast(static_cast(lhs) & static_cast(rhs));
return lhs;
}
// checks whether lhs contains rhs
[[nodiscard]] constexpr bool contains(CastlingRights lhs, CastlingRights rhs)
{
return (lhs & rhs) == rhs;
}
template <>
struct EnumTraits
{
using IdType = int;
using EnumType = CastlingRights;
static constexpr int cardinality = 4;
static constexpr bool isNaturalIndex = false;
static constexpr std::array values{
CastlingRights::WhiteKingSide,
CastlingRights::WhiteQueenSide,
CastlingRights::BlackKingSide,
CastlingRights::BlackQueenSide
};
[[nodiscard]] static constexpr int ordinal(EnumType c) noexcept
{
return static_cast(c);
}
[[nodiscard]] static constexpr EnumType fromOrdinal(IdType id) noexcept
{
return static_cast(id);
}
};
struct CompressedReverseMove;
struct ReverseMove
{
Move move;
Piece capturedPiece;
Square oldEpSquare;
CastlingRights oldCastlingRights;
// We need a well defined case for the starting position.
constexpr ReverseMove() :
move(Move::null()),
capturedPiece(Piece::none()),
oldEpSquare(Square::none()),
oldCastlingRights(CastlingRights::All)
{
}
constexpr ReverseMove(const Move& move_, Piece capturedPiece_, Square oldEpSquare_, CastlingRights oldCastlingRights_) :
move(move_),
capturedPiece(capturedPiece_),
oldEpSquare(oldEpSquare_),
oldCastlingRights(oldCastlingRights_)
{
}
constexpr bool isNull() const
{
return move.from == move.to;
}
[[nodiscard]] constexpr CompressedReverseMove compress() const noexcept;
[[nodiscard]] constexpr friend bool operator==(const ReverseMove& lhs, const ReverseMove& rhs) noexcept
{
return lhs.move == rhs.move
&& lhs.capturedPiece == rhs.capturedPiece
&& lhs.oldEpSquare == rhs.oldEpSquare
&& lhs.oldCastlingRights == rhs.oldCastlingRights;
}
[[nodiscard]] constexpr friend bool operator!=(const ReverseMove& lhs, const ReverseMove& rhs) noexcept
{
return !(lhs == rhs);
}
};
static_assert(sizeof(ReverseMove) == 7);
struct CompressedReverseMove
{
private:
// we use 7 bits because it can be Square::none()
static constexpr std::uint32_t squareMask = 0b1111111u;
static constexpr std::uint32_t pieceMask = 0b1111u;
static constexpr std::uint32_t castlingRightsMask = 0b1111;
public:
constexpr CompressedReverseMove() noexcept :
m_move{},
m_oldState{}
{
}
constexpr CompressedReverseMove(const ReverseMove& rm) noexcept :
m_move(rm.move.compress()),
m_oldState{ static_cast(
((ordinal(rm.capturedPiece) & pieceMask) << 11)
| ((ordinal(rm.oldCastlingRights) & castlingRightsMask) << 7)
| (ordinal(rm.oldEpSquare) & squareMask)
)
}
{
}
[[nodiscard]] constexpr Move move() const
{
return m_move.decompress();
}
[[nodiscard]] const CompressedMove& compressedMove() const
{
return m_move;
}
[[nodiscard]] constexpr Piece capturedPiece() const
{
return fromOrdinal(m_oldState >> 11);
}
[[nodiscard]] constexpr CastlingRights oldCastlingRights() const
{
return fromOrdinal((m_oldState >> 7) & castlingRightsMask);
}
[[nodiscard]] constexpr Square oldEpSquare() const
{
return fromOrdinal(m_oldState & squareMask);
}
[[nodiscard]] constexpr ReverseMove decompress() const noexcept
{
const Piece capturedPiece = fromOrdinal(m_oldState >> 11);
const CastlingRights castlingRights = fromOrdinal((m_oldState >> 7) & castlingRightsMask);
// We could pack the ep square more, but don't have to, because
// can't save another byte anyway.
const Square epSquare = fromOrdinal(m_oldState & squareMask);
return ReverseMove(m_move.decompress(), capturedPiece, epSquare, castlingRights);
}
private:
CompressedMove m_move;
std::uint16_t m_oldState;
};
static_assert(sizeof(CompressedReverseMove) == 4);
[[nodiscard]] constexpr CompressedReverseMove ReverseMove::compress() const noexcept
{
return CompressedReverseMove(*this);
}
// This can be regarded as a perfect hash. Going back is hard.
struct PackedReverseMove
{
static constexpr std::uint32_t mask = 0x7FFFFFFu;
static constexpr std::size_t numBits = 27;
private:
static constexpr std::uint32_t squareMask = 0b111111u;
static constexpr std::uint32_t pieceMask = 0b1111u;
static constexpr std::uint32_t pieceTypeMask = 0b111u;
static constexpr std::uint32_t castlingRightsMask = 0b1111;
static constexpr std::uint32_t fileMask = 0b111;
public:
constexpr PackedReverseMove(const std::uint32_t packed) :
m_packed(packed)
{
}
constexpr PackedReverseMove(const ReverseMove& reverseMove) :
m_packed(
0u
// The only move when square is none() is null move and
// then both squares are none(). No other move is like that
// so we don't lose any information by storing only
// the 6 bits of each square.
| ((ordinal(reverseMove.move.from) & squareMask) << 21)
| ((ordinal(reverseMove.move.to) & squareMask) << 15)
// Other masks are just for code clarity, they should
// never change the values.
| ((ordinal(reverseMove.capturedPiece) & pieceMask) << 11)
| ((ordinal(reverseMove.oldCastlingRights) & castlingRightsMask) << 7)
| ((ordinal(reverseMove.move.promotedPiece.type()) & pieceTypeMask) << 4)
| (((reverseMove.oldEpSquare != Square::none()) & 1) << 3)
// We probably could omit the squareMask here but for clarity it's left.
| (ordinal(Square(ordinal(reverseMove.oldEpSquare) & squareMask).file()) & fileMask)
)
{
}
constexpr std::uint32_t packed() const
{
return m_packed;
}
constexpr ReverseMove unpack(Color sideThatMoved) const
{
ReverseMove rmove{};
rmove.move.from = fromOrdinal((m_packed >> 21) & squareMask);
rmove.move.to = fromOrdinal((m_packed >> 15) & squareMask);
rmove.capturedPiece = fromOrdinal((m_packed >> 11) & pieceMask);
rmove.oldCastlingRights = fromOrdinal((m_packed >> 7) & castlingRightsMask);
const PieceType promotedPieceType = fromOrdinal((m_packed >> 4) & pieceTypeMask);
if (promotedPieceType != PieceType::None)
{
rmove.move.promotedPiece = Piece(promotedPieceType, sideThatMoved);
rmove.move.type = MoveType::Promotion;
}
const bool hasEpSquare = static_cast((m_packed >> 3) & 1);
if (hasEpSquare)
{
// ep square is always where the opponent moved
const Rank rank =
sideThatMoved == Color::White
? rank6
: rank3;
const File file = fromOrdinal(m_packed & fileMask);
rmove.oldEpSquare = Square(file, rank);
if (rmove.oldEpSquare == rmove.move.to)
{
rmove.move.type = MoveType::EnPassant;
}
}
else
{
rmove.oldEpSquare = Square::none();
}
if (rmove.move.type == MoveType::Normal && rmove.oldCastlingRights != CastlingRights::None)
{
// If castling was possible then we know it was the king that moved from e1/e8.
if (rmove.move.from == e1)
{
if (rmove.move.to == h1 || rmove.move.to == a1)
{
rmove.move.type = MoveType::Castle;
}
}
else if (rmove.move.from == e8)
{
if (rmove.move.to == h8 || rmove.move.to == a8)
{
rmove.move.type = MoveType::Castle;
}
}
}
return rmove;
}
private:
// Uses only 27 lowest bits.
// Bit meaning from highest to lowest.
// - 6 bits from
// - 6 bits to
// - 4 bits for the captured piece
// - 4 bits for prev castling rights
// - 3 bits promoted piece type
// - 1 bit to specify if the ep square was valid (false if none())
// - 3 bits for prev ep square file
std::uint32_t m_packed;
};
struct MoveCompareLess
{
[[nodiscard]] bool operator()(const Move& lhs, const Move& rhs) const noexcept
{
if (ordinal(lhs.from) < ordinal(rhs.from)) return true;
if (ordinal(lhs.from) > ordinal(rhs.from)) return false;
if (ordinal(lhs.to) < ordinal(rhs.to)) return true;
if (ordinal(lhs.to) > ordinal(rhs.to)) return false;
if (ordinal(lhs.type) < ordinal(rhs.type)) return true;
if (ordinal(lhs.type) > ordinal(rhs.type)) return false;
if (ordinal(lhs.promotedPiece) < ordinal(rhs.promotedPiece)) return true;
return false;
}
};
struct ReverseMoveCompareLess
{
[[nodiscard]] bool operator()(const ReverseMove& lhs, const ReverseMove& rhs) const noexcept
{
if (MoveCompareLess{}(lhs.move, rhs.move)) return true;
if (MoveCompareLess{}(rhs.move, lhs.move)) return false;
if (ordinal(lhs.capturedPiece) < ordinal(rhs.capturedPiece)) return true;
if (ordinal(lhs.capturedPiece) > ordinal(rhs.capturedPiece)) return false;
if (static_cast(lhs.oldCastlingRights) < static_cast(rhs.oldCastlingRights)) return true;
if (static_cast(lhs.oldCastlingRights) > static_cast(rhs.oldCastlingRights)) return false;
if (ordinal(lhs.oldEpSquare) < ordinal(rhs.oldEpSquare)) return true;
if (ordinal(lhs.oldEpSquare) > ordinal(rhs.oldEpSquare)) return false;
return false;
}
};
struct BitboardIterator
{
using value_type = Square;
using difference_type = std::ptrdiff_t;
using reference = Square;
using iterator_category = std::input_iterator_tag;
using pointer = const Square*;
constexpr BitboardIterator() noexcept :
m_squares(0)
{
}
constexpr BitboardIterator(std::uint64_t v) noexcept :
m_squares(v)
{
}
constexpr BitboardIterator(const BitboardIterator&) = default;
constexpr BitboardIterator(BitboardIterator&&) = default;
constexpr BitboardIterator& operator=(const BitboardIterator&) = default;
constexpr BitboardIterator& operator=(BitboardIterator&&) = default;
[[nodiscard]] constexpr bool friend operator==(BitboardIterator lhs, BitboardIterator rhs) noexcept
{
return lhs.m_squares == rhs.m_squares;
}
[[nodiscard]] constexpr bool friend operator!=(BitboardIterator lhs, BitboardIterator rhs) noexcept
{
return lhs.m_squares != rhs.m_squares;
}
[[nodiscard]] inline Square operator*() const
{
return first();
}
constexpr BitboardIterator& operator++() noexcept
{
popFirst();
return *this;
}
private:
std::uint64_t m_squares;
constexpr void popFirst() noexcept
{
m_squares &= m_squares - 1;
}
[[nodiscard]] inline Square first() const
{
assert(m_squares != 0);
return fromOrdinal(intrin::lsb(m_squares));
}
};
struct Bitboard
{
// bits counted from the LSB
// order is A1 B2 ... G8 H8
// just like in Square
public:
constexpr Bitboard() noexcept :
m_squares(0)
{
}
private:
constexpr explicit Bitboard(Square sq) noexcept :
m_squares(static_cast(1ULL) << ordinal(sq))
{
assert(sq.isOk());
}
constexpr explicit Bitboard(Rank r) noexcept :
m_squares(static_cast(0xFFULL) << (ordinal(r) * 8))
{
}
constexpr explicit Bitboard(File f) noexcept :
m_squares(static_cast(0x0101010101010101ULL) << ordinal(f))
{
}
constexpr explicit Bitboard(Color c) noexcept :
m_squares(c == Color::White ? 0xAA55AA55AA55AA55ULL : ~0xAA55AA55AA55AA55ULL)
{
}
constexpr explicit Bitboard(std::uint64_t bb) noexcept :
m_squares(bb)
{
}
// files A..file inclusive
static constexpr EnumArray m_filesUpToBB{
0x0101010101010101ULL,
0x0303030303030303ULL,
0x0707070707070707ULL,
0x0F0F0F0F0F0F0F0FULL,
0x1F1F1F1F1F1F1F1FULL,
0x3F3F3F3F3F3F3F3FULL,
0x7F7F7F7F7F7F7F7FULL,
0xFFFFFFFFFFFFFFFFULL
};
public:
[[nodiscard]] static constexpr Bitboard none()
{
return Bitboard{};
}
[[nodiscard]] static constexpr Bitboard all()
{
return ~none();
}
[[nodiscard]] static constexpr Bitboard square(Square sq)
{
return Bitboard(sq);
}
[[nodiscard]] static constexpr Bitboard file(File f)
{
return Bitboard(f);
}
[[nodiscard]] static constexpr Bitboard rank(Rank r)
{
return Bitboard(r);
}
[[nodiscard]] static constexpr Bitboard color(Color c)
{
return Bitboard(c);
}
[[nodiscard]] static constexpr Bitboard fromBits(std::uint64_t bits)
{
return Bitboard(bits);
}
// inclusive
[[nodiscard]] static constexpr Bitboard betweenFiles(File left, File right)
{
assert(left <= right);
if (left == fileA)
{
return Bitboard::fromBits(m_filesUpToBB[right]);
}
else
{
return Bitboard::fromBits(m_filesUpToBB[right] ^ m_filesUpToBB[left - 1]);
}
}
[[nodiscard]] constexpr bool isEmpty() const
{
return m_squares == 0;
}
[[nodiscard]] constexpr bool isSet(Square sq) const
{
return !!((m_squares >> ordinal(sq)) & 1ull);
}
constexpr void set(Square sq)
{
*this |= Bitboard(sq);
}
constexpr void unset(Square sq)
{
*this &= ~(Bitboard(sq));
}
constexpr void toggle(Square sq)
{
*this ^= Bitboard(sq);
}
[[nodiscard]] constexpr BitboardIterator begin() const
{
return BitboardIterator(m_squares);
}
[[nodiscard]] constexpr BitboardIterator end() const
{
return BitboardIterator{};
}
[[nodiscard]] constexpr BitboardIterator cbegin() const
{
return BitboardIterator(m_squares);
}
[[nodiscard]] constexpr BitboardIterator cend() const
{
return BitboardIterator{};
}
[[nodiscard]] constexpr bool friend operator==(Bitboard lhs, Bitboard rhs) noexcept
{
return lhs.m_squares == rhs.m_squares;
}
[[nodiscard]] constexpr bool friend operator!=(Bitboard lhs, Bitboard rhs) noexcept
{
return lhs.m_squares != rhs.m_squares;
}
constexpr Bitboard shiftedVertically(int ranks) const
{
if (ranks >= 0)
{
return fromBits(m_squares << 8 * ranks);
}
else
{
return fromBits(m_squares >> -8 * ranks);
}
}
template
constexpr void shift()
{
static_assert(files >= -7);
static_assert(ranks >= -7);
static_assert(files <= 7);
static_assert(ranks <= 7);
if constexpr (files != 0)
{
constexpr Bitboard mask =
files > 0
? Bitboard::betweenFiles(fileA, fileH - files)
: Bitboard::betweenFiles(fileA - files, fileH);
m_squares &= mask.m_squares;
}
constexpr int shift = files + ranks * 8;
if constexpr (shift == 0)
{
return;
}
if constexpr (shift < 0)
{
m_squares >>= -shift;
}
else
{
m_squares <<= shift;
}
}
template
constexpr Bitboard shifted() const
{
Bitboard bbCpy(*this);
bbCpy.shift();
return bbCpy;
}
constexpr void shift(Offset offset)
{
assert(offset.files >= -7);
assert(offset.ranks >= -7);
assert(offset.files <= 7);
assert(offset.ranks <= 7);
if (offset.files != 0)
{
const Bitboard mask =
offset.files > 0
? Bitboard::betweenFiles(fileA, fileH - offset.files)
: Bitboard::betweenFiles(fileA - offset.files, fileH);
m_squares &= mask.m_squares;
}
const int shift = offset.files + offset.ranks * 8;
if (shift < 0)
{
m_squares >>= -shift;
}
else
{
m_squares <<= shift;
}
}
[[nodiscard]] constexpr Bitboard shifted(Offset offset) const
{
Bitboard bbCpy(*this);
bbCpy.shift(offset);
return bbCpy;
}
[[nodiscard]] constexpr Bitboard operator~() const
{
Bitboard bb = *this;
bb.m_squares = ~m_squares;
return bb;
}
constexpr Bitboard& operator^=(Color c)
{
m_squares ^= Bitboard(c).m_squares;
return *this;
}
constexpr Bitboard& operator&=(Color c)
{
m_squares &= Bitboard(c).m_squares;
return *this;
}
constexpr Bitboard& operator|=(Color c)
{
m_squares |= Bitboard(c).m_squares;
return *this;
}
[[nodiscard]] constexpr Bitboard operator^(Color c) const
{
Bitboard bb = *this;
bb ^= c;
return bb;
}
[[nodiscard]] constexpr Bitboard operator&(Color c) const
{
Bitboard bb = *this;
bb &= c;
return bb;
}
[[nodiscard]] constexpr Bitboard operator|(Color c) const
{
Bitboard bb = *this;
bb |= c;
return bb;
}
constexpr Bitboard& operator^=(Square sq)
{
m_squares ^= Bitboard(sq).m_squares;
return *this;
}
constexpr Bitboard& operator&=(Square sq)
{
m_squares &= Bitboard(sq).m_squares;
return *this;
}
constexpr Bitboard& operator|=(Square sq)
{
m_squares |= Bitboard(sq).m_squares;
return *this;
}
[[nodiscard]] constexpr Bitboard operator^(Square sq) const
{
Bitboard bb = *this;
bb ^= sq;
return bb;
}
[[nodiscard]] constexpr Bitboard operator&(Square sq) const
{
Bitboard bb = *this;
bb &= sq;
return bb;
}
[[nodiscard]] constexpr Bitboard operator|(Square sq) const
{
Bitboard bb = *this;
bb |= sq;
return bb;
}
[[nodiscard]] constexpr friend Bitboard operator^(Square sq, Bitboard bb)
{
return bb ^ sq;
}
[[nodiscard]] constexpr friend Bitboard operator&(Square sq, Bitboard bb)
{
return bb & sq;
}
[[nodiscard]] constexpr friend Bitboard operator|(Square sq, Bitboard bb)
{
return bb | sq;
}
constexpr Bitboard& operator^=(Bitboard rhs)
{
m_squares ^= rhs.m_squares;
return *this;
}
constexpr Bitboard& operator&=(Bitboard rhs)
{
m_squares &= rhs.m_squares;
return *this;
}
constexpr Bitboard& operator|=(Bitboard rhs)
{
m_squares |= rhs.m_squares;
return *this;
}
[[nodiscard]] constexpr Bitboard operator^(Bitboard sq) const
{
Bitboard bb = *this;
bb ^= sq;
return bb;
}
[[nodiscard]] constexpr Bitboard operator&(Bitboard sq) const
{
Bitboard bb = *this;
bb &= sq;
return bb;
}
[[nodiscard]] constexpr Bitboard operator|(Bitboard sq) const
{
Bitboard bb = *this;
bb |= sq;
return bb;
}
[[nodiscard]] inline int count() const
{
return static_cast(intrin::popcount(m_squares));
}
[[nodiscard]] constexpr bool moreThanOne() const
{
return !!(m_squares & (m_squares - 1));
}
[[nodiscard]] constexpr bool exactlyOne() const
{
return m_squares != 0 && !moreThanOne();
}
[[nodiscard]] constexpr bool any() const
{
return !!m_squares;
}
[[nodiscard]] inline Square first() const
{
assert(m_squares != 0);
return fromOrdinal(intrin::lsb(m_squares));
}
[[nodiscard]] inline Square nth(int n) const
{
assert(count() > n);
Bitboard cpy = *this;
while (n--) cpy.popFirst();
return cpy.first();
}
[[nodiscard]] inline Square last() const
{
assert(m_squares != 0);
return fromOrdinal(intrin::msb(m_squares));
}
[[nodiscard]] constexpr std::uint64_t bits() const
{
return m_squares;
}
constexpr void popFirst()
{
assert(m_squares != 0);
m_squares &= m_squares - 1;
}
constexpr Bitboard& operator=(const Bitboard& other) = default;
private:
std::uint64_t m_squares;
};
[[nodiscard]] constexpr Bitboard operator^(Square sq0, Square sq1)
{
return Bitboard::square(sq0) ^ sq1;
}
[[nodiscard]] constexpr Bitboard operator&(Square sq0, Square sq1)
{
return Bitboard::square(sq0) & sq1;
}
[[nodiscard]] constexpr Bitboard operator|(Square sq0, Square sq1)
{
return Bitboard::square(sq0) | sq1;
}
[[nodiscard]] constexpr Bitboard operator""_bb(unsigned long long bits)
{
return Bitboard::fromBits(bits);
}
namespace bb
{
namespace fancy_magics
{
// Implementation based on https://github.com/syzygy1/Cfish
alignas(64) constexpr EnumArray g_rookMagics{ {
0x0A80004000801220ull,
0x8040004010002008ull,
0x2080200010008008ull,
0x1100100008210004ull,
0xC200209084020008ull,
0x2100010004000208ull,
0x0400081000822421ull,
0x0200010422048844ull,
0x0800800080400024ull,
0x0001402000401000ull,
0x3000801000802001ull,
0x4400800800100083ull,
0x0904802402480080ull,
0x4040800400020080ull,
0x0018808042000100ull,
0x4040800080004100ull,
0x0040048001458024ull,
0x00A0004000205000ull,
0x3100808010002000ull,
0x4825010010000820ull,
0x5004808008000401ull,
0x2024818004000A00ull,
0x0005808002000100ull,
0x2100060004806104ull,
0x0080400880008421ull,
0x4062220600410280ull,
0x010A004A00108022ull,
0x0000100080080080ull,
0x0021000500080010ull,
0x0044000202001008ull,
0x0000100400080102ull,
0xC020128200040545ull,
0x0080002000400040ull,
0x0000804000802004ull,
0x0000120022004080ull,
0x010A386103001001ull,
0x9010080080800400ull,
0x8440020080800400ull,
0x0004228824001001ull,
0x000000490A000084ull,
0x0080002000504000ull,
0x200020005000C000ull,
0x0012088020420010ull,
0x0010010080080800ull,
0x0085001008010004ull,
0x0002000204008080ull,
0x0040413002040008ull,
0x0000304081020004ull,
0x0080204000800080ull,
0x3008804000290100ull,
0x1010100080200080ull,
0x2008100208028080ull,
0x5000850800910100ull,
0x8402019004680200ull,
0x0120911028020400ull,
0x0000008044010200ull,
0x0020850200244012ull,
0x0020850200244012ull,
0x0000102001040841ull,
0x140900040A100021ull,
0x000200282410A102ull,
0x000200282410A102ull,
0x000200282410A102ull,
0x4048240043802106ull
} };
alignas(64) constexpr EnumArray g_bishopMagics{ {
0x40106000A1160020ull,
0x0020010250810120ull,
0x2010010220280081ull,
0x002806004050C040ull,
0x0002021018000000ull,
0x2001112010000400ull,
0x0881010120218080ull,
0x1030820110010500ull,
0x0000120222042400ull,
0x2000020404040044ull,
0x8000480094208000ull,
0x0003422A02000001ull,
0x000A220210100040ull,
0x8004820202226000ull,
0x0018234854100800ull,
0x0100004042101040ull,
0x0004001004082820ull,
0x0010000810010048ull,
0x1014004208081300ull,
0x2080818802044202ull,
0x0040880C00A00100ull,
0x0080400200522010ull,
0x0001000188180B04ull,
0x0080249202020204ull,
0x1004400004100410ull,
0x00013100A0022206ull,
0x2148500001040080ull,
0x4241080011004300ull,
0x4020848004002000ull,
0x10101380D1004100ull,
0x0008004422020284ull,
0x01010A1041008080ull,
0x0808080400082121ull,
0x0808080400082121ull,
0x0091128200100C00ull,
0x0202200802010104ull,
0x8C0A020200440085ull,
0x01A0008080B10040ull,
0x0889520080122800ull,
0x100902022202010Aull,
0x04081A0816002000ull,
0x0000681208005000ull,
0x8170840041008802ull,
0x0A00004200810805ull,
0x0830404408210100ull,
0x2602208106006102ull,
0x1048300680802628ull,
0x2602208106006102ull,
0x0602010120110040ull,
0x0941010801043000ull,
0x000040440A210428ull,
0x0008240020880021ull,
0x0400002012048200ull,
0x00AC102001210220ull,
0x0220021002009900ull,
0x84440C080A013080ull,
0x0001008044200440ull,
0x0004C04410841000ull,
0x2000500104011130ull,
0x1A0C010011C20229ull,
0x0044800112202200ull,
0x0434804908100424ull,
0x0300404822C08200ull,
0x48081010008A2A80ull
} };
alignas(64) static EnumArray g_rookMasks;
alignas(64) static EnumArray g_rookShifts;
alignas(64) static EnumArray g_rookAttacks;
alignas(64) static EnumArray g_bishopMasks;
alignas(64) static EnumArray g_bishopShifts;
alignas(64) static EnumArray g_bishopAttacks;
alignas(64) static std::array g_allRookAttacks;
alignas(64) static std::array g_allBishopAttacks;
inline Bitboard bishopAttacks(Square s, Bitboard occupied)
{
const std::size_t idx =
(occupied & fancy_magics::g_bishopMasks[s]).bits()
* fancy_magics::g_bishopMagics[s]
>> fancy_magics::g_bishopShifts[s];
return fancy_magics::g_bishopAttacks[s][idx];
}
inline Bitboard rookAttacks(Square s, Bitboard occupied)
{
const std::size_t idx =
(occupied & fancy_magics::g_rookMasks[s]).bits()
* fancy_magics::g_rookMagics[s]
>> fancy_magics::g_rookShifts[s];
return fancy_magics::g_rookAttacks[s][idx];
}
}
[[nodiscard]] constexpr Bitboard square(Square sq)
{
return Bitboard::square(sq);
}
[[nodiscard]] constexpr Bitboard rank(Rank rank)
{
return Bitboard::rank(rank);
}
[[nodiscard]] constexpr Bitboard file(File file)
{
return Bitboard::file(file);
}
[[nodiscard]] constexpr Bitboard color(Color c)
{
return Bitboard::color(c);
}
[[nodiscard]] constexpr Bitboard before(Square sq)
{
return Bitboard::fromBits(nbitmask[ordinal(sq)]);
}
constexpr Bitboard lightSquares = bb::color(Color::White);
constexpr Bitboard darkSquares = bb::color(Color::Black);
constexpr Bitboard fileA = bb::file(chess::fileA);
constexpr Bitboard fileB = bb::file(chess::fileB);
constexpr Bitboard fileC = bb::file(chess::fileC);
constexpr Bitboard fileD = bb::file(chess::fileD);
constexpr Bitboard fileE = bb::file(chess::fileE);
constexpr Bitboard fileF = bb::file(chess::fileF);
constexpr Bitboard fileG = bb::file(chess::fileG);
constexpr Bitboard fileH = bb::file(chess::fileH);
constexpr Bitboard rank1 = bb::rank(chess::rank1);
constexpr Bitboard rank2 = bb::rank(chess::rank2);
constexpr Bitboard rank3 = bb::rank(chess::rank3);
constexpr Bitboard rank4 = bb::rank(chess::rank4);
constexpr Bitboard rank5 = bb::rank(chess::rank5);
constexpr Bitboard rank6 = bb::rank(chess::rank6);
constexpr Bitboard rank7 = bb::rank(chess::rank7);
constexpr Bitboard rank8 = bb::rank(chess::rank8);
constexpr Bitboard a1 = bb::square(chess::a1);
constexpr Bitboard a2 = bb::square(chess::a2);
constexpr Bitboard a3 = bb::square(chess::a3);
constexpr Bitboard a4 = bb::square(chess::a4);
constexpr Bitboard a5 = bb::square(chess::a5);
constexpr Bitboard a6 = bb::square(chess::a6);
constexpr Bitboard a7 = bb::square(chess::a7);
constexpr Bitboard a8 = bb::square(chess::a8);
constexpr Bitboard b1 = bb::square(chess::b1);
constexpr Bitboard b2 = bb::square(chess::b2);
constexpr Bitboard b3 = bb::square(chess::b3);
constexpr Bitboard b4 = bb::square(chess::b4);
constexpr Bitboard b5 = bb::square(chess::b5);
constexpr Bitboard b6 = bb::square(chess::b6);
constexpr Bitboard b7 = bb::square(chess::b7);
constexpr Bitboard b8 = bb::square(chess::b8);
constexpr Bitboard c1 = bb::square(chess::c1);
constexpr Bitboard c2 = bb::square(chess::c2);
constexpr Bitboard c3 = bb::square(chess::c3);
constexpr Bitboard c4 = bb::square(chess::c4);
constexpr Bitboard c5 = bb::square(chess::c5);
constexpr Bitboard c6 = bb::square(chess::c6);
constexpr Bitboard c7 = bb::square(chess::c7);
constexpr Bitboard c8 = bb::square(chess::c8);
constexpr Bitboard d1 = bb::square(chess::d1);
constexpr Bitboard d2 = bb::square(chess::d2);
constexpr Bitboard d3 = bb::square(chess::d3);
constexpr Bitboard d4 = bb::square(chess::d4);
constexpr Bitboard d5 = bb::square(chess::d5);
constexpr Bitboard d6 = bb::square(chess::d6);
constexpr Bitboard d7 = bb::square(chess::d7);
constexpr Bitboard d8 = bb::square(chess::d8);
constexpr Bitboard e1 = bb::square(chess::e1);
constexpr Bitboard e2 = bb::square(chess::e2);
constexpr Bitboard e3 = bb::square(chess::e3);
constexpr Bitboard e4 = bb::square(chess::e4);
constexpr Bitboard e5 = bb::square(chess::e5);
constexpr Bitboard e6 = bb::square(chess::e6);
constexpr Bitboard e7 = bb::square(chess::e7);
constexpr Bitboard e8 = bb::square(chess::e8);
constexpr Bitboard f1 = bb::square(chess::f1);
constexpr Bitboard f2 = bb::square(chess::f2);
constexpr Bitboard f3 = bb::square(chess::f3);
constexpr Bitboard f4 = bb::square(chess::f4);
constexpr Bitboard f5 = bb::square(chess::f5);
constexpr Bitboard f6 = bb::square(chess::f6);
constexpr Bitboard f7 = bb::square(chess::f7);
constexpr Bitboard f8 = bb::square(chess::f8);
constexpr Bitboard g1 = bb::square(chess::g1);
constexpr Bitboard g2 = bb::square(chess::g2);
constexpr Bitboard g3 = bb::square(chess::g3);
constexpr Bitboard g4 = bb::square(chess::g4);
constexpr Bitboard g5 = bb::square(chess::g5);
constexpr Bitboard g6 = bb::square(chess::g6);
constexpr Bitboard g7 = bb::square(chess::g7);
constexpr Bitboard g8 = bb::square(chess::g8);
constexpr Bitboard h1 = bb::square(chess::h1);
constexpr Bitboard h2 = bb::square(chess::h2);
constexpr Bitboard h3 = bb::square(chess::h3);
constexpr Bitboard h4 = bb::square(chess::h4);
constexpr Bitboard h5 = bb::square(chess::h5);
constexpr Bitboard h6 = bb::square(chess::h6);
constexpr Bitboard h7 = bb::square(chess::h7);
constexpr Bitboard h8 = bb::square(chess::h8);
[[nodiscard]] Bitboard between(Square s1, Square s2);
[[nodiscard]] Bitboard line(Square s1, Square s2);
template
[[nodiscard]] Bitboard pseudoAttacks(Square sq);
[[nodiscard]] Bitboard pseudoAttacks(PieceType pt, Square sq);
template
Bitboard attacks(Square sq, Bitboard occupied)
{
static_assert(PieceTypeV != PieceType::None && PieceTypeV != PieceType::Pawn);
assert(sq.isOk());
if constexpr (PieceTypeV == PieceType::Bishop)
{
return fancy_magics::bishopAttacks(sq, occupied);
}
else if constexpr (PieceTypeV == PieceType::Rook)
{
return fancy_magics::rookAttacks(sq, occupied);
}
else if constexpr (PieceTypeV == PieceType::Queen)
{
return
fancy_magics::bishopAttacks(sq, occupied)
| fancy_magics::rookAttacks(sq, occupied);
}
else
{
return pseudoAttacks(sq);
}
}
[[nodiscard]] inline Bitboard attacks(PieceType pt, Square sq, Bitboard occupied)
{
assert(sq.isOk());
switch (pt)
{
case PieceType::Bishop:
return attacks(sq, occupied);
case PieceType::Rook:
return attacks(sq, occupied);
case PieceType::Queen:
return attacks(sq, occupied);
default:
return pseudoAttacks(pt, sq);
}
}
[[nodiscard]] inline Bitboard pawnAttacks(Bitboard pawns, Color color);
[[nodiscard]] inline Bitboard westPawnAttacks(Bitboard pawns, Color color);
[[nodiscard]] inline Bitboard eastPawnAttacks(Bitboard pawns, Color color);
[[nodiscard]] inline bool isAttackedBySlider(
Square sq,
Bitboard bishops,
Bitboard rooks,
Bitboard queens,
Bitboard occupied
);
namespace detail
{
static constexpr std::array knightOffsets{ { {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {-2, -1}, {-2, 1}, {2, -1}, {2, 1} } };
static constexpr std::array kingOffsets{ { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1} } };
enum Direction
{
North = 0,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest
};
constexpr std::array offsets = { {
{ 0, 1 },
{ 1, 1 },
{ 1, 0 },
{ 1, -1 },
{ 0, -1 },
{ -1, -1 },
{ -1, 0 },
{ -1, 1 }
} };
static constexpr std::array bishopOffsets{
offsets[NorthEast],
offsets[SouthEast],
offsets[SouthWest],
offsets[NorthWest]
};
static constexpr std::array rookOffsets{
offsets[North],
offsets[East],
offsets[South],
offsets[West]
};
[[nodiscard]] static EnumArray generatePseudoAttacks_Pawn()
{
// pseudo attacks don't make sense for pawns
return {};
}
[[nodiscard]] static EnumArray generatePseudoAttacks_Knight()
{
EnumArray bbs{};
for (Square fromSq = chess::a1; fromSq != Square::none(); ++fromSq)
{
Bitboard bb{};
for (auto&& offset : knightOffsets)
{
const SquareCoords toSq = fromSq.coords() + offset;
if (toSq.isOk())
{
bb |= Square(toSq);
}
}
bbs[fromSq] = bb;
}
return bbs;
}
[[nodiscard]] static Bitboard generateSliderPseudoAttacks(const std::array & offsets_, Square fromSq)
{
assert(fromSq.isOk());
Bitboard bb{};
for (auto&& offset : offsets_)
{
SquareCoords fromSqC = fromSq.coords();
for (;;)
{
fromSqC += offset;
if (!fromSqC.isOk())
{
break;
}
bb |= Square(fromSqC);
}
}
return bb;
}
[[nodiscard]] static EnumArray generatePseudoAttacks_Bishop()
{
EnumArray bbs{};
for (Square fromSq = chess::a1; fromSq != Square::none(); ++fromSq)
{
bbs[fromSq] = generateSliderPseudoAttacks(bishopOffsets, fromSq);
}
return bbs;
}
[[nodiscard]] static EnumArray generatePseudoAttacks_Rook()
{
EnumArray bbs{};
for (Square fromSq = chess::a1; fromSq != Square::none(); ++fromSq)
{
bbs[fromSq] = generateSliderPseudoAttacks(rookOffsets, fromSq);
}
return bbs;
}
[[nodiscard]] static EnumArray generatePseudoAttacks_Queen()
{
EnumArray bbs{};
for (Square fromSq = chess::a1; fromSq != Square::none(); ++fromSq)
{
bbs[fromSq] =
generateSliderPseudoAttacks(bishopOffsets, fromSq)
| generateSliderPseudoAttacks(rookOffsets, fromSq);
}
return bbs;
}
[[nodiscard]] static EnumArray