Added step undo and table reshuffle

This commit is contained in:
Dmitriy Shishkov 2022-05-12 11:47:53 +03:00
parent ee2df44b63
commit 8f5f7472a8
No known key found for this signature in database
GPG Key ID: 26720CB2A9608C97
9 changed files with 152 additions and 51 deletions

View File

@ -10,7 +10,7 @@ void Controller::resize(const wxSize& tableSize) {
resolution = tableSize;
if (stopwatch >= 0) {
int gridPoint = min(resolution.x / (gridSize.x * TILE_WIDTH),
int gridPoint = mmin(resolution.x / (gridSize.x * TILE_WIDTH),
resolution.y / (gridSize.y * TILE_HEIGHT));
if (gridPoint > 2) {
@ -32,7 +32,7 @@ void Controller::loadLayout(const wxString& path) {
drawer.gridSize = layout.getDimensions();
table = TLVec(drawer.gridSize.z, vector<vector<CardT>>(drawer.gridSize.x, vector<CardT>(drawer.gridSize.y, -2)));
table = TLVec(drawer.gridSize.z, vector<vector<CardT>>(drawer.gridSize.x, vector<CardT>(drawer.gridSize.y, EMPTY)));
layout.readLayout(table);
@ -50,6 +50,13 @@ TLVec& Controller::getTable() {
return table;
}
void Controller::fill(bool solveable) {
if (solveable)
fillSolveableTable();
else
fillRandom();
}
void Controller::fillSolveableTable() {
time_t start_time = time(NULL);
@ -65,7 +72,7 @@ void Controller::fillSolveableTable() {
z = pos / (gridSize.x * gridSize.y);
x = (pos / gridSize.y) % gridSize.x;
y = pos % gridSize.y;
} while (table[z][x][y] != -1);
} while (table[z][x][y] != FREE);
positions.push_back({z, x, y});
@ -111,21 +118,40 @@ int Controller::emplace_rand(int id, std::list<ThreePoint> positions, int past_p
return 0;
}
void Controller::free_table() {
for (int z = 0; z < drawer.gridSize.z; z++)
for (int x = 0; x < drawer.gridSize.x; x++)
for (int y = 0; y < drawer.gridSize.y; y++) {
CardT id = table[z][x][y];
if (id >= 0) {
cardsCounter[id]++;
table[z][x][y] = FREE;
}
}
steps = decltype(steps)();
}
void Controller::fillRandom() {
srand(time(NULL));
int not_end = remaining;
wxLogDebug(wxString::Format("%i", remaining));
auto not_end = remaining;
for (int z = 0; z < drawer.gridSize.z && not_end; z++)
for (int x = 0; x < drawer.gridSize.x && not_end; x++)
for (int y = 0; y < drawer.gridSize.y && not_end; y++)
if (table[z][x][y] == -1) {
if (table[z][x][y] == FREE) {
table[z][x][y] = genRandId();
not_end--;
}
}
int Controller::genRandId() {
int id;
CardT Controller::genRandId() {
CardT id;
int w = 0;
@ -246,19 +272,34 @@ bool Controller::sideFree(const ThreePoint& point) {
return lfree || rfree;
}
void Controller::select(CardT* card) {
wxLogDebug(wxString::Format("%i %p", *card, card));
void Controller::handleClick(const wxPoint& point) {
wxPoint posPlain = drawer.toGrid(point);
if (selected != nullptr && sameValues(*card, *selected) && selected != card) {
*selected = -3;
*card = -3;
selected = nullptr;
ThreePoint pos = {-1, posPlain.x, posPlain.y};
drawer.marked = {-1, -1, -1};
} else
selected = card;
if (pos.x > -1) {
auto card = getCardByPosition(pos);
if (pos.z >= 0 && available(pos)) {
if (selected != nullptr && sameValues(*card, *selected) && selected != card) {
steps.push({CardEntry{drawer.marked, *selected}, CardEntry{pos, *card}});
*selected = MATCHED;
*card = MATCHED;
selected = nullptr;
remaining -= 2;
drawer.marked = {-1, -1, -1};
} else {
selected = card;
drawer.marked = pos;
}
drawer.initScreen(table);
drawer.initScreen(table);
}
}
}
bool Controller::sameValues(CardT a, CardT b) {
@ -270,3 +311,15 @@ bool Controller::sameValues(CardT a, CardT b) {
return false;
}
void Controller::undo() {
if (steps.size()) {
for (const CardEntry& entry : steps.top()) {
table[entry.pos.z][entry.pos.x][entry.pos.y] = entry.id;
cardsCounter[entry.id]++;
}
remaining += 2;
steps.pop();
}
}

View File

@ -3,6 +3,7 @@
#include <array>
#include <list>
#include <stack>
#include "wxw.h"
@ -19,22 +20,17 @@ public:
void loadLayout(const wxString& path);
bool available(const ThreePoint& point);
bool upFree(const ThreePoint& point);
bool sideFree(const ThreePoint& point);
void select(CardT* card);
void handleClick(const wxPoint& point);
TLVec& getTable();
void fillSolveableTable();
void fillRandom();
void free_table();
CardT* getCardByPosition(ThreePoint& point);
std::array<uint8_t, TILE_IMAGES_N>cardsCounter;
void fill(bool solveable);
uint8_t remaining;
void undo();
private:
Drawer& drawer;
XmlLayout layout;
@ -43,11 +39,24 @@ private:
CardT* selected = nullptr;
void fillSolveableTable();
void fillRandom();
int emplace_rand(int id, std::list<ThreePoint> positions, int past_pos, std::list<ThreePoint>::iterator past_ptr);
int genRandId();
CardT genRandId();
CardT* getCardByPosition(ThreePoint& point);
bool available(const ThreePoint& point);
bool upFree(const ThreePoint& point);
bool sideFree(const ThreePoint& point);
bool sameValues(CardT a, CardT b);
std::array<uint8_t, TILE_IMAGES_N>cardsCounter;
std::stack<std::array<CardEntry, 2>> steps;
};
#endif

View File

@ -21,16 +21,16 @@ GamePanel::GamePanel(wxFrame* parent) : wxPanel(parent), controller(drawer) {
void GamePanel::Start(const wxString& path, bool solveable) {
controller.stopwatch = 0;
controller.loadLayout(path);
if (solveable)
controller.fillSolveableTable();
else
controller.fillRandom();
controller.fill(solveable);
timer->Start(1000, wxTIMER_CONTINUOUS);
if (sb == nullptr)
sb = ((wxFrame*)this->GetParent())->GetStatusBar();
sb->SetStatusText(LTimeToStr(controller.stopwatch));
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0);
sb->SetStatusText(PRemaining(controller.remaining), 1);
drawer.initScreen(controller.getTable());
}
void GamePanel::OnPaint(wxPaintEvent& _) {
@ -41,22 +41,28 @@ void GamePanel::OnPaint(wxPaintEvent& _) {
void GamePanel::OnTimer(wxTimerEvent& _) {
controller.stopwatch += 1;
sb->SetStatusText(LTimeToStr(controller.stopwatch));
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0);
}
void GamePanel::OnClick(wxMouseEvent& _) {
wxPoint posPlain = drawer.toGrid(ScreenToClient(wxGetMousePosition()));
ThreePoint pos = {-1, posPlain.x, posPlain.y};
if (pos.x > -1) {
auto card = controller.getCardByPosition(pos);
drawer.marked = pos;
if (pos.z >= 0 && controller.available(pos))
controller.select(card);
}
controller.handleClick(ScreenToClient(wxGetMousePosition()));
sb->SetStatusText(PRemaining(controller.remaining), 1);
Refresh();
}
void GamePanel::undo() {
controller.undo();
drawer.initScreen(controller.getTable());
Refresh();
}
void GamePanel::reshuffle(bool solveable) {
controller.free_table();
controller.fill(solveable);
drawer.initScreen(controller.getTable());
Refresh();
}

View File

@ -14,6 +14,9 @@ public:
void Start(const wxString& path, bool solveable);
void undo();
void reshuffle(bool solveable);
private:
Drawer drawer;
Controller controller;

View File

@ -11,7 +11,7 @@ MainFrame::MainFrame()
initMenu();
bindMenu();
CreateStatusBar();
CreateStatusBar(2);
panel = new GamePanel(this);
panel->SetFocus();
@ -25,6 +25,9 @@ void MainFrame::initMenu() {
menuGame->Append(IDM_Open, _("Открыть карту"));
menuGame->AppendCheckItem(IDM_Solveable, _("Генерировать решаемую карту"));
menuGame->AppendSeparator();
menuGame->Append(IDM_Undo, _("Отменить ход"));
menuGame->Append(IDM_Reshuffle, _("Перемешать поле"));
menuGame->AppendSeparator();
menuGame->Append(IDM_Exit, _("Выход"));
wxMenu *menuHelp = new wxMenu;
@ -72,6 +75,14 @@ void MainFrame::bindMenu() {
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
solveable = _.IsChecked();
}, IDM_Solveable);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
panel->undo();
}, IDM_Undo);
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
panel->reshuffle(solveable);
}, IDM_Reshuffle);
}
void MainFrame::openLayout() {

View File

@ -36,7 +36,9 @@ enum
IDM_About = wxID_ABOUT,
IDM_Rules = wxID_HIGHEST + 1,
IDM_New_Game,
IDM_Solveable
IDM_Solveable,
IDM_Undo,
IDM_Reshuffle
};
#endif

View File

@ -32,7 +32,7 @@ void XmlLayout::readLayout(TLVec& table) {
y = wxAtoi(tilePtr->GetAttribute("y"));
l = wxAtoi(tilePtr->GetAttribute("layer")) - 1;
table[l][x][y] = -1;
table[l][x][y] = FREE;
}
tilePtr = tilePtr->GetNext();

View File

@ -11,3 +11,7 @@ int upDiv(int a, int b) {
wxString itowxS(int a) {
return wxString::Format("%i", a);
}
wxString PRemaining(uint8_t remaining) {
return wxString::Format("%i%%", remaining*100 / 144);
}

17
utils.h
View File

@ -10,9 +10,10 @@ using std::vector;
wxString LTimeToStr(int time);
int upDiv(int a, int b);
wxString itowxS(int a);
wxString PRemaining(uint8_t remaining);
#define min(a, b) (a + b - abs(a - b)) / 2
#define max(a, b) (a + b + abs(a - b)) / 2
#define mmin(a, b) (a + b - abs(a - b)) / 2
#define mmax(a, b) (a + b + abs(a - b)) / 2
using CardT = int16_t;
@ -32,6 +33,18 @@ public:
int z;
};
class CardEntry {
public:
ThreePoint pos;
CardT id;
};
using TLVec = vector<vector<vector<CardT>>>;
enum Values {
MATCHED = -3,
EMPTY,
FREE
};
#endif