Added step undo and table reshuffle
This commit is contained in:
parent
ee2df44b63
commit
8f5f7472a8
@ -10,7 +10,7 @@ void Controller::resize(const wxSize& tableSize) {
|
|||||||
resolution = tableSize;
|
resolution = tableSize;
|
||||||
|
|
||||||
if (stopwatch >= 0) {
|
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));
|
resolution.y / (gridSize.y * TILE_HEIGHT));
|
||||||
|
|
||||||
if (gridPoint > 2) {
|
if (gridPoint > 2) {
|
||||||
@ -32,7 +32,7 @@ void Controller::loadLayout(const wxString& path) {
|
|||||||
|
|
||||||
drawer.gridSize = layout.getDimensions();
|
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);
|
layout.readLayout(table);
|
||||||
|
|
||||||
@ -50,6 +50,13 @@ TLVec& Controller::getTable() {
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::fill(bool solveable) {
|
||||||
|
if (solveable)
|
||||||
|
fillSolveableTable();
|
||||||
|
else
|
||||||
|
fillRandom();
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::fillSolveableTable() {
|
void Controller::fillSolveableTable() {
|
||||||
time_t start_time = time(NULL);
|
time_t start_time = time(NULL);
|
||||||
|
|
||||||
@ -65,7 +72,7 @@ void Controller::fillSolveableTable() {
|
|||||||
z = pos / (gridSize.x * gridSize.y);
|
z = pos / (gridSize.x * gridSize.y);
|
||||||
x = (pos / gridSize.y) % gridSize.x;
|
x = (pos / gridSize.y) % gridSize.x;
|
||||||
y = pos % gridSize.y;
|
y = pos % gridSize.y;
|
||||||
} while (table[z][x][y] != -1);
|
} while (table[z][x][y] != FREE);
|
||||||
|
|
||||||
positions.push_back({z, x, y});
|
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;
|
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() {
|
void Controller::fillRandom() {
|
||||||
srand(time(NULL));
|
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 z = 0; z < drawer.gridSize.z && not_end; z++)
|
||||||
for (int x = 0; x < drawer.gridSize.x && not_end; x++)
|
for (int x = 0; x < drawer.gridSize.x && not_end; x++)
|
||||||
for (int y = 0; y < drawer.gridSize.y && not_end; y++)
|
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();
|
table[z][x][y] = genRandId();
|
||||||
not_end--;
|
not_end--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Controller::genRandId() {
|
CardT Controller::genRandId() {
|
||||||
int id;
|
CardT id;
|
||||||
|
|
||||||
int w = 0;
|
int w = 0;
|
||||||
|
|
||||||
@ -246,19 +272,34 @@ bool Controller::sideFree(const ThreePoint& point) {
|
|||||||
return lfree || rfree;
|
return lfree || rfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::select(CardT* card) {
|
void Controller::handleClick(const wxPoint& point) {
|
||||||
wxLogDebug(wxString::Format("%i %p", *card, card));
|
wxPoint posPlain = drawer.toGrid(point);
|
||||||
|
|
||||||
if (selected != nullptr && sameValues(*card, *selected) && selected != card) {
|
ThreePoint pos = {-1, posPlain.x, posPlain.y};
|
||||||
*selected = -3;
|
|
||||||
*card = -3;
|
|
||||||
selected = nullptr;
|
|
||||||
|
|
||||||
drawer.marked = {-1, -1, -1};
|
if (pos.x > -1) {
|
||||||
} else
|
auto card = getCardByPosition(pos);
|
||||||
selected = card;
|
|
||||||
|
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) {
|
bool Controller::sameValues(CardT a, CardT b) {
|
||||||
@ -270,3 +311,15 @@ bool Controller::sameValues(CardT a, CardT b) {
|
|||||||
|
|
||||||
return false;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
31
Controller.h
31
Controller.h
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
#include "wxw.h"
|
#include "wxw.h"
|
||||||
|
|
||||||
@ -19,22 +20,17 @@ public:
|
|||||||
|
|
||||||
void loadLayout(const wxString& path);
|
void loadLayout(const wxString& path);
|
||||||
|
|
||||||
bool available(const ThreePoint& point);
|
void handleClick(const wxPoint& point);
|
||||||
bool upFree(const ThreePoint& point);
|
|
||||||
bool sideFree(const ThreePoint& point);
|
|
||||||
|
|
||||||
void select(CardT* card);
|
|
||||||
|
|
||||||
TLVec& getTable();
|
TLVec& getTable();
|
||||||
|
|
||||||
void fillSolveableTable();
|
void free_table();
|
||||||
void fillRandom();
|
|
||||||
|
|
||||||
CardT* getCardByPosition(ThreePoint& point);
|
void fill(bool solveable);
|
||||||
|
|
||||||
std::array<uint8_t, TILE_IMAGES_N>cardsCounter;
|
|
||||||
|
|
||||||
uint8_t remaining;
|
uint8_t remaining;
|
||||||
|
|
||||||
|
void undo();
|
||||||
private:
|
private:
|
||||||
Drawer& drawer;
|
Drawer& drawer;
|
||||||
XmlLayout layout;
|
XmlLayout layout;
|
||||||
@ -43,11 +39,24 @@ private:
|
|||||||
|
|
||||||
CardT* selected = nullptr;
|
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 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);
|
bool sameValues(CardT a, CardT b);
|
||||||
|
|
||||||
|
std::array<uint8_t, TILE_IMAGES_N>cardsCounter;
|
||||||
|
|
||||||
|
std::stack<std::array<CardEntry, 2>> steps;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,16 +21,16 @@ GamePanel::GamePanel(wxFrame* parent) : wxPanel(parent), controller(drawer) {
|
|||||||
void GamePanel::Start(const wxString& path, bool solveable) {
|
void GamePanel::Start(const wxString& path, bool solveable) {
|
||||||
controller.stopwatch = 0;
|
controller.stopwatch = 0;
|
||||||
controller.loadLayout(path);
|
controller.loadLayout(path);
|
||||||
if (solveable)
|
controller.fill(solveable);
|
||||||
controller.fillSolveableTable();
|
|
||||||
else
|
|
||||||
controller.fillRandom();
|
|
||||||
|
|
||||||
timer->Start(1000, wxTIMER_CONTINUOUS);
|
timer->Start(1000, wxTIMER_CONTINUOUS);
|
||||||
|
|
||||||
if (sb == nullptr)
|
if (sb == nullptr)
|
||||||
sb = ((wxFrame*)this->GetParent())->GetStatusBar();
|
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& _) {
|
void GamePanel::OnPaint(wxPaintEvent& _) {
|
||||||
@ -41,22 +41,28 @@ void GamePanel::OnPaint(wxPaintEvent& _) {
|
|||||||
|
|
||||||
void GamePanel::OnTimer(wxTimerEvent& _) {
|
void GamePanel::OnTimer(wxTimerEvent& _) {
|
||||||
controller.stopwatch += 1;
|
controller.stopwatch += 1;
|
||||||
sb->SetStatusText(LTimeToStr(controller.stopwatch));
|
sb->SetStatusText(LTimeToStr(controller.stopwatch), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GamePanel::OnClick(wxMouseEvent& _) {
|
void GamePanel::OnClick(wxMouseEvent& _) {
|
||||||
wxPoint posPlain = drawer.toGrid(ScreenToClient(wxGetMousePosition()));
|
controller.handleClick(ScreenToClient(wxGetMousePosition()));
|
||||||
|
sb->SetStatusText(PRemaining(controller.remaining), 1);
|
||||||
ThreePoint pos = {-1, posPlain.x, posPlain.y};
|
|
||||||
|
Refresh();
|
||||||
if (pos.x > -1) {
|
}
|
||||||
auto card = controller.getCardByPosition(pos);
|
|
||||||
|
void GamePanel::undo() {
|
||||||
drawer.marked = pos;
|
controller.undo();
|
||||||
|
|
||||||
if (pos.z >= 0 && controller.available(pos))
|
drawer.initScreen(controller.getTable());
|
||||||
controller.select(card);
|
|
||||||
}
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePanel::reshuffle(bool solveable) {
|
||||||
|
controller.free_table();
|
||||||
|
controller.fill(solveable);
|
||||||
|
drawer.initScreen(controller.getTable());
|
||||||
|
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ public:
|
|||||||
|
|
||||||
void Start(const wxString& path, bool solveable);
|
void Start(const wxString& path, bool solveable);
|
||||||
|
|
||||||
|
void undo();
|
||||||
|
void reshuffle(bool solveable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Drawer drawer;
|
Drawer drawer;
|
||||||
Controller controller;
|
Controller controller;
|
||||||
|
@ -11,7 +11,7 @@ MainFrame::MainFrame()
|
|||||||
initMenu();
|
initMenu();
|
||||||
bindMenu();
|
bindMenu();
|
||||||
|
|
||||||
CreateStatusBar();
|
CreateStatusBar(2);
|
||||||
|
|
||||||
panel = new GamePanel(this);
|
panel = new GamePanel(this);
|
||||||
panel->SetFocus();
|
panel->SetFocus();
|
||||||
@ -25,6 +25,9 @@ void MainFrame::initMenu() {
|
|||||||
menuGame->Append(IDM_Open, _("Открыть карту"));
|
menuGame->Append(IDM_Open, _("Открыть карту"));
|
||||||
menuGame->AppendCheckItem(IDM_Solveable, _("Генерировать решаемую карту"));
|
menuGame->AppendCheckItem(IDM_Solveable, _("Генерировать решаемую карту"));
|
||||||
menuGame->AppendSeparator();
|
menuGame->AppendSeparator();
|
||||||
|
menuGame->Append(IDM_Undo, _("Отменить ход"));
|
||||||
|
menuGame->Append(IDM_Reshuffle, _("Перемешать поле"));
|
||||||
|
menuGame->AppendSeparator();
|
||||||
menuGame->Append(IDM_Exit, _("Выход"));
|
menuGame->Append(IDM_Exit, _("Выход"));
|
||||||
|
|
||||||
wxMenu *menuHelp = new wxMenu;
|
wxMenu *menuHelp = new wxMenu;
|
||||||
@ -72,6 +75,14 @@ void MainFrame::bindMenu() {
|
|||||||
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
|
Bind(wxEVT_MENU, [this](wxCommandEvent& _) -> void {
|
||||||
solveable = _.IsChecked();
|
solveable = _.IsChecked();
|
||||||
}, IDM_Solveable);
|
}, 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() {
|
void MainFrame::openLayout() {
|
||||||
|
@ -36,7 +36,9 @@ enum
|
|||||||
IDM_About = wxID_ABOUT,
|
IDM_About = wxID_ABOUT,
|
||||||
IDM_Rules = wxID_HIGHEST + 1,
|
IDM_Rules = wxID_HIGHEST + 1,
|
||||||
IDM_New_Game,
|
IDM_New_Game,
|
||||||
IDM_Solveable
|
IDM_Solveable,
|
||||||
|
IDM_Undo,
|
||||||
|
IDM_Reshuffle
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,7 +32,7 @@ void XmlLayout::readLayout(TLVec& table) {
|
|||||||
y = wxAtoi(tilePtr->GetAttribute("y"));
|
y = wxAtoi(tilePtr->GetAttribute("y"));
|
||||||
l = wxAtoi(tilePtr->GetAttribute("layer")) - 1;
|
l = wxAtoi(tilePtr->GetAttribute("layer")) - 1;
|
||||||
|
|
||||||
table[l][x][y] = -1;
|
table[l][x][y] = FREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
tilePtr = tilePtr->GetNext();
|
tilePtr = tilePtr->GetNext();
|
||||||
|
@ -11,3 +11,7 @@ int upDiv(int a, int b) {
|
|||||||
wxString itowxS(int a) {
|
wxString itowxS(int a) {
|
||||||
return wxString::Format("%i", a);
|
return wxString::Format("%i", a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxString PRemaining(uint8_t remaining) {
|
||||||
|
return wxString::Format("%i%%", remaining*100 / 144);
|
||||||
|
}
|
||||||
|
17
utils.h
17
utils.h
@ -10,9 +10,10 @@ using std::vector;
|
|||||||
wxString LTimeToStr(int time);
|
wxString LTimeToStr(int time);
|
||||||
int upDiv(int a, int b);
|
int upDiv(int a, int b);
|
||||||
wxString itowxS(int a);
|
wxString itowxS(int a);
|
||||||
|
wxString PRemaining(uint8_t remaining);
|
||||||
|
|
||||||
#define min(a, b) (a + b - abs(a - b)) / 2
|
#define mmin(a, b) (a + b - abs(a - b)) / 2
|
||||||
#define max(a, b) (a + b + abs(a - b)) / 2
|
#define mmax(a, b) (a + b + abs(a - b)) / 2
|
||||||
|
|
||||||
using CardT = int16_t;
|
using CardT = int16_t;
|
||||||
|
|
||||||
@ -32,6 +33,18 @@ public:
|
|||||||
int z;
|
int z;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CardEntry {
|
||||||
|
public:
|
||||||
|
ThreePoint pos;
|
||||||
|
CardT id;
|
||||||
|
};
|
||||||
|
|
||||||
using TLVec = vector<vector<vector<CardT>>>;
|
using TLVec = vector<vector<vector<CardT>>>;
|
||||||
|
|
||||||
|
enum Values {
|
||||||
|
MATCHED = -3,
|
||||||
|
EMPTY,
|
||||||
|
FREE
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user