目錄
1.掃雷簡介
2.游戲實作思路
3.代碼詳細分析
1.掃雷簡介

不知道大家再次看見這張圖有什么感受,自從win10更新后將掃雷移除,似乎這個我們的童年回憶正與我們漸行漸遠,而今天我們要做的就是利用C語言簡單實作這個我們的童年回憶,
先簡單介紹一下掃雷的規則:
1、點擊方格(能力有限這里我們暫時用輸入坐標的方式),如果是地雷,游戲失敗,找到所有地雷游戲勝利,
2、剛開始需要碰運氣,只要選擇一個區域,就可以正式開始了,
3、根據現有情況,判斷出一定有雷的位置,(在選擇一個無雷區域后會顯示周圍雷的數量)
4、重復上述操作直到全部雷被排查,、
2.游戲實作思路
根據上述對游戲規則的介紹我們大致可以得出以下思路
注:本文會涉及到一些有關模塊化設計思想,具體可以去這里查看https://blog.csdn.net/weixin_60778429/article/details/121193665
1.最基本也是最重要的玩家互動界面(參考上個鏈接)
2.初始化雷區
3.列印棋盤
4.隨機布雷
5玩家排雷(回圈)
6.統計所排區域四周雷的數量
7.遞回實作空白拓展(詳解)
3.代碼詳細分析
1.最基本的選單界面之前提過這里就簡單放一下代碼供大家參考就好了
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請做出你的選擇\n");
scanf("%d",&input);
switch (input)
{
case 1:
printf("新的征程\n");
game();
break;
case 0:
printf("結束冒險\n");
break;
default:
printf("輸入錯誤,請重新輸入\n");
break;
}
} while (input);
return 0;
}
2.接下來我們需要初始化棋盤
初始棋盤需要兩個二維陣列,一個用于存放雷(mine),實作各種功能,另一個用于展示給玩家目前操作狀況,便于玩家判斷局勢(show),
因為我們在后續判斷已排區域周圍雷數量時,為方便統計邊緣區域我們的陣列會比實際大兩行兩列,代碼如下,
void IntiBoard(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引數為我們設定棋盤的初始化值,這里我們默認mine陣列全部初始化為‘0’,show陣列初始化為‘*’,(這里采用字符初始化的原因時2為了實作兩個陣列型別的統一便于代碼實作)
3.列印棋盤
這里我們需要列印一個棋盤(show陣列)供玩家觀察目前狀況以做出下一步判斷,為便于玩家游戲,這里我們將棋盤的行與列一同列印出來,代碼如下,
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("--------------------掃雷游戲-------------------\n");
for (i = 0; i <= row; 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");
}
4.既然現在棋盤已經有了,那么接下來我們要實作的就是在雷區布雷(這里用‘1’來代表雷),這里我們還是采用亂數布雷的方式,但是要注意亂數的范圍不要越界,rand隨機值用法可參照下文,那么直接放代碼c語言實作簡單猜數字游戲(rand/do while的基本使用)_invictusQAQ的博客-CSDN博客
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;//控制坐標范圍
int y = rand() % col + 1;
if (mine[x][y] == '0')//確保不重復布雷
{
mine[x][y] = '1';
count--;//控制布雷數量
}
}
}
5.既然雷池已經布置完畢,那么接下來就是我們聰明勇敢的玩家的排雷show了,這里我們采用回圈的方式讓玩家去排雷,直到雷區清慷訓者玩家陣亡而退出回圈,代碼如下,
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-EASY_COUNT)//判斷是否已經排完雷
{
printf("請輸入排查坐標\n");
scanf("%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 = get_mine_count(mine, x, y);
show[x][y] = count + '0';//顯示四周雷的數量
Space(show, mine, x, y);//空白遞回
DisplayBoard(show, row, col);//展示玩家排查后的狀況
win++;//排雷成功數+1
}
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, row, col);
}
}
6.掃雷程序中有一個非常核心的功能,那就是顯示出已排查區域四周雷的數量,便于玩家做出下一步判斷,是讓掃雷不成為一個運氣游戲的關鍵,下面我們著手來實作他,
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x][y - 1] +//這里采用暴力列舉的方式排查周圍的八格
mine[x - 1][y] +
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] - 8 * '0';//字符-‘0’=數字
}
這段代碼可能有些同學不理解,這里稍微解釋一下,由于我們之前布置雷區時采用‘1’來標記雷,而字符數字-字符0就會得到原本數字的ASCII值,而我們非雷區域是用‘0’標記的,因此用周圍八個坐標之和-8*‘0’便可以得到周圍實際的地雷數量,
7.大家玩過掃雷的相信一定遇到過突然拓展出一片空白的現象,這是游戲設計者為了提升玩家游戲體驗而想出的方法,畢竟真讓玩家將每個空格都去排查一遍屬實有些折磨人,而觸發拓展空白的條件就是當統計到坐標處周圍雷的個數為0時,依次檢查周圍八個位置的周圍雷個數,如果統計到任一位置的周圍雷個數為0,則繼續擴展其周圍,如此反復直到把附近位置排查完,
有了大致思路后,還有兩點細節是我們需要注意的
1.檢查過的位置,拓展時不用重復檢查
2.檢查的坐標不能是雷
而既然提到了遞回,我就需要先大致了解一下什么是遞回?
程式呼叫自身的編程技巧稱為遞回( recursion),一個程序或函式在其定義或說明中有直接或間接呼叫自身的一種方法,它通常把一個大型復雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,
使用遞回的條件
- 存在限制條件,當滿足這個限制條件的時候,遞回便不再繼續,
- 每次遞回呼叫之后都越來越接近這個限制條件,
而這里的限制條件便是將所有周圍雷數為0的區域找出遞回結束,代碼如下,
void Space(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)//空白遞回
{
int i = 0;
if (get_mine_count(mine, x, y) == 0)//判斷周圍無雷
{
show[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
int j=0;
for (j = y - 1; j <= y + 1; j++)//遍歷周圍是否滿足遞回條件
{
if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] != '1' && show[i][j] == '*')
{
Space(show, mine, i, j);//滿足條件遞回
}
}
}
}
else//不滿足條件則執行常規操作
show[x][y] = '0' + get_mine_count(mine, x, y);
}
到此我們的核心部分已經完成,接下來我將放出剩余模塊化相關的代碼供大家參考,分為3個檔案
test.c邏輯測驗檔案
#include"game.h"
void menu()
{
printf("****************************************\n");
printf("********** 1.play **********\n");
printf("********** 0.exit **********\n");
printf("****************************************\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
IntiBoard(mine, ROWS, COLS,'0');
IntiBoard(show, ROWS, COLS,'*');
DisplayBoard(show, ROW, COL);
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請做出你的選擇\n");
scanf("%d",&input);
switch (input)
{
case 1:
printf("新的征程\n");
game();
break;
case 0:
printf("結束冒險\n");
break;
default:
printf("輸入錯誤,請重新輸入\n");
break;
}
} while (input);
return 0;
}
game.c函式核心功能實作
#include"game.h"
void IntiBoard(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 <= row; 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 = EASY_COUNT;
while (count)
{
int x = rand() % 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)
{
return mine[x][y - 1] +
mine[x - 1][y] +
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] - 8 * '0';//字符-‘0’=數字
}
void Space(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)//空白遞回
{
int i = 0;
if (get_mine_count(mine, x, y) == 0)
{
show[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
int j=0;
for (j = y - 1; j <= y + 1; j++)
{
if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] != '1' && show[i][j] == '*')
{
Space(show, mine, i, j);
}
}
}
}
else
show[x][y] = '0' + get_mine_count(mine, x, y);
}
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-EASY_COUNT)
{
printf("請輸入排查坐標\n");
scanf("%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 = get_mine_count(mine, x, y);
show[x][y] = count + '0';
Space(show, mine, x, y);
DisplayBoard(show, row, col);
win++;
}
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, row, col);
}
}
game.h函式宣告檔案
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
void IntiBoard(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);
最后感謝大家的觀看,如果有什么好的見解可在評論區下方提出,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/355408.html
標籤:其他
上一篇:淺析3種電池容量監測方案
