Chess: Add basic ChessWidget and ugly piece set

This commit is contained in:
Peter Elliott 2020-08-10 12:54:32 -06:00 committed by Andreas Kling
parent 8d680dd1ad
commit 7333916252
Notes: sideshowbarker 2024-07-19 03:35:56 +09:00
22 changed files with 528 additions and 0 deletions

8
Base/res/apps/Chess.af Normal file
View file

@ -0,0 +1,8 @@
[App]
Name=Chess
Executable=/bin/Chess
Category=Games
[Icons]
16x16=/res/icons/16x16/app-chess.png
32x32=/res/icons/32x32/app-chess.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -1,4 +1,5 @@
add_subdirectory(2048)
add_subdirectory(Chess)
add_subdirectory(Minesweeper)
add_subdirectory(Snake)
add_subdirectory(Solitaire)

View file

@ -0,0 +1,8 @@
set(SOURCES
main.cpp
Chess.cpp
ChessWidget.cpp
)
serenity_bin(Chess)
target_link_libraries(Chess LibGUI)

127
Games/Chess/Chess.cpp Normal file
View file

@ -0,0 +1,127 @@
/*
* 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 "Chess.h"
#include <AK/Assertions.h>
Chess::Square::Square(const StringView& name)
{
ASSERT(name.length() == 2);
char filec = name[0];
char rankc = name[1];
if (filec >= 'a' && filec <= 'h') {
file = filec - 'a';
} else if (filec >= 'A' && filec <= 'H') {
file = filec - 'A';
} else {
ASSERT_NOT_REACHED();
}
if (rankc >= '1' && rankc <= '8') {
rank = rankc - '1';
} else {
ASSERT_NOT_REACHED();
}
}
Chess::Chess()
{
// Fill empty spaces.
for (unsigned rank = 2; rank < 6; ++rank) {
for (unsigned file = 0; file < 8; ++file) {
set_piece({ rank, file }, EmptyPiece);
}
}
// Fill white pawns.
for (unsigned file = 0; file < 8; ++file) {
set_piece({ 1, file }, { Colour::White, Type::Pawn });
}
// Fill black pawns.
for (unsigned file = 0; file < 8; ++file) {
set_piece({ 6, file }, { Colour::Black, Type::Pawn });
}
// Fill while pieces.
set_piece(Square("a1"), { Colour::White, Type::Rook });
set_piece(Square("b1"), { Colour::White, Type::Knight });
set_piece(Square("c1"), { Colour::White, Type::Bishop });
set_piece(Square("d1"), { Colour::White, Type::Queen });
set_piece(Square("e1"), { Colour::White, Type::King });
set_piece(Square("f1"), { Colour::White, Type::Bishop });
set_piece(Square("g1"), { Colour::White, Type::Knight });
set_piece(Square("h1"), { Colour::White, Type::Rook });
// Fill black pieces.
set_piece(Square("a8"), { Colour::Black, Type::Rook });
set_piece(Square("b8"), { Colour::Black, Type::Knight });
set_piece(Square("c8"), { Colour::Black, Type::Bishop });
set_piece(Square("d8"), { Colour::Black, Type::Queen });
set_piece(Square("e8"), { Colour::Black, Type::King });
set_piece(Square("f8"), { Colour::Black, Type::Bishop });
set_piece(Square("g8"), { Colour::Black, Type::Knight });
set_piece(Square("h8"), { Colour::Black, Type::Rook });
}
Chess::Piece Chess::get_piece(const Square& square) const
{
ASSERT(square.rank < 8);
ASSERT(square.file < 8);
return m_board[square.rank][square.file];
}
Chess::Piece Chess::set_piece(const Square& square, const Piece& piece)
{
ASSERT(square.rank < 8);
ASSERT(square.file < 8);
return m_board[square.rank][square.file] = piece;
}
bool Chess::is_legal(const Move& move) const
{
// FIXME: Impelement actual chess logic.
return get_piece(move.from).colour == turn() && get_piece(move.to).colour != turn();
}
bool Chess::apply_move(const Move& move)
{
if (!is_legal(move)) {
return false;
}
set_piece(move.to, get_piece(move.from));
set_piece(move.from, EmptyPiece);
if (m_turn == Colour::White) {
m_turn = Colour::Black;
} else if (m_turn == Colour::Black) {
m_turn = Colour::White;
}
return true;
}

102
Games/Chess/Chess.h Normal file
View file

@ -0,0 +1,102 @@
/*
* 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 <AK/StringView.h>
#include <AK/Traits.h>
class Chess {
public:
enum class Type {
Pawn,
Knight,
Bishop,
Rook,
Queen,
King,
None,
};
enum class Colour {
White,
Black,
None,
};
struct Piece {
Colour colour;
Type type;
bool operator==(const Piece& other) const { return colour == other.colour && type == other.type; }
};
static 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; }
};
struct Move {
Square from;
Square to;
Move(const StringView& algebraic);
Move(const Square& from, const Square& to)
: from(from)
, to(to)
{
}
};
Chess();
Piece get_piece(const Square&) const;
Piece set_piece(const Square&, const Piece&);
bool is_legal(const Move&) const;
bool apply_move(const Move&);
Colour turn() const { return m_turn; };
private:
Piece m_board[8][8];
Colour m_turn { Colour::White };
};
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));
}
};

166
Games/Chess/ChessWidget.cpp Normal file
View file

@ -0,0 +1,166 @@
/*
* 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 "ChessWidget.h"
#include <AK/String.h>
#include <LibGUI/Painter.h>
ChessWidget::ChessWidget(const StringView& set)
{
set_piece_set(set);
}
ChessWidget::ChessWidget()
: ChessWidget("test")
{
}
ChessWidget::~ChessWidget()
{
}
void ChessWidget::paint_event(GUI::PaintEvent& event)
{
GUI::Widget::paint_event(event);
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
size_t tile_width = width() / 8;
size_t tile_height = height() / 8;
for (unsigned rank = 0; rank < 8; ++rank) {
for (unsigned file = 0; file < 8; ++file) {
Gfx::IntRect tile_rect;
if (side() == Chess::Colour::White) {
tile_rect = { file * tile_width, (7 - rank) * tile_height, tile_width, tile_height };
} else {
tile_rect = { (7 - file) * tile_width, rank * tile_height, tile_width, tile_height };
}
painter.fill_rect(tile_rect, ((rank % 2) == (file % 2)) ? m_dark_square_color : m_light_square_color);
Chess::Square square = { rank, file };
if (!(m_dragging_piece && square == m_moving_square)) {
auto bmp = m_pieces.get(board().get_piece(square));
if (bmp.has_value()) {
painter.draw_scaled_bitmap(tile_rect, *bmp.value(), bmp.value()->rect());
}
}
}
}
if (m_dragging_piece) {
auto bmp = m_pieces.get(board().get_piece(m_moving_square));
if (bmp.has_value()) {
auto center = m_drag_point - Gfx::IntPoint(tile_width / 2, tile_height / 2);
painter.draw_scaled_bitmap({ center, { tile_width, tile_height } }, *bmp.value(), bmp.value()->rect());
}
}
}
void ChessWidget::resize_event(GUI::ResizeEvent& event)
{
GUI::Widget::resize_event(event);
}
void ChessWidget::mousedown_event(GUI::MouseEvent& event)
{
GUI::Widget::mousedown_event(event);
auto square = mouse_to_square(event);
auto piece = board().get_piece(square);
if (piece.colour == board().turn()) {
m_dragging_piece = true;
m_drag_point = event.position();
m_moving_square = square;
update();
}
}
void ChessWidget::mouseup_event(GUI::MouseEvent& event)
{
GUI::Widget::mouseup_event(event);
if (!m_dragging_piece)
return;
m_dragging_piece = false;
auto target_square = mouse_to_square(event);
board().apply_move({ m_moving_square, target_square });
update();
}
void ChessWidget::mousemove_event(GUI::MouseEvent& event)
{
GUI::Widget::mousemove_event(event);
if (!m_dragging_piece)
return;
m_drag_point = event.position();
update();
}
static String set_path = String("/res/icons/chess/sets/");
static RefPtr<Gfx::Bitmap> get_piece(const StringView& set, const StringView& image)
{
StringBuilder builder;
builder.append(set_path);
builder.append(set);
builder.append('/');
builder.append(image);
return Gfx::Bitmap::load_from_file(builder.build());
}
void ChessWidget::set_piece_set(const StringView& set)
{
m_piece_set = set;
m_pieces.set({ Chess::Colour::White, Chess::Type::Pawn }, get_piece(set, "white-pawn.png"));
m_pieces.set({ Chess::Colour::Black, Chess::Type::Pawn }, get_piece(set, "black-pawn.png"));
m_pieces.set({ Chess::Colour::White, Chess::Type::Knight }, get_piece(set, "white-knight.png"));
m_pieces.set({ Chess::Colour::Black, Chess::Type::Knight }, get_piece(set, "black-knight.png"));
m_pieces.set({ Chess::Colour::White, Chess::Type::Bishop }, get_piece(set, "white-bishop.png"));
m_pieces.set({ Chess::Colour::Black, Chess::Type::Bishop }, get_piece(set, "black-bishop.png"));
m_pieces.set({ Chess::Colour::White, Chess::Type::Rook }, get_piece(set, "white-rook.png"));
m_pieces.set({ Chess::Colour::Black, Chess::Type::Rook }, get_piece(set, "black-rook.png"));
m_pieces.set({ Chess::Colour::White, Chess::Type::Queen }, get_piece(set, "white-queen.png"));
m_pieces.set({ Chess::Colour::Black, Chess::Type::Queen }, get_piece(set, "black-queen.png"));
m_pieces.set({ Chess::Colour::White, Chess::Type::King }, get_piece(set, "white-king.png"));
m_pieces.set({ Chess::Colour::Black, Chess::Type::King }, get_piece(set, "black-king.png"));
}
Chess::Square ChessWidget::mouse_to_square(GUI::MouseEvent& event) const
{
size_t tile_width = width() / 8;
size_t tile_height = height() / 8;
if (side() == Chess::Colour::White) {
return { 7 - (event.y() / tile_height), event.x() / tile_width };
} else {
return { event.y() / tile_height, 7 - (event.x() / tile_width) };
}
}

69
Games/Chess/ChessWidget.h Normal file
View file

@ -0,0 +1,69 @@
/*
* 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 "Chess.h"
#include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h>
#include <AK/StringView.h>
#include <LibGUI/Widget.h>
#include <LibGfx/Bitmap.h>
class ChessWidget final : public GUI::Widget {
C_OBJECT(ChessWidget)
public:
ChessWidget();
ChessWidget(const StringView& set);
virtual ~ChessWidget() override;
virtual void paint_event(GUI::PaintEvent&) override;
virtual void resize_event(GUI::ResizeEvent&) override;
virtual void mousedown_event(GUI::MouseEvent&) override;
virtual void mouseup_event(GUI::MouseEvent&) override;
virtual void mousemove_event(GUI::MouseEvent&) override;
Chess& board() { return m_board; };
Chess::Colour side() const { return m_side; };
void set_side(Chess::Colour side) { m_side = side; };
void set_piece_set(const StringView& set);
const String& piece_set() const { return m_piece_set; };
Chess::Square mouse_to_square(GUI::MouseEvent& event) const;
private:
Chess m_board;
Color m_dark_square_color { Color::from_rgb(0xb58863) };
Color m_light_square_color { Color::from_rgb(0xf0d9b5) };
Chess::Colour m_side { Chess::Colour::White };
HashMap<Chess::Piece, RefPtr<Gfx::Bitmap>> m_pieces;
String m_piece_set;
Chess::Square m_moving_square { 50, 50 };
Gfx::IntPoint m_drag_point;
bool m_dragging_piece { false };
};

47
Games/Chess/main.cpp Normal file
View file

@ -0,0 +1,47 @@
/*
* 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 "ChessWidget.h"
#include <LibGUI/Application.h>
#include <LibGUI/Window.h>
int main(int argc, char** argv)
{
auto app = GUI::Application::construct(argc, argv);
auto window = GUI::Window::construct();
auto& widget = window->set_main_widget<ChessWidget>();
widget.set_side(Chess::Colour::White);
window->set_title("Chess");
window->resize(512, 512);
window->set_resizable(false);
window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-chess.png"));
window->show();
return app->exec();
}