BenBot 1.7.5
A chess engine
Loading...
Searching...
No Matches
Position.hpp
Go to the documentation of this file.
1/*
2 * ======================================================================================
3 *
4 * ░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░▒▓████████▓▒░
5 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
6 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
7 * ░▒▓███████▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
8 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
9 * ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
10 * ░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░
11 *
12 * ======================================================================================
13 */
14
20
25
26#pragma once
27
28#include <cstdint> // IWYU pragma: keep - for std::uint64_t
41#include <magic_enum/magic_enum.hpp>
42#include <optional>
43#include <ranges>
44#include <string>
45
46namespace chess::moves {
47struct Move;
48} // namespace chess::moves
49
55namespace chess::game {
56
57using board::Bitboard;
58using board::File;
59using board::Pieces;
60using board::Square;
61using moves::Move;
62using pieces::Color;
63
65
74struct [[nodiscard]] Position final {
77
84 Pieces whitePieces { Color::White };
85
92 Pieces blackPieces { Color::Black };
93
95 Color sideToMove { Color::White };
96
99
102
109 std::optional<Square> enPassantTargetSquare;
110
121 std::uint_least8_t halfmoveClock { UINT8_C(0) };
122
127 std::uint_least64_t fullMoveCounter { UINT64_C(1) };
128
130 using Hash = std::uint64_t;
131
137 Hash hash { UINT64_C(0) };
138
140 [[nodiscard]] auto operator==(const Position& other) const noexcept -> bool
141 {
142 return hash == other.hash;
143 }
144
147
149 template <Color Side>
150 [[nodiscard]] auto pieces_for() noexcept -> Pieces&
151 {
152 if constexpr (Side == Color::White)
153 return whitePieces;
154 else
155 return blackPieces;
156 }
157
159 template <Color Side>
160 [[nodiscard]] auto pieces_for() const noexcept -> const Pieces&
161 {
162 if constexpr (Side == Color::White)
163 return whitePieces;
164 else
165 return blackPieces;
166 }
167
169 template <Color Side>
170 [[nodiscard]] auto castling_rights_for() const noexcept -> CastlingRights
171 {
172 if constexpr (Side == Color::White)
173 return whiteCastlingRights;
174 else
175 return blackCastlingRights;
176 }
177
179 [[nodiscard]] auto our_pieces() noexcept -> Pieces&
180 {
181 if (is_white_to_move())
182 return whitePieces;
183
184 return blackPieces;
185 }
186
188 [[nodiscard]] auto our_pieces() const noexcept -> const Pieces&
189 {
190 if (is_white_to_move())
191 return whitePieces;
192
193 return blackPieces;
194 }
195
197 [[nodiscard]] auto their_pieces() noexcept -> Pieces&
198 {
199 if (is_white_to_move())
200 return blackPieces;
201
202 return whitePieces;
203 }
204
206 [[nodiscard]] auto their_pieces() const noexcept -> const Pieces&
207 {
208 if (is_white_to_move())
209 return blackPieces;
210
211 return whitePieces;
212 }
213
215
217 [[nodiscard]] auto is_white_to_move() const noexcept -> bool
218 {
219 return sideToMove == Color::White;
220 }
221
223 [[nodiscard]] auto is_black_to_move() const noexcept -> bool
224 {
225 return sideToMove == Color::Black;
226 }
227
231 [[nodiscard]] auto occupied() const noexcept -> Bitboard { return whitePieces.occupied | blackPieces.occupied; }
232
234 [[nodiscard]] auto free() const noexcept -> Bitboard { return occupied().inverse(); }
235
238
242 [[nodiscard]] auto is_file_open(File file) const noexcept -> bool;
243
249 [[nodiscard]] auto get_open_files() const noexcept;
250
254 [[nodiscard]] auto is_file_half_open(File file) const noexcept -> bool;
255
261 [[nodiscard]] auto get_half_open_files() const noexcept;
262
264
266 [[nodiscard]] auto is_check() const noexcept -> bool { return is_side_in_check(sideToMove); }
267
269 template <Color Side>
270 [[nodiscard]] auto get_passed_pawns() const noexcept -> Bitboard;
271
273 template <Color Side>
274 [[nodiscard]] auto get_backward_pawns() const noexcept -> Bitboard;
275
278
280 [[nodiscard]] auto is_checkmate() const -> bool;
281
283 [[nodiscard]] auto is_stalemate() const -> bool;
284
288 [[nodiscard]] auto is_fifty_move_draw() const -> bool;
289
291 [[nodiscard]] auto is_threefold_repetition() const noexcept -> bool;
292
294 [[nodiscard]] auto is_draw_by_insufficient_material() const noexcept -> bool;
295
297 [[nodiscard]] auto is_draw() const -> bool;
298
302 [[nodiscard]] auto get_result() const -> std::optional<Result>;
303
305
311 [[nodiscard]] auto is_legal(Move move) const -> bool;
312
314 [[nodiscard]] auto is_en_passant(Move move) const noexcept -> bool;
315
317 [[nodiscard]] auto is_capture(Move move) const noexcept -> bool;
318
320 [[nodiscard]] auto is_quiet(Move move) const noexcept -> bool;
321
323 void make_move(Move move);
324
331
333 void flip();
334
337
344 [[nodiscard]] auto is_illegal() const -> std::optional<std::string>;
345
352 [[nodiscard]] static auto empty() -> Position;
353
354private:
355 [[nodiscard]] auto is_side_in_check(Color side) const noexcept -> bool;
356
357 ThreefoldChecker threefoldChecker;
358};
359
368[[nodiscard, gnu::const]] auto after_move(
369 const Position& starting, Move move)
370 -> Position;
371
378[[nodiscard, gnu::const]] auto after_null_move(const Position& starting) -> Position;
379
386[[nodiscard, gnu::const]] auto flipped(const Position& starting) -> Position;
387
397[[nodiscard]] auto print_utf8(const Position& position) -> std::string;
398
408[[nodiscard]] auto print_ascii(const Position& position) -> std::string;
409
410/*
411 ___ ,--,
412 ,---, ,--.'|_ ,--, ,--.'|
413 ,---.'| | | :,' ,--.'| | | :
414 | | : : : ' : | |, : : ' .--.--.
415 | | | ,---. .;__,' / ,--.--. `--'_ | ' | / / '
416 ,--.__| | / \ | | | / \ ,' ,'| ' | | | : /`./
417 / ,' | / / |:__,'| : .--. .-. | ' | | | | : | : ;_
418. ' / |. ' / | ' : |__ \__\/: . . | | : ' : |__ \ \ `.
419' ; |: |' ; /| | | '.'| ," .--.; | ' : |__ | | '.'| `----. \
420| | '/ '' | / | ; : ;/ / ,. | | | '.'|; : ;/ /`--' /__ ___ ___
421| : :|| : | | , /; : .' \; : ;| , /'--'. / .\/ .\/ .\
422 \ \ / \ \ / ---`-' | , .-./| , / ---`-' `--'---'\ ; \ ; \ ; |
423 `----' `----' `--`---' ---`-' `--" `--" `--"
424
425 */
426
427inline auto Position::empty() -> Position
428{
429 Position pos { };
430
431 pos.whitePieces = { };
432 pos.blackPieces = { };
433
434 pos.refresh_zobrist();
435
436 return pos;
437}
438
439inline auto Position::is_threefold_repetition() const noexcept -> bool
440{
441 return threefoldChecker.is_threefold();
442}
443
444inline auto Position::is_side_in_check(const Color side) const noexcept -> bool
445{
446 if (side == Color::White) {
448 blackPieces, whitePieces.king, whitePieces.occupied);
449 }
450
452 whitePieces, blackPieces.king, blackPieces.occupied);
453}
454
455inline auto Position::is_legal(const Move move) const -> bool
456{
457 auto copy { *this };
458
459 copy.make_move(move);
460
461 return not copy.is_side_in_check(sideToMove);
462}
463
464inline auto Position::is_en_passant(const Move move) const noexcept -> bool
465{
466 return move.piece() == PieceType::Pawn
467 and enPassantTargetSquare.has_value()
468 and move.to() == *enPassantTargetSquare;
469}
470
471inline auto Position::is_capture(const Move move) const noexcept -> bool
472{
473 return is_en_passant(move) or their_pieces().occupied.test(move.to());
474}
475
476inline auto Position::is_quiet(const Move move) const noexcept -> bool
477{
478 return not(move.is_promotion() or is_capture(move));
479}
480
481inline auto Position::is_file_open(const File file) const noexcept -> bool
482{
483 return whitePieces.is_file_half_open(file) and blackPieces.is_file_half_open(file);
484}
485
486inline auto Position::get_open_files() const noexcept
487{
488 return magic_enum::enum_values<File>()
489 | std::views::filter([this](const File file) { return is_file_open(file); });
490}
491
492inline auto Position::is_file_half_open(const File file) const noexcept -> bool
493{
494 const bool whiteOpen = whitePieces.is_file_half_open(file);
495 const bool blackOpen = blackPieces.is_file_half_open(file);
496
497 // boolean XOR
498 return whiteOpen != blackOpen;
499}
500
501inline auto Position::get_half_open_files() const noexcept
502{
503 return magic_enum::enum_values<File>()
504 | std::views::filter([this](const File file) { return is_file_half_open(file); });
505}
506
507template <Color Side>
508auto Position::get_passed_pawns() const noexcept -> Bitboard
509{
510 static constexpr auto OtherSide = pieces::other_side<Side>();
511
512 const auto friendlyPawns = pieces_for<Side>().pawns;
513 const auto enemyPawns = pieces_for<OtherSide>().pawns;
514
515 const auto mask = board::fills::pawn_front<OtherSide>(
516 enemyPawns | moves::patterns::pawn_attacks<OtherSide>(enemyPawns));
517
518 return friendlyPawns & mask.inverse();
519}
520
521template <Color Side>
523{
524 static constexpr auto OtherSide = pieces::other_side<Side>();
525
526 const auto friendlyPawns = pieces_for<Side>().pawns;
527 const auto enemyPawns = pieces_for<OtherSide>().pawns;
528
529 const auto ourAttackSpans = board::fills::pawn_front<Side>(
531
532 const auto theirAttacks = moves::patterns::pawn_attacks<OtherSide>(enemyPawns);
533
534 const auto backwardArea = board::fills::pawn_rear<Side>(
535 ourAttackSpans.inverse() & theirAttacks);
536
537 return backwardArea & friendlyPawns;
538}
539
540inline auto after_move(
541 const Position& starting, const Move move)
542 -> Position
543{
544 auto copy { starting };
545
546 copy.make_move(move);
547
548 return copy;
549}
550
551inline auto after_null_move(const Position& starting) -> Position
552{
553 auto copy { starting };
554
555 copy.make_null_move();
556
557 return copy;
558}
559
560inline auto flipped(const Position& starting) -> Position
561{
562 auto copy { starting };
563
564 copy.flip();
565
566 return copy;
567}
568
569} // namespace chess::game
constexpr auto pawn_rear(Bitboard starting) noexcept -> Bitboard
Definition Fills.hpp:243
constexpr auto pawn_front(Bitboard starting) noexcept -> Bitboard
Definition Fills.hpp:234
auto after_move(const Position &starting, Move move) -> Position
Definition Position.hpp:540
auto flipped(const Position &starting) -> Position
Definition Position.hpp:560
auto after_null_move(const Position &starting) -> Position
Definition Position.hpp:551
auto print_utf8(const Position &position) -> std::string
auto print_ascii(const Position &position) -> std::string
constexpr auto squares_attacked(const Pieces &pieces, Bitboard targetSquares, Bitboard enemyPieces) noexcept -> bool
Definition Attacks.hpp:81
constexpr auto pawn_attacks(Bitboard starting) noexcept -> Bitboard
Definition Patterns.hpp:120
constexpr auto other_side() noexcept -> Color
Definition Colors.hpp:40
pieces::Type PieceType
auto is_en_passant(Move move) const noexcept -> bool
Definition Position.hpp:464
std::optional< Square > enPassantTargetSquare
Definition Position.hpp:109
auto is_checkmate() const -> bool
auto is_legal(Move move) const -> bool
Definition Position.hpp:455
auto is_fifty_move_draw() const -> bool
auto our_pieces() const noexcept -> const Pieces &
Definition Position.hpp:188
auto get_result() const -> std::optional< Result >
auto pieces_for() noexcept -> Pieces &
Definition Position.hpp:150
auto their_pieces() noexcept -> Pieces &
Definition Position.hpp:197
auto get_half_open_files() const noexcept
Definition Position.hpp:501
auto is_black_to_move() const noexcept -> bool
Definition Position.hpp:223
static auto empty() -> Position
Definition Position.hpp:427
auto is_file_open(File file) const noexcept -> bool
Definition Position.hpp:481
auto get_open_files() const noexcept
Definition Position.hpp:486
std::uint_least8_t halfmoveClock
Definition Position.hpp:121
auto our_pieces() noexcept -> Pieces &
Definition Position.hpp:179
auto get_backward_pawns() const noexcept -> Bitboard
Definition Position.hpp:522
auto occupied() const noexcept -> Bitboard
Definition Position.hpp:231
auto their_pieces() const noexcept -> const Pieces &
Definition Position.hpp:206
auto get_passed_pawns() const noexcept -> Bitboard
Definition Position.hpp:508
std::uint_least64_t fullMoveCounter
Definition Position.hpp:127
auto pieces_for() const noexcept -> const Pieces &
Definition Position.hpp:160
auto is_illegal() const -> std::optional< std::string >
std::uint64_t Hash
Definition Position.hpp:130
auto is_capture(Move move) const noexcept -> bool
Definition Position.hpp:471
void make_move(Move move)
CastlingRights blackCastlingRights
Definition Position.hpp:101
auto castling_rights_for() const noexcept -> CastlingRights
Definition Position.hpp:170
auto is_quiet(Move move) const noexcept -> bool
Definition Position.hpp:476
auto operator==(const Position &other) const noexcept -> bool
Definition Position.hpp:140
CastlingRights whiteCastlingRights
Definition Position.hpp:98
auto is_stalemate() const -> bool
auto is_draw() const -> bool
auto free() const noexcept -> Bitboard
Definition Position.hpp:234
auto is_check() const noexcept -> bool
Definition Position.hpp:266
auto is_draw_by_insufficient_material() const noexcept -> bool
auto is_threefold_repetition() const noexcept -> bool
Definition Position.hpp:439
auto is_white_to_move() const noexcept -> bool
Definition Position.hpp:217
auto is_file_half_open(File file) const noexcept -> bool
Definition Position.hpp:492