mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:57:35 +00:00
Chess: Add pawn promotion to any piece
This commit is contained in:
parent
9d40472721
commit
abd1f7e563
7 changed files with 144 additions and 5 deletions
|
@ -2,6 +2,7 @@ set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
Chess.cpp
|
Chess.cpp
|
||||||
ChessWidget.cpp
|
ChessWidget.cpp
|
||||||
|
PromotionDialog.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_bin(Chess)
|
serenity_bin(Chess)
|
||||||
|
|
|
@ -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)
|
if (move.to.rank > 7 || move.to.file > 7)
|
||||||
return false;
|
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) {
|
if (piece.type == Type::Pawn) {
|
||||||
int dir = (colour == Colour::White) ? +1 : -1;
|
int dir = (colour == Colour::White) ? +1 : -1;
|
||||||
unsigned start_rank = (colour == Colour::White) ? 1 : 6;
|
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);
|
m_turn = opposing_colour(colour);
|
||||||
|
|
||||||
// FIXME: pawn promotion
|
|
||||||
|
|
||||||
m_last_move = move;
|
m_last_move = move;
|
||||||
|
|
||||||
if (move.from == Square("a1") || move.to == Square("a1") || move.from == Square("e1"))
|
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) {
|
if (get_piece(move.from).type == Type::Pawn && move.from.file != move.to.file && get_piece(move.to).type == Type::None) {
|
||||||
// En passant.
|
// En passant.
|
||||||
if (colour == Colour::White) {
|
if (colour == Colour::White) {
|
||||||
|
@ -379,3 +387,17 @@ Chess::Result Chess::game_result() const
|
||||||
|
|
||||||
return Result::StaleMate;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -87,13 +87,15 @@ public:
|
||||||
struct Move {
|
struct Move {
|
||||||
Square from;
|
Square from;
|
||||||
Square to;
|
Square to;
|
||||||
|
Type promote_to;
|
||||||
Move(const StringView& algebraic);
|
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)
|
: from(from)
|
||||||
, to(to)
|
, 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();
|
Chess();
|
||||||
|
@ -104,6 +106,8 @@ public:
|
||||||
bool is_legal(const Move&, Colour colour = Colour::None) const;
|
bool is_legal(const Move&, Colour colour = Colour::None) const;
|
||||||
bool in_check(Colour colour) 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);
|
bool apply_move(const Move&, Colour colour = Colour::None);
|
||||||
const Optional<Move>& last_move() const { return m_last_move; }
|
const Optional<Move>& last_move() const { return m_last_move; }
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ChessWidget.h"
|
#include "ChessWidget.h"
|
||||||
|
#include "PromotionDialog.h"
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <LibGUI/MessageBox.h>
|
#include <LibGUI/MessageBox.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
|
@ -114,7 +115,14 @@ void ChessWidget::mouseup_event(GUI::MouseEvent& event)
|
||||||
|
|
||||||
auto target_square = mouse_to_square(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) {
|
if (board().game_result() != Chess::Result::NotFinished) {
|
||||||
set_drag_enabled(false);
|
set_drag_enabled(false);
|
||||||
update();
|
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()
|
void ChessWidget::reset()
|
||||||
{
|
{
|
||||||
m_board = Chess();
|
m_board = Chess();
|
||||||
|
|
|
@ -59,6 +59,7 @@ public:
|
||||||
|
|
||||||
bool drag_enabled() const { return m_drag_enabled; }
|
bool drag_enabled() const { return m_drag_enabled; }
|
||||||
void set_drag_enabled(bool e) { m_drag_enabled = e; }
|
void set_drag_enabled(bool e) { m_drag_enabled = e; }
|
||||||
|
RefPtr<Gfx::Bitmap> get_piece_graphic(const Chess::Piece& piece) const;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|
56
Games/Chess/PromotionDialog.cpp
Normal file
56
Games/Chess/PromotionDialog.cpp
Normal 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);
|
||||||
|
}
|
42
Games/Chess/PromotionDialog.h
Normal file
42
Games/Chess/PromotionDialog.h
Normal 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;
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue