閱讀建議

掃雷游戲
Windows系統下的掃雷游戲想必大家耳熟能詳!規則想必不用多說,我們通過下面的分析思路來寫這個游戲
分析
我們來看一下掃雷的界面,這是一個已經玩到一半的游戲

圖中有兩個部分,一個是已經翻開的(有地雷的),另一個是還沒有翻開的(沒有地雷的),我們可以理解為兩張地圖,那么我們要設計一個掃雷游戲的話,是不是第一步就要創建兩個二位陣列來表示這兩張地圖呢?
那地圖創建好了,我們來觀察,地圖上有小格子,每個格子是不是要記錄不同的資訊?而格子是不是也有兩種不同的狀態(翻開和未翻開)?
那著手點我們找到了,是不是就可以下手了!
流程:
接下來我們來簡單梳理一下游戲流程:

拆分細節
第一步:創建地圖并初始化~
1.創建showMap,初始化成全是‘ * ,
2.創建mineMap,先全部初始化成‘0’,隨機生成十個位置作為地雷,設為字符‘1’,
我們來看操作:
#include<stdio.h>
#include<time.h>
#define MINE_COUNT 10
#define MAX_ROW 9
#define MAX_COL 9 //這里設定宏的目的是避免魔幻數字的出現,讓代碼功能更加清晰
void init(char showMap[MAX_ROW][MAX_COL], char mineMap[MAX_ROW][MAX_COL])
{
for (int row = 0; row < MAX_ROW; row++) //除了回圈賦值我們還可以使用庫函式memset(ar,n,size)對陣列的全部變數進行賦值,注意引入頭檔案<string.h>哦
{
for (int col = 0; col < MAX_COL; col++)
{
showMap[row][col] = '*'; //memset(showMap, '*', MAX_ROW * MAX_COL);
}
}
for (int row = 0; row < MAX_ROW; row++)
{
for (int col = 0; col < MAX_COL; col++)
{
mineMap[row][col] = '0'; //memset(mineMap, '0', MAX_ROW * MAX_COL);
}
}
//隨機產生十個位置作為地雷
//先設定亂數種子
srand((unsigned int)time(NULL));
int mineCount = 0; //這里我們需要對雷的數量進行定義,以便作下面回圈的判斷
while(mineCount<MINE_COUNT)
{
int row = rand() % MAX_ROW;
int col = rand() % MAX_COL; //這里又有可能出現問題,如果這十個亂數重復呢?十個地雷位置必須是不同的,那我們就有下面的操作
//判斷一下當前的位置是否已經有雷了
if (mineMap[row][col] == '1')
{
continue;
}
mineMap[row][col] = '1';
mineCount++;
}
}
int main()
{
//1.創建地圖并初始化
char showMap[MAX_ROW][MAX_COL] = { 0 };
char mineMap[MAX_ROW][MAX_COL] = { 0 };
init(showMap, mineMap);
return 0;
}
第二步:列印地圖showMap~
我們要創建一個列印函式,最好能讓他同時具備列印兩種地圖的功能
我們來看操作:
void Print(char theMap[MAX_ROW][MAX_COL])
{
for (int row = 0; row < MAX_ROW; row++)
{
for (int col = 0; col < MAX_COL; col++)
{
printf("*", theMap[row][col]);
}
printf("\n");
}
}
第三步:讓玩家輸入坐標,表示要翻開的位置~
輸入坐標這里我們會遇到的問題是:
1.玩家輸入的位置會不會超出陣列的范圍?
2.玩家輸入的位置會不會已經翻開了?
那么我們就需要進行合法性判定了!我們來看一下操作:
int main()
{
//1.創建地圖并初始化
char showMap[MAX_ROW][MAX_COL] = { 0 };
char mineMap[MAX_ROW][MAX_COL] = { 0 };
init(showMap, mineMap);
while (1)
{
//2.列印地圖
Print(showMap);
// 3. 玩家輸入坐標
int row = 0;
int col = 0;
printf("請輸入坐標(row, col):");
scanf("%d %d", &row, &col); // 玩家會不會輸錯(超出范圍 或者 輸入的位置已經翻開過了)? 所以我們接下來就要進行合法性判定
//進行合法性判定
if (row<0 || row>MAX_ROW || col<0 || col>MAX_COL)
{
printf("您輸入的坐標有誤!請重新輸入:\n");
continue;
}
if (showMap[MAX_ROW][MAX_COL] != '*')
{
printf("您輸入的坐標已經翻開啦,請重新輸入:\n");
continue;
}
}
return 0;
}
第四步:判定是否踩雷~
這部很簡單,用if條件句判斷一下當前的mineMap[row][col]是不是1就好了;
//4.判定是否踩雷了
if (mineMap[row][col] == '1')
{
printf("您踩雷了,再接再厲哦!\n");
break;
}
第五步:更新showMap,再翻開位置顯示周圍有多少個地雷~
這里我們遇到的問題是:
1.這個位置的周圍是什么范圍呢? 通過掃雷游戲我們知道是該位置的周圍8個格子
2.那周圍的位置如何表示呢?
3.那這個位置如果出現在地圖的邊線和角落呢?是不是周圍就有位置越界了?
我把周圍位置的坐標表示在下面這個圖上:

