mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-03 21:10:30 +00:00
Chess: Refactor game logic into LibChess for use in engines
In the future UCI protocol stuff will also go into LibChess.
This commit is contained in:
parent
ffece9cfba
commit
d2cb5e0f48
Notes:
sideshowbarker
2024-07-19 03:22:21 +09:00
Author: https://github.com/petelliott Commit: https://github.com/SerenityOS/serenity/commit/d2cb5e0f48f Pull-request: https://github.com/SerenityOS/serenity/pull/3214 Issue: https://github.com/SerenityOS/serenity/issues/3171 Issue: https://github.com/SerenityOS/serenity/issues/3187 Reviewed-by: https://github.com/Dexesttp Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/awesomekling
7 changed files with 165 additions and 148 deletions
|
@ -1,9 +1,8 @@
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
Chess.cpp
|
|
||||||
ChessWidget.cpp
|
ChessWidget.cpp
|
||||||
PromotionDialog.cpp
|
PromotionDialog.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_bin(Chess)
|
serenity_bin(Chess)
|
||||||
target_link_libraries(Chess LibGUI)
|
target_link_libraries(Chess LibChess LibGUI)
|
||||||
|
|
|
@ -123,22 +123,22 @@ void ChessWidget::mouseup_event(GUI::MouseEvent& event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (board().apply_move(move)) {
|
if (board().apply_move(move)) {
|
||||||
if (board().game_result() != Chess::Result::NotFinished) {
|
if (board().game_result() != Chess::Board::Result::NotFinished) {
|
||||||
|
|
||||||
bool over = true;
|
bool over = true;
|
||||||
String msg;
|
String msg;
|
||||||
switch (board().game_result()) {
|
switch (board().game_result()) {
|
||||||
case Chess::Result::CheckMate:
|
case Chess::Board::Result::CheckMate:
|
||||||
if (board().turn() == Chess::Colour::White) {
|
if (board().turn() == Chess::Colour::White) {
|
||||||
msg = "Black wins by Checkmate.";
|
msg = "Black wins by Checkmate.";
|
||||||
} else {
|
} else {
|
||||||
msg = "White wins by Checkmate.";
|
msg = "White wins by Checkmate.";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Chess::Result::StaleMate:
|
case Chess::Board::Result::StaleMate:
|
||||||
msg = "Draw by Stalemate.";
|
msg = "Draw by Stalemate.";
|
||||||
break;
|
break;
|
||||||
case Chess::Result::FiftyMoveRule:
|
case Chess::Board::Result::FiftyMoveRule:
|
||||||
update();
|
update();
|
||||||
if (GUI::MessageBox::show(window(), "50 moves have elapsed without a capture. Claim Draw?", "Claim Draw?",
|
if (GUI::MessageBox::show(window(), "50 moves have elapsed without a capture. Claim Draw?", "Claim Draw?",
|
||||||
GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::YesNo)
|
GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::YesNo)
|
||||||
|
@ -148,10 +148,10 @@ void ChessWidget::mouseup_event(GUI::MouseEvent& event)
|
||||||
over = false;
|
over = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Chess::Result::SeventyFiveMoveRule:
|
case Chess::Board::Result::SeventyFiveMoveRule:
|
||||||
msg = "Draw by 75 move rule.";
|
msg = "Draw by 75 move rule.";
|
||||||
break;
|
break;
|
||||||
case Chess::Result::ThreeFoldRepitition:
|
case Chess::Board::Result::ThreeFoldRepitition:
|
||||||
update();
|
update();
|
||||||
if (GUI::MessageBox::show(window(), "The same board state has repeated three times. Claim Draw?", "Claim Draw?",
|
if (GUI::MessageBox::show(window(), "The same board state has repeated three times. Claim Draw?", "Claim Draw?",
|
||||||
GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::YesNo)
|
GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::YesNo)
|
||||||
|
@ -161,10 +161,10 @@ void ChessWidget::mouseup_event(GUI::MouseEvent& event)
|
||||||
over = false;
|
over = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Chess::Result::FiveFoldRepitition:
|
case Chess::Board::Result::FiveFoldRepitition:
|
||||||
msg = "Draw by fivefold repitition.";
|
msg = "Draw by fivefold repitition.";
|
||||||
break;
|
break;
|
||||||
case Chess::Result::InsufficientMaterial:
|
case Chess::Board::Result::InsufficientMaterial:
|
||||||
msg = "Draw by insufficient material.";
|
msg = "Draw by insufficient material.";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -239,7 +239,7 @@ RefPtr<Gfx::Bitmap> ChessWidget::get_piece_graphic(const Chess::Piece& piece) co
|
||||||
|
|
||||||
void ChessWidget::reset()
|
void ChessWidget::reset()
|
||||||
{
|
{
|
||||||
m_board = Chess();
|
m_board = Chess::Board();
|
||||||
m_drag_enabled = true;
|
m_drag_enabled = true;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Chess.h"
|
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/NonnullRefPtr.h>
|
#include <AK/NonnullRefPtr.h>
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
|
#include <LibChess/Chess.h>
|
||||||
#include <LibGUI/Widget.h>
|
#include <LibGUI/Widget.h>
|
||||||
#include <LibGfx/Bitmap.h>
|
#include <LibGfx/Bitmap.h>
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public:
|
||||||
virtual void mouseup_event(GUI::MouseEvent&) override;
|
virtual void mouseup_event(GUI::MouseEvent&) override;
|
||||||
virtual void mousemove_event(GUI::MouseEvent&) override;
|
virtual void mousemove_event(GUI::MouseEvent&) override;
|
||||||
|
|
||||||
Chess& board() { return m_board; };
|
Chess::Board& board() { return m_board; };
|
||||||
|
|
||||||
Chess::Colour side() const { return m_side; };
|
Chess::Colour side() const { return m_side; };
|
||||||
void set_side(Chess::Colour side) { m_side = side; };
|
void set_side(Chess::Colour side) { m_side = side; };
|
||||||
|
@ -74,7 +74,7 @@ public:
|
||||||
void set_board_theme(const StringView& name);
|
void set_board_theme(const StringView& name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Chess m_board;
|
Chess::Board m_board;
|
||||||
BoardTheme m_board_theme { "Beige", Color::from_rgb(0xb58863), Color::from_rgb(0xf0d9b5) };
|
BoardTheme m_board_theme { "Beige", Color::from_rgb(0xb58863), Color::from_rgb(0xf0d9b5) };
|
||||||
Color m_move_highlight_color { Color::from_rgba(0x66ccee00) };
|
Color m_move_highlight_color { Color::from_rgba(0x66ccee00) };
|
||||||
Chess::Colour m_side { Chess::Colour::White };
|
Chess::Colour m_side { Chess::Colour::White };
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
add_subdirectory(LibAudio)
|
add_subdirectory(LibAudio)
|
||||||
add_subdirectory(LibC)
|
add_subdirectory(LibC)
|
||||||
|
add_subdirectory(LibChess)
|
||||||
add_subdirectory(LibCore)
|
add_subdirectory(LibCore)
|
||||||
add_subdirectory(LibCrypt)
|
add_subdirectory(LibCrypt)
|
||||||
add_subdirectory(LibCrypto)
|
add_subdirectory(LibCrypto)
|
||||||
|
|
6
Libraries/LibChess/CMakeLists.txt
Normal file
6
Libraries/LibChess/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
set(SOURCES
|
||||||
|
Chess.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
serenity_lib(LibChess chess)
|
||||||
|
target_link_libraries(LibChess LibC)
|
|
@ -24,15 +24,17 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Chess.h"
|
|
||||||
#include <AK/Assertions.h>
|
#include <AK/Assertions.h>
|
||||||
#include <AK/LogStream.h>
|
#include <AK/LogStream.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibChess/Chess.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
String Chess::char_for_piece(Chess::Type type)
|
namespace Chess {
|
||||||
|
|
||||||
|
String char_for_piece(Chess::Type type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::Knight:
|
case Type::Knight:
|
||||||
|
@ -51,7 +53,12 @@ String Chess::char_for_piece(Chess::Type type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Chess::Square::Square(const StringView& name)
|
Colour opposing_colour(Colour colour)
|
||||||
|
{
|
||||||
|
return (colour == Colour::White) ? Colour::Black : Colour::White;
|
||||||
|
}
|
||||||
|
|
||||||
|
Square::Square(const StringView& name)
|
||||||
{
|
{
|
||||||
ASSERT(name.length() == 2);
|
ASSERT(name.length() == 2);
|
||||||
char filec = name[0];
|
char filec = name[0];
|
||||||
|
@ -72,7 +79,7 @@ Chess::Square::Square(const StringView& name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String Chess::Square::to_algebraic() const
|
String Square::to_algebraic() const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append(file - 'a');
|
builder.append(file - 'a');
|
||||||
|
@ -80,7 +87,7 @@ String Chess::Square::to_algebraic() const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
String Chess::Move::to_long_algebraic() const
|
String Move::to_long_algebraic() const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append(from.to_algebraic());
|
builder.append(from.to_algebraic());
|
||||||
|
@ -89,7 +96,7 @@ String Chess::Move::to_long_algebraic() const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
Chess::Chess()
|
Board::Board()
|
||||||
{
|
{
|
||||||
// Fill empty spaces.
|
// Fill empty spaces.
|
||||||
for (unsigned rank = 2; rank < 6; ++rank) {
|
for (unsigned rank = 2; rank < 6; ++rank) {
|
||||||
|
@ -129,21 +136,21 @@ Chess::Chess()
|
||||||
set_piece(Square("h8"), { Colour::Black, Type::Rook });
|
set_piece(Square("h8"), { Colour::Black, Type::Rook });
|
||||||
}
|
}
|
||||||
|
|
||||||
Chess::Piece Chess::get_piece(const Square& square) const
|
Piece Board::get_piece(const Square& square) const
|
||||||
{
|
{
|
||||||
ASSERT(square.rank < 8);
|
ASSERT(square.rank < 8);
|
||||||
ASSERT(square.file < 8);
|
ASSERT(square.file < 8);
|
||||||
return m_board[square.rank][square.file];
|
return m_board[square.rank][square.file];
|
||||||
}
|
}
|
||||||
|
|
||||||
Chess::Piece Chess::set_piece(const Square& square, const Piece& piece)
|
Piece Board::set_piece(const Square& square, const Piece& piece)
|
||||||
{
|
{
|
||||||
ASSERT(square.rank < 8);
|
ASSERT(square.rank < 8);
|
||||||
ASSERT(square.file < 8);
|
ASSERT(square.file < 8);
|
||||||
return m_board[square.rank][square.file] = piece;
|
return m_board[square.rank][square.file] = piece;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::is_legal(const Move& move, Colour colour) const
|
bool Board::is_legal(const Move& move, Colour colour) const
|
||||||
{
|
{
|
||||||
if (colour == Colour::None)
|
if (colour == Colour::None)
|
||||||
colour = turn();
|
colour = turn();
|
||||||
|
@ -151,7 +158,7 @@ bool Chess::is_legal(const Move& move, Colour colour) const
|
||||||
if (!is_legal_no_check(move, colour))
|
if (!is_legal_no_check(move, colour))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Chess clone = *this;
|
Board clone = *this;
|
||||||
clone.apply_illegal_move(move, colour);
|
clone.apply_illegal_move(move, colour);
|
||||||
if (clone.in_check(colour))
|
if (clone.in_check(colour))
|
||||||
return false;
|
return false;
|
||||||
|
@ -172,7 +179,7 @@ bool Chess::is_legal(const Move& move, Colour colour) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& square : check_squares) {
|
for (auto& square : check_squares) {
|
||||||
Chess clone = *this;
|
Board clone = *this;
|
||||||
clone.set_piece(move.from, EmptyPiece);
|
clone.set_piece(move.from, EmptyPiece);
|
||||||
clone.set_piece(square, { colour, Type::King });
|
clone.set_piece(square, { colour, Type::King });
|
||||||
if (clone.in_check(colour))
|
if (clone.in_check(colour))
|
||||||
|
@ -182,7 +189,7 @@ bool Chess::is_legal(const Move& move, Colour colour) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::is_legal_no_check(const Move& move, Colour colour) const
|
bool Board::is_legal_no_check(const Move& move, Colour colour) const
|
||||||
{
|
{
|
||||||
auto piece = get_piece(move.from);
|
auto piece = get_piece(move.from);
|
||||||
if (piece.colour != colour)
|
if (piece.colour != colour)
|
||||||
|
@ -314,7 +321,7 @@ bool Chess::is_legal_no_check(const Move& move, Colour colour) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::in_check(Colour colour) const
|
bool Board::in_check(Colour colour) const
|
||||||
{
|
{
|
||||||
Square king_square = { 50, 50 };
|
Square king_square = { 50, 50 };
|
||||||
Square::for_each([&](const Square& square) {
|
Square::for_each([&](const Square& square) {
|
||||||
|
@ -341,7 +348,7 @@ bool Chess::in_check(Colour colour) const
|
||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::apply_move(const Move& move, Colour colour)
|
bool Board::apply_move(const Move& move, Colour colour)
|
||||||
{
|
{
|
||||||
if (colour == Colour::None)
|
if (colour == Colour::None)
|
||||||
colour = turn();
|
colour = turn();
|
||||||
|
@ -352,9 +359,9 @@ bool Chess::apply_move(const Move& move, Colour colour)
|
||||||
return apply_illegal_move(move, colour);
|
return apply_illegal_move(move, colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::apply_illegal_move(const Move& move, Colour colour)
|
bool Board::apply_illegal_move(const Move& move, Colour colour)
|
||||||
{
|
{
|
||||||
Chess clone = *this;
|
Board clone = *this;
|
||||||
clone.m_previous_states = {};
|
clone.m_previous_states = {};
|
||||||
auto state_count = 0;
|
auto state_count = 0;
|
||||||
if (m_previous_states.contains(clone))
|
if (m_previous_states.contains(clone))
|
||||||
|
@ -431,7 +438,7 @@ bool Chess::apply_illegal_move(const Move& move, Colour colour)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Chess::Result Chess::game_result() const
|
Board::Result Board::game_result() const
|
||||||
{
|
{
|
||||||
bool sufficient_material = false;
|
bool sufficient_material = false;
|
||||||
bool no_more_pieces_allowed = false;
|
bool no_more_pieces_allowed = false;
|
||||||
|
@ -501,7 +508,7 @@ Chess::Result Chess::game_result() const
|
||||||
return Result::StaleMate;
|
return Result::StaleMate;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::is_promotion_move(const Move& move, Colour colour) const
|
bool Board::is_promotion_move(const Move& move, Colour colour) const
|
||||||
{
|
{
|
||||||
if (colour == Colour::None)
|
if (colour == Colour::None)
|
||||||
colour = turn();
|
colour = turn();
|
||||||
|
@ -517,7 +524,7 @@ bool Chess::is_promotion_move(const Move& move, Colour colour) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chess::operator==(const Chess& other) const
|
bool Board::operator==(const Board& other) const
|
||||||
{
|
{
|
||||||
bool equal_squares = true;
|
bool equal_squares = true;
|
||||||
Square::for_each([&](Square sq) {
|
Square::for_each([&](Square sq) {
|
||||||
|
@ -541,3 +548,5 @@ bool Chess::operator==(const Chess& other) const
|
||||||
|
|
||||||
return turn() == other.turn();
|
return turn() == other.turn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -32,80 +32,82 @@
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
#include <AK/Traits.h>
|
#include <AK/Traits.h>
|
||||||
|
|
||||||
class Chess {
|
namespace Chess {
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
Pawn,
|
|
||||||
Knight,
|
|
||||||
Bishop,
|
|
||||||
Rook,
|
|
||||||
Queen,
|
|
||||||
King,
|
|
||||||
None,
|
|
||||||
};
|
|
||||||
static String char_for_piece(Type type);
|
|
||||||
|
|
||||||
enum class Colour {
|
enum class Type {
|
||||||
White,
|
Pawn,
|
||||||
Black,
|
Knight,
|
||||||
None,
|
Bishop,
|
||||||
};
|
Rook,
|
||||||
static Colour opposing_colour(Colour colour) { return (colour == Colour::White) ? Colour::Black : Colour::White; }
|
Queen,
|
||||||
|
King,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
struct Piece {
|
String char_for_piece(Type type);
|
||||||
Colour colour;
|
|
||||||
Type type;
|
|
||||||
bool operator==(const Piece& other) const { return colour == other.colour && type == other.type; }
|
|
||||||
|
|
||||||
};
|
enum class Colour {
|
||||||
static constexpr Piece EmptyPiece = { Colour::None, Type::None };
|
White,
|
||||||
|
Black,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
struct Square {
|
Colour opposing_colour(Colour colour);
|
||||||
unsigned rank; // zero indexed;
|
|
||||||
unsigned file;
|
|
||||||
Square(const StringView& name);
|
|
||||||
Square(const unsigned& rank, const unsigned& file)
|
|
||||||
: rank(rank)
|
|
||||||
, file(file)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
bool operator==(const Square& other) const { return rank == other.rank && file == other.file; }
|
|
||||||
|
|
||||||
template<typename Callback>
|
struct Piece {
|
||||||
static void for_each(Callback callback)
|
Colour colour;
|
||||||
{
|
Type type;
|
||||||
for (int rank = 0; rank < 8; ++rank) {
|
bool operator==(const Piece& other) const { return colour == other.colour && type == other.type; }
|
||||||
for (int file = 0; file < 8; ++file) {
|
};
|
||||||
if (callback(Square(rank, file)) == IterationDecision::Break) {
|
|
||||||
goto exit;
|
constexpr Piece EmptyPiece = { Colour::None, Type::None };
|
||||||
}
|
|
||||||
}
|
struct Square {
|
||||||
|
unsigned rank; // zero indexed;
|
||||||
|
unsigned file;
|
||||||
|
Square(const StringView& name);
|
||||||
|
Square(const unsigned& rank, const unsigned& file)
|
||||||
|
: rank(rank)
|
||||||
|
, file(file)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
bool operator==(const Square& other) const { return rank == other.rank && file == other.file; }
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
static void for_each(Callback callback)
|
||||||
|
{
|
||||||
|
for (int rank = 0; rank < 8; ++rank) {
|
||||||
|
for (int file = 0; file < 8; ++file) {
|
||||||
|
if (callback(Square(rank, file)) == IterationDecision::Break)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
exit:;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool in_bounds() const { return rank < 8 && file < 8; }
|
bool in_bounds() const { return rank < 8 && file < 8; }
|
||||||
bool is_light() const { return (rank % 2) != (file % 2); }
|
bool is_light() const { return (rank % 2) != (file % 2); }
|
||||||
String to_algebraic() const;
|
String to_algebraic() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Move {
|
struct Move {
|
||||||
Square from;
|
Square from;
|
||||||
Square to;
|
Square to;
|
||||||
Type promote_to;
|
Type promote_to;
|
||||||
Move(const StringView& algebraic);
|
Move(const StringView& algebraic);
|
||||||
Move(const Square& from, const Square& to, const Type& promote_to = Type::None)
|
Move(const Square& from, const Square& to, const Type& promote_to = Type::None)
|
||||||
: from(from)
|
: from(from)
|
||||||
, to(to)
|
, to(to)
|
||||||
, promote_to(promote_to)
|
, promote_to(promote_to)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
bool operator==(const Move& other) const { return from == other.from && to == other.to && promote_to == other.promote_to; }
|
bool operator==(const Move& other) const { return from == other.from && to == other.to && promote_to == other.promote_to; }
|
||||||
|
|
||||||
String to_long_algebraic() const;
|
String to_long_algebraic() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Chess();
|
class Board {
|
||||||
|
public:
|
||||||
|
Board();
|
||||||
|
|
||||||
Piece get_piece(const Square&) const;
|
Piece get_piece(const Square&) const;
|
||||||
Piece set_piece(const Square&, const Piece&);
|
Piece set_piece(const Square&, const Piece&);
|
||||||
|
@ -135,7 +137,7 @@ public:
|
||||||
|
|
||||||
Colour turn() const { return m_turn; };
|
Colour turn() const { return m_turn; };
|
||||||
|
|
||||||
bool operator==(const Chess& other) const;
|
bool operator==(const Board& other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_legal_no_check(const Move&, Colour colour) const;
|
bool is_legal_no_check(const Move&, Colour colour) const;
|
||||||
|
@ -151,41 +153,12 @@ private:
|
||||||
bool m_black_can_castle_kingside { true };
|
bool m_black_can_castle_kingside { true };
|
||||||
bool m_black_can_castle_queenside { true };
|
bool m_black_can_castle_queenside { true };
|
||||||
|
|
||||||
HashMap<Chess, int> m_previous_states;
|
HashMap<Board, int> m_previous_states;
|
||||||
friend struct AK::Traits<Chess>;
|
friend struct AK::Traits<Board>;
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct AK::Traits<Chess::Piece> : public GenericTraits<Chess::Piece> {
|
|
||||||
static unsigned hash(Chess::Piece piece)
|
|
||||||
{
|
|
||||||
return pair_int_hash(static_cast<u32>(piece.colour), static_cast<u32>(piece.type));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct AK::Traits<Chess> : public GenericTraits<Chess> {
|
|
||||||
static unsigned hash(Chess chess)
|
|
||||||
{
|
|
||||||
unsigned hash = 0;
|
|
||||||
hash = pair_int_hash(hash, static_cast<u32>(chess.m_white_can_castle_queenside));
|
|
||||||
hash = pair_int_hash(hash, static_cast<u32>(chess.m_white_can_castle_kingside));
|
|
||||||
hash = pair_int_hash(hash, static_cast<u32>(chess.m_black_can_castle_queenside));
|
|
||||||
hash = pair_int_hash(hash, static_cast<u32>(chess.m_black_can_castle_kingside));
|
|
||||||
|
|
||||||
hash = pair_int_hash(hash, static_cast<u32>(chess.m_black_can_castle_kingside));
|
|
||||||
|
|
||||||
Chess::Square::for_each([&](Chess::Square sq) {
|
|
||||||
hash = pair_int_hash(hash, Traits<Chess::Piece>::hash(chess.get_piece(sq)));
|
|
||||||
return IterationDecision::Continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Callback>
|
template<typename Callback>
|
||||||
void Chess::generate_moves(Callback callback, Colour colour) const
|
void Board::generate_moves(Callback callback, Colour colour) const
|
||||||
{
|
{
|
||||||
if (colour == Colour::None)
|
if (colour == Colour::None)
|
||||||
colour = turn();
|
colour = turn();
|
||||||
|
@ -205,27 +178,25 @@ void Chess::generate_moves(Callback callback, Colour colour) const
|
||||||
|
|
||||||
bool keep_going = true;
|
bool keep_going = true;
|
||||||
if (piece.type == Type::Pawn) {
|
if (piece.type == Type::Pawn) {
|
||||||
for (auto& piece : Vector({Type::None, Type::Knight, Type::Bishop, Type::Rook, Type::Queen})) {
|
for (auto& piece : Vector({ Type::None, Type::Knight, Type::Bishop, Type::Rook, Type::Queen })) {
|
||||||
keep_going =
|
keep_going = try_move({ sq, { sq.rank + 1, sq.file }, piece })
|
||||||
try_move({sq, {sq.rank+1, sq.file}, piece}) &&
|
&& try_move({ sq, { sq.rank + 2, sq.file }, piece })
|
||||||
try_move({sq, {sq.rank+2, sq.file}, piece}) &&
|
&& try_move({ sq, { sq.rank - 1, sq.file }, piece })
|
||||||
try_move({sq, {sq.rank-1, sq.file}, piece}) &&
|
&& try_move({ sq, { sq.rank - 2, sq.file }, piece })
|
||||||
try_move({sq, {sq.rank-2, sq.file}, piece}) &&
|
&& try_move({ sq, { sq.rank + 1, sq.file + 1 }, piece })
|
||||||
try_move({sq, {sq.rank+1, sq.file+1}}) &&
|
&& try_move({ sq, { sq.rank + 1, sq.file - 1 }, piece })
|
||||||
try_move({sq, {sq.rank+1, sq.file-1}}) &&
|
&& try_move({ sq, { sq.rank - 1, sq.file + 1 }, piece })
|
||||||
try_move({sq, {sq.rank-1, sq.file+1}}) &&
|
&& try_move({ sq, { sq.rank - 1, sq.file - 1 }, piece });
|
||||||
try_move({sq, {sq.rank-1, sq.file-1}});
|
|
||||||
}
|
}
|
||||||
} else if (piece.type == Type::Knight) {
|
} else if (piece.type == Type::Knight) {
|
||||||
keep_going =
|
keep_going = try_move({ sq, { sq.rank + 2, sq.file + 1 } })
|
||||||
try_move({sq, {sq.rank+2, sq.file+1}}) &&
|
&& try_move({ sq, { sq.rank + 2, sq.file - 1 } })
|
||||||
try_move({sq, {sq.rank+2, sq.file-1}}) &&
|
&& try_move({ sq, { sq.rank + 1, sq.file + 2 } })
|
||||||
try_move({sq, {sq.rank+1, sq.file+2}}) &&
|
&& try_move({ sq, { sq.rank + 1, sq.file - 2 } })
|
||||||
try_move({sq, {sq.rank+1, sq.file-2}}) &&
|
&& try_move({ sq, { sq.rank - 2, sq.file + 1 } })
|
||||||
try_move({sq, {sq.rank-2, sq.file+1}}) &&
|
&& try_move({ sq, { sq.rank - 2, sq.file - 1 } })
|
||||||
try_move({sq, {sq.rank-2, sq.file-1}}) &&
|
&& try_move({ sq, { sq.rank - 1, sq.file + 2 } })
|
||||||
try_move({sq, {sq.rank-1, sq.file+2}}) &&
|
&& try_move({ sq, { sq.rank - 1, sq.file - 2 } });
|
||||||
try_move({sq, {sq.rank-1, sq.file-2}});
|
|
||||||
} else if (piece.type == Type::Bishop) {
|
} else if (piece.type == Type::Bishop) {
|
||||||
for (int dr = -1; dr <= 1; dr += 2) {
|
for (int dr = -1; dr <= 1; dr += 2) {
|
||||||
for (int df = -1; df <= 1; df += 2) {
|
for (int df = -1; df <= 1; df += 2) {
|
||||||
|
@ -280,3 +251,34 @@ void Chess::generate_moves(Callback callback, Colour colour) const
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct AK::Traits<Chess::Piece> : public GenericTraits<Chess::Piece> {
|
||||||
|
static unsigned hash(Chess::Piece piece)
|
||||||
|
{
|
||||||
|
return pair_int_hash(static_cast<u32>(piece.colour), static_cast<u32>(piece.type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct AK::Traits<Chess::Board> : public GenericTraits<Chess::Board> {
|
||||||
|
static unsigned hash(Chess::Board chess)
|
||||||
|
{
|
||||||
|
unsigned hash = 0;
|
||||||
|
hash = pair_int_hash(hash, static_cast<u32>(chess.m_white_can_castle_queenside));
|
||||||
|
hash = pair_int_hash(hash, static_cast<u32>(chess.m_white_can_castle_kingside));
|
||||||
|
hash = pair_int_hash(hash, static_cast<u32>(chess.m_black_can_castle_queenside));
|
||||||
|
hash = pair_int_hash(hash, static_cast<u32>(chess.m_black_can_castle_kingside));
|
||||||
|
|
||||||
|
hash = pair_int_hash(hash, static_cast<u32>(chess.m_black_can_castle_kingside));
|
||||||
|
|
||||||
|
Chess::Square::for_each([&](Chess::Square sq) {
|
||||||
|
hash = pair_int_hash(hash, Traits<Chess::Piece>::hash(chess.get_piece(sq)));
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in a new issue