1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:57:45 +00:00

Chess: Add pawn promotion to any piece

This commit is contained in:
Peter Elliott 2020-08-11 22:53:11 -06:00 committed by Andreas Kling
parent 9d40472721
commit abd1f7e563
7 changed files with 144 additions and 5 deletions

View file

@ -2,6 +2,7 @@ set(SOURCES
main.cpp
Chess.cpp
ChessWidget.cpp
PromotionDialog.cpp
)
serenity_bin(Chess)

View file

@ -153,6 +153,9 @@ bool Chess::is_legal_no_check(const Move& move, Colour colour) const
if (move.to.rank > 7 || move.to.file > 7)
return false;
if (move.promote_to == Type::Pawn || move.promote_to == Type::King || move.promote_to == Type::None)
return false;
if (piece.type == Type::Pawn) {
int dir = (colour == Colour::White) ? +1 : -1;
unsigned start_rank = (colour == Colour::White) ? 1 : 6;
@ -304,8 +307,6 @@ bool Chess::apply_illegal_move(const Move& move, Colour colour)
{
m_turn = opposing_colour(colour);
// FIXME: pawn promotion
m_last_move = move;
if (move.from == Square("a1") || move.to == Square("a1") || move.from == Square("e1"))
@ -347,6 +348,13 @@ bool Chess::apply_illegal_move(const Move& move, Colour colour)
}
}
if (get_piece(move.from).type == Type::Pawn && ((colour == Colour::Black && move.to.rank == 0) || (colour == Colour::White && move.to.rank == 7))) {
// Pawn Promotion
set_piece(move.to, { colour, move.promote_to });
set_piece(move.from, EmptyPiece);
return true;
}
if (get_piece(move.from).type == Type::Pawn && move.from.file != move.to.file && get_piece(move.to).type == Type::None) {
// En passant.
if (colour == Colour::White) {
@ -379,3 +387,17 @@ Chess::Result Chess::game_result() const
return Result::StaleMate;
}
bool Chess::is_promotion_move(const Move& move, Colour colour) const
{
if (colour == Colour::None)
colour = turn();
if (!is_legal(move, colour))
return false;
if (get_piece(move.from).type == Type::Pawn && ((colour == Colour::Black && move.to.rank == 0) || (colour == Colour::White && move.to.rank == 7)))
return true;
return false;
}

View file

@ -87,13 +87,15 @@ public:
struct Move {
Square from;
Square to;
Type promote_to;
Move(const StringView& algebraic);
Move(const Square& from, const Square& to)
Move(const Square& from, const Square& to, const Type& promote_to = Type::Queen)
: from(from)
, to(to)
, promote_to(promote_to)
{
}
bool operator==(const Move& other) const { return from == other.from && to == other.to; }
bool operator==(const Move& other) const { return from == other.from && to == other.to && promote_to == other.promote_to; }
};
Chess();
@ -104,6 +106,8 @@ public:
bool is_legal(const Move&, Colour colour = Colour::None) const;
bool in_check(Colour colour) const;
bool is_promotion_move(const Move&, Colour colour = Colour::None) const;
bool apply_move(const Move&, Colour colour = Colour::None);
const Optional<Move>& last_move() const { return m_last_move; }

View file

@ -25,6 +25,7 @@
*/
#include "ChessWidget.h"
#include "PromotionDialog.h"
#include <AK/String.h>
#include <LibGUI/MessageBox.h>
#include <LibGUI/Painter.h>
@ -114,7 +115,14 @@ void ChessWidget::mouseup_event(GUI::MouseEvent& event)
auto target_square = mouse_to_square(event);
if (board().apply_move({ m_moving_square, target_square })) {
Chess::Move move = { m_moving_square, target_square };
if (board().is_promotion_move(move)) {
auto promotion_dialog = PromotionDialog::construct(*this);
if (promotion_dialog->exec() == PromotionDialog::ExecOK)
move.promote_to = promotion_dialog->selected_piece();
}
if (board().apply_move(move)) {
if (board().game_result() != Chess::Result::NotFinished) {
set_drag_enabled(false);
update();
@ -198,6 +206,11 @@ Chess::Square ChessWidget::mouse_to_square(GUI::MouseEvent& event) const
}
}
RefPtr<Gfx::Bitmap> ChessWidget::get_piece_graphic(const Chess::Piece& piece) const
{
return m_pieces.get(piece).value();
}
void ChessWidget::reset()
{
m_board = Chess();

View file

@ -59,6 +59,7 @@ public:
bool drag_enabled() const { return m_drag_enabled; }
void set_drag_enabled(bool e) { m_drag_enabled = e; }
RefPtr<Gfx::Bitmap> get_piece_graphic(const Chess::Piece& piece) const;
void reset();

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "PromotionDialog.h"
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Button.h>
#include <LibGUI/Frame.h>
PromotionDialog::PromotionDialog(ChessWidget& chess_widget)
: Dialog(chess_widget.window())
{
set_title("Choose piece to promote to");
resize(70 * 4, 70);
auto& main_widget = set_main_widget<GUI::Frame>();
main_widget.set_frame_shape(Gfx::FrameShape::Container);
main_widget.set_fill_with_background_color(true);
main_widget.set_layout<GUI::HorizontalBoxLayout>();
for (auto& type : Vector({ Chess::Type::Queen, Chess::Type::Knight, Chess::Type::Rook, Chess::Type::Bishop })) {
auto& button = main_widget.add<GUI::Button>("");
button.set_icon(chess_widget.get_piece_graphic({ chess_widget.board().turn(), type }));
button.on_click = [this, type](auto) {
m_selected_piece = type;
done(ExecOK);
};
}
}
void PromotionDialog::event(Core::Event& event)
{
Dialog::event(event);
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "ChessWidget.h"
#include <LibGUI/Dialog.h>
class PromotionDialog final : public GUI::Dialog {
C_OBJECT(PromotionDialog)
public:
Chess::Type selected_piece() const { return m_selected_piece; }
private:
explicit PromotionDialog(ChessWidget& chess_widget);
virtual void event(Core::Event&) override;
Chess::Type m_selected_piece;
};