C++实现控制台版扫雷程序
更新时间:2022年05月07日 11:55:04 作者:AillMe
这篇文章主要为大家详细介绍了C++实现控制台版扫雷程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文实例为大家分享了C++实现控制台版扫雷程序的具体代码,供大家参考,具体内容如下
测试平台: WIN7
工具: VC6.0 , VS2008都能编译得过。
花了两天时间写的,里面涉及的算法大都是自己想的,所以可能有些BUG。
#include <iostream> #include <time.h> #include <windows.h> using namespace std; #pragma comment (linker,"/subsystem:console") #define BLACK 0 //空白 #define MINE 100 //地雷 #define NOSWEEP 0 //未扫过 #define SWEEP 1 //扫雷 #define FLAG 2 //标记 class WinMine { public: WinMine(int iRow, int iColumn); ~WinMine(); public: void InitMine(int iMineMax); void SetColor(); void StatisticsMine(); void Map(); bool OpenWhites(int x, int y, int status); void GameStart(int iMineMax); void GameOver(); bool GoodOver(); private: int **m_ppMine; int **m_ppSweep; bool m_bMineflag; bool m_bSweepflag; int m_row; int m_column; int m_minenum; }; int main(void) { system("color 0d"); while (true) { int level; WinMine *Mine; cout << "请输入游戏等级:" <<endl; cout << "1.初级" <<endl; cout << "2.中级" <<endl; cout << "3.高级" <<endl; cout << "4.退出" <<endl; cin >> level; if (level == 1) { Mine = new WinMine(9,9); Mine->GameStart(10); } else if (level == 2) { Mine = new WinMine(16,16); Mine->GameStart(40); } else if (level == 3) { Mine = new WinMine(16,30); Mine->GameStart(70); } else if (level == 4) { return 0; } else { cout << "输入错误!" <<endl; continue; } delete Mine; } return 0; } WinMine::WinMine(int iRow, int iColumn) { int i; //储雷状态 m_ppMine = (int **) new int[iRow]; if (!m_ppMine) return; m_ppMine[0] = new int[iRow * iColumn]; if (!*m_ppMine) { delete[] m_ppMine; m_ppMine = NULL; return;} m_bMineflag = true; //扫雷状态 m_ppSweep = (int **) new int[iRow]; if (!m_ppSweep) return; m_ppSweep[0] = new int[iRow * iColumn]; if (!*m_ppSweep) { delete[] m_ppSweep; m_ppSweep = NULL; return;} m_bSweepflag = true; m_row = iRow; m_column = iColumn; for (i = 1; i < iRow; i++) { m_ppMine[i] = m_ppMine[0] + i * iRow; } for (i = 1; i < iRow; i++) { m_ppSweep[i] = m_ppSweep[0] + i * iRow; } memset(m_ppSweep[0], 0, iRow * iColumn * sizeof(int)); memset(m_ppMine[0], 0, iRow * iColumn * sizeof(int)); } WinMine::~WinMine() { if (m_bMineflag) { if (m_ppMine[0]) delete[] m_ppMine[0]; if (m_ppMine) delete[] m_ppMine; } if (m_bSweepflag) { if (m_ppSweep[0]) delete[] m_ppSweep[0]; if (m_ppSweep) delete[] m_ppSweep; } } //设置颜色 void WinMine::SetColor() { system("color 0a"); } //初始化雷数 void WinMine::InitMine(int iMineMax) { int x, y,num = 0; srand( (unsigned)time(NULL) ); for (int i = 0; num != iMineMax; i++) { x = rand()%m_row; y = rand()%m_column; if (MINE != m_ppMine[x][y]) { m_ppMine[x][y] = MINE; num++; } } m_minenum = num; } //统计雷数 void WinMine::StatisticsMine() { int i, j; int n, m; int num = 0; //保存雷数 //中间 for ( i = 1; i < m_row - 1; i++) { for ( j = 1; j < m_column - 1; j++) { if ( m_ppMine[i][j] == BLACK) { for (n = i - 1; n <= i + 1; n++) { for (m = j - 1; m <= j + 1; m++) { if ( m_ppMine[n][m] == MINE ) num++; } } m_ppMine[i][j] = num; num = 0; } } } //顶边 for ( i = 1; i < m_column - 1; i++) { if (m_ppMine[0][i] == BLACK) { for (n = 0; n < 2; n++) { for (m = i - 1; m <= i + 1; m++) { if (m_ppMine[n][m] == MINE) num++; } } m_ppMine[0][i] = num; num = 0; } } //下边 for ( i = 1; i < m_column - 1; i++) { if (m_ppMine[m_row - 1][i] == BLACK) { for (n = m_row - 2; n < m_row; n++) { for (m = i - 1; m <= i + 1; m++) { if (m_ppMine[n][m] == MINE) num++; } } m_ppMine[m_row - 1][i] = num; num = 0; } } //左边 for ( i = 1; i < m_row - 1; i++ ) { if (m_ppMine[i][0] == BLACK) { for (n = i - 1; n <= i + 1; n++) { for (m = 0; m < 2; m++) if (m_ppMine[n][m] == MINE) num++; } m_ppMine[i][0] = num; num = 0; } } //右边 for ( i = 1; i < m_row - 1; i++ ) { if (m_ppMine[i][m_column - 1] == BLACK) { for (n = i - 1; n <= i + 1; n++) { for (m = m_column - 2; m < m_column; m++) { if (m_ppMine[n][m] == MINE) num++; } } m_ppMine[i][m_column - 1] = num; num = 0; } } //左上角 if (m_ppMine[0][0] != MINE) { if (m_ppMine[0][1] == MINE) num++; if (m_ppMine[1][1] == MINE) num++; if (m_ppMine[1][0] == MINE) num++; m_ppMine[0][0] = num; num = 0; } //左下角 if (m_ppMine[m_row - 1][0] != MINE) { if (m_ppMine[m_row - 2][0] == MINE) num++; if (m_ppMine[m_row - 2][1] == MINE) num++; if (m_ppMine[m_row - 1][1] == MINE) num++; m_ppMine[m_row - 1][0] = num; num = 0; } //右上角 if (m_ppMine[0][m_column - 1] != MINE) { if (m_ppMine[1][m_column - 1] == MINE) num++; if (m_ppMine[1][m_column - 2] == MINE) num++; if (m_ppMine[0][m_column - 2] == MINE) num++; m_ppMine[0][m_column - 1] = num; num = 0; } //右下角 if (m_ppMine[m_row - 1][m_column - 1] != MINE) { if (m_ppMine[m_row - 2][m_column - 1] == MINE) num++; if (m_ppMine[m_row - 2][m_column - 2] == MINE) num++; if (m_ppMine[m_row - 1][m_column - 2] == MINE) num++; m_ppMine[m_row - 1][m_column - 1] = num; num = 0; } } //展开空白 bool WinMine::OpenWhites(int row, int column, int status) { if (row < 0 || row > (m_row - 1) || column < 0 || column > (m_column - 1)) return true; if (status == SWEEP && m_ppMine[row][column] == MINE) { return false; } if (status == FLAG) { m_ppSweep[row][column] = FLAG; return true; } if (m_ppSweep[row][column] == NOSWEEP && m_ppMine[row][column] != MINE) { if (m_ppMine[row][column] > 0) { m_ppSweep[row][column] = SWEEP; return true; } else { m_ppSweep[row][column] = SWEEP; OpenWhites(row-1, column, status); OpenWhites(row-1, column-1, status); OpenWhites(row-1, column+1, status); OpenWhites(row, column-1, status); OpenWhites(row, column+1, status); OpenWhites(row+1, column, status); OpenWhites(row+1, column-1, status); OpenWhites(row+1, column+1, status); } } return true; } //地图 void WinMine::Map() { SetColor(); int i, j; for ( i = 0; i < m_row; i++) { for (j = 0; j < m_column; j++) { if (m_ppSweep[i][j] == SWEEP) { if (m_ppMine[i][j] == 0) { cout << "□"; } else if (m_ppMine[i][j] == 1) { cout << "①"; } else if (m_ppMine[i][j] == 2) { cout << "②"; } else if (m_ppMine[i][j] == 3) { cout << "③"; } else if (m_ppMine[i][j] == 4) { cout << "④"; } else if (m_ppMine[i][j] == 5) { cout << "⑤"; } else if (m_ppMine[i][j] == 6) { cout << "⑥"; } else if (m_ppMine[i][j] == 7) { cout << "⑦"; } else if (m_ppMine[i][j] == 8) { cout << "⑧"; } } else if (m_ppSweep[i][j] == FLAG) { cout << "⊙"; } else cout << "▇"; } cout << endl; } } //游戏结束 void WinMine::GameOver() { int i, j; for ( i = 0; i < m_row; i++) { for (j = 0; j < m_column; j++) { if (m_ppMine[i][j] == MINE) cout << "★"; else cout << "□"; } cout << endl; } } //检查是否雷标记正确。 bool WinMine::GoodOver() { int i, j; int num = 0; for (i = 0; i < m_row; i++) { for (j = 0; j < m_column; j++) { if (m_ppSweep[i][j] == FLAG && m_ppMine[i][j] == MINE) num++; } } if (num == m_minenum) return true; else return false; } //开始游戏 void WinMine::GameStart(int iMineMax) { int x, y; int flag; begin: memset(m_ppSweep[0], 0, m_row * m_column * sizeof(int)); memset(m_ppMine[0], 0, m_row * m_column * sizeof(int)); InitMine(iMineMax); StatisticsMine(); while (true) { system("cls"); Map(); cout << "请输入要扫的区域的坐标:" <<endl; cout << "请输入纵坐标:";cin>>x; cout << "请输入横坐标:";cin>>y; if (x <= 0 || x > m_row || y <= 0 || y > m_column) { cout <<"输入错误请重新输入" <<endl; Sleep(1000); continue; } cout << "请输入是要做标记还是扫雷,扫雷请按1,标记请按2:" <<endl; cin >> flag; if ( false == OpenWhites(x-1, y-1, flag) ) { int i; system("cls"); GameOver(); cout << "游戏结束!" <<endl; cout << "继续游戏请按1,退出游戏请按0:"<<endl; cin >> i; if (i == 1) goto begin; else goto end; } else { if (GoodOver() == true) { int i; cout << "扫雷成功!" <<endl; cout << "继续游戏请按1,退出游戏请按0:"<<endl; cin >> i; if (i == 1) goto begin; else goto end; } } } end: return; }
再为大家分享一位作者的文章:控制台版扫雷(支持鼠标操作)
//说明: 左键双击对应windows自带扫雷中的双键点击 #include <windows.h> #include <cstdlib> #include <ctime> #include <list> using namespace std; //数组索引转成坐标 #define X(v) (v) % WIDTH #define Y(v) (v) / WIDTH //坐标系统转换 #define BOARDX(x) ((x) - OFFSETX) / 2 #define BOARDY(y) (y) - OFFSETY #define SCREENX(x) 2 * (x) + OFFSETX #define SCREENY(y) (y) + OFFSETY #define BACKGROUND_WHITE (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED) #define FOREGROUND_WHITE (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED) typedef enum { UNKNOWN, DISPLAY, MARKED } State; typedef enum { NUMBER, EMPTY, MINE } MType; typedef struct { State state; MType mtype; int val; } Cell; typedef bool (* CmpProc)(Cell& cell, void* pData); int HEIGHT = 16; int WIDTH = 16; int MINE_CNT = 40; //地雷数 int CELL_CNT; //第一个格子的x, y偏移 int OFFSETX = 3; int OFFSETY = 3; int flagCnt; //标记数 int mineLeft; //地雷剩余 int unkwLeft; //未知剩余 int liveLeft; //生命剩余 COORD tmPos; //计时坐标 COORD mnPos; //地雷(剩余)坐标 COORD lvPos; //生命坐标 bool bGameStart, bGameStop; //游戏开始.结束标记 DWORD dwStart; HANDLE hOut, hIn; Cell cells[16][30]; void writeChar(LPCSTR pChar, COORD wrtCrd) { DWORD wtn; WriteConsoleOutputCharacter(hOut, pChar, strlen(pChar), wrtCrd, &wtn); } void fillChar(TCHAR cChar, DWORD len, COORD wrtCrd) { DWORD wtn; FillConsoleOutputCharacter(hOut, cChar, len, wrtCrd, &wtn); } void fillAttr(WORD attr, DWORD len, COORD wrtCrd) { DWORD wtn; FillConsoleOutputAttribute(hOut, attr, len, wrtCrd, &wtn); } bool isCell(int x, int y) { return x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT; } bool isMine(int x, int y) { return isCell(x, y) && cells[y][x].mtype == MINE; } bool cmpState(Cell& cell, void* pData) { return cell.state == *(State*)pData; } bool cmpMtype(Cell& cell, void* pData) { return cell.mtype == *(MType*)pData; } //四周格子作比较 int aroundCmp(int x, int y, CmpProc cmp, void* pData) { int nRet = 0; for (int y0=y-1; y0<=y+1; y0++) { for (int x0=x-1; x0<=x+1; x0++) { if (isCell(x0, y0) && !(x0 == x && y0 == y)) //not self { nRet += cmp(cells[y0][x0], pData); } } } return nRet; } int aroundMines(int x, int y) { int val = MINE; return aroundCmp(x, y, cmpMtype, &val); } int aroundMarks(int x, int y) { int val = MARKED; return aroundCmp(x, y, cmpState, &val); } //扰乱数组的前n个元素 void ruffle(int* arr, int len, int n) { for (int i=0; i<n; i++) { int j = rand() % len; int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } //计时 void setElapsedTime() { if (bGameStart && !bGameStop) { DWORD dwDelt = (GetTickCount() - dwStart) / 1000; if (dwDelt < 1000) { char buf[5] = {0}; sprintf(buf, "%.3d\0", dwDelt); writeChar(buf, tmPos); } } } //剩余雷数(仅显示, 非实际) void setMinesLeft() { char buf[5] = {0}; sprintf(buf, "%2d\0", MINE_CNT - flagCnt); writeChar(buf, mnPos); } //剩余生命 void setLivesLeft(int delt = 0) { char buf[5] = {0}; liveLeft += delt; sprintf(buf, "%2d\0", liveLeft); writeChar(buf, lvPos); } void drawCell(int x, int y) { Cell* pCell = &cells[y][x]; COORD cellCrd = {SCREENX(x), SCREENY(y)}; char buf[3] = {0}; switch (pCell->state) { case UNKNOWN: sprintf(buf, "□\0"); break; case MARKED: sprintf(buf, " P\0"); break; case DISPLAY: switch (pCell->mtype) { case MINE: sprintf(buf, " *\0"); break; case EMPTY: sprintf(buf, " \0"); break; case NUMBER: sprintf(buf, " %d\0", pCell->val); fillAttr((WORD)pCell->val, 2, cellCrd); //数字着色 break; } break; } writeChar(buf, cellCrd); } //初始化信息栏 void initInfoBar() { char buf[50] = {0}; sprintf(buf, "生命: %2d 地雷: %2d 用时: 000\0", liveLeft, MINE_CNT); COORD crd = {(80 - strlen(buf)) / 2, SCREENY(HEIGHT) + 1}; //水平居中 writeChar(buf, crd); crd.X += 6; lvPos = crd; crd.X += 11; mnPos = crd; crd.X += 11; tmPos = crd; } void clearScreen() { COORD crd = {0, 0}; CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hOut, &csbi); fillChar(' ', csbi.dwSize.X * csbi.dwSize.Y, crd); fillAttr(FOREGROUND_WHITE, csbi.dwSize.X * csbi.dwSize.Y, crd); } void initGame() { srand((unsigned)time(NULL)); SetConsoleTitle("扫雷控制台版 F2: 初级; F3: 中级; F4: 高级"); clearScreen(); CELL_CNT = HEIGHT * WIDTH; OFFSETX = (80 - WIDTH * 2) / 2; //水平居中 int* idxs = new int[CELL_CNT]; //地雷索引 int i, x, y; //init cells and indexs for (i=0; i<CELL_CNT; i++) { cells[Y(i)][X(i)].mtype = EMPTY; cells[Y(i)][X(i)].state = UNKNOWN; idxs[i] = i; } ruffle(idxs, CELL_CNT, MINE_CNT); //fill mines for (i=0; i<MINE_CNT; i++) { cells[Y(idxs[i])][X(idxs[i])].mtype = MINE; } //fill nums && print the game for (y=0; y<HEIGHT; y++) { for (x=0; x<WIDTH; x++) { if (!isMine(x, y)) { cells[y][x].val = aroundMines(x, y); cells[y][x].mtype = cells[y][x].val > 0 ? NUMBER : EMPTY; } drawCell(x, y); } } delete[] idxs; bGameStart = false; bGameStop = false; mineLeft = MINE_CNT; unkwLeft = CELL_CNT; liveLeft = MINE_CNT / 20 + 1; //每二十个雷加一条命 flagCnt = 0; initInfoBar(); } void showAll() { for (int i=0; i<CELL_CNT; i++) { cells[Y(i)][X(i)].state = DISPLAY; drawCell(X(i), Y(i)); } } void showTip(const char* tipMsg, WORD attr = FOREGROUND_WHITE) { COORD tipCrd = {(80 - strlen(tipMsg)) / 2, 1}; writeChar(tipMsg, tipCrd); fillAttr(attr, strlen(tipMsg), tipCrd); } void gameWin() { if (!bGameStop) { showTip("恭喜你, 你赢了! ", FOREGROUND_GREEN | FOREGROUND_INTENSITY); bGameStop = true; } } void gameOver(int x, int y) { setLivesLeft(-1); if (liveLeft == 0) { showAll(); showTip("游戏结束, 请重新来过!", FOREGROUND_RED | FOREGROUND_INTENSITY); bGameStop = true; } else { COORD crd = {SCREENX(x), SCREENY(y)}; WORD attr = FOREGROUND_WHITE; for (int i=0; i<6; i++) //触雷闪烁 { attr = FOREGROUND_WHITE ^ FOREGROUND_RED ^ attr; fillAttr(attr, 2, crd); Sleep(100); } } } void showAround(int x, int y) { list<COORD> lst; COORD crd = {x, y}; lst.push_back(crd); while (!lst.empty()) { crd = lst.front(); lst.pop_front(); x = crd.X; y = crd.Y; for (int x0=x-1; x0<=x+1; x0++) { for (int y0=y-1; y0<=y+1; y0++) { if (!isCell(x0, y0) || (x0 == x && y0 == y)) { continue; } Cell* pCell = &cells[y0][x0]; if (pCell->state == UNKNOWN) { if (pCell->mtype == MINE) { gameOver(x0, y0); break; } else if (pCell->mtype == EMPTY) { crd.X = x0; crd.Y = y0; lst.push_back(crd); } unkwLeft--; pCell->state = DISPLAY; drawCell(x0, y0); } } //end for } //end for } } void onCellLDBLClick(int x, int y) { Cell* pCell = &cells[y][x]; //左双击对显示的数字格子起作用, 且该格子周围的标记数等于该数字 if (pCell->mtype == NUMBER && pCell->state == DISPLAY && aroundMarks(x, y) == pCell->val) { showAround(x, y); } } void onCellLClick(int x, int y) { Cell* pCell = &cells[y][x]; //左击只对未知格子起作用 if (pCell->state == UNKNOWN) { if (pCell->mtype == MINE) { gameOver(x, y); } else { pCell->state = DISPLAY; unkwLeft--; drawCell(x, y); if (pCell->mtype == EMPTY) { showAround(x, y); } } } } void onCellRClick(int x, int y) { Cell* pCell = &cells[y][x]; //右击对未知, 标记格子起作用 if (pCell->state != DISPLAY) { if (pCell->state == UNKNOWN) { pCell->state = MARKED; mineLeft -= pCell->mtype == MINE ? 1 : 0; unkwLeft--; flagCnt++; } else { pCell->state = UNKNOWN; mineLeft += pCell->mtype == MINE ? 1 : 0; unkwLeft++; flagCnt--; } drawCell(x, y); setMinesLeft(); } } void onKeyDown(WORD keyCode) { switch (keyCode) { case VK_F2: //初级 HEIGHT = 9; WIDTH = 9; MINE_CNT = 10; initGame(); break; case VK_F3: //中级 HEIGHT = 16; WIDTH = 16; MINE_CNT = 40; initGame(); break; case VK_F4: //高级 HEIGHT = 16; WIDTH = 30; MINE_CNT = 99; initGame(); break; case VK_F12: if (liveLeft < 99) { setLivesLeft(1); } break; default: break; } } void afterMouseEvent() { if (!bGameStart) { bGameStart = true; dwStart = GetTickCount(); } if (mineLeft == 0 && unkwLeft == 0) { gameWin(); } } int main(int argc, char* argv[]) { hIn = GetStdHandle(STD_INPUT_HANDLE); hOut = CreateConsoleScreenBuffer( GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); CONSOLE_CURSOR_INFO cci; cci.dwSize = 1; cci.bVisible = FALSE; SetConsoleCursorInfo(hOut, &cci); //隐藏光标 SetConsoleActiveScreenBuffer(hOut); initGame(); //监听事件 for (;;) { DWORD nEvts; GetNumberOfConsoleInputEvents(hIn, &nEvts); if (nEvts > 0) { INPUT_RECORD inpRec; ReadConsoleInput(hIn, &inpRec, 1, &nEvts); bool bClked = false; //是否有合法鼠标事件发生 if (!bGameStop && inpRec.EventType == MOUSE_EVENT) { int x = BOARDX(inpRec.Event.MouseEvent.dwMousePosition.X); int y = BOARDY(inpRec.Event.MouseEvent.dwMousePosition.Y); if (!isCell(x, y)) { continue; } bClked = true; switch (inpRec.Event.MouseEvent.dwButtonState) { case FROM_LEFT_1ST_BUTTON_PRESSED: if (inpRec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK) { onCellLDBLClick(x, y); break; //处理过双击不再处理单击 } onCellLClick(x, y); break; case RIGHTMOST_BUTTON_PRESSED: onCellRClick(x, y); break; default: bClked = false; break; } if (bClked) { afterMouseEvent(); } } if (inpRec.EventType == KEY_EVENT) //按键事件 { onKeyDown(inpRec.Event.KeyEvent.wVirtualKeyCode); Sleep(100); } FlushConsoleInputBuffer(hIn); } setElapsedTime(); Sleep(50); } return 0; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
C++文件IO流及stringstream流读写文件和字符串操作详解
本文详细介绍C++中的文件IO流和stringstream流的使用方法,包括文件的打开、读写操作,以及字符串的输入输出、转换等操作。同时提供实用的示例代码和技巧,帮助读者更好地掌握这两种流的使用2023-04-04
最新评论