C++實現掃雷小遊戲(控制檯版)
阿新 • • 發佈:2020-03-18
本文為大家分享了C++實現掃雷小遊戲的具體程式碼,供大家參考,具體內容如下
程式功能:
提供三種模式:初級、中級、高階
操作模式:wsad控制游標移動,空格鍵開啟方塊
提供掃雷地圖的類
map.h
#ifndef MAP_H_ #define MAP_H_ #define MAX_LENGTH 32 //可以提供的地圖最大長度 #define MAX_WIDTH 18 //可以提供的地圖最大寬度 #define UP_EDGE 1 //上邊界 #define DOWN_EDGE _wid //下邊界 #define LEFT_EDGE 1 //左邊界 #define RIGHT_EDGE _lng //右邊界 void gotoxy(int,int); //移動游標的介面函式 struct Position{ int x; int y; }; struct Info{ int n; //用於標記雷、數字、空格的屬性 bool flag; //用於標記是否要開啟方塊 }; class Map{ private: int _lng,_wid; //長和寬 int _mines,_blanks; //雷數、未開啟空格數目 Position _pos = {1,1}; //游標位置 Info data[MAX_WIDTH][MAX_LENGTH]; //包含地圖資訊的矩陣 public: void AcceptCond(); //選擇模式 void InitMap(); //初始化地圖 void SetMine(); //佈置地雷 void SetNumber(); //計算數字 void SetPosition(); //移動游標至指示區域 void ResetPosition(); //重置初始座標 void ShowMap(); //顯示地圖 void ShowAll(); //顯示全部地圖,遊戲失敗時候呼叫 void OpenBlock(); //開啟方塊,即將 flag 值設定為 true,在 ShowMap() 中將開啟方塊 void FirstStep(); //預先處理遊戲,防止第一步就觸雷導致失敗,這是無意義的 bool PlayGame(); //提供的遊戲操作介面 bool Move(char); //移動游標,同時改變 _pos 的值用於指代目前要訪問(開啟)的方塊 bool IfLose(); //遊戲失敗,則返回真 bool IfWin(); //遊戲成功,則返回真 }; #endif
實現思路:
1.接收遊戲模式引數,確定地圖規模
2.初始化地圖,值全部設定為 0,flag 全部設定為 false,表示未曾開啟
3.根據使用者操作,確定要開啟的第一個空格的,然後再開始佈雷,避免開局觸雷結束,這樣沒什麼意義。
4.佈雷採用生成隨機數的方法
5.根據地雷分佈計算其他空格所對應的數字
6.通過PlayGame() 介面進行遊戲操作
Map類的實現
#include <cstdlib> #include <cstdio> #include <ctime> //提供時間函式 #include <conio.h> //提供getch() #include <windows.h> #include <iostream> #include "map.h" #define GOTO(pos) gotoxy(2 * (pos.x) - 1,(pos.y) - 1) //定義用於移動游標的 巨集 //由於一個方塊佔 2 個格子,所以 pos.x 每加 1,則游標要移動 2 格 using std::cout; using std::cin; using std::endl; void gotoxy(int x,int y) { //移動游標的介面 COORD pos = { short(x),short(y) }; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hOut,pos); } void Map::AcceptCond() { //接收遊戲模式引數 cout << "Choose Mode" << endl; cout << "1 : Beginner" << endl; cout << "2 : Intermediate" << endl; cout << "3 : Expert" << endl << "Mode: "; char mode; cin >> mode; while (('1' != mode) && ('2' != mode) && ('3' != mode)) { //僅僅接受 1, 2, 3,其他字元跳過 cout << "Wrong Mode,Enter number again\n Mode: "; cin >> mode; } switch (mode) { case '1' : _lng = 8; _wid = 8; _mines = 10; break; case '2' : _lng = 16; _wid = 16; _mines = 40; break; default: _lng = 30; _wid = 16; _mines = 99; } _blanks = _lng * _wid - _mines; //計算空格數,用於判斷是否贏,_blanks = 0 時判定贏 } void Map::InitMap() { //初始化地圖,顯示的地圖下標從 1 - wid,1 - _lng,邊界外面還有空格,用於計算空格對應數字的,邊界相當於0 for (int i = 0; i < _wid + 2; i++) { for (int j = 0; j < _lng + 2; j++) { data[i][j].n = 0; data[i][j].flag = false; } } } void Map::SetMine() { int i,j; int m = _mines; srand(time(NULL)); while (m) { i = rand() % _wid + 1; j = rand() % _lng + 1; if ((-1 != data[i][j].n) && (j != _pos.x && i != _pos.y)) { //後面的條件用於避免使用者第一個開啟的空格處佈置地雷 data[i][j].n = -1; m--; } } } void Map::SetNumber() { for (int i = 1; i <= _wid; i++) { for (int j = 1; j <= _lng; j++) { //依次檢查周圍的 8 個空格的雷數 if (-1 == data[i][j].n) continue; if (-1 == data[i-1][j-1].n) data[i][j].n++; if (-1 == data[i][j-1].n) data[i][j].n++; if (-1 == data[i+1][j-1].n) data[i][j].n++; if (-1 == data[i-1][j].n) data[i][j].n++; if (-1 == data[i+1][j].n) data[i][j].n++; if (-1 == data[i-1][j+1].n) data[i][j].n++; if (-1 == data[i][j+1].n) data[i][j].n++; if (-1 == data[i+1][j+1].n) data[i][j].n++; } } } void Map::SetPosition() { GOTO(_pos); } void Map::ResetPosition() { _pos.x = _pos.y = 1; } void Map::ShowMap() { system("cls"); //清屏 system("color 03"); //調整控制檯顯示顏色 SetConsoleOutputCP(437); //使方塊能夠正常顯示 for (int i = 1; i <= _wid; i++) { cout << '|'; //左邊界 for (int j = 1; j <= _lng; j++) { if (data[i][j].flag) { switch (data[i][j].n) { case 0 : cout << " "; break; //由於方塊佔兩個格子,因此其他的輸出,如空格、數字等也要佔2個格子,對齊 default: cout << data[i][j].n << ' '; } } else printf("%c",219); } cout << '|' << endl; //右邊界 } gotoxy(0,_wid+2); //在地圖下方輸出座標資訊和空格數 printf("Position : (%d,%d)\n Blanks : %d",_pos.x,_pos.y,_blanks); GOTO(_pos); //歸位到原先地圖座標對應的位置 } void Map::ShowAll() { //類似上面的ShowMap(),但在遊戲失敗時呼叫 system("cls"); system("color 03"); SetConsoleOutputCP(437); for (int i = 1; i <= _wid; i++) { cout << '|'; for (int j = 1; j <= _lng; j++) { switch (data[i][j].n) { case 0 : printf("%c",219); break; case -1: if (i == _pos.y && j == _pos.x) cout << "X "; else cout << "* "; break; default: cout << data[i][j].n << ' '; } } cout << '|' << endl; } } #define SOLVE_IT(t) {stack[++top] = (t); data[(t).y][(t).x].flag = true; _blanks--;} #define FALSE_FLAG(t) !data[(t).y][(t).x].flag void Map::OpenBlock() { //用棧來將連著的空格區域遍歷一遍,並將其 flag 值置為 true if (data[_pos.y][_pos.x].flag) return; //如果已經開啟過就不需要再次開啟,否則 _blanks--; 會多次執行,無法判斷贏 Position stack[_lng * _wid << 1]; Position t; int top = 0; stack[top] = _pos; data[_pos.y][_pos.x].flag = true; _blanks--; while (top != -1) { t = stack[top--]; if (0 == data[t.y][t.x].n) { //如果該位置為 0 ,那麼它周圍的格子都要開啟 t.y--; //判斷上方三個格子 if (t.y >= UP_EDGE) { //如果上方三個格子 y 不越界 if (FALSE_FLAG(t)) SOLVE_IT(t) t.x--; if (t.x >= LEFT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t) t.x += 2; if (t.x <= RIGHT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t) t.x--; } t.y++; t.x--;//判斷左右兩個格子,此時 t.y 復原 if (t.x >= LEFT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t) t.x += 2; if (t.x <= RIGHT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t) t.y++; //下方三個格子,此時 t.x 是最右邊的格子 if (t.y <= DOWN_EDGE) { //如果下方三個格子 y 不越界,與上面判斷基本相同 if (t.x <= RIGHT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t) t.x--; if (FALSE_FLAG(t)) SOLVE_IT(t) t.x--; if (t.x >= LEFT_EDGE && FALSE_FLAG(t)) SOLVE_IT(t) } } } } void Map::FirstStep() { //函式結束後將改變 _pos,就是我們用的預先處理函式,防止第一步就觸雷的 char op; do { op = getch(); while ((op != 'a') && (op != 's') && (op != 'd') && (op != 'w') && (op !=' ')) op = getch(); } while (Move(op)); } bool Map::Move(char op) { switch (op) { //通過不同的操作,改變座標,然後再通過 GOTO巨集 移動到該位置上 case ' ': return false; case 'w': if (UP_EDGE != _pos.y) _pos.y--; break; case 'a': if (LEFT_EDGE != _pos.x) _pos.x--; break; case 's': if (DOWN_EDGE != _pos.y) _pos.y++; break; default: if (RIGHT_EDGE != _pos.x) _pos.x++; } gotoxy(0,_wid + 2); printf("Position : (%d,_blanks); GOTO(_pos); return true; } bool Map::IfLose() { return -1 == data[_pos.y][_pos.x].n; } bool Map::IfWin() { return 0 == _blanks; } bool Map::PlayGame() { char op; float start,end; while (!IfWin()) { do { op = getch(); while ((op != 'a') && (op != 's') && (op != 'd') && (op != 'w') && (op !=' ')) op = getch(); } while (Move(op)); if (IfLose()) { //觸雷 ShowAll(); gotoxy (0,_wid + 3); return false; } else { OpenBlock(); //開啟方塊,實質上時將 flag 的值置為 true,接著 ShowMap()將可以顯示該方塊資訊 ShowMap(); GOTO(_pos); } } gotoxy(0,_wid + 3); return true; }
主程式
mineweeper.cpp
#include <iostream> #include <cstdlib> #include <conio.h> #include <ctime> #include "map.h" using namespace std; int main() { Map game; float start,end; char ch; while (1) { game.AcceptCond(); //選擇模式 game.InitMap(); //初始化 game.ShowMap(); //顯示地圖。 注:此時地圖未生成完畢 game.FirstStep(); //預處理,防止第一步就觸雷結束 game.SetMine(); //設定地雷 game.SetNumber(); //根據地雷分佈計算數字 game.OpenBlock(); //開啟開局預先想要開啟的第一個空 start = clock(); game.ShowMap(); if (game.PlayGame()) { //根據PlayGame()介面的返回值判定輸贏 cout << endl << "~ Congratulation ~\n ~ You Win ~" << endl; } else { cout << endl << "BOOM!!! ~ Game Over ~\n" << endl; } end = clock(); printf("\nTime : %.2f\n",(end - start) / CLK_TCK); //輸出遊戲所用時間 cout << endl << "Please enter 'q' to quit,or any other keys to continue" << endl; game.SetPosition(); //用於觸雷失敗時,將游標返回到觸雷的位置,提示哪一步失敗,同時觸碰的雷也將顯示為 ‘X' ch = getch(); if ('q' == ch) { // q 用於退出遊戲 system("cls"); cout << "~ Bye ~" << endl; break; } else { game.ResetPosition(); system("cls"); } } system("pause"); return 0; }
遊戲截圖
更多精彩遊戲小程式碼,請點選《遊戲專題》閱讀
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。