目錄
掃雷
掃雷游戲規則介紹
如何將掃雷游戲實作代碼
基本思路
分步代碼實作
創建和列印游戲選單
初始化棋盤
列印棋盤
布置雷
排查雷
游戲主體——game()函式
總代碼實作
game.h
test.c
game.c
總結
掃雷
實作掃雷的演算法有很多種,我在這里給大家最詳細的代碼介紹以及思考方法,細節滿滿哦!!
掃雷游戲規則介紹
每個格子有兩種狀態,有地雷或者沒有地雷,玩家點到地雷游戲結束,玩家標記出所有地雷游戲勝利,
每個沒有地雷的格子點開后顯示相鄰8個格子里面存在地雷的數目,周邊沒有地雷則可以遞回地打開與空相鄰的方塊;如果不幸觸雷,則游戲結束,
如何將掃雷游戲實作代碼
與上次三子棋游戲模塊一致,分類創建:
game.h:相關游戲函式的宣告,變數的宏定義等;
game.c:游戲相關函式的功能實作;
test.c:游戲的測驗,游戲的主題體;
基本思路
1.創建和列印游戲選單
2.創建兩個棋盤陣列,一個是布置雷的棋盤陣列,一個是排查雷的棋盤陣列
3.初始化兩個棋盤,為了防止后期統計排查雷的個數出現矛盾,所以我這里把布置雷的那個棋盤全部初始為'0',把排查雷的棋盤全部初始化為'*'
4.列印棋盤
5.布置雷,由電腦自主完成隨機布置雷的個數,個數可以自己在頭檔案中定義
6.排查雷,在布置雷的陣列里排查,如果是雷則列印被炸死,并退出游戲,列印排查雷的棋盤;如果不是雷,則統計雷的個數,是0則展開空白,不是0則將雷的個數傳給排查雷的那個陣列
7.判斷輸贏,如果空格的總的個數于行和列的乘積減去布雷的個數,則表示排雷成功
分步代碼實作
創建和列印游戲選單
void menu()
{
printf("**********************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("**********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//用于隨機函式rand的呼叫
do
{
menu();
scanf_s("%d", &input);
switch (input)
{
case 1:
printf("掃雷游戲開始\n");
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯誤,請重新選擇!\n");
}
} while (input);
return 0;
}
初始化棋盤
//兩個陣列初始化陣列的實作,char board[ROWS][COLS]接收mine陣列和show陣列
//這里設定一個字符set接收mine和show陣列傳參過來的‘0’和‘*’
void Initboard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
這里的初始化函式呼叫兩次,分別初始化布雷陣列和排雷陣列,字符set分別接收'0'和'*';
列印棋盤
void Displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("————掃雷游戲 ————\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i); //為了方便用戶判斷行數和列數,將列數列印出來
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i); //每行開頭列印行數
for (j = 1; j <=col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("————掃雷游戲————\n");
}
列印棋盤結果如下:

這樣列印出來的棋盤好看也方便用戶寫入坐標
布置雷
void Setmine(char mine[ROWS][COLS], int row, int col)
{
int count = SETCOUNT; //為了靈活變通,可以自己設定布置雷的個數,在頭檔案中自定義個數
while (count) //直到count為0才退出回圈,并且每次都是隨機坐標布置雷,count是幾,
{ x和y就要隨機幾次
int x = rand() % row + 1;//用戶輸入的坐標的范圍就是行數列數的范圍,應該是row+1才是正確
的坐標范圍
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;//每布置一個,雷的個數就減一;
}
}
}
排查雷
void Findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//記錄不是雷的個數,總數
while (win < row * col - SETCOUNT)
{
int count_blank = 0;//空白個數,每次進入回圈都要重置,如果放在回圈外面依舊是上一次的值會重復疊加空白;
printf("請輸入排查雷的坐標:\n");
scanf_s("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判斷坐標合法性
{
if (mine[x][y] == '1') //遍歷到了雷
{
printf("很遺憾,你被炸死了!\n");
Displayboard(mine, row, col);
break;
}
else
{
int count_mine = get_mine_count(mine, x, y);
if (count_mine == 0 && show[x][y]=='*')//周圍雷的個數為0,且沒有被遍歷
{
count_blank=get_showblank(mine, show, x, y);//遞回展開空白,回傳空白個數
win += count_blank;//空白的個數加到不是雷個數的總數中
}
else if(show[x][y]=='*')//這里用else不太合適,這里控制的條件是當周圍個數不是0,但是沒有被遍歷
{
show[x][y] = count_mine + '0';//不是雷,則統計周圍有幾個雷,放入show陣列對應坐標
win++;//他統計了周圍雷的個數,但本身不是雷,加1
}
Displayboard(show, row, col);
}
}
else
{
printf("坐標不合法,請重新輸入!\n");
}
}
if (win== row * col - SETCOUNT)
{
printf("恭喜你,排雷成功!\n");
Displayboard(show, row, col);
}
}
當排查雷時,(x,y)處不是雷且周圍雷的個數不為0則需要統計周圍雷的個數:
static int get_mine_count(char mine[ROWS][COLS], int x,int y) //static修飾函式,那這個函式
就只能在當前源檔案里使用了
{ //統計這些坐標周圍有幾個雷
return mine[x][y - 1] +
mine[x - 1][y - 1] +
mine[x + 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x][y + 1] +
mine[x + 1][y + 1] +
mine[x + 1][y] - 8 * '0'; //這里回傳的是數字,num+'0'='num';即一個數字加上字符0等于數字代表的字符
}
當(x,y)周圍雷的個數為0時展開為空格,去遞回遍歷周圍四個坐標雷的個數,并且統計空格的個數:
int get_showblank(char mine[ROWS][COLS], char show[ROWS][COLS], int x , int y)//當雷的個數為0時計算展開的空白個數
{
int count_mine = get_mine_count(mine, x,y);
int count_blank = 0;//初始化空白的個數
if(count_mine == 0)
{
show[x][y] = ' ';
count_blank++;//只要是空白就加1;下次遞回也一樣
//判斷周圍四個坐標合法性,并且要滿足沒有被遍歷,依舊是*號,以防重復遍歷
if (x - 1 >= 1 && x - 1 <= ROW && y >= 1 && y <= COL && show[x - 1][y] == '*')
{
count_blank+=get_showblank(mine, show, x - 1, y);//每遞回一次,空白個數要累加,包括了x,y的空白和周圍四個坐標的空白;
}
if (x >= 1 && x <= ROW && y - 1 >= 1 && y - 1 <= COL && show[x][y - 1] == '*')
{
count_blank += get_showblank(mine, show, x, y - 1);
}
if (x + 1 >= 1 && x + 1 <= ROW && y >= 1 && y <= COL && show[x + 1][y] == '*')
{
count_blank += get_showblank(mine, show, x + 1, y);
}
if (x >= 1 && x <= ROW && y + 1 >= 1 && y + 1 <= COL && show[x][y + 1] == '*')
{
count_blank += get_showblank(mine, show, x, y + 1);
}
}
return count_blank;//回傳空白個數
}
游戲主體——game()函式
void game()
{
char mine[ROWS][COLS] = { 0 }; //創建存放布置雷的陣列
char show[ROWS][COLS] = { 0 }; //創建存放排查雷的陣列
Initboard(mine, ROWS, COLS,'0'); //初始化布置雷的棋盤,mine表示布置雷陣列的首地址
Initboard(show, ROWS, COLS, '*'); //初始化排查雷的陣列,show表示排查雷陣列的首地址
//Displayboard(mine, ROW, COL); //列印布置雷的棋盤,這里不用列印擴展的兩行兩列,只需在中間的棋盤布雷,掃雷也同樣
Displayboard(show, ROW, COL);
Setmine(mine, ROW, COL); //布置雷
//Displayboard(mine, ROW, COL);
Findmine(mine,show, ROW, COL); //排查雷
}
掃雷游戲主體函式則相比三子棋簡單,只需要呼叫游戲相關函式即可,這里需要注意的是在呼叫這些函式進行陣列傳參時,要注意引數的順序,這里創建mine和show陣列都是用的ROWS和COLS,所以在實作相關函式的功能時用的依然是ROWS和COLS,
總代碼實作
game.h
#pragma once
#define ROW 9
#define COL 9
#define SETCOUNT 10
#define ROWS ROW+2 //為了能成功遍歷邊界的棋盤,所以需要創建兩個擴展的陣列將布雷和掃雷一一對應,初始化時也一樣要擴展
#define COLS COL+2
void Initboard(char board[ROWS][COLS], int rows, int cols,char set);
void Displayboard(char board[ROWS][COLS], int row, int col); //列印棋盤只列印中間的棋盤,不需要列印擴列的
void Setmine(char mine[ROWS][COLS], int row, int col);
void Findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
test.c
#include<stdio.h>
#include"game.h"
#include<stdlib.h>
#include<time.h>
void game()
{
char mine[ROWS][COLS] = { 0 }; //創建存放布置雷的陣列
char show[ROWS][COLS] = { 0 }; //創建存放排查雷的陣列
Initboard(mine, ROWS, COLS,'0'); //初始化布置雷的棋盤,mine表示布置雷陣列的首地址
Initboard(show, ROWS, COLS, '*'); //初始化排查雷的陣列,show表示排查雷陣列的首地址
//Displayboard(mine, ROW, COL); //列印布置雷的棋盤,這里不用列印擴展的兩行兩列,只需在中間的棋盤布雷,掃雷也同樣
Displayboard(show, ROW, COL);
Setmine(mine, ROW, COL); //布置雷
//Displayboard(mine, ROW, COL);
Findmine(mine,show, ROW, COL); //排查雷
}
void menu()
{
printf("**********************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("**********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//用于隨機函式rand的呼叫
do
{
menu();
scanf_s("%d", &input);
switch (input)
{
case 1:
printf("掃雷游戲開始\n");
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯誤,請重新選擇!\n");
}
} while (input);
return 0;
}
game.c
#include<stdio.h>
#include"game.h"
//兩個陣列初始化陣列的實作,char board[ROWS][COLS]接收mine陣列和show陣列
//這里設定一個字符set接收mine和show陣列傳參過來的‘0’和‘*’
void Initboard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void Displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("————掃雷游戲 ————\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i); //為了方便用戶判斷行數和列數,將列數列印出來
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i); //每行開頭列印行數
for (j = 1; j <=col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("————掃雷游戲————\n");
}
void Setmine(char mine[ROWS][COLS], int row, int col)
{
int count = SETCOUNT; //為了靈活變通,可以自己設定布置雷的個數,在頭檔案中自定義個數
while (count)
{ //直到count為0才退出回圈,并且每次都是隨機坐標布置雷,count是幾,x和y就要隨機幾次
int x = rand() % row + 1;//用戶輸入的坐標的范圍就是行數列數的范圍,應該是row+1才是正確的坐標范圍
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;//每布置一個,雷的個數就減一;
}
}
}
static int get_mine_count(char mine[ROWS][COLS], int x,int y) //static修飾函式,那這個函式就只能在當前源檔案里使用了
{ //統計這些坐標周圍有幾個雷
return mine[x][y - 1] +
mine[x - 1][y - 1] +
mine[x + 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x][y + 1] +
mine[x + 1][y + 1] +
mine[x + 1][y] - 8 * '0'; //這里回傳的是數字,num+'0'='num';即一個數字加上字符0等于數字代表的字符
}
int get_showblank(char mine[ROWS][COLS], char show[ROWS][COLS], int x , int y)//當雷的個數為0時計算展開的空白個數
{
int count_mine = get_mine_count(mine, x,y);
int count_blank = 0;//初始化空白的個數
if(count_mine == 0)
{
show[x][y] = ' ';
count_blank++;//只要是空白就加1;下次遞回也一樣
//判斷周圍四個坐標合法性,并且要滿足沒有被遍歷,依舊是*號,以防重復遍歷
if (x - 1 >= 1 && x - 1 <= ROW && y >= 1 && y <= COL && show[x - 1][y] == '*')
{
count_blank+=get_showblank(mine, show, x - 1, y);//每遞回一次,空白個數要累加,包括了x,y的空白和周圍四個坐標的空白;
}
if (x >= 1 && x <= ROW && y - 1 >= 1 && y - 1 <= COL && show[x][y - 1] == '*')
{
count_blank += get_showblank(mine, show, x, y - 1);
}
if (x + 1 >= 1 && x + 1 <= ROW && y >= 1 && y <= COL && show[x + 1][y] == '*')
{
count_blank += get_showblank(mine, show, x + 1, y);
}
if (x >= 1 && x <= ROW && y + 1 >= 1 && y + 1 <= COL && show[x][y + 1] == '*')
{
count_blank += get_showblank(mine, show, x, y + 1);
}
}
return count_blank;//回傳空白個數
}
void Findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//記錄不是雷的個數,總數
while (win < row * col - SETCOUNT)
{
int count_blank = 0;//空白個數,每次進入回圈都要重置,如果放在回圈外面依舊是上一次的值會重復疊加空白;
printf("請輸入排查雷的坐標:\n");
scanf_s("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判斷坐標合法性
{
if (mine[x][y] == '1') //遍歷到了雷
{
printf("很遺憾,你被炸死了!\n");
Displayboard(mine, row, col);
break;
}
else
{
int count_mine = get_mine_count(mine, x, y);
if (count_mine == 0 && show[x][y]=='*')//周圍雷的個數為0,且沒有被遍歷
{
count_blank=get_showblank(mine, show, x, y);//遞回展開空白,回傳空白個數
win += count_blank;//空白的個數加到不是雷個數的總數中
}
else if(show[x][y]=='*')//這里用else不太合適,這里控制的條件是當周圍個數不是0,但是沒有被遍歷
{
show[x][y] = count_mine + '0';//不是雷,則統計周圍有幾個雷,放入show陣列對應坐標
win++;//他統計了周圍雷的個數,但本身不是雷,加1
}
Displayboard(show, row, col);
}
}
else
{
printf("坐標不合法,請重新輸入!\n");
}
}
if (win== row * col - SETCOUNT)
{
printf("恭喜你,排雷成功!\n");
Displayboard(show, row, col);
}
}
總結
這是掃雷優化也叫完整版的掃雷游戲,博主花了一天時間思考和修改代碼,整理博客,實屬不易,這讓我感覺到每一個游戲的實作離不開每一個細節的把控,如果忽略一個細節,整個游戲就無法完成,掃雷游戲細節頗多,對于我們的思考能力和代碼實作能力有一定考驗,徹底掌握則需要我們反復去琢磨,去動手實作,只有這樣我們才能變得更強,如果這篇文章能幫助到你,請給我一鍵三連,有了大家的鼓勵和指教我才能更進一步!!謝謝大家
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/282743.html
標籤:其他
