working before mass controller and drawer rewrite
This commit is contained in:
parent
a2cf3d1c30
commit
cb5738cde6
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ build/
|
||||
class_diagram/
|
||||
diagrams/
|
||||
.vscode/*.log
|
||||
report/.~lock*
|
60
.vscode/settings.json
vendored
60
.vscode/settings.json
vendored
@ -27,6 +27,62 @@
|
||||
"typeinfo": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"set": "cpp",
|
||||
"tuple": "cpp"
|
||||
}
|
||||
"tuple": "cpp",
|
||||
"hash_map": "cpp",
|
||||
"hash_set": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"string_view": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"any": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"cfenv": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"complex": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"map": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"system_error": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"mutex": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"valarray": "cpp",
|
||||
"variant": "cpp"
|
||||
},
|
||||
"lldb.displayFormat": "auto",
|
||||
"lldb.showDisassembly": "auto",
|
||||
"lldb.dereferencePointers": true,
|
||||
"lldb.consoleMode": "commands"
|
||||
}
|
6
App.cpp
6
App.cpp
@ -8,13 +8,13 @@ wxIMPLEMENT_APP(MyApp);
|
||||
bool MyApp::OnInit() {
|
||||
wxImage::AddHandler(new wxPNGHandler());
|
||||
|
||||
MainFrame* frame = new MainFrame();
|
||||
MainFrame* frame = new MainFrame(); // Создаём,
|
||||
|
||||
if (argc >= 2 && wxFileExists(argv[1]))
|
||||
frame->layoutPath = argv[1];
|
||||
|
||||
frame->Show(true);
|
||||
SetTopWindow(frame);
|
||||
frame->Show(true); // показываем
|
||||
SetTopWindow(frame); // и устанавливаем главным окном, а так же выносим вперёд основное окно игры
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ void Controller::fillSolveableTable() {
|
||||
int id = genRandId();
|
||||
|
||||
if (id < 34) {
|
||||
emplace_rand(id, positions, next_ptr, true);
|
||||
emplace_rand(id, positions, next_ptr, false);
|
||||
emplace_rand(id, positions, next_ptr, true);
|
||||
|
||||
cardsCounter[id]--;
|
||||
not_end -= 2;
|
||||
@ -69,7 +69,7 @@ wxPoint Controller::getRandLowest() {
|
||||
|
||||
do {
|
||||
int pos = rand() % overall;
|
||||
x = (pos / gridSize.y) % gridSize.x;
|
||||
x = pos / gridSize.y;
|
||||
y = pos % gridSize.y;
|
||||
} while (table[0][x][y] != FREE);
|
||||
|
||||
@ -103,16 +103,15 @@ void Controller::emplace_rand(int id, std::set<ThreePoint>& positions,
|
||||
push_available(positions, *next_ptr);
|
||||
|
||||
auto prev_ptr = next_ptr;
|
||||
auto prev = *next_ptr;
|
||||
ThreePoint prev = *next_ptr;
|
||||
|
||||
do {
|
||||
next_ptr++;
|
||||
|
||||
if (next_ptr == positions.end())
|
||||
next_ptr = positions.begin();
|
||||
} while (!canBeUp && !wouldBeUpFree(prev, *next_ptr));
|
||||
cyclic_shift(next_ptr, positions);
|
||||
|
||||
positions.erase(prev_ptr);
|
||||
|
||||
do
|
||||
cyclic_shift(next_ptr, positions);
|
||||
while (!canBeUp && !wouldBeUpFree(prev, *next_ptr));
|
||||
}
|
||||
|
||||
bool Controller::wouldBeUpFree(const ThreePoint& prev, const ThreePoint& next) {
|
||||
@ -167,10 +166,10 @@ void Controller::push_available(std::set<ThreePoint>& positions,
|
||||
if (table[z+1][x-1][y] == FREE) // straight
|
||||
positions.emplace(z+1, x-1, y);
|
||||
|
||||
if (y >= 1 && (x < 2 || table[z][x-2][y-1] != FREE) && table[z+1][x-1][y-1] == FREE) // half top
|
||||
if (y >= 1 && (x < 2 || table[z][x-2][y-1] != FREE || (y < 2 || table[z][x-2][y-2] != FREE)) && (y < 2 || table[z][x][y-2] != FREE) && table[z+1][x-1][y-1] == FREE) // half top
|
||||
positions.emplace(z+1, x-1, y-1);
|
||||
|
||||
if (y + 1 < gridSize.y && (x < 2 || table[z][x-2][y+1] != FREE) && table[z+1][x-1][y+1] == FREE) //half bottom
|
||||
if (y + 1 < gridSize.y && (x < 2 || table[z][x-2][y+1] != FREE || (y + 2 < gridSize.y || table[z][x-2][y+2] != FREE)) && (y + 2 >= gridSize.y || table[z][x][y+2] != FREE) && table[z+1][x-1][y+1] == FREE) // half bottom
|
||||
positions.emplace(z+1, x-1, y+1);
|
||||
}
|
||||
|
||||
@ -178,10 +177,10 @@ void Controller::push_available(std::set<ThreePoint>& positions,
|
||||
if (table[z+1][x+1][y] == FREE) // straight
|
||||
positions.emplace(z+1, x+1, y);
|
||||
|
||||
if (y >= 1 && (x + 2 >= gridSize.x || table[z][x+2][y-1] != FREE) && table[z+1][x+1][y-1] == FREE) // half top
|
||||
if (y >= 1 && (x + 2 >= gridSize.x || table[z][x+2][y-1] != FREE || (y < 2 || table[z][x+2][y-2] != FREE)) && (y < 2 || table[z][x][y-2] != FREE) && table[z+1][x+1][y-1] == FREE) // half top
|
||||
positions.emplace(z+1, x+1, y-1);
|
||||
|
||||
if (y + 1 < gridSize.y && (x + 2 >= gridSize.x || table[z][x+2][y+1] != FREE) && table[z+1][x+1][y+1] == FREE) //half bottom
|
||||
if (y + 1 < gridSize.y && (x + 2 >= gridSize.x || table[z][x+2][y+1] != FREE || (y + 2 < gridSize.y || table[z][x+2][y+2] != FREE)) && (y + 2 >= gridSize.y || table[z][x][y+2] != FREE) && table[z+1][x+1][y+1] == FREE) // half bottom
|
||||
positions.emplace(z+1, x+1, y+1);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@
|
||||
#include "events.h"
|
||||
#include "utils.h"
|
||||
|
||||
/**
|
||||
* Объявляем таблицу обработчиков событий
|
||||
*/
|
||||
// clang-format off
|
||||
wxBEGIN_EVENT_TABLE(GamePanel, wxPanel)
|
||||
EVT_PAINT(GamePanel::OnPaint)
|
||||
@ -15,32 +18,32 @@ wxEND_EVENT_TABLE();
|
||||
// clang-format on
|
||||
|
||||
GamePanel::GamePanel(wxFrame* parent)
|
||||
: wxPanel(parent), controller(drawer),
|
||||
sb(((wxFrame*)this->GetParent())->GetStatusBar()), timer(this, TIMER_ID) {
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
: wxPanel(parent), controller(drawer), // вызываем родительский конструктор и передаём drawer в конструктор
|
||||
sb(((wxFrame*)this->GetParent())->GetStatusBar()), timer(this, TIMER_ID) { // инициализируем указатель на статус бар окна, создаём таймер
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT); // Устанавливаем wxBG_STYLE_PAINT для того, чтобы при вызове OnPaint не мерцало окно
|
||||
}
|
||||
|
||||
void GamePanel::Start(const wxString& path, bool solvable,
|
||||
std::function<void(const wxSize& size)> setMinSize) {
|
||||
wxLogDebug(_("Started game"));
|
||||
wxLogDebug(_("Started game")); // здесь и далее - сообщения, которые выводятся в консоль в дебаг-версии
|
||||
|
||||
controller.stopwatch = 0;
|
||||
controller.loadLayout(path);
|
||||
controller.fill(solvable);
|
||||
controller.stopwatch = 0; // сбрасываем таймер
|
||||
controller.loadLayout(path); // загружаем в контроллер схему
|
||||
controller.fill(solvable); // заполняем игровое поле
|
||||
|
||||
setMinSize(drawer.composeMinSize(controller.gridSize));
|
||||
setMinSize(drawer.composeMinSize(controller.gridSize)); // устанавливаем минимальный размер окна
|
||||
|
||||
timer.Start(1000, wxTIMER_CONTINUOUS);
|
||||
timer.Start(1000, wxTIMER_CONTINUOUS); // Запускаем таймер для вызова события каждую секунду
|
||||
|
||||
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0);
|
||||
sb->SetStatusText(PRemaining(controller.remaining), 1);
|
||||
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0); // В первую колонку статус бара пишем время игры,
|
||||
sb->SetStatusText(PRemaining(controller.remaining), 1); // во вторую процент оставшихся камней
|
||||
|
||||
bool redrawn =
|
||||
drawer.resizeBoard(controller.getTable(), controller.gridSize);
|
||||
if (!redrawn)
|
||||
drawer.composeBoard(controller.getTable(), controller.gridSize);
|
||||
drawer.resizeBoard(controller.getTable(), controller.gridSize); // Изменяем размер доски
|
||||
if (!redrawn) // и если при этом изменился размер камней,
|
||||
drawer.composeBoard(controller.getTable(), controller.gridSize); // перерисовываем доску
|
||||
|
||||
Refresh();
|
||||
Refresh(); // вызываем перерисовку окна
|
||||
}
|
||||
|
||||
void GamePanel::undo() {
|
||||
@ -52,32 +55,32 @@ void GamePanel::undo() {
|
||||
}
|
||||
|
||||
void GamePanel::reshuffle(bool solvable) {
|
||||
controller.free_table();
|
||||
controller.fill(solvable);
|
||||
controller.free_table(); // очищаем стол в контроллере
|
||||
controller.fill(solvable); // заполняем его заново
|
||||
|
||||
drawer.composeBoard(controller.getTable(), controller.gridSize);
|
||||
drawer.composeBoard(controller.getTable(), controller.gridSize); // перерисовываем стол
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void GamePanel::OnPaint(wxPaintEvent& _) {
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
wxAutoBufferedPaintDC dc(this); // создаём контекст с буфером для предотвращения мерцания
|
||||
|
||||
wxLogDebug(_("OnPaint"));
|
||||
|
||||
drawer.drawTable(dc);
|
||||
drawer.drawTable(dc); // отрисовываем в нём кадр
|
||||
}
|
||||
|
||||
void GamePanel::OnResize(wxSizeEvent& _) {
|
||||
const wxSize& resolution = GetClientSize();
|
||||
const wxSize& resolution = GetClientSize(); // получаем размер клиентской части окна (без рамок, тулбара и статусбара)
|
||||
|
||||
wxLogDebug(wxString::Format("OnResize %i %i", resolution.x, resolution.y));
|
||||
|
||||
if (isPositive(resolution)) {
|
||||
drawer.resizeBg(resolution);
|
||||
if (isPositive(resolution)) { // на некоторых платформах первоначальный размер экрана может установиться в 0, поэтому лучше проверять это
|
||||
drawer.resizeBg(resolution); // изменяем размер фона
|
||||
|
||||
if (controller.gameStarted())
|
||||
drawer.resizeBoard(controller.getTable(), controller.gridSize);
|
||||
if (controller.gameStarted()) // если уже начали игру
|
||||
drawer.resizeBoard(controller.getTable(), controller.gridSize); // перерисовываем доску
|
||||
}
|
||||
|
||||
Refresh();
|
||||
|
@ -14,41 +14,35 @@ MainFrame::MainFrame()
|
||||
dataDirPath(wxStandardPaths::Get().GetUserDataDir()) {
|
||||
SetIcon(logo_icon);
|
||||
|
||||
initMenu();
|
||||
bindMenu();
|
||||
initMenu(); // Создание пунктов меню
|
||||
bindMenu(); // Подключение обработчиков пунктов меню
|
||||
|
||||
Bind(wxEVT_SHOW, [this](wxShowEvent& _) -> void {
|
||||
if (!layoutPath.IsEmpty() || openLayout())
|
||||
panel->Start(layoutPath, solveable,
|
||||
[this](const wxSize& size) -> void {
|
||||
this->SetMinClientSize(size);
|
||||
});
|
||||
Bind(wxEVT_SHOW, [this](wxShowEvent& _) -> void { // обработчик события отображения окна
|
||||
if (!layoutPath.IsEmpty() || openLayout()) // если пользователь выбрал схему
|
||||
startGame();
|
||||
});
|
||||
|
||||
Bind(END_EVT, [this](wxCommandEvent& evt) -> void {
|
||||
Bind(END_EVT, [this](wxCommandEvent& evt) -> void { // обработчик кастомного события окончания игры
|
||||
wxMessageDialog dlg(this, _("Хотите сыграть снова?"),
|
||||
_("Игра окончена"), wxYES_NO);
|
||||
_("Игра окончена"), wxYES_NO); // Создаём диалог с предложением начать игру заново
|
||||
dlg.SetExtendedMessage(_("Поздравляем, вы закончили игру за ") +
|
||||
evt.GetString());
|
||||
if (dlg.ShowModal() == wxID_YES) {
|
||||
panel->Start(layoutPath, solveable,
|
||||
[this](const wxSize& size) -> void {
|
||||
this->SetMinClientSize(size);
|
||||
});
|
||||
}
|
||||
evt.GetString()); // Устанавливаем время, затраченное на полное выполнение карты
|
||||
if (dlg.ShowModal() == wxID_YES) // Если пользователь хочет,
|
||||
startGame(); // начинаме игру заново
|
||||
});
|
||||
|
||||
CreateStatusBar(2);
|
||||
CreateStatusBar(2); // Создаём статусбар с двумя колонками
|
||||
|
||||
panel = new GamePanel(this);
|
||||
panel = new GamePanel(this); // Создаём
|
||||
panel->SetFocus(); // и показываем панель, где отрисовывается игровое поле
|
||||
}
|
||||
|
||||
void MainFrame::initMenu() {
|
||||
wxMenu* menuGame = new wxMenu;
|
||||
menuGame->Append(IDM_New_Game, _("Начать сначала"));
|
||||
wxMenu* menuGame = new wxMenu; // Создаём подменю
|
||||
menuGame->Append(IDM_New_Game, _("Начать сначала")); // Создаем пункт меню с id обработчика IDM_New_Game, далее аналогично
|
||||
menuGame->Append(IDM_Open, _("Открыть карту"));
|
||||
menuGame->AppendCheckItem(IDM_Solveable, _("Генерировать решаемую карту"));
|
||||
menuGame->AppendSeparator();
|
||||
menuGame->AppendSeparator(); // Добавляем горизонтальный разделитель в меню
|
||||
menuGame->Append(IDM_Undo, _("Отменить ход"));
|
||||
menuGame->Append(IDM_Reshuffle, _("Перемешать поле"));
|
||||
menuGame->AppendSeparator();
|
||||
@ -59,71 +53,64 @@ void MainFrame::initMenu() {
|
||||
menuHelp->Append(IDM_Rules, _("Правила игры"));
|
||||
menuHelp->Append(IDM_About, _("О программе"));
|
||||
|
||||
wxMenuBar* menuBar = new wxMenuBar;
|
||||
menuBar->Append(menuGame, _("Игра"));
|
||||
wxMenuBar* menuBar = new wxMenuBar; // Создаём меню бар,
|
||||
menuBar->Append(menuGame, _("Игра")); // куда подключаем созданные выше подменю
|
||||
menuBar->Append(menuHelp, _("Помощь"));
|
||||
|
||||
SetMenuBar(menuBar);
|
||||
SetMenuBar(menuBar); // И устанавливаем его как основной для этой панели
|
||||
}
|
||||
|
||||
void MainFrame::bindMenu() {
|
||||
Bind(
|
||||
wxEVT_MENU, [this](wxCommandEvent& _) -> void { Close(); }, IDM_Exit);
|
||||
wxEVT_MENU, [this](wxCommandEvent& _) -> void { Close(); }, IDM_Exit); // Вешаем обработчик закртытия игры при выборе соответствующего пункта меню
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для открытия схемы и запуска соответствующей игры
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& _) -> void {
|
||||
if (openLayout())
|
||||
panel->Start(layoutPath, solveable,
|
||||
[this](const wxSize& size) -> void {
|
||||
this->SetMinClientSize(size);
|
||||
});
|
||||
startGame();
|
||||
},
|
||||
IDM_Open);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для открытия диалога с "помощью"
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& _) -> void {
|
||||
(new HelpDlg(this, wxID_ANY))->Show();
|
||||
},
|
||||
IDM_Help);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для открытия диалога "о программе"
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& _) -> void {
|
||||
(new AboutDlg(this, wxID_ANY))->Show();
|
||||
},
|
||||
IDM_About);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для открытия диалога с "правилами"
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& _) -> void {
|
||||
(new RulesDlg(this, wxID_ANY))->Show();
|
||||
},
|
||||
IDM_Rules);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для запуска новой игры
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& _) -> void {
|
||||
if (!layoutPath.IsEmpty() || openLayout()) {
|
||||
panel->Start(layoutPath, solveable,
|
||||
[this](const wxSize& size) -> void {
|
||||
this->SetMinClientSize(size);
|
||||
});
|
||||
}
|
||||
if (!layoutPath.IsEmpty() || openLayout())
|
||||
startGame();
|
||||
},
|
||||
IDM_New_Game);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для установки режима генерации поля
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& evt) -> void { solveable = evt.IsChecked(); },
|
||||
IDM_Solveable);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для отмены хода
|
||||
wxEVT_MENU, [this](wxCommandEvent& _) -> void { panel->undo(); },
|
||||
IDM_Undo);
|
||||
|
||||
Bind(
|
||||
Bind( // Вешаем обработчик для перемешивания поля
|
||||
wxEVT_MENU,
|
||||
[this](wxCommandEvent& _) -> void { panel->reshuffle(solveable); },
|
||||
IDM_Reshuffle);
|
||||
@ -135,10 +122,10 @@ void MainFrame::bindMenu() {
|
||||
* @return true if user have chosen a file, false if cancelled
|
||||
*/
|
||||
bool MainFrame::openLayout() {
|
||||
wxFileDialog openFileDlg(
|
||||
wxFileDialog openFileDlg( // Создаём диалог для запроса файла схемы
|
||||
this, _("Открыть карту"),
|
||||
dataDirPath + wxFileName::GetPathSeparator() + _("layouts"),
|
||||
_("Turtle.smlf"), _("Файлы Mahjong карт (*.smlf)|*.smlf"),
|
||||
dataDirPath + wxFileName::GetPathSeparator() + _("layouts"), // Стандартный путь до файлов приложения
|
||||
_("Turtle.smlf"), _("Файлы Mahjong карт (*.smlf)|*.smlf"), // Стандартный файл и поддерживаемые форматы файлов
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
if (openFileDlg.ShowModal() == wxID_CANCEL)
|
||||
@ -147,3 +134,9 @@ bool MainFrame::openLayout() {
|
||||
layoutPath = openFileDlg.GetPath();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainFrame::startGame() {
|
||||
panel->Start(layoutPath, solveable, // запускаем игру с путём до схемы, выбранным режимом заполнения поля
|
||||
setMinSize_fn // и проброшенной функцией установки минимального размера окна
|
||||
);
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ private:
|
||||
GamePanel* panel;
|
||||
|
||||
bool openLayout();
|
||||
void startGame();
|
||||
|
||||
const std::function<void(const wxSize&)> setMinSize_fn =
|
||||
[this](const wxSize& size) -> void { this->SetMinClientSize(size); };
|
||||
|
||||
const wxString dataDirPath;
|
||||
|
||||
|
@ -20,3 +20,9 @@ wxString PRemaining(uint8_t remaining) {
|
||||
bool isPositive(const wxSize& size) {
|
||||
return size.x > 0 && size.y > 0;
|
||||
}
|
||||
|
||||
void cyclic_shift(std::set<ThreePoint>::iterator& it, const std::set<ThreePoint>& cont) {
|
||||
it++;
|
||||
if (it == cont.end())
|
||||
it = cont.begin();
|
||||
}
|
||||
|
4
utils.h
4
utils.h
@ -3,7 +3,9 @@
|
||||
|
||||
#include "wxw.h"
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
using std::vector;
|
||||
|
||||
@ -55,4 +57,6 @@ enum Values { MATCHED = -3, EMPTY, FREE };
|
||||
|
||||
bool isPositive(const wxSize& size);
|
||||
|
||||
void cyclic_shift(std::set<ThreePoint>::iterator& it, const std::set<ThreePoint>& cont);
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user