Compare commits

...

5 Commits

Author SHA1 Message Date
6b3fea857a Added release to build action 2022-05-12 12:18:23 +03:00
e40d6f5a5e Disabled solveable menu checkbox 2022-05-12 12:02:35 +03:00
8f5f7472a8 Added step undo and table reshuffle 2022-05-12 11:47:53 +03:00
ee2df44b63 Fixed image loading 2022-05-12 10:22:56 +03:00
41fa4c3833 Added github linux build action 2022-05-11 10:49:30 +03:00
17 changed files with 307 additions and 67 deletions

View File

@ -1,8 +0,0 @@
compilation_database_dir: build
output_directory: diagrams
generate_method_arguments: none
diagrams:
config_class:
type: class
glob:
- ./*.h

27
.github/workflows/linux-ci.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: C/C++ CI
on:
push:
tags:
- v**
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install GCC11, pCap, wxWidgets
run: sudo apt update && sudo apt install -y gcc-11 g++-11 libwxgtk3.0-gtk3-dev && sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110 --slave /usr/bin/g++ g++ /usr/bin/g++-11
- name: Display versions of tools and libraries
continue-on-error: true
run: uname -a; wx-config --list; wx-config --cxxflags; wx-config --libs; g++ --version | grep g++
- name: make
run: make
- name: Check binary file
run: ls -alh build/wxMahjong; file build/wxMahjong
- uses: softprops/action-gh-release@v1
name: Release
if: startsWith(github.ref, 'refs/tags/')
with:
files: build/wxMahjong

29
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "make - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/build/wxMahjong",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [{"name": "LD_LIBRARY_PATH", "value": "$LD_LIBRARY_PATH:/usr/local/lib"}],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: g++ build active file",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}

74
.vscode/settings.json vendored
View File

@ -3,5 +3,77 @@
"${default}",
"/usr/local/include/wx-3.1/",
"/usr/local/lib/wx/include/gtk3-unicode-3.1"
]
],
"files.associations": {
"hash_map": "cpp",
"*.tcc": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"vector": "cpp",
"hash_set": "cpp",
"valarray": "cpp",
"iterator": "cpp",
"random": "cpp",
"array": "cpp",
"string_view": "cpp",
"chrono": "cpp",
"numeric": "cpp",
"streambuf": "cpp",
"*.inc": "cpp",
"any": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"map": "cpp",
"set": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"optional": "cpp",
"ratio": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}
}

19
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ build active file",
"command": "make",
"args": [],
"options": {
"cwd": "${fileDirname}"
},
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Make build this project."
}
],
"version": "2.0.0"
}

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

@ -2,10 +2,6 @@
static const char* tileImageNames[] = { "Pin1", "Pin2", "Pin3", "Pin4", "Pin5", "Pin6", "Pin7", "Pin8", "Pin9", "Sou1", "Sou2", "Sou3", "Sou4", "Sou5", "Sou6", "Sou7", "Sou8", "Sou9", "Man1", "Man2", "Man3", "Man4", "Man5", "Man6", "Man7", "Man8", "Man9", "Chun", "Haku", "Hatsu", "Nan", "Pei", "Shaa", "Ton", "Flower1", "Flower2", "Flower3", "Flower4", "Season1", "Season2", "Season3", "Season4" };
/**
* TODO: fix not loading last two tiles icons
*/
Drawer::Drawer(): marked{-1, -1, -1} {
for (int i = 0; i < TILE_IMAGES_N; i++) {
if (!tileImages[i].LoadFile(_("./resources/tiles/") + _(tileImageNames[i]) + _(".png"), wxBITMAP_TYPE_PNG))

View File

@ -18,7 +18,7 @@ public:
wxSize tableSize;
wxSize tilePixelSize; // 600x800
wxSize tilePixelSize; // кратно 600x800
wxSize resolution;
Dimensions gridSize;
wxRect tablePixelRect;
@ -36,7 +36,7 @@ private:
void drawScreen(wxDC& dc);
void drawTile(wxDC& dc, int8_t index, const wxPoint& position, uint8_t zIndex);
wxImage tileImages[40];
wxImage tileImages[TILE_IMAGES_N];
wxBitmap bgBitmap;
wxBitmap screenBitmap;

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();
@ -24,6 +24,10 @@ void MainFrame::initMenu() {
menuGame->Append(IDM_New_Game, _("Начать сначала"));
menuGame->Append(IDM_Open, _("Открыть карту"));
menuGame->AppendCheckItem(IDM_Solveable, _("Генерировать решаемую карту"));
menuGame->Enable(IDM_Solveable, false); // TODO: finish solveable table generation
menuGame->AppendSeparator();
menuGame->Append(IDM_Undo, _("Отменить ход"));
menuGame->Append(IDM_Reshuffle, _("Перемешать поле"));
menuGame->AppendSeparator();
menuGame->Append(IDM_Exit, _("Выход"));
@ -72,6 +76,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

@ -9,10 +9,13 @@ OBJECTS := $(addprefix $(BDIR)/,$(patsubst %.cpp,%.o,$(wildcard *.cpp)))
$(BDIR)/%.o: %.cpp
$(CXX) -c `wx-config --cxxflags` -o $@ $<
all: $(PROGRAM)
all: $(BDIR) $(PROGRAM)
$(PROGRAM): $(OBJECTS)
$(CXX) -o $(BDIR)/$(PROGRAM) $(OBJECTS) `wx-config --libs`
$(BDIR):
mkdir $@
clean:
rm -f $(BDIR)/*.o $(PROGRAM)

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