C語言實(shí)現(xiàn)掃雷小游戲的全過程記錄
第一步思考要實(shí)現(xiàn)的功能
想必大家都知道掃雷這個小游戲,今天我們來用C語言實(shí)現(xiàn)一下,首先要掃雷,我們首先就需要有一個布置了雷的棋盤,然后開始掃雷,玩過掃雷的小伙伴都知道,如果選中的格子旁邊沒有雷,那么旁邊的格子就會自動清空,大概的思路有了,現(xiàn)在我們開始實(shí)現(xiàn)。
第二步實(shí)現(xiàn)
初級版掃雷
首先創(chuàng)建棋盤的作用是用來存儲雷的信息,這時我們思考一下,一個棋盤到底夠不夠用?棋盤多大才合適?我們打印出來的棋盤肯定是不能出現(xiàn)雷的信息的,不然游戲就無法正常進(jìn)行了,但是我們雷的信息又需要棋盤存儲,這樣一想,一個棋盤似乎做不到,那么我們就可以再創(chuàng)建一個棋盤以達(dá)到既能存儲雷的信息也能打印一個不含雷的棋盤,保證游戲的正常進(jìn)行。
char mineboard[ROWS][COLS] = { 0 };//存放雷的數(shù)組
char showboard[ROWS][COLS] = { 0 };//展示信息的數(shù)組
讀者可以思考一下為什么這里創(chuàng)建的是字符數(shù)組,現(xiàn)在棋盤有了,但是里面放了什么我們并不確定放了些什么,所有我們將它初始化一下
initboard(mineboard, ROWS, COLS, '0'); initboard(showboard, ROWS, COLS, '*');
void initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for(j = 0; j < cols; j++)
{
board[i][j] = ret;//ret為要初始化字符
}
}
}
有些讀者看到ROWS和COLS可能就會疑惑了,下標(biāo)怎么是符號,其實(shí)這是因?yàn)椴┲魍ㄟ^#define將這兩個符號定義成了兩個數(shù)字
#define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define MINE_NUM 10
這樣做的好處就是,當(dāng)需要修改棋盤的大小時,只需要改變這一處即可,至于MINE_NUM為要布置的雷的個數(shù),暫時用不上,我們先繼續(xù)實(shí)現(xiàn),既然棋盤有了,也初始化了,那么我們先打印出來看一下,是不是和我們預(yù)想的一樣,我們封裝一個打印函數(shù)實(shí)現(xiàn)打印
void printboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
if (1 == i)
{
for (j = 0; j <= col; j++)
{
printf(" %d", j);
}
printf("\n");
}
for (j = 1; j <= col; j++)
{
if (1 == j)
{
printf(" %d", i);
}
printf(" %c", board[i][j]);
}
printf("\n");
}
}
printboard(mineboard, ROW, COL);//調(diào)用函數(shù)打印雷的信息 printboard(showboard, ROW, COL);//調(diào)用函數(shù)打印展示的的信息
接下來我們布置雷,同樣封裝一個布雷函數(shù),但是布雷,總不能人為布置吧,不然雷都知道了就沒得完了,所以我們應(yīng)該讓電腦幫我們布雷,而且多次游戲的話,雷的位置肯定不能一樣,所以我們就需要一個庫函數(shù)來幫我們實(shí)現(xiàn)隨機(jī)布雷
srand((unsigned int)time(NULL));//生成隨機(jī)數(shù)的種子
void setmine(char board[ROWS][COLS], int mine_num, int row, int col)
{
while (mine_num)
{
int x = rand() % row + 1;//生成隨機(jī)數(shù)
int y = rand() % col + 1;//生成隨機(jī)數(shù)
if (board[x][y] == '0')
{
board[x][y] = '1';
mine_num--;
}
}
}
setmine(mineboard, MINE_NUM, ROW, COL);//調(diào)用布雷函數(shù)布雷
我們可以看到我們傳遞參數(shù)的時候就用到了MINE_NUM雷的個數(shù)這個符號常量,因?yàn)槌A坎豢杀恍薷?,所以我們將它放在?shí)參的位置上而不是直接用它,布置好雷之后,我們調(diào)用打印函數(shù)將雷盤信息打印一下。
printboard(mineboard, ROW, COL);
確認(rèn)信息無誤后,我們接著實(shí)現(xiàn)游戲,同樣封裝成一個函數(shù)
void play(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int n = 0;//記錄已排除的格子
while (row * col - MINE_NUM - n)
{
printf("請輸入坐標(biāo),以空格隔開輸入>:");
scanf("%d %d", &x, &y);
if (x > ROW || y > COL || x < 1 || y < 1)
{
printf("坐標(biāo)非法,請重新輸入\n");
continue;
}
if (mineboard[x][y] != '1')
{
int ret = find(mineboard, x, y);
if (ret != 0)
{
showboard[x][y] = ret + '0';
//n++;
}
else
{
showboard[x][y] = ' ';
//spread(showboard, mineboard, x, y);
}
n = Isblank(showboard, row, col);//檢查已經(jīng)有多少格子已經(jīng)排除
system("cls");
printboard(showboard, row, col);
}
else
{
break;
}
}
if (row * col - MINE_NUM - n)
{
printf("很遺憾,你被炸死了\n");
}
else
{
printf("恭喜你,掃雷成功\n");
}
printboard(mineboard, row, col);
}
play(mineboard, showboard, ROW, COL);//調(diào)用函數(shù),開始游戲
在開始游戲之前無疑我們要先要先將展示的棋盤和提示信息打印出來,讓玩家看到,從而進(jìn)行游戲,所以我們在開始游戲之前調(diào)用一下打印函數(shù)
printboard(showboard, ROW, COL);
接下來就是游戲的實(shí)現(xiàn)了,可以看到游戲函數(shù)中有很多函數(shù)的接口比如find,Isblank等這就是我們接下來要實(shí)現(xiàn)的功能函數(shù),開始游戲給與提示,讀入坐標(biāo),并對坐標(biāo)進(jìn)行判斷,是否合法,不合法則提示玩家重新輸入,如果合法,則判斷斷當(dāng)前坐標(biāo)是不是雷如果是則退出循環(huán),游戲結(jié)束,并給與提示,如果不是雷則判斷附近有沒有雷,沒有則置為空也就是空格,有雷則放入附近雷的信息,然后判斷是不是所有不是雷的空格都找到了,如果不是則繼續(xù)游戲,重復(fù)剛才的判斷,是則游戲結(jié)束,掃雷成功,排雷功能在上面代碼已經(jīng)實(shí)現(xiàn),接下來實(shí)現(xiàn)判斷功能,其實(shí)很簡單,遍歷棋盤,看看是不是所有不是雷的元素都被找出來即可
查找輸入坐標(biāo)附近雷的信息
int find(char board[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
if (x > 0 && x < ROW && y > 0 && y < COL)
{
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (board[i][j] == '1')
count++;
}
}
}
return count;
}
統(tǒng)計(jì)已排除的坐標(biāo)個數(shù)
int Isblank(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int n = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (board[i][j] != '*')
{
n++;
}
}
}
return n;
}
返回值就是我們需要的已經(jīng)被排查的坐標(biāo)個數(shù)了,判斷一下總的坐標(biāo)個數(shù)和雷的個數(shù)加已排除的坐標(biāo)個數(shù)即可,所以我們將這個條件作為循環(huán)停止的條件,如play函數(shù)中的while即可。
因?yàn)橥顺鲅h(huán)的原因有兩個,所以我們對,退出的條件進(jìn)行一下判斷,用如下代碼即可
if (row * col - MINE_NUM - n)
{
printf("很遺憾,你被炸死了\n");
}
else
{
printf("恭喜你,掃雷成功\n");
}
到此初級的簡單掃雷游戲就實(shí)現(xiàn)了。
掃雷進(jìn)階—遞歸實(shí)現(xiàn)自動清空
初級版本的掃雷游戲只能輸入一個坐標(biāo)判斷一次,完成掃雷無疑是巨大的挑戰(zhàn),甚至說完全憑運(yùn)氣,所以我們來實(shí)現(xiàn)一下自動清空附近沒有一個雷的坐標(biāo),來降低游戲的難度,接下來我們來實(shí)現(xiàn)這個功能,細(xì)心的小伙伴們會發(fā)現(xiàn)我在play函數(shù)中我留了一個名為spread的函數(shù)接口,而這個接口就是我們調(diào)用自動清空函數(shù)的入口,因?yàn)樯厦鏇]有實(shí)現(xiàn)這個函數(shù),所以我就將它注釋掉了,現(xiàn)在我們將它還原即可。
void spread(char showboard[ROWS][COLS], char mineboard[ROWS][COLS], int x, int y)
{
showboard[x][y] = ' ';
int i = 0;
int j = 0;
int ret = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (i > 0 && i <= ROW && j > 0 && j <= COL && mineboard[i][j] != '1' && showboard[i][j] == '*')
{
ret = find(mineboard, i, j);
if (!ret)
{
spread(showboard, mineboard, i, j);
}
if (ret)
{
showboard[i][j] = ret + '0';
}
else if (showboard[i][j] == '*')
{
showboard[i][j] = ' ';
}
}
}
}
}
到此我們的掃雷就很好的實(shí)現(xiàn)了。小伙伴們趕緊試試吧。
完整的源碼
頭文件
#pragma once #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define MINE_NUM 10 #include<stdio.h> #include<windows.h> #include<stdlib.h> #include<time.h> //打印菜單 void menu(); //提示 void playgame(); //初始化雷盤 void initboard(char board[ROWS][COLS], int rows, int cols, char ret); //打印雷盤 void printboard(char board[ROWS][COLS], int row, int col); //布置雷 void setmine(char board[ROWS][COLS], int mine_num, int row, int col); //掃雷 void play(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col);
函數(shù)定義的源文件
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu()
{
printf("****************************\n");
printf("*** 1.play 0.exit ***\n");
printf("****************************\n");
}
void playgame()
{
printf("游戲開始");
Sleep(650);
system("cls");
}
void initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for(j = 0; j < cols; j++)
{
board[i][j] = ret;
}
}
}
void printboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
if (1 == i)
{
for (j = 0; j <= col; j++)
{
printf(" %d", j);
}
printf("\n");
}
for (j = 1; j <= col; j++)
{
if (1 == j)
{
printf(" %d", i);
}
printf(" %c", board[i][j]);
}
printf("\n");
}
}
void setmine(char board[ROWS][COLS], int mine_num, int row, int col)
{
while (mine_num)
{
int x = rand() % row + 1;//生成隨機(jī)數(shù)
int y = rand() % col + 1;//生成隨機(jī)數(shù)
if (board[x][y] == '0')
{
board[x][y] = '1';
mine_num--;
}
}
}
int find(char board[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
if (x > 0 && x < ROW && y > 0 && y < COL)
{
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (board[i][j] == '1')
count++;
}
}
}
return count;
}
void spread(char showboard[ROWS][COLS], char mineboard[ROWS][COLS], int x, int y)
{
//if (x > 0 && x <= ROW && y > 0 && y <= COL)
//{
showboard[x][y] = ' ';
//*pn += 1;
int i = 0;
int j = 0;
int ret = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (i > 0 && i <= ROW && j > 0 && j <= COL && mineboard[i][j] != '1' && showboard[i][j] == '*')
{
ret = find(mineboard, i, j);
if (!ret)
{
spread(showboard, mineboard, i, j);
}
if (ret)
{
showboard[i][j] = ret + '0';
//*pn += 1;
}
else if (showboard[i][j] == '*')
{
showboard[i][j] = ' ';
//*pn += 1;
}
}
}
}
//}
}
int Isblank(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int n = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (board[i][j] != '*')
{
n++;
}
}
}
return n;
}
void play(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int n = 0;//記錄已排除的格子
while (row * col - MINE_NUM - n)
{
printf("請輸入坐標(biāo),以空格隔開輸入>:");
scanf("%d %d", &x, &y);
if (x > ROW || y > COL || x < 1 || y < 1)
{
printf("坐標(biāo)非法,請重新輸入\n");
continue;
}
if (mineboard[x][y] != '1')
{
int ret = find(mineboard, x, y);
if (ret != 0)
{
showboard[x][y] = ret + '0';
//n++;
}
else
{
//showboard[x][y] = ' ';
spread(showboard, mineboard, x, y);
}
n = Isblank(showboard, row, col);//檢查已經(jīng)有多少格子已經(jīng)排除
system("cls");
printboard(showboard, row, col);
}
else
{
break;
}
}
if (row * col - MINE_NUM - n)
{
printf("很遺憾,你被炸死了\n");
}
else
{
printf("恭喜你,掃雷成功\n");
}
printboard(mineboard, row, col);
}
測試的源文件
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void game()
{
playgame();
char mineboard[ROWS][COLS] = { 0 };//存放雷的數(shù)組
char showboard[ROWS][COLS] = { 0 };//展示信息的數(shù)組
initboard(mineboard, ROWS, COLS, '0');
//printboard(mineboard, ROW, COL);
initboard(showboard, ROWS, COLS, '*');
printboard(showboard, ROW, COL);
setmine(mineboard, MINE_NUM, ROW, COL);
//printboard(mineboard, ROW, COL);
play(mineboard, showboard, ROW, COL);
}
int main()
{
int Input = 0;
do
{
srand((unsigned int)time(NULL));//隨機(jī)數(shù)的種子
menu();
printf("請選擇>:");
scanf("%d", &Input);
switch (Input)
{
case 1:game();
break;
case 0:printf("退出游戲\n");
break;
default:printf("選擇錯誤,請重新選擇\n");
break;
}
} while (Input);
return 0;
}
寫在最后的話
現(xiàn)在我們來解釋一下,之前留下的問題,為什么要用字符數(shù)組,因?yàn)樽址麛?shù)組的打印可以更多形式可以是#,*,!等等比數(shù)字無疑多出很多可能,而且字符也有數(shù)字可以表示,玩家看上去并無區(qū)別,棋盤為什么是11 * 11的呢,那是因?yàn)檫@樣輸入的下標(biāo)就可以直接當(dāng)作棋盤的下標(biāo)使用了,也可以防止遍歷的時候越界,這樣說小伙伴們可以理解嗎?
到此這篇關(guān)于C語言實(shí)現(xiàn)掃雷小游戲的文章就介紹到這了,更多相關(guān)C語言掃雷小游戲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MacOS下C++使用WebRTC注意事項(xiàng)及問題解決
這篇文章主要介紹了MacOS下C++使用WebRTC注意事項(xiàng),對于iOS/macOS平臺,開啟openh264,去除test,使用一些命令可以輕松解決,下面小編給大家?guī)砹藛栴}及解決方法,需要的朋友可以參考下2022-09-09

