From 9b6b68c06597cdfb981efd7bb274e1d5d1e53c73 Mon Sep 17 00:00:00 2001 From: kersh1337228 Date: Sat, 4 Nov 2023 21:26:45 +0300 Subject: [PATCH 1/2] Initial commit. --- .gitignore | 76 +---------- CMakeLists.txt | 37 ++++++ MatchThreeTask.pro | 27 ---- README.md | 9 -- main.cpp | 10 +- src/figure/figure.cpp | 30 +++++ src/figure/figure.hpp | 65 +++++++++ src/figure/figure_generator.hpp | 49 +++++++ src/gamefield/gamefield.cpp | 193 +++++++++++++++++++++++++++ src/gamefield/gamefield.h | 43 ++++++ src/gamefield/table.cpp | 74 +++++++++++ src/gamefield/table.h | 41 ++++++ src/match_three/match_three.cpp | 70 ++++++++++ src/match_three/match_three.h | 23 ++++ src/match_three/match_three.ui | 229 ++++++++++++++++++++++++++++++++ widget.cpp | 9 -- widget.h | 22 --- widget.ui | 22 --- 18 files changed, 861 insertions(+), 168 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 MatchThreeTask.pro delete mode 100644 README.md create mode 100644 src/figure/figure.cpp create mode 100644 src/figure/figure.hpp create mode 100644 src/figure/figure_generator.hpp create mode 100644 src/gamefield/gamefield.cpp create mode 100644 src/gamefield/gamefield.h create mode 100644 src/gamefield/table.cpp create mode 100644 src/gamefield/table.h create mode 100644 src/match_three/match_three.cpp create mode 100644 src/match_three/match_three.h create mode 100644 src/match_three/match_three.ui delete mode 100644 widget.cpp delete mode 100644 widget.h delete mode 100644 widget.ui diff --git a/.gitignore b/.gitignore index 4a0b530..41c83bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,74 +1,2 @@ -# This file is used to ignore files which are generated -# ---------------------------------------------------------------------------- - -*~ -*.autosave -*.a -*.core -*.moc -*.o -*.obj -*.orig -*.rej -*.so -*.so.* -*_pch.h.cpp -*_resource.rc -*.qm -.#* -*.*# -core -!core/ -tags -.DS_Store -.directory -*.debug -Makefile* -*.prl -*.app -moc_*.cpp -ui_*.h -qrc_*.cpp -Thumbs.db -*.res -*.rc -/.qmake.cache -/.qmake.stash - -# qtcreator generated files -*.pro.user* -CMakeLists.txt.user* - -# xemacs temporary files -*.flc - -# Vim temporary files -.*.swp - -# Visual Studio generated files -*.ib_pdb_index -*.idb -*.ilk -*.pdb -*.sln -*.suo -*.vcproj -*vcproj.*.*.user -*.ncb -*.sdf -*.opensdf -*.vcxproj -*vcxproj.* - -# MinGW generated files -*.Debug -*.Release - -# Python byte code -*.pyc - -# Binaries -# -------- -*.dll -*.exe - +/.idea/ +/cmake-build-debug/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1e457b4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.26) +project(match_three) + +set(CMAKE_CXX_STANDARD 26) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(CMAKE_PREFIX_PATH "D:/Code/C++/qt6/qt6-static/lib/cmake") + +find_package(Qt6 COMPONENTS + Core + Gui + Widgets + REQUIRED) + +include_directories(src) + +add_executable(match_three main.cpp + src/match_three/match_three.cpp + src/match_three/match_three.ui + src/gamefield/table.cpp + src/gamefield/gamefield.cpp + src/figure/figure_generator.hpp + src/figure/figure.hpp + src/figure/figure.cpp) + +target_link_libraries(match_three + Qt::Core + Qt::Gui + Qt::Widgets +) + +file(COPY + src/gamefield/gamefield.h + src/gamefield/table.h + DESTINATION ${PROJECT_NAME}_autogen/include) diff --git a/MatchThreeTask.pro b/MatchThreeTask.pro deleted file mode 100644 index a73c814..0000000 --- a/MatchThreeTask.pro +++ /dev/null @@ -1,27 +0,0 @@ -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -CONFIG += c++17 - -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -SOURCES += \ - main.cpp \ - widget.cpp - -HEADERS += \ - widget.h - -FORMS += \ - widget.ui - -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target - -DISTFILES += \ - README.md diff --git a/README.md b/README.md deleted file mode 100644 index 3b9fab8..0000000 --- a/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Реализовать примитивную версию тетриса. - -Модернизировать обычный тетрис до игры "три в ряд". - -1. Фигуры должны иметь клетки с разными цветами, которые формируются случайно из заданного набора. -2. Должна быть возможность (не более 3 раз для каждой фигуры) случайно поменять местами цвета у падающей фигуры. -3. В интерфейсе "Следующая фигура" так же должна отображать цвета следующей фигуры, а не только ее размер. - -Вместо сбора и удаления целой строки, теперь должны удаляться совпадения по правилу (вертикально или горизонтально) 3+ клетки с одинаковым цветом. \ No newline at end of file diff --git a/main.cpp b/main.cpp index 50cd235..70b6cf4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,10 +1,10 @@ #include -#include "widget.h" +#include "match_three/match_three.h" int main(int argc, char *argv[]) { - QApplication a(argc, argv); - widget w; - w.show(); - return a.exec(); + QApplication a(argc, argv); + MatchThree mt; + mt.show(); + return QApplication::exec(); } diff --git a/src/figure/figure.cpp b/src/figure/figure.cpp new file mode 100644 index 0000000..6b8ec91 --- /dev/null +++ b/src/figure/figure.cpp @@ -0,0 +1,30 @@ +#include "figure.hpp" + +const QVarLengthArray, 5> Figure::presets_ = { + { + {{2, 0, 0}, {2, 1, 1}, {2, 2, 2}, {2, 3, 3}}, + {{0, 1, 0}, {1, 1, 1}, {2, 1, 2}, {3, 1, 3}}, + {{1, 0, 3}, {1, 1, 2}, {1, 2, 1}, {1, 3, 0}}, + {{0, 2, 3}, {1, 2, 2}, {2, 2, 1}, {3, 2, 0}} + }, { + {{1, 0, 0}, {0, 1, 1}, {1, 1, 2}, {1, 2, 3}}, + {{0, 1, 0}, {1, 1, 2}, {2, 1, 3}, {1, 2, 1}}, + {{1, 0, 3}, {1, 1, 2}, {2, 1, 1}, {1, 2, 0}}, + {{1, 0, 1}, {0, 1, 3}, {1, 1, 2}, {2, 1, 0}} + }, { + {{0, 1, 0}, {1, 1, 1}, {2, 1, 2}, {0, 2, 3}}, + {{1, 0, 2}, {1, 1, 1}, {1, 2, 0}, {2, 2, 3}}, + {{2, 0, 3}, {0, 1, 2}, {1, 1, 1}, {2, 1, 0}}, + {{0, 0, 3}, {1, 0, 0}, {1, 1, 1}, {1, 2, 2}} + }, { + {{1, 1, 0}, {2, 1, 1}, {0, 2, 2}, {1, 2, 3}}, + {{1, 0, 1}, {1, 1, 0}, {2, 1, 3}, {2, 2, 2}}, + {{1, 0, 3}, {2, 0, 2}, {0, 1, 1}, {1, 1, 0}}, + {{1, 0, 2}, {0, 1, 3}, {1, 1, 0}, {0, 2, 1}}, + }, { + {{0, 0, 0}, {1, 0, 1}, {0, 1, 2}, {1, 1, 3}}, + {{0, 0, 1}, {1, 0, 3}, {0, 1, 0}, {1, 1, 2}}, + {{0, 0, 3}, {1, 0, 2}, {0, 1, 1}, {1, 1, 0}}, + {{0, 0, 2}, {1, 0, 0}, {0, 1, 3}, {1, 1, 1}} + } +}; diff --git a/src/figure/figure.hpp b/src/figure/figure.hpp new file mode 100644 index 0000000..94eda09 --- /dev/null +++ b/src/figure/figure.hpp @@ -0,0 +1,65 @@ +#ifndef FIGURE_HPP +#define FIGURE_HPP + +#include +#include +#include + +#include +#include +#include + +class Figure final { +public: + using pos_t = QVarLengthArray, 4>; +public: + Figure() = default; + Figure(uint preset, uint rotation, const QVarLengthArray& colors) + : preset_(preset), rotation_(rotation), colors_(colors) {}; +public: + [[nodiscard]] Figure Rotated() const { + return { preset_, (rotation_ + 1) % 4, colors_ }; + } + void Rotate() { + rotation_ = (rotation_ + 1) % 4; + } + [[nodiscard]] auto GetGeometry() const { + return presets_[preset_][rotation_]; + } + [[nodiscard]] auto GetColors() const { + return colors_; + } + void SetColors(const QVarLengthArray& colors) { + colors_ = colors; + } + [[nodiscard]] uint GetLeft() const { + return std::ranges::min( + presets_[preset_][rotation_], {}, + [](auto&& arg) { return std::forward(arg)[1]; } + )[1]; + } + [[nodiscard]] uint GetRight() const { + return std::ranges::max( + presets_[preset_][rotation_], {}, + [](auto&& arg) { return std::forward(arg)[1]; } + )[1]; + } + [[nodiscard]] uint GetTop() const { + return std::ranges::min( + presets_[preset_][rotation_], {}, + [](auto&& arg) { return std::forward(arg)[0]; } + )[0]; + } + [[nodiscard]] uint GetBottom() const { + return std::ranges::max( + presets_[preset_][rotation_], {}, + [](auto&& arg) { return std::forward(arg)[0]; } + )[0]; + } +private: + static const QVarLengthArray, 5> presets_; + uint preset_ = 0, rotation_ = 0; + QVarLengthArray colors_; +}; + +#endif diff --git a/src/figure/figure_generator.hpp b/src/figure/figure_generator.hpp new file mode 100644 index 0000000..c984aa7 --- /dev/null +++ b/src/figure/figure_generator.hpp @@ -0,0 +1,49 @@ +#ifndef FIGURE_GENERATOR_HPP +#define FIGURE_GENERATOR_HPP + +#include +#include +#include + +#include +#include +#include + +#include "figure.hpp" + +template +class FigureGenerator final { +public: + explicit FigureGenerator( + RandGen&& randGen = std::mt19937{std::random_device{}()}, + const QVarLengthArray& colors = { + {236, 39, 51}, // Red + {246, 149, 34}, // Orange + {251, 224, 0}, // Yellow + {78, 182, 72}, // Green + {43, 171, 225}, // Light blue + {0, 90, 156}, // Dark blue + {145, 43, 139} // Purple + } + ) : randGen_{std::forward(randGen)}, colors_(colors) {}; +public: + [[nodiscard]] QVarLengthArray GenerateColors() { + return { + colors_[randGen_() % c], + colors_[randGen_() % c], + colors_[randGen_() % c], + colors_[randGen_() % c], + }; + }; + [[nodiscard]] Figure operator()() { + return { randGen_() % 5, randGen_() % 4, GenerateColors() }; + }; +private: + RandGen randGen_; + const QVarLengthArray colors_; +}; + +template +FigureGenerator(RandGen&&, const QVarLengthArray&) -> FigureGenerator; + +#endif diff --git a/src/gamefield/gamefield.cpp b/src/gamefield/gamefield.cpp new file mode 100644 index 0000000..05a0d29 --- /dev/null +++ b/src/gamefield/gamefield.cpp @@ -0,0 +1,193 @@ +#include "gamefield.h" + +#include +#include +#include + +GameField::GameField(QWidget *parent, int colorSwitchLimit) +: Table(parent), colorSwitchLimit_(colorSwitchLimit), timer_(this) { + connect(&timer_, &QTimer::timeout, this, &GameField::FigureDrop); +} + +void GameField::StartNewGame() { + setFocus(); + ResetCellsColor(); + nextFigure_ = figureGenerator_(); + NextFigure(); + score_ = 0; + emit ScoreUpdated(score_); + timer_.start(500); +} + +void GameField::StopGame() { + ResetCellsColor(); + timer_.stop(); + score_ = 0; + emit GameOver(); +} + +void GameField::FigureDrop() { + DrawFigure(currentFigure_); + repaint(); + if (IsOutOfBounds(currentFigure_, 0, 1)) { + if (currentFigure_.GetTop() + dy_) + NextFigure(); + else { + QMessageBox::critical( + (QWidget*)this->parent(), "Game over", + "Your block stack has grown too high." + ); + StopGame(); + } + return; + } + EraseFigure(currentFigure_); + ++dy_; +} + +void GameField::NextFigure() { + dy_ = 0, dx_ = 0, colorSwitches_ = 0; + emit FigureColorSwitched(colorSwitchLimit_); + currentFigure_ = std::exchange(nextFigure_, figureGenerator_()); + emit NextFigureUpdated(nextFigure_); + if (uint score = CountScore()) { + repaint(); + score_ += score; + emit ScoreUpdated(score_); + } +} + +void GameField::keyPressEvent(QKeyEvent *event) { + switch(event->key()) { + case Qt::Key_Up: + RedrawFigure( + !IsOutOfBounds(currentFigure_.Rotated()), + [this](){ currentFigure_.Rotate(); } + ); + break; + case Qt::Key_Left: + RedrawFigure( + !IsOutOfBounds(currentFigure_, -1), + [this](){ --dx_; } + ); + break; + case Qt::Key_Down: + RedrawFigure( + !IsOutOfBounds(currentFigure_, 0, 1), + [this](){ ++dy_; } + ); + break; + case Qt::Key_Right: + RedrawFigure( + !IsOutOfBounds(currentFigure_, 1), + [this](){ ++dx_; } + ); + break; + case Qt::Key_Space: + RedrawFigure( + colorSwitches_ < colorSwitchLimit_, + [this](){ + ++colorSwitches_; + emit FigureColorSwitched(colorSwitchLimit_ - colorSwitches_); + currentFigure_.SetColors( + figureGenerator_.GenerateColors() + ); + } + ); + break; + } +} + +void GameField::RedrawFigure(bool condition, auto&& function) { + if (condition) { + EraseFigure(currentFigure_); + std::forward(function)(); + DrawFigure(currentFigure_); + repaint(); + } +} + +void GameField::DrawFigure(const Figure& figure) { + auto colors = figure.GetColors(); + for (auto&& cell : figure.GetGeometry()) + cellsColors_[cell[0] + dy_][cell[1] + dx_] = colors[cell[2]]; +} + +void GameField::EraseFigure(const Figure& figure) { + for (auto&& cell : figure.GetGeometry()) + cellsColors_[cell[0] + dy_][cell[1] + dx_] = kCellDefaultColor; +} + +bool GameField::IsOutOfBounds(const Figure& figure, int dx, int dy) { + bool result = static_cast(figure.GetLeft()) + dx_ + dx < 0 + || static_cast(figure.GetRight())+ dx_ + dx >= columnsCount_ + || static_cast(figure.GetBottom()) + dy_ + dy >= rowsCount_ + || std::ranges::any_of( + std::ranges::views::filter(figure.GetGeometry(), [this, dx, dy](auto&& cell){ + return std::ranges::all_of( + currentFigure_.GetGeometry(), + [this, cell = std::forward(cell), dx, dy](auto&& current){ + return std::forward(current)[0] + dy_ != cell[0] + dy_ + dy + || std::forward(current)[1] + dx_ != cell[1] + dx_ + dx; + } + ); + }), [this, dx, dy](auto&& cell) { + return cellsColors_[std::forward(cell)[0] + dy_ + dy] + [std::forward(cell)[1] + dx_ + dx] != kCellDefaultColor; + } + ); + return result; +} + +uint GameField::CountScore() noexcept { + uint vseq = 2, hseq = 2, score = 0; + while (vseq > 1 && hseq > 1) { + for (uint i = 1; i < rowsCount_; ++i) { + hseq = 1; + for (uint j = 1; j < columnsCount_; ++j) { + if (cellsColors_[i][j] != kCellDefaultColor && cellsColors_[i][j] == cellsColors_[i][j - 1]) + ++hseq; + else { + if (hseq > 2) { + score += hseq; + for (uint k = i; k > 0; --k) + for (uint l = 1; l <= hseq; ++l) + cellsColors_[k][j - l] = cellsColors_[k - 1][j - l]; + } + hseq = 1; + } + } + if (hseq > 2) { + score += hseq; + for (uint k = i; k > 0; --k) + for (uint l = 1; l <= hseq; ++l) + cellsColors_[k][columnsCount_ - l] = cellsColors_[k - 1][columnsCount_ - l]; + } + } + for (uint j = 0; j < columnsCount_; ++j) { + vseq = 1; + for (uint i = 1; i < rowsCount_; ++i) { + if (cellsColors_[i][j] != kCellDefaultColor && cellsColors_[i][j] == cellsColors_[i - 1][j]) + ++vseq; + else { + if (vseq > 2) { + score += vseq; + for (uint k = 1; k < i - vseq; ++k) + cellsColors_[i - k][j] = cellsColors_[i - vseq - k][j]; + for (uint k = i - vseq; k <= i; ++k) + cellsColors_[i - k][j] = kCellDefaultColor; + } + vseq = 1; + } + } + if (vseq > 2) { + score += vseq; + for (uint k = 1; k <= rowsCount_ - vseq; ++k) + cellsColors_[rowsCount_ - k][j] = cellsColors_[rowsCount_ - vseq - k][j]; + for (uint k = rowsCount_ - vseq; k <= rowsCount_; ++k) + cellsColors_[rowsCount_ - k][j] = kCellDefaultColor; + } + } + } + return score; +} diff --git a/src/gamefield/gamefield.h b/src/gamefield/gamefield.h new file mode 100644 index 0000000..8b91de0 --- /dev/null +++ b/src/gamefield/gamefield.h @@ -0,0 +1,43 @@ +#ifndef GAMEFIELD_H +#define GAMEFIELD_H + +#include +#include + +#include "table.h" +#include "figure/figure_generator.hpp" + +class GameField final : public Table { +Q_OBJECT +public: + explicit GameField(QWidget *parent = nullptr, int colorSwitchLimit = 3); +signals: + void NextFigureUpdated(const Figure&); + void ScoreUpdated(int); + void FigureColorSwitched(int); + void GameOver(); +public slots: + void StartNewGame(); +private slots: + void DrawFigure(const Figure&) final; +protected: + void keyPressEvent(QKeyEvent *event) final; +private: + [[nodiscard]] bool IsOutOfBounds(const Figure&, int dx = 0, int dy = 0); + void RedrawFigure(bool, auto&&); + void EraseFigure(const Figure&); + void NextFigure(); + void FigureDrop(); + void StopGame(); + uint CountScore() noexcept; +private: + FigureGenerator<> figureGenerator_; + Figure currentFigure_, nextFigure_; + int dx_ = 0, dy_ = 0; + uint score_ = 0; + QTimer timer_; + const uint colorSwitchLimit_ = 3; + uint colorSwitches_ = 0; +}; + +#endif \ No newline at end of file diff --git a/src/gamefield/table.cpp b/src/gamefield/table.cpp new file mode 100644 index 0000000..e6be6be --- /dev/null +++ b/src/gamefield/table.cpp @@ -0,0 +1,74 @@ +#include "table.h" + +#include +#include + +Table::Table(QWidget *parent) : QWidget{parent} { + setFocusPolicy(Qt::StrongFocus); + connect( + this, &Table::InitialisationStarted, + this, &Table::SetCells, Qt::QueuedConnection + ); + emit InitialisationStarted(); +} + +uint Table::GetRowsNumber() const { return rowsCount_; } + +void Table::SetRowsNumber(uint rowsCount) { + if (rowsCount_ == rowsCount) return; + rowsCount_ = rowsCount; + emit RowsNumberChanged(); +} + +uint Table::GetColumnsNumber() const { return columnsCount_; } + +void Table::SetColumnsNumber(uint columnsCount) { + if (columnsCount_ == columnsCount) return; + columnsCount_ = columnsCount; + emit ColumnsNumberChanged(); +} + +void Table::paintEvent(QPaintEvent *event) { + Q_UNUSED(event) + QPainter painter(this); + painter.setPen(QPen(Qt::white, 1, Qt::SolidLine)); + DrawCells(&painter); +} + +void Table::SetCells() { + cellsColors_.resize(rowsCount_); + for (auto& cell : cellsColors_) { + cell.resize(columnsCount_); + cell.fill(kCellDefaultColor); + } + setFixedSize(GetSize()); +} + +void Table::ResetCellsColor() { + for (auto& cell : cellsColors_) + cell.fill(kCellDefaultColor); +} + +QSize Table::GetSize() const { + return QSize(columnsCount_, rowsCount_) * kCellSize; +} + +void Table::DrawCells(QPainter* painter) { + for (auto&& [i, row] : std::ranges::views::enumerate(cellsColors_)) { + for (auto&& [j, color] : std::ranges::views::enumerate(row)) { + painter->setBrush(color); + painter->drawRect( + j * kCellSize, i * kCellSize, + (j + 1) * kCellSize, (i + 1) * kCellSize + ); + } + } +} + +void Table::DrawFigure(const Figure& figure) { + ResetCellsColor(); + auto colors = figure.GetColors(); + for (auto&& cell : figure.GetGeometry()) + cellsColors_[cell[0]][cell[1]] = colors[cell[2]]; + repaint(); +} diff --git a/src/gamefield/table.h b/src/gamefield/table.h new file mode 100644 index 0000000..a5431e5 --- /dev/null +++ b/src/gamefield/table.h @@ -0,0 +1,41 @@ +#ifndef TABLE_H +#define TABLE_H + +#include +#include "figure/figure_generator.hpp" + +class Table : public QWidget { +Q_OBJECT + Q_PROPERTY(uint rowsNumber READ GetRowsNumber WRITE SetRowsNumber + NOTIFY RowsNumberChanged FINAL) + Q_PROPERTY(uint columnsNumber READ GetColumnsNumber WRITE SetColumnsNumber + NOTIFY ColumnsNumberChanged FINAL) +public: + explicit Table(QWidget *parent = nullptr); +signals: + void RowsNumberChanged(); + void ColumnsNumberChanged(); + void InitialisationStarted(); +public slots: + virtual void DrawFigure(const Figure&); + void ResetCellsColor(); +protected slots: + void SetCells(); +public: + [[nodiscard]] uint GetColumnsNumber() const; + [[maybe_unused]] void SetColumnsNumber(uint columnsCount); + [[nodiscard]] uint GetRowsNumber() const; + [[maybe_unused]] void SetRowsNumber(uint rowsCount); +protected: + void paintEvent(QPaintEvent *event) final; + void DrawCells(QPainter *painter); + [[nodiscard]] QSize GetSize() const; +protected: + static constexpr uint kCellSize = 20; + static constexpr QColor kCellDefaultColor = QColor(150, 150, 150); + QVector> cellsColors_; + uint rowsCount_ = 0; + uint columnsCount_ = 0; +}; + +#endif \ No newline at end of file diff --git a/src/match_three/match_three.cpp b/src/match_three/match_three.cpp new file mode 100644 index 0000000..aa6c790 --- /dev/null +++ b/src/match_three/match_three.cpp @@ -0,0 +1,70 @@ +#include "match_three.h" + +#include "ui_match_three.h" + +#include + +MatchThree::MatchThree(QWidget *parent) : QMainWindow(parent), ui(new Ui::MatchThree) { + ui->setupUi(this); + connect( + ui->newGameButton, &QPushButton::clicked, + ui->gameField, &GameField::StartNewGame + ); + connect( + ui->menuNewGame, &QAction::triggered, + ui->gameField, &GameField::StartNewGame + ); + connect( + ui->gameField, &GameField::NextFigureUpdated, + ui->nextFigureDisplay, &Table::DrawFigure + ); + connect( + ui->gameField, &GameField::ScoreUpdated, + ui->scoreDisplay, static_cast(&QLCDNumber::display) + ); + connect( + ui->gameField, &GameField::FigureColorSwitched, + ui->colorSwitchesDisplay, static_cast(&QLCDNumber::display) + ); + connect( + ui->gameField, &GameField::GameOver, + ui->nextFigureDisplay, &Table::ResetCellsColor + ); + connect( + ui->gameField, &GameField::GameOver, + [this](){ ui->scoreDisplay->display(0); } + ); + connect( + ui->gameField, &GameField::GameOver, + [this](){ ui->colorSwitchesDisplay->display(0); } + ); + connect( + ui->exitButton, &QPushButton::clicked, + this, &MatchThree::close + ); + connect( + ui->menuExit, &QAction::triggered, + this, &MatchThree::close + ); + connect( + ui->menuAbout, &QAction::triggered, + this, &MatchThree::ShowHelp + ); +} + +MatchThree::~MatchThree() { delete ui; } + +void MatchThree::ShowHelp() const { + QMessageBox::about((QWidget*)this, "How to play", + "Controls:\n" + "\t-Move Left: Left Key;\n" + "\t-Move Right: Right Key;\n" + "\t-Rotate right: Up Key;\n" + "\t-Soft drop: Down Key;\n" + "\t-Switch color: Space Key;\n" + "\nGame rules:\n" + "\t- matching three and more cells with same colors causes them to be destroyed" + " resulting in score award in an amount equal to match length;\n" + "\t- if you stack blocks too high the game will over;\n" + ); +} diff --git a/src/match_three/match_three.h b/src/match_three/match_three.h new file mode 100644 index 0000000..c701f20 --- /dev/null +++ b/src/match_three/match_three.h @@ -0,0 +1,23 @@ +#ifndef TETRIS_H +#define TETRIS_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { + class MatchThree; +} +QT_END_NAMESPACE + +class MatchThree final : public QMainWindow { +Q_OBJECT +public: + explicit MatchThree(QWidget *parent = nullptr); + ~MatchThree() final; +private slots: + void ShowHelp() const; +private: + Ui::MatchThree *ui; +}; + +#endif diff --git a/src/match_three/match_three.ui b/src/match_three/match_three.ui new file mode 100644 index 0000000..eae595a --- /dev/null +++ b/src/match_three/match_three.ui @@ -0,0 +1,229 @@ + + + MatchThree + + + + 0 + 0 + 800 + 600 + + + + Match Three + + + + + + 80 + 190 + 101 + 113 + + + + + 8 + + + + + + + New Game + + + + + + + Exit + + + + + + + + + + + Score + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::Box + + + QFrame::Plain + + + 0 + + + QLCDNumber::Flat + + + + + + + + + + + 290 + 10 + 241 + 539 + + + + + 0 + 0 + + + + 20 + + + 10 + + + + + + 590 + 70 + 131 + 171 + + + + + + + Next + + + + + + + + 0 + 0 + + + + 4 + + + 4 + + + + + + + + + 590 + 250 + 101 + 80 + + + + + + + Remaining color switches for the current figure + + + true + + + + + + + Qt::LeftToRight + + + QFrame::Plain + + + QLCDNumber::Flat + + + + + + + + + + 0 + 0 + 800 + 22 + + + + + Game + + + + + + + Help + + + + + + + + + + New Game + + + + + Exit + + + + + How to play + + + + + + Table + QWidget +
table.h
+
+ + GameField + Table +
gamefield.h
+
+
+ + +
diff --git a/widget.cpp b/widget.cpp deleted file mode 100644 index 422a761..0000000 --- a/widget.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "widget.h" - -#include "ui_widget.h" - -widget::widget(QWidget *parent) : QMainWindow(parent), ui(new Ui::widget) { - ui->setupUi(this); -} - -widget::~widget() { delete ui; } diff --git a/widget.h b/widget.h deleted file mode 100644 index bb47411..0000000 --- a/widget.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef WIDGET_H -#define WIDGET_H - -#include - -QT_BEGIN_NAMESPACE -namespace Ui { -class widget; -} -QT_END_NAMESPACE - -class widget : public QMainWindow { - Q_OBJECT - - public: - widget(QWidget *parent = nullptr); - ~widget(); - - private: - Ui::widget *ui; -}; -#endif // WIDGET_H diff --git a/widget.ui b/widget.ui deleted file mode 100644 index 01fc341..0000000 --- a/widget.ui +++ /dev/null @@ -1,22 +0,0 @@ - - - widget - - - - 0 - 0 - 800 - 600 - - - - widget - - - - - - - - From 3b82ef1f8c5e6b91416249c2c60d858cad5df313 Mon Sep 17 00:00:00 2001 From: kersh1337228 Date: Tue, 7 Nov 2023 19:32:43 +0300 Subject: [PATCH 2/2] refactor: -Required syntax fixes. --- src/gamefield/gamefield.cpp | 57 +++++++++++++++++++------------------ src/gamefield/gamefield.h | 2 +- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/gamefield/gamefield.cpp b/src/gamefield/gamefield.cpp index 05a0d29..7217869 100644 --- a/src/gamefield/gamefield.cpp +++ b/src/gamefield/gamefield.cpp @@ -5,7 +5,7 @@ #include GameField::GameField(QWidget *parent, int colorSwitchLimit) -: Table(parent), colorSwitchLimit_(colorSwitchLimit), timer_(this) { +: Table(parent), colorSwitchCount_(colorSwitchLimit), timer_(this) { connect(&timer_, &QTimer::timeout, this, &GameField::FigureDrop); } @@ -47,7 +47,7 @@ void GameField::FigureDrop() { void GameField::NextFigure() { dy_ = 0, dx_ = 0, colorSwitches_ = 0; - emit FigureColorSwitched(colorSwitchLimit_); + emit FigureColorSwitched(colorSwitchCount_); currentFigure_ = std::exchange(nextFigure_, figureGenerator_()); emit NextFigureUpdated(nextFigure_); if (uint score = CountScore()) { @@ -85,10 +85,10 @@ void GameField::keyPressEvent(QKeyEvent *event) { break; case Qt::Key_Space: RedrawFigure( - colorSwitches_ < colorSwitchLimit_, + colorSwitches_ < colorSwitchCount_, [this](){ ++colorSwitches_; - emit FigureColorSwitched(colorSwitchLimit_ - colorSwitches_); + emit FigureColorSwitched(colorSwitchCount_ - colorSwitches_); currentFigure_.SetColors( figureGenerator_.GenerateColors() ); @@ -140,51 +140,52 @@ bool GameField::IsOutOfBounds(const Figure& figure, int dx, int dy) { } uint GameField::CountScore() noexcept { - uint vseq = 2, hseq = 2, score = 0; - while (vseq > 1 && hseq > 1) { + constexpr uint minSeq = 3; + uint vSeq = minSeq, hSeq = minSeq, score = 0; + while (vSeq > 1 && hSeq > 1) { for (uint i = 1; i < rowsCount_; ++i) { - hseq = 1; + hSeq = 1; for (uint j = 1; j < columnsCount_; ++j) { if (cellsColors_[i][j] != kCellDefaultColor && cellsColors_[i][j] == cellsColors_[i][j - 1]) - ++hseq; + ++hSeq; else { - if (hseq > 2) { - score += hseq; + if (hSeq >= minSeq) { + score += hSeq; for (uint k = i; k > 0; --k) - for (uint l = 1; l <= hseq; ++l) + for (uint l = 1; l <= hSeq; ++l) cellsColors_[k][j - l] = cellsColors_[k - 1][j - l]; } - hseq = 1; + hSeq = 1; } } - if (hseq > 2) { - score += hseq; + if (hSeq >= minSeq) { + score += hSeq; for (uint k = i; k > 0; --k) - for (uint l = 1; l <= hseq; ++l) + for (uint l = 1; l <= hSeq; ++l) cellsColors_[k][columnsCount_ - l] = cellsColors_[k - 1][columnsCount_ - l]; } } for (uint j = 0; j < columnsCount_; ++j) { - vseq = 1; + vSeq = 1; for (uint i = 1; i < rowsCount_; ++i) { if (cellsColors_[i][j] != kCellDefaultColor && cellsColors_[i][j] == cellsColors_[i - 1][j]) - ++vseq; + ++vSeq; else { - if (vseq > 2) { - score += vseq; - for (uint k = 1; k < i - vseq; ++k) - cellsColors_[i - k][j] = cellsColors_[i - vseq - k][j]; - for (uint k = i - vseq; k <= i; ++k) + if (vSeq >= minSeq) { + score += vSeq; + for (uint k = 1; k < i - vSeq; ++k) + cellsColors_[i - k][j] = cellsColors_[i - vSeq - k][j]; + for (uint k = i - vSeq; k <= i; ++k) cellsColors_[i - k][j] = kCellDefaultColor; } - vseq = 1; + vSeq = 1; } } - if (vseq > 2) { - score += vseq; - for (uint k = 1; k <= rowsCount_ - vseq; ++k) - cellsColors_[rowsCount_ - k][j] = cellsColors_[rowsCount_ - vseq - k][j]; - for (uint k = rowsCount_ - vseq; k <= rowsCount_; ++k) + if (vSeq >= minSeq) { + score += vSeq; + for (uint k = 1; k <= rowsCount_ - vSeq; ++k) + cellsColors_[rowsCount_ - k][j] = cellsColors_[rowsCount_ - vSeq - k][j]; + for (uint k = rowsCount_ - vSeq; k <= rowsCount_; ++k) cellsColors_[rowsCount_ - k][j] = kCellDefaultColor; } } diff --git a/src/gamefield/gamefield.h b/src/gamefield/gamefield.h index 8b91de0..e83f6f0 100644 --- a/src/gamefield/gamefield.h +++ b/src/gamefield/gamefield.h @@ -36,7 +36,7 @@ private slots: int dx_ = 0, dy_ = 0; uint score_ = 0; QTimer timer_; - const uint colorSwitchLimit_ = 3; + const uint colorSwitchCount_ = 3; uint colorSwitches_ = 0; };