是不是一目了然呢? 我們還得進行條件判斷,來看下面這個更新函式:
void update(char showMap[MAX_ROW][MAX_COL],
char mineMap[MAX_ROW][MAX_COL], int row, int col)
{
//定義一下雷數
int count = 0;
//這里我們需要作八個判斷,這樣顯然很難受,我們只需要一個回圈判定,遍歷[row][col]周圍的八個位置即可
for (int r = row - 1; r <= row + 1; r++)
{
for (int c = col - 1; c <= col + 1; c++)
{
if (r == row&&c == col)
{
//此時[r][c]在[row][col]的位置,不用判斷,直接下次回圈
continue;
}
if (r<0 || r>MAX_ROW || c<0 || c>MAX_COL)
{
//此時r,c坐標超出陣列范圍,越界了就直接跳過了
continue;
}
if (mineMap[r][c] == '1')
{
//r,c位置的mineMap是地雷,count就加,回圈結束,把count的值傳給showMap[row][col]即可;
count++;
}
}
}
//如果寫成 showMap[row][col] ==count; 假社此時count=2,當前 row, col 位置的元素就被設定成了 ASCII 值為 2 的“字符”,而不是 2
//但ASCII碼表中,ASCII 值為 n 的“字符”和 n剛好相差48個位元組,而字符‘0’剛好是48個位元組
//我們就可以通過 '0'+ n 得到 n
//但注意這僅僅是c語言里的操作,其他的主流編程語言中, 一般是不允許 字符型別 和 整數型別 進行混合運算的
showMap[row][col] = '0' + count;
}
第六步:判定玩家是否翻開了所有的位置~ 如果沒有翻完,就回到第二部的流程
那我們如何判定呢?
最簡單粗暴的方式就是,設定一個計數器,每翻一次記錄一下,
我們總共有9*9=81個格子,十個地雷,是不是計數器到達71的時候就只剩下地雷沒有被翻開了,這時候游戲勝利!
int main()
{
while (1)
{
//6.進行游戲勝利的判定
opendCount++;
if (opendCount == MAX_ROW*MAX_COL-MINE_COUNT)
{
printf("恭喜你獲勝了,真厲害!\n");
break;
}
}
return 0;
}
完整代碼
那所有的步驟我們都做完了,來整理一下代碼吧,可供娛樂哦!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define _CRT_SECURE_NO_WARNINGS
#define MAX_ROW 9
#define MAX_COL 9 //這里設定宏的目的是避免魔幻數字的出現,讓代碼功能更加清晰,改動更加方便
#define MINE_COUNT 10
void init(char showMap[MAX_ROW][MAX_COL], char mineMap[MAX_ROW][MAX_COL])
{
for (int row = 0; row < MAX_ROW; row++) //除了回圈賦值我們還可以使用庫函式memset(ar,n,size)對陣列的全部變數進行賦值
{
for (int col = 0; col < MAX_COL; col++)
{
showMap[row][col] = '*'; //memset(showMap, '*', MAX_ROW * MAX_COL);
}
}
for (int row = 0; row < MAX_ROW; row++)
{
for (int col = 0; col < MAX_COL; col++)
{
mineMap[row][col] = '0'; //memset(mineMap, '0', MAX_ROW * MAX_COL);
}
}
//隨機產生十個位置作為地雷
//先設定亂數種子
srand((unsigned int)time(0));
int mineCount = 0;
while (mineCount<MINE_COUNT)
{
int row = rand() % MAX_ROW;
int col = rand() % MAX_COL; //這里又有可能出現問題,如果這十個亂數重復呢?十個地雷位置必須是不同的,那我們就有下面的操作
//判斷一下當前的位置是否已經有雷了
if (mineMap[row][col] == '1')
{
continue;
}
mineMap[row][col] = '1';
mineCount++;
}
}
//希望這一個可以有同時列印兩種地圖的功能
//取決于實參填啥
void Print(char theMap[MAX_ROW][MAX_COL])
{
for (int row = 0; row < MAX_ROW; row++)
{
for (int col = 0; col < MAX_COL; col++)
{
printf("%c ", theMap[row][col]);
}
printf("\n");
}
}
void update(char showMap[MAX_ROW][MAX_COL],
char mineMap[MAX_ROW][MAX_COL], int row, int col)
{
//定義一下雷數
int count = 0;
//這里我們需要作八個判斷,這樣顯然很難受,我們只需要一個回圈判定,遍歷[row][col]周圍的八個位置即可
for (int r = row - 1; r <= row + 1; r++)
{
for (int c = col - 1; c <= col + 1; c++)
{
if (r == row&&c == col)
{
//此時[r][c]在[row][col]的位置,不用判斷,直接下次回圈
continue;
}
if (r<0 || r>MAX_ROW || c<0 || c>MAX_COL)
{
//此時r,c坐標超出陣列范圍,越界了就直接跳過了
continue;
}
if (mineMap[r][c] == '1')
{
//r,c位置的mineMap是地雷,count就加,回圈結束,把count的值傳給showMap[row][col]即可;
count++;
}
}
}
//如果寫成 showMap[row][col] ==count; 假社此時count=2,當前 row, col 位置的元素就被設定成了 ASCII 值為 2 的“字符”,而不是 2
//但ASCII碼表中,ASCII 值為 n 的“字符”和 n剛好相差48個位元組,而字符‘0’剛好是48個位元組
//我們就可以通過 '0'+ n 得到 n
//但注意這僅僅是c語言里的操作,其他的主流編程語言中, 一般是不允許 字符型別 和 整數型別 進行混合運算的
showMap[row][col] = '0' + count;
}
int main()
{
int opendCount = 0;
//1.創建地圖并初始化
char showMap[MAX_ROW][MAX_COL] = { 0 };
char mineMap[MAX_ROW][MAX_COL] = { 0 };
init(showMap, mineMap);
while (1)
{
//2.列印地圖
Print(showMap);
// 3. 玩家輸入坐標
int row = 0;
int col = 0;
printf("請輸入坐標(row, col):");
scanf("%d %d", &row, &col); // 玩家會不會輸錯(超出范圍 或者 輸入的位置已經翻開過了)? 所以我們接下來就要進行合法性判定
//進行合法性判定
if (row<0 || row>MAX_ROW || col<0 || col>MAX_COL)
{
printf("您輸入的坐標有誤!請重新輸入:\n");
continue;
}
if (showMap[row][col] != '*')
{
printf("您輸入的坐標已經翻開啦,請重新輸入:\n");
continue;
}
//4.判定是否踩雷了
if (mineMap[row][col] == '1')
{
printf("您踩雷了,再接再厲哦!\n");
break;
}
//5.那如果沒有踩雷呢? 是不是要更新showMap讓他在翻開位置顯示出周圍有多少個雷
//我們來做一個update()函式; 那都要什么引數呢? 我們要顯示,所以showMap是必須的,但是我們還得知道有多少個雷,那么mineMap也是要的,行列就不用多說了
update(showMap,mineMap, row, col);
//清一下屏,讓螢屏顯示清楚一些
system("cls");
//其實到這里我們已經可以開始玩了,但是不要忘記一個重要的步驟,
//那就是判斷游戲的勝利,也就是判定是否翻開了所有的不是雷的位置
//6.進行游戲勝利的判定
opendCount++;
if (opendCount == MAX_ROW*MAX_COL-MINE_COUNT)
{
printf("恭喜你獲勝了,真厲害!\n");
break;
}
}
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/280986.html
標籤:其他
上一篇:華為機試HJ17
