summaryrefslogtreecommitdiff
path: root/Games/Chess
diff options
context:
space:
mode:
authorAnicJov <contact.andrija@gmail.com>2020-12-10 17:54:11 +0100
committerAndreas Kling <kling@serenityos.org>2020-12-10 20:40:51 +0100
commite119d7d6b905835bd52d5fc2b66ced0eaa41b9e1 (patch)
tree56b25028634623c3bedba6279aade526970c52c5 /Games/Chess
parentf631e73519a7b6dabe75b6b60d3579d3afb342c7 (diff)
downloadserenity-e119d7d6b905835bd52d5fc2b66ced0eaa41b9e1.zip
Chess: Added ability to put markings on the board
With this patch you can use right-click to mark a square on the board. You can add modifier keys to the click to change to alternate color (with CTRL) or secondary color (with Shift). If you right-click and drag from one square to another you will create an arrow. The markings go away as soon as you left-click on the board or the board state changes. Note: The arrows sometimes look weird, and horizontal ones get cut off. They also don't account for alpha. This is not a bug in Chess code, rather, likely in the fill_path() function that's used to draw the arrows. If anyone might know what's up with that I urge you to take a look. :)
Diffstat (limited to 'Games/Chess')
-rw-r--r--Games/Chess/ChessWidget.cpp85
-rw-r--r--Games/Chess/ChessWidget.h27
2 files changed, 111 insertions, 1 deletions
diff --git a/Games/Chess/ChessWidget.cpp b/Games/Chess/ChessWidget.cpp
index bca2cb0f67..36b3260bf8 100644
--- a/Games/Chess/ChessWidget.cpp
+++ b/Games/Chess/ChessWidget.cpp
@@ -32,6 +32,7 @@
#include <LibGUI/MessageBox.h>
#include <LibGUI/Painter.h>
#include <LibGfx/Font.h>
+#include <LibGfx/Path.h>
#include <unistd.h>
ChessWidget::ChessWidget(const StringView& set)
@@ -88,6 +89,13 @@ void ChessWidget::paint_event(GUI::PaintEvent& event)
painter.draw_text(shrunken_rect, coord.substring_view(1, 1), Gfx::Font::default_bold_font(), Gfx::TextAlignment::TopLeft, text_color);
}
+ for (auto& m : m_board_markings) {
+ if (m.type() == BoardMarking::Type::Square && m.from == sq) {
+ Gfx::Color color = m.secondary_color ? m_marking_secondary_color : (m.alternate_color ? m_marking_alternate_color : m_marking_primary_color);
+ painter.fill_rect(tile_rect, color);
+ }
+ }
+
if (!(m_dragging_piece && sq == m_moving_square)) {
auto bmp = m_pieces.get(active_board.get_piece(sq));
if (bmp.has_value()) {
@@ -98,6 +106,53 @@ void ChessWidget::paint_event(GUI::PaintEvent& event)
return IterationDecision::Continue;
});
+ auto draw_arrow = [&painter](Gfx::FloatPoint A, Gfx::FloatPoint B, float w1, float w2, float h, Gfx::Color color) {
+ float dx = B.x() - A.x();
+ float dy = A.y() - B.y();
+ float phi = atan2f(dy, dx);
+ float hdx = h * cos(phi);
+ float hdy = h * sin(phi);
+
+ Gfx::FloatPoint A1(A.x() - (w1 / 2) * cos(M_PI_2 - phi), A.y() - (w1 / 2) * sin(M_PI_2 - phi));
+ Gfx::FloatPoint B3(A.x() + (w1 / 2) * cos(M_PI_2 - phi), A.y() + (w1 / 2) * sin(M_PI_2 - phi));
+ Gfx::FloatPoint A2(A1.x() + (dx - hdx), A1.y() - (dy - hdy));
+ Gfx::FloatPoint B2(B3.x() + (dx - hdx), B3.y() - (dy - hdy));
+ Gfx::FloatPoint A3(A2.x() - w2 * cos(M_PI_2 - phi), A2.y() - w2 * sin(M_PI_2 - phi));
+ Gfx::FloatPoint B1(B2.x() + w2 * cos(M_PI_2 - phi), B2.y() + w2 * sin(M_PI_2 - phi));
+
+ auto path = Gfx::Path();
+ path.move_to(A);
+ path.line_to(A1);
+ path.line_to(A2);
+ path.line_to(A3);
+ path.line_to(B);
+ path.line_to(B1);
+ path.line_to(B2);
+ path.line_to(B3);
+ path.line_to(A);
+ path.close();
+
+ painter.fill_path(path, color, Gfx::Painter::WindingRule::EvenOdd);
+ };
+
+ for (auto& m : m_board_markings) {
+ if (m.type() == BoardMarking::Type::Arrow) {
+ Gfx::FloatPoint arrow_start;
+ Gfx::FloatPoint arrow_end;
+
+ if (side() == Chess::Colour::White) {
+ arrow_start = { m.from.file * tile_width + tile_width / 2.0f, (7 - m.from.rank) * tile_height + tile_height / 2.0f };
+ arrow_end = { m.to.file * tile_width + tile_width / 2.0f, (7 - m.to.rank) * tile_height + tile_height / 2.0f };
+ } else {
+ arrow_start = { (7 - m.from.file) * tile_width + tile_width / 2.0f, m.from.rank * tile_height + tile_height / 2.0f };
+ arrow_end = { (7 - m.to.file) * tile_width + tile_width / 2.0f, m.to.rank * tile_height + tile_height / 2.0f };
+ }
+
+ Gfx::Color color = m.secondary_color ? m_marking_secondary_color : (m.alternate_color ? m_marking_primary_color : m_marking_alternate_color);
+ draw_arrow(arrow_start, arrow_end, tile_width / 8.0f, tile_width / 10.0f, tile_height / 2.5f, color);
+ }
+ }
+
if (m_dragging_piece) {
auto bmp = m_pieces.get(active_board.get_piece(m_moving_square));
if (bmp.has_value()) {
@@ -110,19 +165,43 @@ void ChessWidget::paint_event(GUI::PaintEvent& event)
void ChessWidget::mousedown_event(GUI::MouseEvent& event)
{
GUI::Widget::mousedown_event(event);
+
+ if (event.button() == GUI::MouseButton::Right) {
+ m_current_marking.from = mouse_to_square(event);
+ return;
+ }
+ m_board_markings.clear();
+
auto square = mouse_to_square(event);
auto piece = board().get_piece(square);
if (drag_enabled() && piece.colour == board().turn() && !m_playback) {
m_dragging_piece = true;
m_drag_point = event.position();
m_moving_square = square;
- update();
}
+
+ update();
}
void ChessWidget::mouseup_event(GUI::MouseEvent& event)
{
GUI::Widget::mouseup_event(event);
+
+ if (event.button() == GUI::MouseButton::Right) {
+ m_current_marking.secondary_color = event.shift();
+ m_current_marking.alternate_color = event.ctrl();
+ m_current_marking.to = mouse_to_square(event);
+ auto match_index = m_board_markings.find_first_index(m_current_marking);
+ if (match_index.has_value()) {
+ m_board_markings.remove(match_index.value());
+ update();
+ return;
+ }
+ m_board_markings.append(m_current_marking);
+ update();
+ return;
+ }
+
if (!m_dragging_piece)
return;
@@ -286,6 +365,7 @@ RefPtr<Gfx::Bitmap> ChessWidget::get_piece_graphic(const Chess::Piece& piece) co
void ChessWidget::reset()
{
+ m_board_markings.clear();
m_playback = false;
m_playback_move_number = 0;
m_board_playback = Chess::Board();
@@ -325,6 +405,7 @@ void ChessWidget::maybe_input_engine_move()
ASSERT(board().apply_move(move));
m_playback_move_number = m_board.moves().size();
m_playback = false;
+ m_board_markings.clear();
update();
});
}
@@ -335,6 +416,7 @@ void ChessWidget::playback_move(PlaybackDirection direction)
return;
m_playback = true;
+ m_board_markings.clear();
switch (direction) {
case PlaybackDirection::Backward:
@@ -469,6 +551,7 @@ bool ChessWidget::import_pgn(const StringView& import_path)
}
}
+ m_board_markings.clear();
m_board_playback = m_board;
m_playback_move_number = m_board_playback.moves().size();
m_playback = true;
diff --git a/Games/Chess/ChessWidget.h b/Games/Chess/ChessWidget.h
index 7535ce8bdf..493c93d25b 100644
--- a/Games/Chess/ChessWidget.h
+++ b/Games/Chess/ChessWidget.h
@@ -100,13 +100,40 @@ public:
void set_coordinates(bool coordinates) { m_coordinates = coordinates; }
bool coordinates() const { return m_coordinates; }
+ struct BoardMarking {
+ Chess::Square from { 50, 50 };
+ Chess::Square to { 50, 50 };
+ bool alternate_color { false };
+ bool secondary_color { false };
+ enum class Type {
+ Square,
+ Arrow,
+ None
+ };
+ Type type() const
+ {
+ if (from.in_bounds() && to.in_bounds() && from != to)
+ return Type::Arrow;
+ else if ((from.in_bounds() && !to.in_bounds()) || (from.in_bounds() && to.in_bounds() && from == to))
+ return Type::Square;
+
+ return Type::None;
+ }
+ bool operator==(const BoardMarking& other) const { return from == other.from && to == other.to; }
+ };
+
private:
Chess::Board m_board;
Chess::Board m_board_playback;
bool m_playback { false };
size_t m_playback_move_number { 0 };
+ BoardMarking m_current_marking;
+ Vector<BoardMarking> m_board_markings;
BoardTheme m_board_theme { "Beige", Color::from_rgb(0xb58863), Color::from_rgb(0xf0d9b5) };
Color m_move_highlight_color { Color::from_rgba(0x66ccee00) };
+ Color m_marking_primary_color { Color::from_rgba(0x66ff0000) };
+ Color m_marking_alternate_color { Color::from_rgba(0x66ffaa00) };
+ Color m_marking_secondary_color { Color::from_rgba(0x6655dd55) };
Chess::Colour m_side { Chess::Colour::White };
HashMap<Chess::Piece, RefPtr<Gfx::Bitmap>> m_pieces;
String m_piece_set;