diff --git a/engine/src/CMakeLists.txt b/engine/src/CMakeLists.txt index 1153389d8..0ee00ba23 100644 --- a/engine/src/CMakeLists.txt +++ b/engine/src/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(wisdom-chess-core STATIC board.cpp board.hpp - board_builder.cpp board_builder.hpp + board_builder.hpp board_code.cpp board_code.hpp check.cpp check.hpp coord.cpp coord.hpp diff --git a/engine/src/board.cpp b/engine/src/board.cpp index 31d636798..200710218 100644 --- a/engine/src/board.cpp +++ b/engine/src/board.cpp @@ -11,7 +11,7 @@ namespace wisdom static constexpr int Board_Length_In_Chars = 31; Board::Board() - : Board { BoardBuilder::fromDefaultPosition() } + : Board { default_board_builder } { } @@ -136,7 +136,7 @@ namespace wisdom auto convert = [color](char ch) -> char { return color == Color::Black - ? gsl::narrow_cast (tolower (ch)) + ? narrow_cast (tolower (ch)) : ch; }; @@ -175,9 +175,9 @@ namespace wisdom row_string += std::to_string (none_count); none_count = 0; - char ch = gsl::narrow_cast (toupper (pieceChar (piece))); + char ch = narrow_cast (toupper (pieceChar (piece))); if (pieceColor (piece) == Color::Black) - ch = gsl::narrow_cast (tolower (ch)); + ch = narrow_cast (tolower (ch)); row_string.append (1, ch); } @@ -268,7 +268,7 @@ namespace wisdom for (int8_t source_col = 0; source_col < Num_Columns; source_col++) { int8_t first_source_row = 0; - auto last_source_row = gsl::narrow (Num_Rows - 1); + auto last_source_row = narrow (Num_Rows - 1); removeInvalidPawns (result, first_source_row, source_col, result.my_squares); removeInvalidPawns (result, last_source_row, source_col, result.my_squares); @@ -304,7 +304,7 @@ namespace wisdom coord_end, finder ); - auto diff = gsl::narrow (result - coord_begin); + auto diff = narrow (result - coord_begin); return (result != coord_end) ? std::make_optional (Coord::fromIndex (diff)) diff --git a/engine/src/board.hpp b/engine/src/board.hpp index 08825596f..f5b93a722 100644 --- a/engine/src/board.hpp +++ b/engine/src/board.hpp @@ -251,7 +251,7 @@ namespace wisdom constexpr auto coordColor (Coord coord) -> Color { int parity = (coord.row() % 2 + coord.column() % 2) % 2; - return colorFromColorIndex (gsl::narrow_cast (parity)); + return colorFromColorIndex (narrow_cast (parity)); } // white moves up (-) @@ -262,6 +262,6 @@ namespace wisdom static_assert (std::is_integral_v); assert (color == Color::Black || color == Color::White); int8_t color_as_int = toInt8 (color); - return gsl::narrow_cast (-1 + 2 * (color_as_int - 1)); + return narrow_cast (-1 + 2 * (color_as_int - 1)); } } diff --git a/engine/src/board_builder.cpp b/engine/src/board_builder.cpp deleted file mode 100644 index 19d163492..000000000 --- a/engine/src/board_builder.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "board_builder.hpp" - -#include "board.hpp" -#include "move.hpp" -#include "coord.hpp" - -namespace wisdom -{ - auto BoardBuilder::fromDefaultPosition() -> BoardBuilder - { - auto result = BoardBuilder {}; - result.addRowOfSameColor (0, Color::Black, Default_Piece_Row); - result.addRowOfSameColorAndPiece (1, Color::Black, Piece::Pawn); - result.addRowOfSameColorAndPiece (6, Color::White, Piece::Pawn); - result.addRowOfSameColor (7, Color::White, Default_Piece_Row); - - result.setCastling (Color::White, Either_Side_Eligible); - result.setCastling (Color::Black, Either_Side_Eligible); - - return result; - } - - void BoardBuilder::addPiece (const string& coord_str, Color who, Piece piece_type) - { - if (coord_str.size() != 2) - throw BoardBuilderError ("Invalid coordinate string!"); - - Coord algebraic = coordParse (coord_str); - - addPiece (algebraic.row(), algebraic.column(), who, piece_type); - } - - auto BoardBuilder::calculateCastleStateFromPosition (Color who) const - -> CastlingEligibility - { - auto row = castlingRowForColor (who); - int8_t king_col = King_Column; - - CastlingEligibility state = Either_Side_Eligible; - ColoredPiece prospective_king = pieceAt (makeCoord (row, king_col)); - ColoredPiece prospective_queen_rook = pieceAt (makeCoord (row, 0)); - ColoredPiece prospective_king_rook = pieceAt (makeCoord (row, 7)); - - if (pieceType (prospective_king) != Piece::King || pieceColor (prospective_king) != who - || pieceType (prospective_queen_rook) != Piece::Rook - || pieceColor (prospective_queen_rook) != who) - { - state |= CastlingIneligible::Queenside; - } - if (pieceType (prospective_king) != Piece::King || pieceColor (prospective_king) != who - || pieceType (prospective_king_rook) != Piece::Rook - || pieceColor (prospective_king_rook) != who) - { - state |= CastlingIneligible::Kingside; - } - - return state; - } - - void BoardBuilder::addPiece (int row, int col, Color who, Piece piece_type) - { - if (row < 0 || row >= Num_Rows) - throw BoardBuilderError ("Invalid row!"); - - if (col < 0 || col >= Num_Columns) - throw BoardBuilderError ("Invalid column!"); - - if (piece_type == Piece::None) - return; - - auto coord = Coord::make (row, col); - my_squares[coord.index()] = ColoredPiece::make (who, piece_type); - - if (piece_type == Piece::King) - my_king_positions[colorIndex (who)] = coord; - } - - void BoardBuilder::addPieces (Color who, const vector& pieces) - { - for (auto&& it : pieces) - addPiece (it.coord, who, it.piece_type); - } - - void BoardBuilder::addRowOfSameColorAndPiece (int row, Color who, Piece piece_type) - { - for (int col = 0; col < Num_Columns; col++) - addPiece (row, col, who, piece_type); - } - - void - BoardBuilder::addRowOfSameColorAndPiece (const string& coord_str, Color who, Piece piece_type) - { - Coord coord = coordParse (coord_str); - - for (int col = 0; col < Num_Columns; col++) - addPiece (coord.row(), col, who, piece_type); - } - - void BoardBuilder::addRowOfSameColor (int row, Color who, const PieceRow& piece_types) - { - for (auto col = 0; col < Num_Columns; col++) - addPiece (row, col, who, piece_types[col]); - } - - void BoardBuilder::addRowOfSameColor ( - const string& coord_str, - Color who, - const PieceRow& piece_types - ) { - Coord coord = coordParse (coord_str); - - for (auto col = 0; col < Num_Columns; col++) - addPiece (coord.row(), col, who, piece_types[col]); - } - - void BoardBuilder::setCurrentTurn (Color who) - { - my_current_turn = who; - } - - void BoardBuilder::setEnPassantTarget (Color vulnerable_color, const string& coord_str) - { - my_en_passant_target = { - .coord = coordParse (coord_str), - .vulnerable_color = vulnerable_color - }; - } - - void BoardBuilder::setCastling (Color who, CastlingEligibility state) - { - my_castle_states[colorIndex (who)] = state; - } - - void BoardBuilder::setHalfMovesClock (int new_half_moves_clock) - { - my_half_moves_clock = new_half_moves_clock; - } - - void BoardBuilder::setFullMoves (int new_full_moves) - { - my_full_moves = new_full_moves; - } -} diff --git a/engine/src/board_builder.hpp b/engine/src/board_builder.hpp index ec122976c..1e5210636 100644 --- a/engine/src/board_builder.hpp +++ b/engine/src/board_builder.hpp @@ -6,6 +6,18 @@ namespace wisdom { + namespace + { + [[nodiscard]] consteval auto + emptySquares() + -> array + { + array result {}; + std::fill (std::begin (result), std::end (result), Piece_And_Color_None); + return result; + } + } + class BoardBuilderError : public Error { public: @@ -31,77 +43,177 @@ namespace wisdom { } - static auto fromDefaultPosition() -> BoardBuilder; + static constexpr PieceRow Default_Piece_Row = { + Piece::Rook, Piece::Knight, Piece::Bishop, Piece::Queen, + Piece::King, Piece::Bishop, Piece::Knight, Piece::Rook, + }; - [[nodiscard]] static constexpr auto emptySquares() - -> array + static constexpr auto + fromDefaultPosition() + -> BoardBuilder { - array result {}; - std::fill (std::begin (result), std::end (result), Piece_And_Color_None); + auto result = BoardBuilder {}; + result.addRowOfSameColor (0, Color::Black, Default_Piece_Row); + result.addRowOfSameColorAndPiece (1, Color::Black, Piece::Pawn); + result.addRowOfSameColorAndPiece (6, Color::White, Piece::Pawn); + result.addRowOfSameColor (7, Color::White, Default_Piece_Row); + + result.setCastling (Color::White, Either_Side_Eligible); + result.setCastling (Color::Black, Either_Side_Eligible); + return result; } - static constexpr PieceRow Default_Piece_Row = { - Piece::Rook, Piece::Knight, Piece::Bishop, Piece::Queen, - Piece::King, Piece::Bishop, Piece::Knight, Piece::Rook, - }; + constexpr void + addPiece (string_view coord_str, Color who, Piece piece_type) + { + if (coord_str.size() != 2) + throw BoardBuilderError ("Invalid coordinate string!"); - void addPiece (const string& coord_str, Color who, Piece piece_type); + Coord algebraic = coordParse (coord_str); - void addPiece (int row, int col, Color who, Piece piece_type); + addPiece (algebraic.row(), algebraic.column(), who, piece_type); + } - void addPieces (Color who, const vector& pieces); + constexpr void + addPiece (int row, int col, Color who, Piece piece_type) + { + if (row < 0 || row >= Num_Rows) + throw BoardBuilderError ("Invalid row!"); - void addRowOfSameColor (int row, Color who, const PieceRow& piece_types); + if (col < 0 || col >= Num_Columns) + throw BoardBuilderError ("Invalid column!"); - void addRowOfSameColor (const string& coord_str, Color who, const PieceRow& piece_types); + if (piece_type == Piece::None) + return; - void addRowOfSameColorAndPiece (int row, Color who, Piece piece_type); + auto coord = Coord::make (row, col); + my_squares[coord.index()] = ColoredPiece::make (who, piece_type); - void addRowOfSameColorAndPiece (const string& coord_str, Color who, Piece piece_type); + if (piece_type == Piece::King) + my_king_positions[colorIndex (who)] = coord; + } - void setEnPassantTarget (Color vulnerable_color, const string& coord_str); + // Not constexpr because of dynamic vector: + void addPieces (Color who, const vector& pieces) + { + for (auto&& it : pieces) + { + string_view str_view { it.coord }; + addPiece (str_view, who, it.piece_type); + } + } - void setCastling (Color who, CastlingEligibility state); + constexpr void + addRowOfSameColorAndPiece (int row, Color who, Piece piece_type) + { + for (int col = 0; col < Num_Columns; col++) + addPiece (row, col, who, piece_type); + } - void setCurrentTurn (Color who); + constexpr void + addRowOfSameColorAndPiece (string_view coord_str, Color who, Piece piece_type) + { + Coord coord = coordParse (coord_str); + + for (int col = 0; col < Num_Columns; col++) + addPiece (coord.row(), col, who, piece_type); + } + + constexpr void + addRowOfSameColor (int row, Color who, const PieceRow& piece_types) + { + for (auto col = 0; col < Num_Columns; col++) + addPiece (row, col, who, piece_types[col]); + } + + constexpr void + addRowOfSameColor ( + string_view coord_str, + Color who, + const PieceRow& piece_types + ) { + Coord coord = coordParse (coord_str); + + for (auto col = 0; col < Num_Columns; col++) + addPiece (coord.row(), col, who, piece_types[col]); + } + + constexpr void + setCurrentTurn (Color who) + { + my_current_turn = who; + } + + constexpr void + setEnPassantTarget (Color vulnerable_color, string_view coord_str) + { + my_en_passant_target = { + .coord = coordParse (coord_str), + .vulnerable_color = vulnerable_color + }; + } - void setHalfMovesClock (int new_half_moves_clock); + constexpr void + setCastling (Color who, CastlingEligibility state) + { + my_castle_states[colorIndex (who)] = state; + } - void setFullMoves (int new_full_moves); + constexpr void + setHalfMovesClock (int new_half_moves_clock) + { + my_half_moves_clock = new_half_moves_clock; + } + + constexpr void + setFullMoves (int new_full_moves) + { + my_full_moves = new_full_moves; + } - [[nodiscard]] auto getSquares() const& -> const array& + [[nodiscard]] constexpr auto + getSquares() const& + -> const array& { return my_squares; } void getSquares() const&& = delete; - [[nodiscard]] auto pieceAt (Coord coord) const -> ColoredPiece + [[nodiscard]] constexpr auto + pieceAt (Coord coord) const + -> ColoredPiece { assert (coord.index() < Num_Squares); return my_squares[coord.index()]; } - [[nodiscard]] auto getCurrentTurn() const -> Color + [[nodiscard]] constexpr auto + getCurrentTurn() const + -> Color { return my_current_turn; } - [[nodiscard]] auto + [[nodiscard]] constexpr auto getEnPassantTarget() const -> optional { return my_en_passant_target; } - [[nodiscard]] auto getCastleState (Color who) const -> CastlingEligibility + [[nodiscard]] constexpr auto + getCastleState (Color who) const + -> CastlingEligibility { if (my_castle_states[colorIndex (who)]) return *my_castle_states[colorIndex (who)]; return calculateCastleStateFromPosition (who); } - [[nodiscard]] auto getKingPosition (Color who) -> Coord + [[nodiscard]] constexpr auto + getKingPosition (Color who) + -> Coord { auto index = colorIndex (who); if (!my_king_positions[index]) @@ -109,26 +221,57 @@ namespace wisdom return *my_king_positions[index]; } - [[nodiscard]] auto getKingPositions() const -> array + [[nodiscard]] constexpr auto + getKingPositions() const + -> array { if (!my_king_positions[Color_Index_White] || !my_king_positions[Color_Index_Black]) throw BoardBuilderError { "Missing king position in constructing board." }; return { *my_king_positions[Color_Index_White], *my_king_positions[Color_Index_Black] }; } - [[nodiscard]] auto getHalfMoveClock() const -> int + [[nodiscard]] constexpr auto + getHalfMoveClock() const + -> int { return my_half_moves_clock; } - [[nodiscard]] auto getFullMoveClock() const -> int + [[nodiscard]] constexpr auto + getFullMoveClock() const + -> int { return my_full_moves; } private: - [[nodiscard]] auto calculateCastleStateFromPosition (Color who) const - -> CastlingEligibility; + [[nodiscard]] constexpr auto + calculateCastleStateFromPosition (Color who) const + -> CastlingEligibility + { + auto row = castlingRowForColor (who); + int8_t king_col = King_Column; + + CastlingEligibility state = Either_Side_Eligible; + ColoredPiece prospective_king = pieceAt (makeCoord (row, king_col)); + ColoredPiece prospective_queen_rook = pieceAt (makeCoord (row, 0)); + ColoredPiece prospective_king_rook = pieceAt (makeCoord (row, 7)); + + if (pieceType (prospective_king) != Piece::King || pieceColor (prospective_king) != who + || pieceType (prospective_queen_rook) != Piece::Rook + || pieceColor (prospective_queen_rook) != who) + { + state |= CastlingIneligible::Queenside; + } + if (pieceType (prospective_king) != Piece::King || pieceColor (prospective_king) != who + || pieceType (prospective_king_rook) != Piece::Rook + || pieceColor (prospective_king_rook) != who) + { + state |= CastlingIneligible::Kingside; + } + + return state; + } private: array my_squares; @@ -137,7 +280,7 @@ namespace wisdom int my_full_moves = 0; Color my_current_turn = Color::White; - array, Num_Players> my_castle_states { + array, Num_Players> my_castle_states { nullopt, nullopt }; @@ -150,4 +293,16 @@ namespace wisdom optional my_en_passant_target { nullopt }; }; + namespace + { + consteval auto + initDefaultBoardBuilder() + -> BoardBuilder + { + return BoardBuilder::fromDefaultPosition(); + } + } + + inline constexpr BoardBuilder default_board_builder = + initDefaultBoardBuilder(); } diff --git a/engine/src/board_code.hpp b/engine/src/board_code.hpp index e7f73b617..2d040f3e9 100644 --- a/engine/src/board_code.hpp +++ b/engine/src/board_code.hpp @@ -132,7 +132,7 @@ namespace wisdom target_bits &= EN_PASSANT_MASK << EN_PASSANT_TARGET_BIT; target_bits >>= target_bit_shift; - auto col = gsl::narrow (target_bits & 0x7); + auto col = narrow (target_bits & 0x7); bool is_present = ((target_bits & EN_PASSANT_PRESENT) > 0); Color vulnerable_color = ((target_bits & EN_PASSANT_IS_WHITE) > 0) ? Color::White @@ -193,7 +193,7 @@ namespace wisdom -> Color { auto bits = getMetadataBits(); - auto index = gsl::narrow_cast ( + auto index = narrow_cast ( bits & (CURRENT_TURN_MASK << CURRENT_TURN_BIT) ); return colorFromColorIndex (index); @@ -203,7 +203,7 @@ namespace wisdom getMetadataBits() const noexcept -> std::uint16_t { - return gsl::narrow_cast (my_code & 0xffff); + return narrow_cast (my_code & 0xffff); } [[nodiscard]] auto diff --git a/engine/src/check.cpp b/engine/src/check.cpp index ae51f42e2..5d27a0d9a 100644 --- a/engine/src/check.cpp +++ b/engine/src/check.cpp @@ -8,14 +8,14 @@ namespace wisdom { - bool isCheckmated (const Board& board, Color who, MoveGenerator& generator) + bool isCheckmated (const Board& board, Color who) { auto coord = board.getKingPosition (who); if (!isKingThreatened (board, who, coord)) return false; - MoveList legal_moves = generator.generateLegalMoves (board, who); + MoveList legal_moves = MoveGenerator::generateLegalMoves (board, who); return legal_moves.isEmpty(); } @@ -54,10 +54,10 @@ namespace wisdom return true; } - auto isStalemated (const Board& board, Color who, MoveGenerator& generator) -> bool + auto isStalemated (const Board& board, Color who) -> bool { auto coord = board.getKingPosition (who); - auto legal_moves = generator.generateLegalMoves (board, who); + auto legal_moves = MoveGenerator::generateLegalMoves (board, who); return legal_moves.isEmpty() && !isKingThreatened (board, who, coord); } diff --git a/engine/src/check.hpp b/engine/src/check.hpp index 44b1f7fe7..067afe974 100644 --- a/engine/src/check.hpp +++ b/engine/src/check.hpp @@ -64,11 +64,13 @@ namespace wisdom } // Whether the board is in a checkmated position for the computer_player. - [[nodiscard]] auto isCheckmated (const Board& board, Color who, MoveGenerator& generator) + [[nodiscard]] auto + isCheckmated (const Board& board, Color who) -> bool; // Whether in a stalemate position for white or black. - [[nodiscard]] auto isStalemated (const Board& board, Color who, MoveGenerator& generator) + [[nodiscard]] auto + isStalemated (const Board& board, Color who) -> bool; // Whether this move could cause a draw. diff --git a/engine/src/coord.cpp b/engine/src/coord.cpp index a31196200..154921fe7 100644 --- a/engine/src/coord.cpp +++ b/engine/src/coord.cpp @@ -5,20 +5,6 @@ namespace wisdom { - auto coordParse (const string& str) -> Coord - { - if (str.size() != 2) - throw CoordParseError ("Invalid coordinate!"); - - int col = charToCol (str.at (0)); - int row = charToRow (str.at (1)); - - if (!isValidRow (row) || !isValidColumn (col)) - throw CoordParseError ("Invalid coordinate!"); - - return makeCoord (row, col); - } - auto asString (Coord coord) -> string { string result = ""; // NOLINT(readability-redundant-string-init) diff --git a/engine/src/coord.hpp b/engine/src/coord.hpp index 55f41d7f4..6271b7882 100644 --- a/engine/src/coord.hpp +++ b/engine/src/coord.hpp @@ -5,19 +5,32 @@ namespace wisdom { template - constexpr auto isValidRow (IntegerType row) -> bool + constexpr auto + isValidRow (IntegerType row) + -> bool { static_assert (std::is_integral_v); return row >= 0 && row < Num_Rows; } template - constexpr auto isValidColumn (IntegerType col) -> bool + constexpr auto + isValidColumn (IntegerType col) + -> bool { static_assert (std::is_integral_v); return col >= 0 && col < Num_Columns; } + class CoordParseError : public Error + { + public: + explicit CoordParseError (string message) + : Error (std::move (message)) + { + } + }; + struct Coord { int8_t row_and_col; @@ -28,7 +41,7 @@ namespace wisdom -> Coord { assert (index >= 0 && index < Num_Squares); - return { .row_and_col = gsl::narrow_cast (index) }; + return { .row_and_col = narrow_cast (index) }; } [[nodiscard]] static constexpr auto @@ -36,7 +49,7 @@ namespace wisdom -> Coord { assert (isValidRow (row) && isValidColumn (col)); - Coord result = { .row_and_col = gsl::narrow_cast (row << 3 | col) }; + Coord result = { .row_and_col = narrow_cast (row << 3 | col) }; return result; } @@ -46,7 +59,7 @@ namespace wisdom index() -> IntegerType { - return gsl::narrow_cast (row_and_col); + return narrow_cast (row_and_col); } template @@ -55,7 +68,7 @@ namespace wisdom -> IntegerType { static_assert (std::is_integral_v); - return gsl::narrow_cast (row_and_col >> 3); + return narrow_cast (row_and_col >> 3); } template @@ -64,26 +77,32 @@ namespace wisdom -> IntegerType { static_assert (std::is_integral_v); - return gsl::narrow_cast (row_and_col & 0b111); + return narrow_cast (row_and_col & 0b111); } }; static_assert (std::is_trivial_v); template - constexpr auto nextRow (IntegerType row, int direction) -> IntegerType + constexpr auto + nextRow (IntegerType row, int direction) + -> IntegerType { static_assert (std::is_integral_v); - return gsl::narrow_cast (row + direction); + return narrow_cast (row + direction); } template - constexpr auto nextColumn (T col, int direction) -> T + constexpr auto + nextColumn (T col, int direction) + -> T { static_assert (std::is_integral_v); - return gsl::narrow_cast (col + direction); + return narrow_cast (col + direction); } - constexpr auto makeCoord (int row, int col) -> Coord + constexpr auto + makeCoord (int row, int col) + -> Coord { return Coord::make (row, col); } @@ -92,18 +111,24 @@ namespace wisdom constexpr Coord End_Coord = { .row_and_col = Num_Squares }; template - [[nodiscard]] constexpr auto coordRow (Coord pos) -> IntegerType + [[nodiscard]] constexpr auto + coordRow (Coord pos) + -> IntegerType { return pos.row(); } template - [[nodiscard]] constexpr auto coordColumn (Coord pos) -> IntegerType + [[nodiscard]] constexpr auto + coordColumn (Coord pos) + -> IntegerType { return pos.column(); } - [[nodiscard]] constexpr auto nextCoord (Coord coord, int direction) -> optional + [[nodiscard]] constexpr auto + nextCoord (Coord coord, int direction) + -> optional { Expects (direction == +1 || direction == -1); int index = coord.index(); @@ -115,47 +140,74 @@ namespace wisdom return Coord::fromIndex (index); } - constexpr auto operator== (Coord first, Coord second) -> bool + constexpr auto + operator== (Coord first, Coord second) + -> bool { return first.row_and_col == second.row_and_col; } - constexpr bool operator!= (Coord first, Coord second) + constexpr bool + operator!= (Coord first, Coord second) { return !operator== (first, second); } - constexpr auto operator++ (Coord& coord) -> Coord& + constexpr auto + operator++ (Coord& coord) + -> Coord& { coord.row_and_col++; return coord; } - static inline int charToRow (char chr) + static constexpr auto + charToRow (char chr) + -> int { - return 8 - (tolower (chr) - '0'); + return 8 - (toLower (chr) - '0'); } - static inline int charToCol (char chr) + static constexpr auto + charToCol (char chr) + -> int { - return tolower (chr) - 'a'; + return toLower (chr) - 'a'; } - constexpr char rowToChar (int8_t row) + constexpr auto + rowToChar (int8_t row) + -> char { assert (isValidRow (row)); - return gsl::narrow (8 - row + '0'); + return narrow (8 - row + '0'); } - constexpr char colToChar (int8_t col) + constexpr auto + colToChar (int8_t col) + -> char { assert (isValidColumn (col)); - return gsl::narrow (col + 'a'); + return narrow (col + 'a'); } auto asString (Coord coord) -> string; - auto coordParse (const string& str) -> Coord; + constexpr auto + coordParse (string_view str) + -> Coord + { + if (str.size() != 2) + throw CoordParseError ("Invalid coordinate!"); + + int col = charToCol (str.at (0)); + int row = charToRow (str.at (1)); + + if (!isValidRow (row) || !isValidColumn (col)) + throw CoordParseError ("Invalid coordinate!"); + + return makeCoord (row, col); + } auto operator<< (std::ostream& ostream, Coord coord) -> std::ostream&; @@ -167,41 +219,53 @@ namespace wisdom using reference = Coord&; using iterator_category = std::forward_iterator_tag; - CoordIterator() + constexpr CoordIterator() : my_coord { First_Coord } {} - explicit CoordIterator (Coord coord) + explicit constexpr CoordIterator (Coord coord) : my_coord (coord) {} - [[nodiscard]] auto begin() -> CoordIterator // NOLINT(readability-convert-member-functions-to-static) + [[nodiscard]] constexpr auto + begin() + -> CoordIterator // NOLINT(readability-convert-member-functions-to-static) { return CoordIterator { First_Coord }; } - [[nodiscard]] auto end() -> CoordIterator // NOLINT(readability-convert-member-functions-to-static) + [[nodiscard]] constexpr auto + end() + -> CoordIterator // NOLINT(readability-convert-member-functions-to-static) { return CoordIterator { End_Coord }; } - auto operator*() const -> Coord + constexpr auto + operator*() const + -> Coord { return my_coord; } - auto operator++() -> CoordIterator& + constexpr auto + operator++() + -> CoordIterator& { ++my_coord; return *this; } - auto operator== (const CoordIterator& other) const -> bool + constexpr auto + operator== (const CoordIterator& other) const + -> bool { return other.my_coord == my_coord; } - auto operator!= (const CoordIterator& other) const -> bool + constexpr auto + operator!= (const CoordIterator& other) const + -> bool { return !(*this == other); } @@ -210,12 +274,4 @@ namespace wisdom Coord my_coord {}; }; - class CoordParseError : public Error - { - public: - explicit CoordParseError (string message) - : Error (std::move (message)) - { - } - }; } diff --git a/engine/src/evaluate.cpp b/engine/src/evaluate.cpp index 7d50b5d7b..87a46accb 100644 --- a/engine/src/evaluate.cpp +++ b/engine/src/evaluate.cpp @@ -53,7 +53,7 @@ namespace wisdom } } - auto evaluate (const Board& board, Color who, int moves_away, MoveGenerator& generator) -> int + auto evaluate (const Board& board, Color who, int moves_away) -> int { int score = 0; Color opponent = colorInvert (who); @@ -61,11 +61,11 @@ namespace wisdom score += board.getMaterial().overallScore (who); score += board.getPosition().overallScore (who); - if (isCheckmated (board, who, generator)) + if (isCheckmated (board, who)) { score = -1 * checkmateScoreInMoves (moves_away); } - else if (isCheckmated (board, opponent, generator)) + else if (isCheckmated (board, opponent)) { score = checkmateScoreInMoves (moves_away); } diff --git a/engine/src/evaluate.hpp b/engine/src/evaluate.hpp index a7e2fcb32..8330cc9a4 100644 --- a/engine/src/evaluate.hpp +++ b/engine/src/evaluate.hpp @@ -13,7 +13,7 @@ namespace wisdom class MoveGenerator; // Evaluate the board. - auto evaluate (const Board& board, Color who, int moves_away, MoveGenerator& generator) -> int; + auto evaluate (const Board& board, Color who, int moves_away) -> int; // When there are no legal moves present, return the score of this move, which // checks for either a stalemate or checkmate position. diff --git a/engine/src/game.cpp b/engine/src/game.cpp index e27ca752e..d87ba07d9 100644 --- a/engine/src/game.cpp +++ b/engine/src/game.cpp @@ -79,10 +79,10 @@ namespace wisdom auto Game::status() const -> GameStatus { - if (isCheckmated (my_current_board, getCurrentTurn(), my_move_generator)) + if (isCheckmated (my_current_board, getCurrentTurn())) return GameStatus::Checkmate; - if (isStalemated (my_current_board, getCurrentTurn(), my_move_generator)) + if (isStalemated (my_current_board, getCurrentTurn())) return GameStatus::Stalemate; if (my_history.isThirdRepetition (my_current_board)) @@ -203,14 +203,9 @@ namespace wisdom return my_history; } - auto Game::getMoveGenerator() const& -> MoveGenerator& - { - return my_move_generator; - } - auto Game::computerWantsDraw (Color who) const -> bool { - int score = evaluate (my_current_board, who, 1, my_move_generator); + int score = evaluate (my_current_board, who, 1); return score <= Min_Draw_Score; } diff --git a/engine/src/game.hpp b/engine/src/game.hpp index e35b1691c..62789357c 100644 --- a/engine/src/game.hpp +++ b/engine/src/game.hpp @@ -69,9 +69,6 @@ namespace wisdom [[nodiscard]] auto getHistory() & -> History&; [[nodiscard]] auto getHistory() && -> History& = delete; - [[nodiscard]] auto getMoveGenerator() const& -> MoveGenerator&; - [[nodiscard]] auto getMoveGenerator() const&& -> MoveGenerator& = delete; - [[nodiscard]] auto getCurrentPlayer() const -> Player { return getPlayer (my_current_board.getCurrentTurn()); @@ -160,7 +157,6 @@ namespace wisdom private: Board my_current_board {}; - mutable MoveGenerator my_move_generator {}; History my_history; optional my_periodic_function {}; diff --git a/engine/src/generate.cpp b/engine/src/generate.cpp index 36231099a..1d201518b 100644 --- a/engine/src/generate.cpp +++ b/engine/src/generate.cpp @@ -12,7 +12,6 @@ namespace wisdom int piece_row; int piece_col; const Color who; - const MoveGenerator& generator; void generate (ColoredPiece piece, Coord coord); @@ -47,51 +46,6 @@ namespace wisdom } } - void MoveGenerator::knightMoveListInit() const - { - for (auto coord : CoordIterator {}) - { - int row = coord.row(); - int col = coord.column(); - - for (int k_row = -2; k_row <= 2; k_row++) - { - if (!k_row) - continue; - - if (!isValidRow (k_row + row)) - continue; - - for (int k_col = 3 - abs (k_row); k_col >= -2; k_col -= 2 * abs (k_col)) - { - if (!isValidColumn (k_col + col)) - continue; - - Move knight_move = Move::make ( - k_row + row, - k_col + col, - row, - col - ); - int dst_row = k_row + row; - int dst_col = k_col + col; - auto index = Coord::make (dst_row, dst_col).index(); - my_knight_moves[index].append (knight_move); - } - } - } - } - - auto MoveGenerator::generateKnightMoves (int row, int col) const -> const MoveList& - { - auto coord = Coord::make (row, col); - - if (my_knight_moves[0].isEmpty()) - knightMoveListInit(); - - return my_knight_moves[coord.index()]; - } - static auto isPawnUnmoved (const Board& board, int row, int col) -> bool { ColoredPiece piece = board.pieceAt (row, col); @@ -257,7 +211,7 @@ namespace wisdom void MoveGeneration::knight() { - const auto& kt_moves = generator.generateKnightMoves (piece_row, piece_col); + const auto& kt_moves = MoveGenerator::getKnightMoveList (piece_row, piece_col); for (const auto& knight_move : kt_moves) appendMove (knight_move); @@ -406,7 +360,8 @@ namespace wisdom appendMove (new_move); } - auto MoveGenerator::generateLegalMoves (const Board& board, Color who) const -> MoveList + auto + MoveGenerator::generateLegalMoves (const Board& board, Color who) -> MoveList { MoveList non_checks; @@ -527,11 +482,12 @@ namespace wisdom return promotingOrCoordCompare (a, b); } - auto MoveGenerator::generateAllPotentialMoves (const Board& board, Color who) const + auto + MoveGenerator::generateAllPotentialMoves (const Board& board, Color who) -> MoveList { MoveList result; - MoveGeneration generation { board, result, 0, 0, who, *this }; + MoveGeneration generation { board, result, 0, 0, who }; for (auto coord : Board::allCoords()) { diff --git a/engine/src/generate.hpp b/engine/src/generate.hpp index 00b6aefc7..87d9eb97e 100644 --- a/engine/src/generate.hpp +++ b/engine/src/generate.hpp @@ -9,33 +9,108 @@ namespace wisdom { struct MoveGeneration; - class MoveGenerator final + struct KnightMoveList { - private: - mutable array my_knight_moves {}; + size_t size; + array moves; + }; - void knightMoveListInit() const; + using KnightMoveLists = array; - [[nodiscard]] auto generateKnightMoves (int row, int col) const -> const MoveList&; + namespace + { + consteval auto + absoluteValue (auto integer) + -> decltype (integer) + { + static_assert (std::is_integral_v); + return integer < 0 ? integer * -1 : integer; + } - public: + consteval auto + knightMoveListInit() + -> KnightMoveLists + { + KnightMoveLists result {}; + + for (auto coord : CoordIterator {}) + { + auto row = coord.row(); + auto col = coord.column(); + + for (int k_row = -2; k_row <= 2; k_row++) + { + if (!k_row) + continue; + + if (!isValidRow (k_row + row)) + continue; + + for (auto k_col = 3 - absoluteValue (k_row); + k_col >= -2; + k_col -= 2 * absoluteValue (k_col)) + { + if (!isValidColumn (k_col + col)) + continue; + + Move knight_move = Move::make ( + k_row + row, + k_col + col, + row, + col + ); + auto index = knight_move.getSrc().index(); + + auto& size_ref = result[index].size; + auto& array_ref = result[index].moves; + array_ref[size_ref] = knight_move; + size_ref++; + } + } + } + return result; + } + } + + class MoveGenerator final + { MoveGenerator() = default; - [[nodiscard]] auto generateAllPotentialMoves (const Board& board, Color who) const + public: + [[nodiscard]] static auto + generateAllPotentialMoves (const Board& board, Color who) -> MoveList; - [[nodiscard]] auto generateLegalMoves (const Board& board, Color who) const + [[nodiscard]] static auto + generateLegalMoves (const Board& board, Color who) -> MoveList; - friend class MoveGeneration; + // Store a list of knight moves and their sizes, generated at + // compile-time: + static constexpr KnightMoveLists Knight_Moves = + knightMoveListInit(); + + // Get a std::span of the knight move list and the compile-time + // calculated length: + [[nodiscard]] static auto + getKnightMoveList (int row, int col) + -> span + { + auto coord = Coord::make (row, col); + const auto square = coord.index(); + + const auto& list = Knight_Moves[square]; + return { list.moves.data(), list.size }; + } }; - [[nodiscard]] auto needPawnPromotion (int row, Color who) + [[nodiscard]] auto + needPawnPromotion (int row, Color who) -> bool; // Return en passant column if the board is the player is eligible. - [[nodiscard]] auto eligibleEnPassantColumn (const Board& board, - int row, int column, Color who) + [[nodiscard]] auto + eligibleEnPassantColumn (const Board& board, int row, int column, Color who) -> optional; } diff --git a/engine/src/global.hpp b/engine/src/global.hpp index f1e69732d..1e40dcb88 100644 --- a/engine/src/global.hpp +++ b/engine/src/global.hpp @@ -50,6 +50,8 @@ namespace wisdom using std::string; using std::unique_ptr; using std::vector; + using std::string_view; + using std::span; template using observer_ptr = T*; @@ -122,6 +124,62 @@ namespace wisdom // accept a draw offer. inline constexpr int Min_Draw_Score = -500; + // constexpr versoin of narrow_cast (no exception at runtime: + template constexpr auto + narrow_cast (Source value) noexcept + -> Target + { + static_assert (std::is_arithmetic_v); + static_assert (std::is_arithmetic_v); + + // Check if Source can fit into Target without truncation + if (std::is_constant_evaluated()) + { + if (value < std::numeric_limits::min() || + value > std::numeric_limits::max()) + { + // At compile-time, trigger an error if there's truncation + std::terminate (); + } + } + + return gsl::narrow_cast (value); + } + + // constexpr versoin of narrow (exception at runtime): + template constexpr auto + narrow (Source value) + -> Target + { + static_assert (std::is_arithmetic_v); + static_assert (std::is_arithmetic_v); + + // Check if Source can fit into Target without truncation + if (std::is_constant_evaluated()) + { + if (value < std::numeric_limits::min() || + value > std::numeric_limits::max()) + { + // At compile-time, trigger an error if there's truncation + throw std::runtime_error("narrow_cast: narrowing occurred"); + } + } + + return gsl::narrow (value); + } + + // constexpr version of tolower(): + constexpr auto + toLower (int ch) noexcept + -> int + { + if (ch >= 'A' && ch <= 'Z') + { + return ch + ('a' - 'A'); + } + return ch; + } + // Errors in this application. class Error : public std::exception { diff --git a/engine/src/move.hpp b/engine/src/move.hpp index f6d416783..d35c1b9da 100644 --- a/engine/src/move.hpp +++ b/engine/src/move.hpp @@ -136,10 +136,10 @@ namespace wisdom -> Move { return Move { - .src = gsl::narrow_cast (src.index()), - .dst = gsl::narrow_cast (dst.index()), - .promoted_piece = gsl::narrow_cast (0), - .move_category = gsl::narrow_cast (0), + .src = narrow_cast (src.index()), + .dst = narrow_cast (dst.index()), + .promoted_piece = narrow_cast (0), + .move_category = narrow_cast (0), }; } @@ -239,10 +239,10 @@ namespace wisdom fromInt (int packed_move) -> Move { return Move { - .src = gsl::narrow (packed_move & 0x7f), - .dst = gsl::narrow ((packed_move >> 8) & 0x7f), - .promoted_piece = gsl::narrow ((packed_move >> 16) & 0x7f), - .move_category = gsl::narrow ((packed_move >> 24) & 0x7f), + .src = narrow_cast (packed_move & 0x7f), + .dst = narrow_cast ((packed_move >> 8) & 0x7f), + .promoted_piece = narrow_cast ((packed_move >> 16) & 0x7f), + .move_category = narrow_cast ((packed_move >> 24) & 0x7f), }; } @@ -272,7 +272,7 @@ namespace wisdom Move result = *this; auto promoted_piece_type = toInt8 (pieceType (piece)); auto promoted_color_index = colorIndex (pieceColor (piece)); - result.promoted_piece = gsl::narrow_cast ( + result.promoted_piece = narrow_cast ( (promoted_color_index << 4) | (promoted_piece_type & 0xf) ); return result; @@ -359,7 +359,7 @@ namespace wisdom -> IntegerType { static_assert (std::is_integral_v); - return gsl::narrow_cast (who == Color::White ? Last_Row : First_Row); + return narrow_cast (who == Color::White ? Last_Row : First_Row); } using PlayerCastleState = array; diff --git a/engine/src/move_list.cpp b/engine/src/move_list.cpp index 95db804f0..3d061aa9d 100644 --- a/engine/src/move_list.cpp +++ b/engine/src/move_list.cpp @@ -4,16 +4,6 @@ namespace wisdom { - MoveList::MoveList (Color color, std::initializer_list list) noexcept - : MoveList {} - { - for (auto&& it : list) - { - append (moveParse (it, color)); - color = colorInvert (color); - } - } - auto MoveList::asString() const -> string { string result = "{ "; diff --git a/engine/src/move_list.hpp b/engine/src/move_list.hpp index 4dc4c43ee..5e15639d6 100644 --- a/engine/src/move_list.hpp +++ b/engine/src/move_list.hpp @@ -16,11 +16,19 @@ namespace wisdom array my_moves; public: - MoveList() = default; + constexpr MoveList() = default; // NOLINT(*-pro-type-member-init) - MoveList (Color color, std::initializer_list list) noexcept; + constexpr MoveList (Color color, std::initializer_list list) noexcept + : MoveList {} + { + for (auto&& it : list) + { + append (moveParse (it, color)); + color = colorInvert (color); + } + } - MoveList (const MoveList& other) // NOLINT(*-pro-type-member-init) + constexpr MoveList (const MoveList& other) // NOLINT(*-pro-type-member-init) { std::copy ( other.my_moves.begin(), @@ -30,7 +38,9 @@ namespace wisdom my_size = other.my_size; } - auto operator= (const MoveList& other) -> MoveList& + constexpr auto + operator= (const MoveList& other) + -> MoveList& { if (&other != this) { @@ -44,87 +54,108 @@ namespace wisdom return *this; } - void push_back (Move move) noexcept + constexpr void + push_back (Move move) noexcept { append (move); } - void pop_back() noexcept + constexpr void + pop_back() noexcept { removeLast(); } - void append (Move move) noexcept + constexpr void + append (Move move) noexcept { - Expects (my_size < Max_Move_List_Size); my_moves[my_size++] = move; } - void removeLast() noexcept + constexpr void + removeLast() noexcept { Expects (my_size > 0); my_size--; } - [[nodiscard]] auto begin() const noexcept + [[nodiscard]] + constexpr auto + begin() const noexcept { return my_moves.begin(); } - [[nodiscard]] auto end() const noexcept + [[nodiscard]] constexpr auto + end() const noexcept { return my_moves.begin() + my_size; } - [[nodiscard]] auto cbegin() const noexcept + [[nodiscard]] constexpr auto + cbegin() const noexcept { return my_moves.cbegin(); } - [[nodiscard]] auto cend() const noexcept + [[nodiscard]] constexpr auto + cend() const noexcept { return my_moves.cbegin() + my_size; } - [[nodiscard]] auto begin() noexcept + [[nodiscard]] constexpr auto + begin() noexcept { return my_moves.begin(); } - [[nodiscard]] auto end() noexcept + [[nodiscard]] constexpr auto + end() noexcept { return my_moves.begin() + my_size; } - [[nodiscard]] auto empty() const noexcept -> bool + [[nodiscard]] constexpr auto + empty() const noexcept + -> bool { return isEmpty(); } - [[nodiscard]] auto isEmpty() const noexcept -> bool + [[nodiscard]] constexpr auto + isEmpty() const noexcept + -> bool { return my_size == 0; } - [[nodiscard]] auto size() const noexcept -> size_t + [[nodiscard]] constexpr auto + size() const noexcept + -> size_t { return my_size; } [[nodiscard]] auto asString() const -> string; - auto operator== (const MoveList& other) const -> bool + constexpr auto + operator== (const MoveList& other) const + -> bool { return size() == other.size() && std::equal (begin(), end(), other.begin()); } - auto operator!= (const MoveList& other) const -> bool + constexpr auto + operator!= (const MoveList& other) const + -> bool { return !(*this == other); } - [[nodiscard]] auto data() const& noexcept + [[nodiscard]] constexpr auto + data() const& noexcept { return my_moves; } diff --git a/engine/src/piece.hpp b/engine/src/piece.hpp index b62a98c44..0b5ae025c 100644 --- a/engine/src/piece.hpp +++ b/engine/src/piece.hpp @@ -92,16 +92,20 @@ namespace wisdom constexpr auto colorIndex (Color who) -> ColorIndex { assert (who == Color::White || who == Color::Black); - return gsl::narrow_cast (toInt8 (who) - 1); + return narrow_cast (toInt8 (who) - 1); } - constexpr auto colorInvert (Color who) -> Color + constexpr auto + colorInvert (Color who) + -> Color { assert (isColorValid (who)); - return colorFromColorIndex (gsl::narrow_cast (!colorIndex (who))); + return colorFromColorIndex (narrow_cast (!colorIndex (who))); } - constexpr auto pieceIndex (Piece piece) -> int + constexpr auto + pieceIndex (Piece piece) + -> int { auto piece_as_int = static_cast (piece); assert (piece_as_int >= toInt8 (Piece::None) && piece_as_int <= toInt8 (Piece::King)); @@ -126,7 +130,7 @@ namespace wisdom (piece_type != Piece::None && color != Color::None)); auto color_as_int = toInt8 (color); auto piece_as_int = toInt8 (piece_type); - auto result = gsl::narrow_cast( + auto result = narrow_cast( (color_as_int << Piece_Color_Shift) | (piece_as_int & Piece_Type_Mask) ); @@ -136,7 +140,7 @@ namespace wisdom [[nodiscard]] constexpr auto color() const noexcept -> Color { - auto result = gsl::narrow_cast ( + auto result = narrow_cast ( (piece_type_and_color & Piece_Color_Mask) >> Piece_Color_Shift ); return colorFromInt8 (result); @@ -144,7 +148,7 @@ namespace wisdom [[nodiscard]] constexpr auto type() const noexcept -> Piece { - auto result = gsl::narrow_cast( + auto result = narrow_cast( (piece_type_and_color & Piece_Type_Mask) ); return pieceFromInt8 (result); diff --git a/engine/src/position.cpp b/engine/src/position.cpp index 96b39c138..4e74f7351 100644 --- a/engine/src/position.cpp +++ b/engine/src/position.cpp @@ -80,8 +80,8 @@ namespace wisdom int8_t col = coord.column(); return makeCoord ( - gsl::narrow_cast (Last_Row - row), - gsl::narrow_cast (Last_Column - col) + narrow_cast (Last_Row - row), + narrow_cast (Last_Column - col) ); } @@ -182,11 +182,11 @@ namespace wisdom case MoveCategory::Castling: { int8_t rook_src_row = castlingRowFromColor (who); - auto rook_src_col = gsl::narrow_cast ( + auto rook_src_col = narrow_cast ( move.isCastlingOnKingside() ? King_Rook_Column : Queen_Rook_Column ); - auto rook_dst_col = gsl::narrow_cast ( + auto rook_dst_col = narrow_cast ( move.isCastlingOnKingside() ? Kingside_Castled_Rook_Column : Queenside_Castled_Rook_Column ); @@ -216,7 +216,7 @@ namespace wisdom Position::Position (const Board& board) { - for (auto coord : board.allCoords()) + for (auto coord : Board::allCoords()) { auto piece = board.pieceAt (coord); if (piece != Piece_And_Color_None) diff --git a/engine/src/search.cpp b/engine/src/search.cpp index ccf199ef0..3236a3b77 100644 --- a/engine/src/search.cpp +++ b/engine/src/search.cpp @@ -51,7 +51,6 @@ namespace wisdom History my_history; nonnull_observer_ptr my_output; SearchResult my_current_result {}; - MoveGenerator my_generator {}; MoveTimer my_timer; int my_total_depth; @@ -104,7 +103,7 @@ namespace wisdom std::optional best_move {}; int best_score = -Initial_Alpha; - auto moves = my_generator.generateAllPotentialMoves (parent_board, side); + auto moves = MoveGenerator::generateAllPotentialMoves (parent_board, side); for (auto move : moves) { int score; @@ -132,8 +131,7 @@ namespace wisdom } else { - score = evaluate (child_board, side, - my_search_depth - depth, my_generator); + score = evaluate (child_board, side, my_search_depth - depth); } } else diff --git a/engine/test/en_passant_test.cpp b/engine/test/en_passant_test.cpp index 7aeb50c7a..f4171808a 100644 --- a/engine/test/en_passant_test.cpp +++ b/engine/test/en_passant_test.cpp @@ -34,7 +34,6 @@ TEST_CASE( "en passant" ) SUBCASE( "En passant moves work on the right" ) { BoardBuilder builder; - MoveGenerator move_generator; const auto& back_rank = BoardBuilder::Default_Piece_Row; @@ -54,7 +53,7 @@ TEST_CASE( "en passant" ) Move pawn_move = moveParse ("f7f5"); board = board.withMove (Color::Black, pawn_move); - MoveList move_list = move_generator.generateAllPotentialMoves (board, Color::White); + MoveList move_list = MoveGenerator::generateAllPotentialMoves (board, Color::White); auto maybe_en_passant_move = std::find_if ( move_list.begin(), move_list.end(), std::mem_fn (&Move::isEnPassant)); @@ -87,7 +86,6 @@ TEST_CASE( "en passant" ) SUBCASE( "En passant moves work on the left" ) { BoardBuilder builder; - MoveGenerator move_generator; const auto& back_rank = BoardBuilder::Default_Piece_Row; builder.addRowOfSameColor ("a8", Color::Black, back_rank); @@ -103,7 +101,7 @@ TEST_CASE( "en passant" ) board = board.withMove (Color::Black, pawn_move); - MoveList move_list = move_generator.generateAllPotentialMoves (board, Color::White); + MoveList move_list = MoveGenerator::generateAllPotentialMoves (board, Color::White); auto maybe_en_passant_move = std::find_if ( move_list.begin(), move_list.end(), std::mem_fn (&Move::isEnPassant)); diff --git a/engine/test/generate_test.cpp b/engine/test/generate_test.cpp index 39e07b725..c406ab950 100644 --- a/engine/test/generate_test.cpp +++ b/engine/test/generate_test.cpp @@ -8,9 +8,8 @@ using namespace wisdom; TEST_CASE("generate default moves") { Board board; - MoveGenerator move_generator; - auto move_list = move_generator.generateAllPotentialMoves (board, Color::White); + auto move_list = MoveGenerator::generateAllPotentialMoves (board, Color::White); std::string expected = "{ [a2 a4] [a2 a3] [b2 b4] [b2 b3] [c2 c4] [c2 c3] " "[d2 d4] [d2 d3] [e2 e4] [e2 e3] [f2 f4] [f2 f3] " @@ -22,14 +21,13 @@ TEST_CASE("generate default moves") TEST_CASE("generate en passant moves") { Board board; - MoveGenerator move_generator; board = board.withMove (Color::White, moveParse ("e2 e4", Color::White)); board = board.withMove (Color::Black, moveParse ("d7 d5", Color::Black)); board = board.withMove (Color::White, moveParse ("e4 e5", Color::White)); board = board.withMove (Color::Black, moveParse ("f7 f5", Color::Black)); - auto move_list = move_generator.generateAllPotentialMoves (board, Color::White).asString(); + auto move_list = MoveGenerator::generateAllPotentialMoves (board, Color::White).asString(); auto pos = move_list.find ("[e5 f6 ep]"); INFO( move_list ); @@ -50,8 +48,7 @@ TEST_CASE( "Generated moves are sorted by capturing difference of pieces" ) auto board = Board { builder }; - MoveGenerator move_generator; - auto move_list = move_generator.generateAllPotentialMoves (board, Color::Black); + auto move_list = MoveGenerator::generateAllPotentialMoves (board, Color::Black); std::string expected = "{ [c4xd3] [c4xb3] "; std::string converted = move_list.asString().substr (0, expected.size()); diff --git a/engine/test/move_list_test.cpp b/engine/test/move_list_test.cpp index 140c185b0..9e3c2ad41 100644 --- a/engine/test/move_list_test.cpp +++ b/engine/test/move_list_test.cpp @@ -28,10 +28,10 @@ TEST_CASE( "Initializing move list" ) REQUIRE( moves == expected ); } -static auto copy_moves_and_ptr (const Move **ptr, MoveGenerator& generator) -> MoveList +static auto copy_moves_and_ptr (const Move **ptr) -> MoveList { Board board; - MoveList moves = generator.generateAllPotentialMoves (board, Color::White); + MoveList moves = MoveGenerator::generateAllPotentialMoves (board, Color::White); return moves; } @@ -45,8 +45,7 @@ TEST_CASE( "Converting moves to a string" ) TEST_CASE( "Returning move list moves ptr" ) { const Move* ptr; - MoveGenerator move_generator; - MoveList result = copy_moves_and_ptr (&ptr, move_generator); + MoveList result = copy_moves_and_ptr (&ptr); // std::cout << "Moves first" << &result.get_my_moves()[0] << "\n"; REQUIRE( result.size() > 0 ); diff --git a/engine/test/move_perft_test.cpp b/engine/test/move_perft_test.cpp index 296b92aa9..3adefb0a6 100644 --- a/engine/test/move_perft_test.cpp +++ b/engine/test/move_perft_test.cpp @@ -25,9 +25,7 @@ using wisdom::MoveGenerator; TEST_CASE( "Perft cases loaded from https://www.chessprogramming.org/Perft_Results" ) { - MoveGenerator move_generator; - - auto do_check = [&move_generator]( + auto do_check = []( Board &board, const vector &expectations, Color color @@ -35,7 +33,7 @@ TEST_CASE( "Perft cases loaded from https://www.chessprogramming.org/Perft_Resul for (const auto [depth, expectation] : expectations) { Stats stats; - stats.searchMoves (board, color, 0, depth, move_generator); + stats.searchMoves (board, color, 0, depth); CHECK( stats.counters.nodes == expectation.nodes ); CHECK( stats.counters.captures == expectation.captures ); @@ -66,7 +64,7 @@ TEST_CASE( "Perft cases loaded from https://www.chessprogramming.org/Perft_Resul { 2, { 400, 0, 0 } } }; - auto perft_results = wisdom::perft::perftResults (perft_board, Color::White, 2, move_generator); + auto perft_results = wisdom::perft::perftResults (perft_board, Color::White, 2); do_check (test_board, expectations, Color::White); auto test_sum = perft_results.total_nodes; @@ -84,7 +82,7 @@ TEST_CASE( "Perft cases loaded from https://www.chessprogramming.org/Perft_Resul { 3, { 8'902, 34, 0 } } }; - auto perft_results = wisdom::perft::perftResults (perft_board, Color::White, 3, move_generator); + auto perft_results = wisdom::perft::perftResults (perft_board, Color::White, 3); auto sum = perft_results.total_nodes; do_check (test_board, expectations, Color::White); @@ -108,7 +106,7 @@ TEST_CASE( "Perft cases loaded from https://www.chessprogramming.org/Perft_Resul { 5, { 193'690'690, 35'043'416, 73'365 } }, }; - auto perft_results = wisdom::perft::perftResults (perft_board, Color::White, 2, move_generator); + auto perft_results = wisdom::perft::perftResults (perft_board, Color::White, 2); auto sum = perft_results.total_nodes; CHECK( sum == 2039 ); diff --git a/engine/test/perft.cpp b/engine/test/perft.cpp index 6e774c6a0..db9b0c67d 100644 --- a/engine/test/perft.cpp +++ b/engine/test/perft.cpp @@ -9,16 +9,18 @@ namespace wisdom using wisdom::perft::Stats; using wisdom::perft::toMoveList; - void Stats::searchMoves (const wisdom::Board& board, wisdom::Color side, int depth, - int max_depth, // NOLINT(misc-no-recursion) - MoveGenerator& generator) - { + void Stats::searchMoves ( // NOLINT(misc-no-recursion) + const wisdom::Board& board, + wisdom::Color side, + int depth, + int max_depth + ) { if (depth >= max_depth) return; auto target_depth = max_depth - 1; - const auto moves = generator.generateAllPotentialMoves (board, side); + const auto moves = MoveGenerator::generateAllPotentialMoves (board, side); for (auto move : moves) { @@ -37,7 +39,7 @@ namespace wisdom counters.en_passants++; } - searchMoves (new_board, colorInvert (side), depth + 1, max_depth, generator); + searchMoves (new_board, colorInvert (side), depth + 1, max_depth); } } @@ -146,13 +148,14 @@ namespace wisdom return wisdom::asString (move.getSrc()) + wisdom::asString (move.getDst()); } - auto wisdom::perft::perftResults (const Board& board, Color active_player, int depth, - MoveGenerator& generator) -> PerftResults + auto + wisdom::perft::perftResults (const Board& board, Color active_player, int depth) + -> PerftResults { wisdom::perft::PerftResults results; Stats cumulative; - auto moves = generator.generateAllPotentialMoves (board, active_player); + auto moves = MoveGenerator::generateAllPotentialMoves (board, active_player); for (const auto& move : moves) { @@ -164,7 +167,7 @@ namespace wisdom if (!wisdom::isLegalPositionAfterMove (new_board, active_player, move)) continue; - stats.searchMoves (new_board, next_player, 1, depth, generator); + stats.searchMoves (new_board, next_player, 1, depth); auto perft_move = wisdom::perft::toPerftMove (move, active_player); results.move_results.push_back ({ stats.counters.nodes, perft_move }); diff --git a/engine/test/perft.hpp b/engine/test/perft.hpp index a401cb769..857b58588 100644 --- a/engine/test/perft.hpp +++ b/engine/test/perft.hpp @@ -52,8 +52,7 @@ namespace wisdom::perft { MoveCounter counters; - void searchMoves (const wisdom::Board& board, wisdom::Color side, int depth, int max_depth, - MoveGenerator& generator); + void searchMoves (const wisdom::Board& board, wisdom::Color side, int depth, int max_depth); void operator+= (const Stats& source) { @@ -62,22 +61,32 @@ namespace wisdom::perft }; // Convert a perft move list to a wisdom::MoveList. - auto toMoveList (const wisdom::Board& board, Color who, const string& move_list) + auto + toMoveList (const wisdom::Board& board, Color who, const string& move_list) -> wisdom::MoveList; // Convert a wisdom move to a perft move. - auto toPerftMove (const Move& move, Color who) -> string; + auto + toPerftMove (const Move& move, Color who) + -> string; // Convert a perft move to a wisdom::Move. - auto convertMove (const Board& board, Color who, string move_str) -> wisdom::Move; + auto + convertMove (const Board& board, Color who, string move_str) + -> wisdom::Move; // Output the perf results to a string. - auto perftResults (const Board& board, Color active_player, int depth, - MoveGenerator& generator) -> PerftResults; + auto + perftResults (const Board& board, Color active_player, int depth) + -> PerftResults; // Apply the list of moves, update active color and return it. - auto applyList (Board& board, Color color, const MoveList& list) -> Color; + auto + applyList (Board& board, Color color, const MoveList& list) + -> Color; // Convert the PerftResults to a string. - auto asString (const PerftResults& perft_results) -> string; + auto + asString (const PerftResults& perft_results) + -> string; } diff --git a/engine/test/perft_main.cpp b/engine/test/perft_main.cpp index c751c075a..3bb6d59b5 100644 --- a/engine/test/perft_main.cpp +++ b/engine/test/perft_main.cpp @@ -16,8 +16,6 @@ using wisdom::perft::PerftResults; int main (int argc, char *argv[]) { - wisdom::MoveGenerator move_generator; - if (argc != 3 && argc != 4) { std::cerr << "Need two or three args" << "\n"; @@ -41,7 +39,7 @@ int main (int argc, char *argv[]) current_player = wisdom::perft::applyList (board, current_player, moves); } - PerftResults results = wisdom::perft::perftResults (board, current_player, *depth, move_generator); + PerftResults results = wisdom::perft::perftResults (board, current_player, *depth); std::cout << wisdom::perft::asString (results); return EXIT_SUCCESS; diff --git a/engine/test/search_test.cpp b/engine/test/search_test.cpp index 7a0969d04..4cb0001d7 100644 --- a/engine/test/search_test.cpp +++ b/engine/test/search_test.cpp @@ -254,7 +254,7 @@ TEST_CASE( "Checkmate is preferred to stalemate" ) REQUIRE( result.move.has_value() ); game.move (*result.move); - auto is_stalemate = isStalemated (game.getBoard(), Color::White, game.getMoveGenerator()); + auto is_stalemate = isStalemated (game.getBoard(), Color::White); CHECK( !is_stalemate ); } @@ -273,7 +273,7 @@ TEST_CASE( "Can avoid stalemate" ) game.move (*result.move); - auto is_stalemate = isStalemated (game.getBoard(), Color::White, game.getMoveGenerator()); + auto is_stalemate = isStalemated (game.getBoard(), Color::White); CHECK( !is_stalemate ); } diff --git a/ui/console/play.cpp b/ui/console/play.cpp index 7e8a8d4f0..fc26173bb 100644 --- a/ui/console/play.cpp +++ b/ui/console/play.cpp @@ -297,10 +297,8 @@ namespace wisdom::ui::console void printAvailableMoves() { - MoveGenerator generator; - MoveList moves - = generator.generateLegalMoves (game->getBoard(), game->getCurrentTurn()); + = MoveGenerator::generateLegalMoves (game->getBoard(), game->getCurrentTurn()); std::cout << "\nAvailable moves:\n "; int count = 0; @@ -535,9 +533,8 @@ namespace wisdom::ui::console return result; // check the generated move list for this move to see if its valid - MoveGenerator generator; MoveList moves - = generator.generateLegalMoves (game->getBoard(), game->getCurrentTurn()); + = MoveGenerator::generateLegalMoves (game->getBoard(), game->getCurrentTurn()); for (auto legal_move : moves) { diff --git a/ui/qml/main/chess_game.cpp b/ui/qml/main/chess_game.cpp index 522f02b6c..10ccec331 100644 --- a/ui/qml/main/chess_game.cpp +++ b/ui/qml/main/chess_game.cpp @@ -2,6 +2,7 @@ #include "fen_parser.hpp" #include "game_settings.hpp" #include "move_timer.hpp" +#include "generate.hpp" #include @@ -18,6 +19,7 @@ using wisdom::Game; using wisdom::Move; using wisdom::MoveTimer; using wisdom::Piece; +using wisdom::MoveGenerator; auto ChessGame::fromPlayers (wisdom::Player whitePlayer, wisdom::Player blackPlayer, const Config& config) -> unique_ptr @@ -63,8 +65,7 @@ auto ChessGame::isLegalMove (Move selectedMove) const -> bool } auto who = game->getCurrentTurn(); - auto& generator = game->getMoveGenerator(); - auto legalMoves = generator.generateLegalMoves (game->getBoard(), who); + auto legalMoves = MoveGenerator::generateLegalMoves (game->getBoard(), who); return std::any_of (legalMoves.cbegin(), legalMoves.cend(), [selectedMove] (const auto& move) diff --git a/ui/qml/main/chess_game.hpp b/ui/qml/main/chess_game.hpp index 46d2f6320..becff5aa1 100644 --- a/ui/qml/main/chess_game.hpp +++ b/ui/qml/main/chess_game.hpp @@ -86,11 +86,6 @@ class ChessGame [[nodiscard]] auto isLegalMove (wisdom::Move selectedMove) const -> bool; - [[nodiscard]] auto moveGenerator() const -> gsl::not_null - { - return my_move_generator.get(); - } - void setConfig (const Config& config); void setPeriodicFunction (const wisdom::MoveTimer::PeriodicFunction& func); void setPlayers (wisdom::Player whitePLayer, wisdom::Player blackPlayer); @@ -101,8 +96,6 @@ class ChessGame private: std::unique_ptr my_engine; - std::unique_ptr my_move_generator - = std::make_unique(); Config my_config; }; diff --git a/ui/wasm/web_game.cpp b/ui/wasm/web_game.cpp index cfda9c8bb..4c59c33a9 100644 --- a/ui/wasm/web_game.cpp +++ b/ui/wasm/web_game.cpp @@ -85,8 +85,7 @@ namespace wisdom } auto who = my_game.getCurrentTurn(); - auto& generator = my_game.getMoveGenerator(); - auto legalMoves = generator.generateLegalMoves (my_game.getBoard(), who); + auto legalMoves = MoveGenerator::generateLegalMoves (my_game.getBoard(), who); auto result = std::any_of (legalMoves.cbegin(), legalMoves.cend(), [selectedMove] (const auto& move)