From 6afe22cc8103e4134a0ddca328a6aa82775004c0 Mon Sep 17 00:00:00 2001 From: zgh20060114 <2987408354@qq.com> Date: Wed, 9 Apr 2025 21:45:30 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=9A=20=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E6=9D=BF=E5=A4=A7=E5=B0=8F=E5=92=8C=E5=9C=B0?= =?UTF-8?q?=E9=9B=B7=E6=95=B0=E9=87=8F=20=E7=AC=AC=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E6=B0=B8=E8=BF=9C=E5=AE=89=E5=85=A8=EF=BC=88?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E8=B8=A9=E5=88=B0=E5=9C=B0=E9=9B=B7=EF=BC=89?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81=E6=A0=87=E8=AE=B0=E5=8F=AF=E7=96=91?= =?UTF-8?q?=E5=9C=B0=E9=9B=B7=EF=BC=88F=E9=94=AE=EF=BC=89=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=87=AA=E5=8A=A8=E5=B1=95=E5=BC=80=E7=A9=BA=E7=99=BD?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=20=E6=94=AF=E6=8C=81=E6=B8=B8=E6=88=8F?= =?UTF-8?q?=E8=83=9C=E5=88=A9=E5=92=8C=E5=A4=B1=E8=B4=A5=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=20=E7=95=8C=E9=9D=A2=E7=89=B9=E6=80=A7=EF=BC=9A=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8Unicode=E5=AD=97=E7=AC=A6=E7=BB=98=E5=88=B6=E7=B2=BE?= =?UTF-8?q?=E7=BE=8E=E8=BE=B9=E6=A1=86=20=E4=BD=BF=E7=94=A8=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E9=A2=9C=E8=89=B2=E6=98=BE=E7=A4=BA=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E7=8A=B6=E6=80=81=20=E6=94=AF=E6=8C=81=E5=85=89=E6=A0=87?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=20=E6=98=BE=E7=A4=BA=E5=89=A9=E4=BD=99?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E6=A0=BC=E5=AD=90=E6=95=B0=E9=87=8F=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=96=B9=E5=BC=8F=EF=BC=9A=20=E6=96=B9?= =?UTF-8?q?=E5=90=91=E9=94=AE/WASD/VIM=E9=94=AE=E4=BD=8D=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E5=85=89=E6=A0=87=20=E7=A9=BA=E6=A0=BC/=E5=9B=9E=E8=BD=A6?= =?UTF-8?q?=E9=94=AE=E6=8F=AD=E7=A4=BA=E6=A0=BC=E5=AD=90=20F=E9=94=AE?= =?UTF-8?q?=E6=A0=87=E8=AE=B0/=E5=8F=96=E6=B6=88=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E5=9C=B0=E9=9B=B7=20ESC=E9=94=AE=E9=80=80=E5=87=BA=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=20=E6=B8=B8=E6=88=8F=E8=A7=84=E5=88=99=EF=BC=9A=20?= =?UTF-8?q?=E8=B8=A9=E5=88=B0=E5=9C=B0=E9=9B=B7=E6=B8=B8=E6=88=8F=E7=BB=93?= =?UTF-8?q?=E6=9D=9F=20=E6=8F=AD=E7=A4=BA=E6=89=80=E6=9C=89=E9=9D=9E?= =?UTF-8?q?=E5=9C=B0=E9=9B=B7=E6=A0=BC=E5=AD=90=E8=8E=B7=E8=83=9C=20?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E6=A0=87=E8=AE=B0=E5=8F=AF=E7=96=91=E7=9A=84?= =?UTF-8?q?=E5=9C=B0=E9=9B=B7=E4=BD=8D=E7=BD=AE=20=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E7=9A=84=E4=BD=8D=E7=BD=AE=E4=B8=8D=E8=83=BD=E8=A2=AB=E6=8F=AD?= =?UTF-8?q?=E7=A4=BA=20=E4=BD=BF=E7=94=A8=E6=96=B9=E6=B3=95=EF=BC=9A=20?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=B8=B8=E6=88=8F=E5=90=8E=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E6=A8=A1=E5=BC=8F=EF=BC=881=E4=B8=BA?= =?UTF-8?q?=E6=95=B0=E7=8B=AC=EF=BC=8C2=E4=B8=BA=E6=89=AB=E9=9B=B7?= =?UTF-8?q?=EF=BC=89=20=E9=80=89=E6=8B=A9=E6=8C=89=E9=94=AE=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=88WASD=E6=88=96VIM=EF=BC=89=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=96=B9=E5=90=91=E9=94=AE=E7=A7=BB=E5=8A=A8=E5=85=89?= =?UTF-8?q?=E6=A0=87=20=E7=A9=BA=E6=A0=BC=E6=88=96=E5=9B=9E=E8=BD=A6?= =?UTF-8?q?=E9=94=AE=E6=8F=AD=E7=A4=BA=E6=A0=BC=E5=AD=90=20F=E9=94=AE?= =?UTF-8?q?=E6=A0=87=E8=AE=B0=E5=8F=AF=E7=96=91=E7=9A=84=E5=9C=B0=E9=9B=B7?= =?UTF-8?q?=20ESC=E9=94=AE=E9=80=80=E5=87=BA=E6=B8=B8=E6=88=8F=20=E8=BF=99?= =?UTF-8?q?=E4=B8=AA=E5=AE=9E=E7=8E=B0=E4=BF=9D=E6=8C=81=E4=BA=86=E4=B8=8E?= =?UTF-8?q?=E5=8E=9F=E6=9C=89=E6=95=B0=E7=8B=AC=E6=B8=B8=E6=88=8F=E7=9B=B8?= =?UTF-8?q?=E4=BC=BC=E7=9A=84=E7=95=8C=E9=9D=A2=E9=A3=8E=E6=A0=BC=E5=92=8C?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=96=B9=E5=BC=8F=EF=BC=8C=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E6=89=AB=E9=9B=B7=E7=89=B9=E6=9C=89?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/input.cpp | 2 +- src/main.cpp | 100 +++++++++--- src/minesweeper.cpp | 371 ++++++++++++++++++++++++++++++++++++++++++++ src/minesweeper.h | 58 +++++++ 4 files changed, 505 insertions(+), 26 deletions(-) create mode 100644 src/minesweeper.cpp create mode 100644 src/minesweeper.h diff --git a/src/input.cpp b/src/input.cpp index 7d5713d..33cc231 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -34,7 +34,7 @@ int inputDifficulty() break; } } - catch(...) + catch(...) //catch all types of exceptions { need_erase_grids = 0; } diff --git a/src/main.cpp b/src/main.cpp index 8331a30..a1d2e30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,46 +4,96 @@ #include "i18n.h" #include "input.h" #include "scene.h" +#include "minesweeper.h" #include "system_env.hpp" #include "utility.inl" static void printHelp() { std::cout << std::endl; - std::cout << "sudoku - a little game in command line" << std::endl - << std::endl; + std::cout << "Game Menu - Choose your game:" << std::endl << std::endl; std::cout << "Usage:" << std::endl; - std::cout << "\t sudoku [-l ]" << std::endl << std::endl; + std::cout << "\t game [-l ] [-m ]" << std::endl << std::endl; std::cout << "Options:" << std::endl; - std::cout << "\t -l \t specify path of progress file to load, optional." << std::endl - << std::endl; + std::cout << "\t -l \t specify path of progress file to load, optional." << std::endl; + std::cout << "\t -m \t specify game mode: 1 for Sudoku, 2 for Minesweeper" << std::endl << std::endl; +} + +// 选择游戏模式 +int selectGameMode() { + std::string mode; + do { + std::cout << "选择游戏模式 / Select game mode:" << std::endl; + std::cout << "1. 数独 / Sudoku" << std::endl; + std::cout << "2. 扫雷 / Minesweeper" << std::endl; + std::cout << "请选择 / Please choose (1-2): "; + + std::cin >> mode; + if(mode == "1" || mode == "2") { + return std::stoi(mode); + } + message(I18n::Instance().Get(I18n::Key::INPUT_ERROR)); + } while(true); } int main(int argc, char **argv) { - SetSystemEnv(); + SetSystemEnv(); + InputLanguage(); - CScene scene; + int gameMode = 1; // 默认为数独模式 + const char* loadFile = nullptr; - if (argc == 1) { - InputLanguage(); - int eraseGridNumber = inputDifficulty(); - scene.generate(); - scene.eraseRandomGrids(eraseGridNumber); - } else if (argc == 3 && !strcmp(argv[1], "-l")) { - // load saved game progress - if (!scene.load(argv[2])) { - message(I18n::Instance().Get(I18n::Key::LOAD_PROGRESS_FAIL)); - return 0; + // 处理命令行参数 + for(int i = 1; i < argc; i++) { + if(strcmp(argv[i], "-l") == 0 && i + 1 < argc) { + loadFile = argv[++i]; + } + else if(strcmp(argv[i], "-m") == 0 && i + 1 < argc) { + gameMode = std::stoi(argv[++i]); + } + else { + printHelp(); + return 0; + } } - InputLanguage(); - } else { - printHelp(); - return 0; - } - scene.setMode(inputKeyMode()); + // 如果没有通过命令行指定游戏模式,则提示用户选择 + if(argc == 1 || (argc == 3 && loadFile != nullptr)) { + gameMode = selectGameMode(); + } - scene.play(); + KeyMode keyMode = inputKeyMode(); - return 0; + if(gameMode == 1) { // 数独模式 + CScene scene; + scene.setMode(keyMode); + + if(loadFile) { + if(!scene.load(loadFile)) { + message(I18n::Instance().Get(I18n::Key::LOAD_PROGRESS_FAIL)); + return 0; + } + } else { + int eraseGridNumber = inputDifficulty(); + scene.generate(); + scene.eraseRandomGrids(eraseGridNumber); + } + + scene.play(); + } + else if(gameMode == 2) { // 扫雷模式 + CMinesweeper minesweeper(9, 9, 10); // 默认9x9,10个地雷 + minesweeper.setMode(keyMode); + + if(loadFile) { + if(!minesweeper.load(loadFile)) { + message(I18n::Instance().Get(I18n::Key::LOAD_PROGRESS_FAIL)); + return 0; + } + } + + minesweeper.play(); + } + + return 0; } \ No newline at end of file diff --git a/src/minesweeper.cpp b/src/minesweeper.cpp new file mode 100644 index 0000000..d93246e --- /dev/null +++ b/src/minesweeper.cpp @@ -0,0 +1,371 @@ +#include "minesweeper.h" +#include "display_symbol.h" +#include "i18n.h" +#include "utility.inl" +#include +#include +#include +#include + +CMinesweeper::CMinesweeper(int width, int height, int mines) + : _width(width) + , _height(height) + , _total_mines(mines) + , _remaining_cells(width * height - mines) + , _cur_point({0, 0}) + , _first_move(true) + , _game_over(false) + , keyMap(nullptr) +{ + init(); +} + +CMinesweeper::~CMinesweeper() +{ + if(keyMap) delete keyMap; +} + +void CMinesweeper::setMode(KeyMode mode) +{ + if(keyMap) delete keyMap; + + switch (mode) + { + case KeyMode::NORMAL: + keyMap = new Normal; + break; + case KeyMode::VIM: + keyMap = new Vim; + break; + } +} + +void CMinesweeper::init() { + // 初始化游戏板 + _board.resize(_height, std::vector(_width)); + for(int y = 0; y < _height; ++y) { + for(int x = 0; x < _width; ++x) { + _board[y][x] = {false, 0, MineState::HIDDEN}; + } + } +} + +void CMinesweeper::generate() { + // 生成地雷 + int mines_placed = 0; + while(mines_placed < _total_mines) { + int x = random(0, _width - 1); + int y = random(0, _height - 1); + + // 确保第一次点击的位置及其周围没有地雷 + if(_first_move && abs(x - _cur_point.x) <= 1 && abs(y - _cur_point.y) <= 1) { + continue; + } + + if(!_board[y][x].is_mine) { + _board[y][x].is_mine = true; + mines_placed++; + } + } + + // 计算每个格子周围的地雷数 + for(int y = 0; y < _height; ++y) { + for(int x = 0; x < _width; ++x) { + if(!_board[y][x].is_mine) { + _board[y][x].adjacent_mines = countAdjacentMines(x, y); + } + } + } +} + +int CMinesweeper::countAdjacentMines(int x, int y) const { + int count = 0; + for(int dy = -1; dy <= 1; ++dy) { + for(int dx = -1; dx <= 1; ++dx) { + int nx = x + dx; + int ny = y + dy; + if(isValid(nx, ny) && _board[ny][nx].is_mine) { + count++; + } + } + } + return count; +} + +bool CMinesweeper::isValid(int x, int y) const { + return x >= 0 && x < _width && y >= 0 && y < _height; +} + +void CMinesweeper::show() const { + cls(); + + // 显示游戏信息 + std::cout << "Mines: " << _total_mines << " | Remaining safe cells: " << _remaining_cells << std::endl; + + // 显示游戏板 + // 显示第一行上边框 + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED); + for(int x = 0; x < _width; ++x) { + std::cout << CORNER << LINE << LINE << LINE; + } + std::cout << CORNER << Color::Modifier() << std::endl; + + // 显示每一行内容 + for(int y = 0; y < _height; ++y) { + // 显示格子内容 + for(int x = 0; x < _width; ++x) { + const MineCell& cell = _board[y][x]; + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED) + << PIPE << Color::Modifier() << " "; + + // 显示不同状态的格子 + if(_cur_point.x == x && _cur_point.y == y) { + std::cout << Color::Modifier(Color::BOLD, Color::BG_CYAN, Color::FG_BLACK); + } + + switch(cell.state) { + case MineState::HIDDEN: + std::cout << "■"; + break; + case MineState::FLAGGED: + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED) << "F"; + break; + case MineState::REVEALED: + if(cell.is_mine) { + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED) << "*"; + } else if(cell.adjacent_mines == 0) { + std::cout << " "; + } else { + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_GREEN) + << cell.adjacent_mines; + } + break; + case MineState::EXPLODED: + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED) << "X"; + break; + } + std::cout << Color::Modifier() << " "; + } + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED) + << PIPE << Color::Modifier() << std::endl; + + // 显示分隔线 + std::cout << Color::Modifier(Color::BOLD, Color::BG_DEFAULT, Color::FG_RED); + for(int x = 0; x < _width; ++x) { + std::cout << CORNER << LINE; + // 在光标位置的下方显示^ + if(_cur_point.x == x && _cur_point.y == y) { + std::cout << ARROW; + } else { + std::cout << LINE; + } + std::cout << LINE; + } + std::cout << CORNER << Color::Modifier() << std::endl; + } +} + +void CMinesweeper::revealCell(int x, int y) { + if(!isValid(x, y) || _board[y][x].state != MineState::HIDDEN) { + return; + } + + _board[y][x].state = MineState::REVEALED; + if(!_board[y][x].is_mine) { + _remaining_cells--; + } + + // 如果是空格子,递归揭示周围的格子 + if(_board[y][x].adjacent_mines == 0) { + for(int dy = -1; dy <= 1; ++dy) { + for(int dx = -1; dx <= 1; ++dx) { + revealCell(x + dx, y + dy); + } + } + } +} + +bool CMinesweeper::reveal(int x, int y) { + if(!isValid(x, y) || _board[y][x].state == MineState::FLAGGED) { + return false; + } + + if(_first_move) { + _first_move = false; + generate(); + } + + if(_board[y][x].is_mine) { + _board[y][x].state = MineState::EXPLODED; + _game_over = true; + revealAllMines(); + return false; + } + + revealCell(x, y); + return true; +} + +void CMinesweeper::toggleFlag(int x, int y) { + if(!isValid(x, y) || _board[y][x].state == MineState::REVEALED) { + return; + } + + if(_board[y][x].state == MineState::HIDDEN) { + _board[y][x].state = MineState::FLAGGED; + } else if(_board[y][x].state == MineState::FLAGGED) { + _board[y][x].state = MineState::HIDDEN; + } +} + +void CMinesweeper::revealAllMines() { + for(int y = 0; y < _height; ++y) { + for(int x = 0; x < _width; ++x) { + if(_board[y][x].is_mine && _board[y][x].state != MineState::EXPLODED) { + _board[y][x].state = MineState::REVEALED; + } + } + } +} + +bool CMinesweeper::isGameOver() const { + return _game_over; +} + +bool CMinesweeper::isWin() const { + return _remaining_cells == 0; +} + +bool CMinesweeper::save(const char* filename) { + auto filepath = std::filesystem::path(filename); + if (std::filesystem::exists(filepath)) { + return false; + } + + std::fstream fs; + fs.open(filename, std::fstream::out); + if (!fs.is_open()) { + return false; + } + + // 保存基本信息 + fs << _width << ' ' << _height << ' ' << _total_mines << ' ' + << _remaining_cells << ' ' << _first_move << ' ' << _game_over << std::endl; + + // 保存当前光标位置 + fs << _cur_point.x << ' ' << _cur_point.y << std::endl; + + // 保存游戏板状态 + for(int y = 0; y < _height; ++y) { + for(int x = 0; x < _width; ++x) { + const MineCell& cell = _board[y][x]; + fs << cell.is_mine << ' ' + << cell.adjacent_mines << ' ' + << static_cast(cell.state) << std::endl; + } + } + + fs.close(); + return true; +} + +bool CMinesweeper::load(const char* filename) { + auto filepath = std::filesystem::path(filename); + if (!std::filesystem::exists(filepath)) { + return false; + } + + std::fstream fs; + fs.open(filename, std::fstream::in); + if (!fs.is_open()) { + return false; + } + + // 读取基本信息 + fs >> _width >> _height >> _total_mines + >> _remaining_cells >> _first_move >> _game_over; + + // 读取当前光标位置 + fs >> _cur_point.x >> _cur_point.y; + + // 初始化游戏板 + _board.resize(_height, std::vector(_width)); + + // 读取游戏板状态 + for(int y = 0; y < _height; ++y) { + for(int x = 0; x < _width; ++x) { + bool is_mine; + int adjacent_mines; + int state; + fs >> is_mine >> adjacent_mines >> state; + + _board[y][x].is_mine = is_mine; + _board[y][x].adjacent_mines = adjacent_mines; + _board[y][x].state = static_cast(state); + } + } + + fs.close(); + return true; +} + +void CMinesweeper::play() { + show(); + + while(!isGameOver() && !isWin()) { + char key = _getch(); + + if(key == keyMap->ESC) { + message(I18n::Instance().Get(I18n::Key::ASK_QUIT)); + std::string strInput; + std::cin >> strInput; + if(strInput[0] == 'y' || strInput[0] == 'Y') { + message(I18n::Instance().Get(I18n::Key::ASK_SAVE)); + std::cin >> strInput; + if(strInput[0] == 'y' || strInput[0] == 'Y') { + do { + message(I18n::Instance().Get(I18n::Key::ASK_SAVE_PATH)); + std::cin >> strInput; + if(!save(strInput.c_str())) { + message(I18n::Instance().Get(I18n::Key::FILE_EXIST_ERROR)); + } else { + break; + } + } while(true); + } + return; + } + } + else if(key == ' ' || key == '\r') { // 空格或回车键揭示格子 + reveal(_cur_point.x, _cur_point.y); + show(); + } + else if(key == 'f' || key == 'F') { // F键标记地雷 + toggleFlag(_cur_point.x, _cur_point.y); + show(); + } + else if(key == keyMap->LEFT) { + _cur_point.x = (_cur_point.x - 1 + _width) % _width; + show(); + } + else if(key == keyMap->RIGHT) { + _cur_point.x = (_cur_point.x + 1) % _width; + show(); + } + else if(key == keyMap->UP) { + _cur_point.y = (_cur_point.y - 1 + _height) % _height; + show(); + } + else if(key == keyMap->DOWN) { + _cur_point.y = (_cur_point.y + 1) % _height; + show(); + } + } + + if(isWin()) { + message("恭喜你赢了!"); + } else { + message("游戏结束!"); + } + _getch(); +} diff --git a/src/minesweeper.h b/src/minesweeper.h new file mode 100644 index 0000000..f670b6c --- /dev/null +++ b/src/minesweeper.h @@ -0,0 +1,58 @@ +#ifndef _SUDOKU_MINESWEEPER_H_ +#define _SUDOKU_MINESWEEPER_H_ + +#include +#include "common.h" +#include "color.h" + +// 扫雷格子的状态 +enum class MineState { + HIDDEN, // 未翻开 + REVEALED, // 已翻开 + FLAGGED, // 已标记 + EXPLODED // 爆炸 +}; + +// 扫雷格子的内容 +struct MineCell { + bool is_mine; // 是否是地雷 + int adjacent_mines; // 周围地雷数量 + MineState state; // 格子状态 +}; + +class CMinesweeper { +public: + CMinesweeper(int width = 9, int height = 9, int mines = 10); + ~CMinesweeper(); + + void setMode(KeyMode mode); // 设置按键模式 + void generate(); // 生成地雷 + void play(); // 游戏主循环 + void show() const; // 显示游戏界面 + bool reveal(int x, int y); // 揭示一个格子 + void toggleFlag(int x, int y); // 切换旗帜标记 + bool isGameOver() const; // 检查游戏是否结束 + bool isWin() const; // 检查是否获胜 + bool save(const char* filename); // 保存游戏 + bool load(const char* filename); // 加载游戏 + +private: + void init(); // 初始化游戏 + void revealCell(int x, int y); // 揭示格子的具体实现 + int countAdjacentMines(int x, int y) const; // 计算周围地雷数 + bool isValid(int x, int y) const; // 检查坐标是否有效 + void revealAllMines(); // 游戏结束时显示所有地雷 + +private: + int _width; // 游戏区域宽度 + int _height; // 游戏区域高度 + int _total_mines; // 地雷总数 + int _remaining_cells; // 剩余未揭示的安全格子 + point_t _cur_point; // 当前光标位置 + std::vector> _board; // 游戏板 + bool _first_move; // 是否是第一次点击 + bool _game_over; // 游戏是否结束 + KeyMap* keyMap; // 按键映射 +}; + +#endif \ No newline at end of file From 39d7bda40f128a6999bc0bc08b07f56cb15c8f63 Mon Sep 17 00:00:00 2001 From: Zgh20060114 <123386524+Zgh20060114@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:27:20 +0800 Subject: [PATCH 2/2] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7265db5..633ca3e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # sudoku -C++ 实现的跨平台数独游戏,命令行操作易上手,可以在开发间隙用来放松身心。数百行代码,初学者也可以轻松掌握。 +C++ 实现的跨平台数独游戏和扫雷游戏(fork了大佬的数独游戏,然后使用相同的代码风和操作界面,添加了扫雷玩法),命令行操作易上手,可以在开发间隙用来放松身心。数百行代码,初学者也可以轻松掌握。 欢迎通过pull request的方式来添加功能或修复缺陷。 ## 感谢贡献者