C++實(shí)現(xiàn)掃雷小游戲(控制臺(tái)版)
本文為大家分享了C++實(shí)現(xiàn)掃雷小游戲的具體代碼,供大家參考,具體內(nèi)容如下
程序功能:
提供三種模式:初級(jí)、中級(jí)、高級(jí)
操作模式:wsad控制光標(biāo)移動(dòng),空格鍵打開(kāi)方塊
提供掃雷地圖的類
map.h
#ifndef MAP_H_
#define MAP_H_
#define MAX_LENGTH 32 //可以提供的地圖最大長(zhǎng)度
#define MAX_WIDTH 18 //可以提供的地圖最大寬度
#define UP_EDGE 1 //上邊界
#define DOWN_EDGE _wid //下邊界
#define LEFT_EDGE 1 //左邊界
#define RIGHT_EDGE _lng //右邊界
void gotoxy(int, int); //移動(dòng)光標(biāo)的接口函數(shù)
struct Position{
int x;
int y;
};
struct Info{
int n; //用于標(biāo)記雷、數(shù)字、空格的屬性
bool flag; //用于標(biāo)記是否要打開(kāi)方塊
};
class Map{
private:
int _lng, _wid; //長(zhǎng)和寬
int _mines, _blanks; //雷數(shù)、未開(kāi)啟空格數(shù)目
Position _pos = {1, 1}; //光標(biāo)位置
Info data[MAX_WIDTH][MAX_LENGTH]; //包含地圖信息的矩陣
public:
void AcceptCond(); //選擇模式
void InitMap(); //初始化地圖
void SetMine(); //布置地雷
void SetNumber(); //計(jì)算數(shù)字
void SetPosition(); //移動(dòng)光標(biāo)至指示區(qū)域
void ResetPosition(); //重置初始坐標(biāo)
void ShowMap(); //顯示地圖
void ShowAll(); //顯示全部地圖,游戲失敗時(shí)候調(diào)用
void OpenBlock(); //打開(kāi)方塊,即將 flag 值設(shè)置為 true,在 ShowMap() 中將打開(kāi)方塊
void FirstStep(); //預(yù)先處理游戲,防止第一步就觸雷導(dǎo)致失敗,這是無(wú)意義的
bool PlayGame(); //提供的游戲操作接口
bool Move(char); //移動(dòng)光標(biāo),同時(shí)改變 _pos 的值用于指代目前要訪問(wèn)(打開(kāi))的方塊
bool IfLose(); //游戲失敗,則返回真
bool IfWin(); //游戲成功,則返回真
};
#endif實(shí)現(xiàn)思路:
1.接收游戲模式參數(shù),確定地圖規(guī)模
2.初始化地圖,值全部設(shè)置為 0,flag 全部設(shè)置為 false,表示未曾打開(kāi)
3.根據(jù)用戶操作,確定要打開(kāi)的第一個(gè)空格的,然后再開(kāi)始布雷,避免開(kāi)局觸雷結(jié)束,這樣沒(méi)什么意義。
4.布雷采用生成隨機(jī)數(shù)的方法
5.根據(jù)地雷分布計(jì)算其他空格所對(duì)應(yīng)的數(shù)字
6.通過(guò)PlayGame() 接口進(jìn)行游戲操作
Map類的實(shí)現(xiàn)
#include <cstdlib>
#include <cstdio>
#include <ctime> //提供時(shí)間函數(shù)
#include <conio.h> //提供getch()
#include <windows.h>
#include <iostream>
#include "map.h"
#define GOTO(pos) gotoxy(2 * (pos.x) - 1, (pos.y) - 1) //定義用于移動(dòng)光標(biāo)的 宏
//由于一個(gè)方塊占 2 個(gè)格子,所以 pos.x 每加 1,則光標(biāo)要移動(dòng) 2 格
using std::cout;
using std::cin;
using std::endl;
void gotoxy(int x, int y) { //移動(dòng)光標(biāo)的接口
COORD pos = { short(x), short(y) };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut, pos);
}
void Map::AcceptCond() { //接收游戲模式參數(shù)
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,其他字符跳過(guò)
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; //計(jì)算空格數(shù),用于判斷是否贏,_blanks = 0 時(shí)判定贏
}
void Map::InitMap() { //初始化地圖,顯示的地圖下標(biāo)從 1 - wid, 1 - _lng, 邊界外面還有空格,用于計(jì)算空格對(duì)應(yīng)數(shù)字的,邊界相當(dāng)于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)) { //后面的條件用于避免用戶第一個(gè)打開(kāi)的空格處布置地雷
data[i][j].n = -1;
m--;
}
}
}
void Map::SetNumber() {
for (int i = 1; i <= _wid; i++) {
for (int j = 1; j <= _lng; j++) { //依次檢查周?chē)?8 個(gè)空格的雷數(shù)
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"); //調(diào)整控制臺(tái)顯示顏色
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; //由于方塊占兩個(gè)格子,因此其他的輸出,如空格、數(shù)字等也要占2個(gè)格子,對(duì)齊
default: cout << data[i][j].n << ' ';
}
}
else printf("%c", 219);
}
cout << '|' << endl; //右邊界
}
gotoxy(0, _wid+2); //在地圖下方輸出坐標(biāo)信息和空格數(shù)
printf("Position : (%d, %d)\n Blanks : %d", _pos.x, _pos.y, _blanks);
GOTO(_pos); //歸位到原先地圖坐標(biāo)對(duì)應(yīng)的位置
}
void Map::ShowAll() { //類似上面的ShowMap(),但在游戲失敗時(shí)調(diào)用
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() { //用棧來(lái)將連著的空格區(qū)域遍歷一遍,并將其 flag 值置為 true
if (data[_pos.y][_pos.x].flag) return; //如果已經(jīng)打開(kāi)過(guò)就不需要再次打開(kāi),否則 _blanks--; 會(huì)多次執(zhí)行,無(wú)法判斷贏
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 ,那么它周?chē)母褡佣家蜷_(kāi)
t.y--; //判斷上方三個(gè)格子
if (t.y >= UP_EDGE) { //如果上方三個(gè)格子 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--;//判斷左右兩個(gè)格子, 此時(shí) t.y 復(fù)原
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++; //下方三個(gè)格子, 此時(shí) t.x 是最右邊的格子
if (t.y <= DOWN_EDGE) { //如果下方三個(gè)格子 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() { //函數(shù)結(jié)束后將改變 _pos,就是我們用的預(yù)先處理函數(shù),防止第一步就觸雷的
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) { //通過(guò)不同的操作,改變坐標(biāo),然后再通過(guò) GOTO宏 移動(dòng)到該位置上
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, %d)\n Blanks : %d", _pos.x, _pos.y, _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(); //打開(kāi)方塊,實(shí)質(zhì)上時(shí)將 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(); //顯示地圖。 注:此時(shí)地圖未生成完畢
game.FirstStep(); //預(yù)處理,防止第一步就觸雷結(jié)束
game.SetMine(); //設(shè)置地雷
game.SetNumber(); //根據(jù)地雷分布計(jì)算數(shù)字
game.OpenBlock(); //打開(kāi)開(kāi)局預(yù)先想要打開(kāi)的第一個(gè)空
start = clock();
game.ShowMap();
if (game.PlayGame()) { //根據(jù)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); //輸出游戲所用時(shí)間
cout << endl << "Please enter 'q' to quit, or any other keys to continue" << endl;
game.SetPosition(); //用于觸雷失敗時(shí),將光標(biāo)返回到觸雷的位置,提示哪一步失敗,同時(shí)觸碰的雷也將顯示為 ‘X'
ch = getch();
if ('q' == ch) { // q 用于退出游戲
system("cls");
cout << "~ Bye ~" << endl;
break;
}
else {
game.ResetPosition();
system("cls");
}
}
system("pause");
return 0;
}游戲截圖


更多精彩游戲小代碼,請(qǐng)點(diǎn)擊《游戲?qū)n}》閱讀
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
QT中窗口關(guān)閉自動(dòng)銷毀的實(shí)現(xiàn)示例
這篇文章主要介紹了QT中窗口關(guān)閉自動(dòng)銷毀,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
關(guān)于C++中定義比較函數(shù)的三種方法小結(jié)
下面小編就為大家?guī)?lái)一篇關(guān)于C++中定義比較函數(shù)的三種方法小結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單版三子棋
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單版三子棋,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
OpenCV圖像變換與處理實(shí)戰(zhàn)方法記錄
這篇文章主要給大家介紹了關(guān)于OpenCV圖像變換與處理實(shí)戰(zhàn)的相關(guān)資料,包括重映射、縮放、旋轉(zhuǎn)、顏色變換、邊緣檢測(cè)、高斯模糊、圖像銳化和顏色反轉(zhuǎn)等,通過(guò)實(shí)例分析詳細(xì)解釋了這些功能的實(shí)現(xiàn)原理和代碼實(shí)現(xiàn),需要的朋友可以參考下2024-12-12

