??窮且益堅,不墜青云之志,
??上篇博客實作了雞肋的掃雷小游戲,不能實作雷盤(場)的展開,體驗不是很好,這篇博客對其進行優化了,實作了展開功能,
文章目錄
- 一、新的頭檔案
- 二、游戲框架
- 三、功能函式
- 1、未更改的部分
- 2、更改后的FindMine函式
- 3、GetCount
- 4、OpenMine
- 5、Win
- 四、完整程式
- 五、游戲測驗
- 六、寫在后面
一、新的頭檔案
??增加了一些新的函式宣告,
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 5
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void test(void);
void menu(void);
void game(void);
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 board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int GetCount(char mine[ROWS][COLS], int x, int y);
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
int Win(char show[ROWS][COLS], int row, int col);
二、游戲框架
??游戲基本實作框架沒有任何改動,
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
int main(void)
{
test();
return 0;
}
//游戲執行測驗框架
void test (void)
{
int input = 0;
do {
menu();//系統列印游戲選單,呼叫menu函式
printf("請選擇:>>>");
scanf("%d", &input);
switch (input)//switch開關陳述句,作用顯而易見
{
case 1:
game();//呼叫game函式
break;
case 2:
printf("退出游戲\n");
input = 0;
break;
default:
printf("輸入值無效\n");
break;
}
} while (input);
}
//選單列印函式
void menu(void)
{
printf("************************************\n");
printf("*****1.開始游戲 0.退出游戲******\n");
printf("************************************\n");
}
//游戲主函式
void game(void)
{
//第一步,創建兩個陣列,一個是布盤陣列,一個是用戶陣列,兩個陣列大小相同,屬于疊加的雙層結構,
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//第二步,初始化
InitBoard(mine, ROWS, COLS, '0');//初始化兩個陣列,陣列一開始需要有能分辨的初始化元素,
InitBoard(show, ROWS, COLS, '*');
//第三步,布置雷
SetMine(mine, ROW, COL);
DisplayBoard(mine, ROW, COL);//測驗需要,玩家操作時注釋掉
DisplayBoard(show, ROW, COL);
//第四步,掃雷
FindMine(mine, show, ROW, COL);
}
三、功能函式
??功能函式在保留了上篇博客中提到的三個函式InitBoard、DisplayBoard、SetMine外,又增加了三個新的函式GetCount 、OpenMine、 Win,并對FindMine函式做出了更改,
??先把三個未更改的函式放在這里,再介紹FindMine函式的更改,從而引出三個新增函式吧,
1、未更改的部分
#include "game.h"
//初始化函式
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;//讓布雷陣列全部元素為字符'0',玩家陣列全部為字符'*'
}
}
}
//列印雷盤
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 board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//雷的個數
srand((unsigned int)time(NULL));//使用系統時間作為隨機值的種子
while (count)//控制雷個數
{
int x = rand() % row + 1;//x會在1-9之間隨機生成一個值
int y = rand() % col + 1;//y會在1-9之間隨機生成一個值
if (board[x][y] == '0')//避免重復放雷
{
board[x][y] = '1';//雷的放置用字符'1'表示
count--;
}
}
}
2、更改后的FindMine函式
??這個主要的更改邏輯是這樣,目的不是要實作當玩家所選坐標周圍的八個坐標不存在雷也就是周圍八個坐標的個數和為0的時候對雷盤進行展開嗎?
??我們要把計算玩家周圍雷的總數的這一個演算法單獨封裝成一個單獨的函式,從而引出
if(玩家周圍雷的總數為0)
對雷盤進行展開,展開的邏輯是這樣,對周圍的八個坐標進行判斷是否滿足其所在周圍的八個坐標雷的總數為0,如果是的,再次進行展開,如果不是就讓此處坐標格子顯示其周圍雷的個數,有點繞,不知道講明白沒,
else if(玩家周圍雷的總數不為0)
那么就進入不了遞回展開函式的判斷條件,直接讓此處的坐標格子顯示周圍雷的個數,
??勝負判斷,如果玩家棋盤上未揭開的’*'的個數是雷的總數,那么玩家勝利,退出回圈,
//掃雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
//row和col傳遞過來的是ROW和COL,這里用x,y來控制陣列下標,
int x = 0;
int y = 0;
while (1)
{
printf("請輸入排查雷的坐標:>>>");
scanf("%d %d", &x, &y);
//玩家輸入坐標后,面臨兩種情況,第一種,坐標合法,第二種坐標非法,
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//玩家輸入的坐標埋放了雷,那么對不起,列印雷分布情況,游戲結束
if (mine[x][y] == '1')
{
printf("\n很不幸,您被炸死了!!!\n\n雷的分布情況如下:\n");
DisplayBoard(mine, row, col);
printf("-----------游戲結束----------\n\n");
break;
}
//如果玩家所選坐標安全,那么面臨有兩種情況,
//情況一:玩家周圍有雷,直接顯示周圍雷的個數,
//情況二:玩家周圍八個坐標均沒雷,那么進行遞回展開,向四面八方展開,直至遇到情況一,
else if (mine[x][y] == '0')
{
show[x][y] = GetCount(mine, x, y) + '0';//先將此處的賦值為此處坐標周圍雷的個數,如果是0進入展開,
//其實這個放到后面也可以,但是會有一些差別,放在后面雷盤上會出現周圍有0個雷的字樣,放在前面,0會被下一層的空格頂掉,
OpenMine(mine, show, x, y);//情況一周圍雷的個數是0展開直到每個坐標都遇到了周圍8個坐標雷的個數不再是0,
//情況二周圍雷的個數不是0,不滿足展開函式條件跳過此函式,
DisplayBoard(show, row, col);//更新此時的雷盤,
if (Win(show, ROW, COL) == EASY_COUNT)//判斷此時雷盤剩余的*數目,如果等于雷數,則玩家排雷成功,
{
printf("恭喜你成功排雷,太棒了,太棒了,送你一朵小紅花!!!\n");
printf("雷的分布情況如下:\n");
DisplayBoard(mine, row, col);
break;
}
}
}
//坐標非法
else
{
printf("非法輸入!!!\n");
}
}
}
3、GetCount
//計算周圍八個格子中雷的個數
int GetCount(char mine[ROWS][COLS], int x, int y)
{
int count= mine[x - 1][y] + mine[x - 1][y - 1] +
mine[x][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';
return count;
}
4、OpenMine
//遞回展開(套娃行為,心累哇...)
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (GetCount(mine, x, y) == 0)
{
show[x][y] = ' ';//先將周圍的8個格子全變成空格,在進行每個格子的判斷,遞回,好好理解下
if ((x - 1) > 0 && (y - 1) > 0 && (show[x - 1][y - 1] == '*'))
OpenMine(mine, show, x - 1, y - 1);
if ((x - 1) > 0 && (y) > 0 && (show[x - 1][y] == '*'))
OpenMine(mine, show, x - 1, y);
if ((x - 1) > 0 && (y + 1) > 0 && (show[x - 1][y + 1] == '*'))
OpenMine(mine, show, x - 1, y + 1);
if ((x) > 0 && (y - 1) > 0 && (show[x][y - 1] == '*'))
OpenMine(mine, show, x, y - 1);
if ((x) > 0 && (y + 1) > 0 && (show[x][y + 1] == '*'))
OpenMine(mine, show, x, y + 1);
if ((x + 1) > 0 && (y - 1) > 0 && (show[x + 1][y - 1] == '*'))
OpenMine(mine, show, x + 1, y - 1);
if ((x + 1) > 0 && (y) > 0 && (show[x + 1][y] == '*'))
OpenMine(mine, show, x + 1, y);
if ((x + 1) > 0 && (y + 1) > 0 && (show[x + 1][y + 1] == '*'))
OpenMine(mine, show, x + 1, y + 1);
}
else //如果周圍的8個格子雷的個數不再為0,就顯示周圍雷的個數,
{
show[x][y] = GetCount(mine, x, y) + '0';
}
}
5、Win
//勝利計算
//玩家棋盤上符號*個數為雷數就掃雷成功了
int Win(char show[ROWS][COLS], int row, int col)
{
int i = 0, j = 0, count = 0;
for (i = 1; i <= row; i++)//注意!注意!注意!這個地方i和j需要從第二行第二列開始記錄到ROW和COL停止
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
四、完整程式
#define _CRT_SECURE_NO_WARNINGS 1
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 5
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void test(void);
void menu(void);
void game(void);
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 board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int GetCount(char mine[ROWS][COLS], int x, int y);
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
int Win(char show[ROWS][COLS], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
int main(void)
{
test();
return 0;
}
//游戲執行測驗框架
void test (void)
{
int input = 0;
do {
menu();//系統列印游戲選單,呼叫menu函式
printf("請選擇:>>>");
scanf("%d", &input);
switch (input)//switch開關陳述句,作用顯而易見
{
case 1:
game();//呼叫game函式
break;
case 2:
printf("退出游戲\n");
input = 0;
break;
default:
printf("輸入值無效\n");
break;
}
} while (input);
}
//選單列印函式
void menu(void)
{
printf("************************************\n");
printf("*****1.開始游戲 0.退出游戲******\n");
printf("************************************\n");
}
//游戲主函式
void game(void)
{
//第一步,創建兩個陣列,一個是布盤陣列,一個是用戶陣列,兩個陣列大小相同,屬于疊加的雙層結構,
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//第二步,初始化
InitBoard(mine, ROWS, COLS, '0');//初始化兩個陣列,陣列一開始需要有能分辨的初始化元素,
InitBoard(show, ROWS, COLS, '*');
//第三步,布置雷
SetMine(mine, ROW, COL);
DisplayBoard(mine, ROW, COL);//測驗需要,玩家操作時注釋掉
DisplayBoard(show, ROW, COL);
//第四步,掃雷
FindMine(mine, show, ROW, COL);
}
#include "game.h"
//初始化函式
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;//讓布雷陣列全部元素為字符'0',玩家陣列全部為字符'*'
}
}
}
//列印雷盤
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 board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//雷的個數
srand((unsigned int)time(NULL));//使用系統時間作為隨機值的種子
while (count)//控制雷個數
{
int x = rand() % row + 1;//x會在1-9之間隨機生成一個值
int y = rand() % col + 1;//y會在1-9之間隨機生成一個值
if (board[x][y] == '0')//避免重復放雷
{
board[x][y] = '1';//雷的放置用字符'1'表示
count--;
}
}
}
//掃雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
//row和col傳遞過來的是ROW和COL,這里用x,y來控制陣列下標,
int x = 0;
int y = 0;
while (1)
{
printf("請輸入排查雷的坐標:>>>");
scanf("%d %d", &x, &y);
//玩家輸入坐標后,面臨兩種情況,第一種,坐標合法,第二種坐標非法,
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//玩家輸入的坐標埋放了雷,那么對不起,列印雷分布情況,游戲結束
if (mine[x][y] == '1')
{
printf("\n很不幸,您被炸死了!!!\n\n雷的分布情況如下:\n");
DisplayBoard(mine, row, col);
printf("-----------游戲結束----------\n\n");
break;
}
//如果玩家所選坐標安全,那么面臨有兩種情況,
//情況一:玩家周圍有雷,直接顯示周圍雷的個數,
//情況二:玩家周圍八個坐標均沒雷,那么進行遞回展開,向四面八方展開,直至遇到情況一,
else if (mine[x][y] == '0')
{
show[x][y] = GetCount(mine, x, y) + '0';
OpenMine(mine, show, x, y);//情況一不滿足展開函式條件跳過此函式,情況二展開直到每個坐標都遇到了情況一,
DisplayBoard(show, row, col);//更新此時的雷盤,
if (Win(show, ROW, COL) == EASY_COUNT)//判斷此時雷盤剩余的*數目,如果等于雷數,則玩家排雷成功,
{
printf("恭喜你成功排雷,太棒了,太棒了,送你一朵小紅花!!!\n");
printf("雷的分布情況如下:\n");
DisplayBoard(mine, row, col);
break;
}
}
}
//坐標非法
else
{
printf("非法輸入!!!\n");
}
}
}
//計算周圍八個格子中雷的個數
int GetCount(char mine[ROWS][COLS], int x, int y)
{
int count= mine[x - 1][y] + mine[x - 1][y - 1] +
mine[x][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';
return count;
}
//遞回展開(套娃行為,心累哇...)
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (GetCount(mine, x, y) == 0)
{
show[x][y] = ' ';
if ((x - 1) > 0 && (y - 1) > 0 && (show[x - 1][y - 1] == '*'))
OpenMine(mine, show, x - 1, y - 1);
if ((x - 1) > 0 && (y) > 0 && (show[x - 1][y] == '*'))
OpenMine(mine, show, x - 1, y);
if ((x - 1) > 0 && (y + 1) > 0 && (show[x - 1][y + 1] == '*'))
OpenMine(mine, show, x - 1, y + 1);
if ((x) > 0 && (y - 1) > 0 && (show[x][y - 1] == '*'))
OpenMine(mine, show, x, y - 1);
if ((x) > 0 && (y + 1) > 0 && (show[x][y + 1] == '*'))
OpenMine(mine, show, x, y + 1);
if ((x + 1) > 0 && (y - 1) > 0 && (show[x + 1][y - 1] == '*'))
OpenMine(mine, show, x + 1, y - 1);
if ((x + 1) > 0 && (y) > 0 && (show[x + 1][y] == '*'))
OpenMine(mine, show, x + 1, y);
if ((x + 1) > 0 && (y + 1) > 0 && (show[x + 1][y + 1] == '*'))
OpenMine(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = GetCount(mine, x, y) + '0';
}
}
//勝利計算
//玩家棋盤上符號*個數為雷數就掃雷成功了
int Win(char show[ROWS][COLS], int row, int col)
{
int i = 0, j = 0, count = 0;
for (i = 1; i <= row; i++)//注意!注意!注意!這個地方i和j需要從第二行第二列開始記錄到ROW和COL停止
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
五、游戲測驗
??將EASY_COUNT改成5進行測驗,
************************************
*****1.開始游戲 0.退出游戲******
************************************
請選擇:>>>1
0 1 2 3 4 5 6 7 8 9
1 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 1 0
4 0 0 0 0 0 0 0 1 0
5 0 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0 0
7 0 0 0 0 0 0 0 0 0
8 0 0 0 1 0 0 1 0 0
9 0 0 0 0 0 1 0 0 0
0 1 2 3 4 5 6 7 8 9
1 * * * * * * * * *
2 * * * * * * * * *
3 * * * * * * * * *
4 * * * * * * * * *
5 * * * * * * * * *
6 * * * * * * * * *
7 * * * * * * * * *
8 * * * * * * * * *
9 * * * * * * * * *
請輸入排查雷的坐標:>>>2 2
0 1 2 3 4 5 6 7 8 9
1
2 1 1 1
3 2 * 2
4 2 * 2
5 1 1 1
6
7 1 1 1 1 1 1
8 1 * * * * 1
9 1 * * * * 1
請輸入排查雷的坐標:>>>9 4
0 1 2 3 4 5 6 7 8 9
1
2 1 1 1
3 2 * 2
4 2 * 2
5 1 1 1
6
7 1 1 1 1 1 1
8 1 * * * * 1
9 1 1 * * * 1
請輸入排查雷的坐標:>>>9 5
0 1 2 3 4 5 6 7 8 9
1
2 1 1 1
3 2 * 2
4 2 * 2
5 1 1 1
6
7 1 1 1 1 1 1
8 1 * * * * 1
9 1 1 2 * * 1
請輸入排查雷的坐標:>>>8 5
0 1 2 3 4 5 6 7 8 9
1
2 1 1 1
3 2 * 2
4 2 * 2
5 1 1 1
6
7 1 1 1 1 1 1
8 1 * 2 * * 1
9 1 1 2 * * 1
請輸入排查雷的坐標:>>>8 6
0 1 2 3 4 5 6 7 8 9
1
2 1 1 1
3 2 * 2
4 2 * 2
5 1 1 1
6
7 1 1 1 1 1 1
8 1 * 2 2 * 1
9 1 1 2 * * 1
請輸入排查雷的坐標:>>>9 7
0 1 2 3 4 5 6 7 8 9
1
2 1 1 1
3 2 * 2
4 2 * 2
5 1 1 1
6
7 1 1 1 1 1 1
8 1 * 2 2 * 1
9 1 1 2 * 2 1
恭喜你成功排雷,太棒了,太棒了,送你一朵小紅花!!!
0 1 2 3 4 5 6 7 8 9
1 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 1 0
4 0 0 0 0 0 0 0 1 0
5 0 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0 0
7 0 0 0 0 0 0 0 0 0
8 0 0 0 1 0 0 1 0 0
9 0 0 0 0 0 1 0 0 0
************************************
*****1.開始游戲 0.退出游戲******
************************************
請選擇:>>>1
0 1 2 3 4 5 6 7 8 9
1 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 1 0 0
6 0 0 1 0 1 0 0 0 0
7 0 0 1 0 0 0 1 0 0
8 0 0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9
1 * * * * * * * * *
2 * * * * * * * * *
3 * * * * * * * * *
4 * * * * * * * * *
5 * * * * * * * * *
6 * * * * * * * * *
7 * * * * * * * * *
8 * * * * * * * * *
9 * * * * * * * * *
請輸入排查雷的坐標:>>>6 3
很不幸,您被炸死了!!!
雷的分布情況如下:
0 1 2 3 4 5 6 7 8 9
1 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 1 0 0
6 0 0 1 0 1 0 0 0 0
7 0 0 1 0 0 0 1 0 0
8 0 0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0 0 0
-----------游戲結束----------
************************************
*****1.開始游戲 0.退出游戲******
************************************
請選擇:>>>
六、寫在后面
??emmmmm,整體來說,掃雷小游戲游戲算是基本實作了吧,寫了好久,參考了好些資料,對于我這個新手來說太難了,艱難的寫完所有程式,測驗了應該沒問題,一些演算法很細節,我也是除錯了好久,,,,
??敬請各位大佬檢閱、批評和指正,希望可以獎勵我個一鍵三連以此鼓勵,非常感謝!!!非常感謝!!!非常感謝!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/259819.html
標籤:其他
上一篇:資料結構優化DP
