C語言實(shí)現(xiàn)掃雷游戲詳解
本文實(shí)例為大家分享了C語言實(shí)現(xiàn)掃雷游戲的具體代碼,供大家參考,具體內(nèi)容如下
功能設(shè)計(jì)
掃雷大家應(yīng)該都玩過,這是一個(gè)十分經(jīng)典的游戲,今天我將給大家講解如何用C語言實(shí)現(xiàn)掃雷,我的這個(gè)掃雷有如下幾個(gè)功能:
1、顯示該點(diǎn)周圍雷的個(gè)數(shù)
2、第一次下子,不炸死
3、坐標(biāo)周圍沒雷,可以實(shí)現(xiàn)展開
4、游戲結(jié)束后展示玩家用時(shí)
效果展示
話不多說,先附上效果圖:
設(shè)計(jì)思路
我們只要輸入坐標(biāo)就可以掃雷了,是不是很有趣?
其實(shí)要想實(shí)現(xiàn)這也不難,我們要用幾個(gè)算法模塊來模擬游戲規(guī)則,需要用函數(shù)來調(diào)用各個(gè)模塊使游戲跑起來。
那么第一步我們就要構(gòu)思一個(gè)棋盤,看見上面第一張圖沒,在開始游戲的界面我打印了兩個(gè)棋盤,有0和1的棋盤是給我們設(shè)計(jì)者看的,它可以顯示出當(dāng)前雷的真實(shí)分布,這有利于我們測試代碼的正確性,而全是 * 的棋盤是給玩家掃雷用的。
那我們就需要用二維數(shù)組來打印兩個(gè)棋盤,假如我們要打印10X10的棋盤,那我們的二維數(shù)組元素也要為10X10個(gè)嗎?,不能,因?yàn)槲覀冊谠O(shè)計(jì)算法時(shí)需要統(tǒng)計(jì)坐標(biāo)周圍8個(gè)方位雷的個(gè)數(shù),假如要統(tǒng)計(jì)邊界坐標(biāo)周圍雷的個(gè)數(shù),那么就會(huì)有數(shù)組越界的問題,那我們就要在10X10的邊界多上一圈元素,也就要定義12X12的數(shù)組元素,這些元素我們不要打印出來,心里有數(shù)就行,如下圖:
#define row 12 #define col 12 show_mine[row][col];//玩家數(shù)組 real_mine[row][col];//設(shè)計(jì)者數(shù)組
我們在一個(gè)項(xiàng)目里建立三個(gè)源文件,如下:
1、我們打印設(shè)計(jì)者棋盤要用數(shù)組real_mine,打印玩家棋盤要用數(shù)組show_mine,兩個(gè)數(shù)組在開始必須要初始化,在設(shè)計(jì)者棋盤中字符0代表不是雷,字符1代表雷,先初始化兩個(gè)數(shù)組
代碼如下:
void init_mine()//初始化兩個(gè)雷陣 { int i = 0; int j = 0; for (int i = 0; i < row; i++) { for (j = 0; j < col; j++) { show_mine[i][j] = '*'; real_mine[i][j] = '0'; } } }
2、打印兩個(gè)雷陣(不要忘了打印橫豎序號(hào)以便確定坐標(biāo))
void print_player()//打印玩家棋盤 { int i = 0; int j = 0; printf("0 "); for (i = 1; i <row-1; i++) { printf("%d ", i);//打印橫標(biāo)(0--10) } printf("\n"); for (i = 1; i <row-2; i++)//打印豎標(biāo)(1--10) { printf("%d ", i); for (j = 1; j < col-1; j++) { printf("%c ", show_mine[i][j]);//玩家棋盤數(shù)組 } printf("\n"); } printf("10 ");//開始打印最后一行 for (i = 1; i < row-1; i++) { printf("%c ", show_mine[10][i]); } printf("\n"); } void print_mine()//打印設(shè)計(jì)者棋盤 { int i = 0; int j = 0; printf("0 "); for (i = 1; i <row - 1; i++) { printf("%d ", i);//打印橫標(biāo)(0--10) } printf("\n"); for (i = 1; i <row - 2; i++)//打印豎標(biāo)(1--10) { printf("%d ", i); for (j = 1; j < col - 1; j++) { printf("%c ", real_mine[i][j]); } printf("\n"); } printf("10 ");//開始打印最后一行 for (i = 1; i < row - 1; i++) { printf("%c ", real_mine[10][i]); } printf("\n"); }
3、我們在每一次玩的時(shí)候設(shè)計(jì)者棋盤中的雷分布都必須不相同,使用隨機(jī)數(shù)生成橫豎坐標(biāo)確定布雷坐標(biāo),代碼如下:
void set_mine()//給設(shè)計(jì)者棋盤布雷 { int x = 0; int y = 0; int count = COUNT;//雷總數(shù) while (count)//雷布完后跳出循環(huán) { int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 if (real_mine[x][y] == '0')//找不是雷的地方布雷 { real_mine[x][y] = '1'; count--; } } }
4、為了統(tǒng)計(jì)當(dāng)前坐標(biāo)周圍雷的個(gè)數(shù),定義一個(gè)函數(shù)實(shí)現(xiàn):
int count_mine(int x, int y)//檢測周圍8個(gè)區(qū)域雷的個(gè)數(shù) { int count = 0; if (real_mine[x - 1][y - 1] == '1') count++; if (real_mine[x - 1][y] == '1') count++; if (real_mine[x - 1][y + 1] == '1') count++; if (real_mine[x][y - 1] == '1') count++; if (real_mine[x][y + 1] == '1') count++; if (real_mine[x + 1][y - 1] == '1') count++; if (real_mine[x + 1][y] == '1') count++; if (real_mine[x + 1][y + 1] == '1') count++; return count; }
5、為了確保第一次不被雷炸死,我們需要定義個(gè)函數(shù)來實(shí)現(xiàn),如果第一次選到雷就將這顆雷放在其他不是雷的地方,代碼如下:
void safe_mine()//避免第一次炸死 { int x = 0; int y = 0; char ch = 0; int count = 0; int ret = 1; printf("輸入坐標(biāo)掃雷\n"); while (1) { scanf("%d%d", &x, &y);//只能輸入1到10,輸入錯(cuò)誤重新輸入 if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤 { if (real_mine[x][y] == '1')//第一次踩到雷后補(bǔ)救 { real_mine[x][y] = '0'; char ch = count_mine(x, y); show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值 open_mine(x, y); while (ret)//在其余有空的地方設(shè)置一個(gè)雷 { int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 if (real_mine[x][y] == '0')//找不是雷的地方布雷 { real_mine[x][y] = '1'; ret--; break; } }break;//跳出此函數(shù) } if (real_mine[x][y] == '0') { char ch = count_mine(x, y); show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值 open_mine(x, y); break; } } else//坐標(biāo)錯(cuò)誤 { printf("輸入錯(cuò)誤重新輸入\n"); } } }
6、為了實(shí)現(xiàn)展開功能,需要展開函數(shù)模塊(展開的坐標(biāo)還要顯示其坐標(biāo)周圍的雷數(shù)),如下:
void open_mine(int x, int y)//坐標(biāo)周圍展開函數(shù) { if (real_mine[x - 1][y - 1]== '0') { show_mine[x - 1][y - 1] = count_mine(x - 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x - 1][y] == '0') { show_mine[x - 1][y] = count_mine(x - 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x - 1][y + 1] == '0') { show_mine[x - 1][y + 1] = count_mine(x - 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x][y - 1] == '0') { show_mine[x][y - 1] = count_mine(x, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x][y + 1] == '0') { show_mine[x][y + 1] = count_mine(x, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x + 1][y - 1] == '0') { show_mine[x + 1][y - 1] = count_mine(x + 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x + 1][y] == '0') { show_mine[x + 1][y] = count_mine(x + 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x + 1][y + 1] == '0') { show_mine[x + 1][y + 1] = count_mine(x + 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } }
7、掃雷函數(shù)是一個(gè)重要的模塊,代碼如下:
int sweep_mine()//掃雷函數(shù),踩到雷返回1,沒有踩到雷返回0 { int x = 0; int y = 0; int count = 0; printf("輸入坐標(biāo)掃雷\n"); scanf("%d%d", &x, &y);//只能輸入1到10 if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤,輸入錯(cuò)誤重新輸入 { if (real_mine[x][y] == '0')//沒踩到雷 { char ch = count_mine(x,y); show_mine[x][y] = ch+'0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值 open_mine(x, y); if (count_show_mine() == COUNT)//判斷剩余未知區(qū)域的個(gè)數(shù),個(gè)數(shù)為雷數(shù)時(shí)玩家贏 { print_mine(); printf("玩家贏!\n\n"); return 0; } } else if (real_mine[x][y]=='1')//踩到雷 { return 1; } } else { printf("輸入錯(cuò)誤重新輸入\n"); } return 0;//沒踩到雷 }
到最后需要確定游戲勝利的條件,我們要統(tǒng)計(jì)當(dāng)前狀態(tài)玩家棋盤中顯示的剩余 * 的個(gè)數(shù),如果個(gè)數(shù)等于總雷數(shù)時(shí)說明掃雷完成,游戲勝利,定義一個(gè)函數(shù)實(shí)現(xiàn):
int count_show_mine()//判斷剩余未知區(qū)域的個(gè)數(shù),個(gè)數(shù)為雷數(shù)時(shí)玩家贏 { int count = 0; int i = 0; int j = 0; for (i = 1; i <= row - 2; i++) { for (j = 1; j <= col - 2; j++) { if (show_mine[i][j] == '*') { count++; } } } return count; }
我們將以上函數(shù)的定義放在 game.c 文件中
以上就是我們要實(shí)現(xiàn)掃雷的模塊,要想把這些模塊整合起來運(yùn)行,就需要一個(gè)游戲執(zhí)行函數(shù)來調(diào)用這些模塊,定義個(gè)game()函數(shù)實(shí)現(xiàn),代碼如下:
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" double start, finish; void game() { int ret = 0; init_mine();//初始化玩家棋盤和設(shè)計(jì)者棋盤 set_mine();//給設(shè)計(jì)者棋盤布雷 print_mine();//打印設(shè)計(jì)者棋盤(可不打?。? printf("\n"); print_player();//打印玩家棋盤 start = clock(); safe_mine();//避免第一次被炸死 if (count_show_mine() == COUNT)//一步就贏的情況 { print_mine(); printf("玩家贏!\n\n"); return ; }print_player();打印玩家棋盤 while (1)//循環(huán)掃雷 { int ret=sweep_mine();//掃雷,踩到雷返回1,沒有踩到雷返回0 if (count_show_mine() == COUNT)//若玩家棋盤的'*'個(gè)數(shù)為雷數(shù)時(shí),掃雷完成,游戲勝利 { print_mine();//打印設(shè)計(jì)者棋盤 printf("玩家贏!\n\n"); finish = clock();//取結(jié)束時(shí)間 printf("用時(shí)%d 秒\n",(int) (finish - start) / CLOCKS_PER_SEC); break; } if (ret)//判斷是否踩到雷 { printf("被雷炸死\t"); finish = clock();//取結(jié)束時(shí)間 printf("用時(shí)%d 秒\n", (int)(finish - start) / CLOCKS_PER_SEC); print_mine();//打印設(shè)計(jì)者雷陣查看雷的分布 break; }print_player();//打印玩家棋盤 } } int main() { srand((unsigned int)time(NULL));//產(chǎn)生隨機(jī)數(shù)生成器 int input = 0; muen();//菜單 do { scanf("%d", &input); switch (input) { case 1:game(); break; case 0:exit(1);//退出游戲 break; default: printf("輸入錯(cuò)誤,重新輸入\n"); break; } muen(); printf("contiue?\n"); } while (1);//循環(huán)玩游戲 system("pause"); return 0; }
在頭文件game.h中聲明各種函數(shù):
game.h
#ifndef __GAME_H__ #define __GAME__H__ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> #define row 12 #define col 12 #define COUNT 10//棋盤中雷的總數(shù) extern char show_mine[row][col];//展示數(shù)組 extern char real_mine[row][col];//布雷數(shù)組 void muen();//菜單函數(shù) void init_mine();//初始化數(shù)組函數(shù) void set_mine();//布雷函數(shù) int count_mine();//統(tǒng)計(jì)周圍雷的個(gè)數(shù) void print_player();//打印玩家棋盤 void print_mine();//打印設(shè)計(jì)者棋盤 int sweep_mine();//掃雷函數(shù) void safe_mine();//避免第一次被雷炸死的函數(shù) void open_mine(int x, int y);//展開函數(shù) int count_show_mine(); ///判斷玩家棋盤剩余未知區(qū)域的個(gè)數(shù) #endif //__GAME_H__
將上面這個(gè)函數(shù)放在main.c文件中
以上我們便完成了掃雷的所有C語言代碼,接下來我們測驗(yàn)一下:
1、檢測第一次是否能不被炸死
2、檢測周圍沒雷可以展開
總結(jié)
測試顯示我們的代碼沒有問題,我們總結(jié)一下,這個(gè)程序的難點(diǎn)的就是如何實(shí)現(xiàn)展開和和保證第一次不被炸死,如果你仔細(xì)理解以上的程序,你就會(huì)明白其中的原理,包括為什么要在數(shù)組元素周圍多一圈元素,這些模塊都是游戲的核心,等你掌握了這個(gè)程序,你的邏輯思維能力就會(huì)有很大的提高!
附game.c文件源碼:
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" char show_mine[row][col] = { 0 }; char real_mine[row][col] = { 0 }; void muen() { printf("*******************************\n"); printf("*****1.play 0.exit*******\n"); printf("*******************************\n"); } void init_mine()//初始化兩個(gè)棋盤 { int i = 0; int j = 0; for (int i = 0; i < row; i++) { for (j = 0; j < col; j++) { show_mine[i][j] = '*'; real_mine[i][j] = '0'; } } } void print_player()//打印玩家棋盤 { int i = 0; int j = 0; printf("0 "); for (i = 1; i <row-1; i++) { printf("%d ", i);//打印橫標(biāo)(0--10) } printf("\n"); for (i = 1; i <row-2; i++)//打印豎標(biāo)(1--10) { printf("%d ", i); for (j = 1; j < col-1; j++) { printf("%c ", show_mine[i][j]);//玩家棋盤數(shù)組 } printf("\n"); } printf("10 ");//開始打印最后一行 for (i = 1; i < row-1; i++) { printf("%c ", show_mine[10][i]); } printf("\n"); } void print_mine()//打印設(shè)計(jì)者棋盤 { int i = 0; int j = 0; printf("0 "); for (i = 1; i <row - 1; i++) { printf("%d ", i);//打印橫標(biāo)(0--10) } printf("\n"); for (i = 1; i <row - 2; i++)//打印豎標(biāo)(1--10) { printf("%d ", i); for (j = 1; j < col - 1; j++) { printf("%c ", real_mine[i][j]); } printf("\n"); } printf("10 ");//開始打印最后一行 for (i = 1; i < row - 1; i++) { printf("%c ", real_mine[10][i]); } printf("\n"); } void set_mine()//給設(shè)計(jì)者棋盤布雷 { int x = 0; int y = 0; int count = COUNT;//雷總數(shù) while (count)//雷布完后跳出循環(huán) { int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 if (real_mine[x][y] == '0')//找不是雷的地方布雷 { real_mine[x][y] = '1'; count--; } } } int count_mine(int x, int y)//檢測周圍8個(gè)區(qū)域雷的個(gè)數(shù) { int count = 0; if (real_mine[x - 1][y - 1] == '1') count++; if (real_mine[x - 1][y] == '1') count++; if (real_mine[x - 1][y + 1] == '1') count++; if (real_mine[x][y - 1] == '1') count++; if (real_mine[x][y + 1] == '1') count++; if (real_mine[x + 1][y - 1] == '1') count++; if (real_mine[x + 1][y] == '1') count++; if (real_mine[x + 1][y + 1] == '1') count++; return count; } void safe_mine()//避免第一次炸死 { int x = 0; int y = 0; char ch = 0; int count = 0; int ret = 1; printf("輸入坐標(biāo)掃雷\n"); while (1) { scanf("%d%d", &x, &y);//只能輸入1到10,輸入錯(cuò)誤重新輸入 if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤 { if (real_mine[x][y] == '1')//第一次踩到雷后補(bǔ)救 { real_mine[x][y] = '0'; char ch = count_mine(x, y); show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值 open_mine(x, y); while (ret)//在其余有空的地方設(shè)置一個(gè)雷 { int x = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 int y = rand() % 10 + 1;//產(chǎn)生1到10的隨機(jī)數(shù),在數(shù)組下標(biāo)為1到10的范圍內(nèi)布雷 if (real_mine[x][y] == '0')//找不是雷的地方布雷 { real_mine[x][y] = '1'; ret--; break; } }break;//跳出此函數(shù) } if (real_mine[x][y] == '0') { char ch = count_mine(x, y); show_mine[x][y] = ch + '0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值 open_mine(x, y); break; } } else//坐標(biāo)錯(cuò)誤 { printf("輸入錯(cuò)誤重新輸入\n"); } } } int sweep_mine()//掃雷函數(shù),踩到雷返回1,沒有踩到雷返回0 { int x = 0; int y = 0; int count = 0; printf("輸入坐標(biāo)掃雷\n"); scanf("%d%d", &x, &y);//只能輸入1到10 if ((x >= 1 && x <= 10) && (y >= 1 && y <= 10))//判斷輸入坐標(biāo)是否有誤,輸入錯(cuò)誤重新輸入 { if (real_mine[x][y] == '0')//沒踩到雷 { char ch = count_mine(x,y); show_mine[x][y] = ch+'0';//數(shù)字對應(yīng)的ASCII值和數(shù)字字符對應(yīng)的ASCII值相差48,即'0'的ASCII值 open_mine(x, y); if (count_show_mine() == COUNT)//判斷剩余未知區(qū)域的個(gè)數(shù),個(gè)數(shù)為雷數(shù)時(shí)玩家贏 { print_mine(); printf("玩家贏!\n\n"); return 0; } } else if (real_mine[x][y]=='1')//踩到雷 { return 1; } } else { printf("輸入錯(cuò)誤重新輸入\n"); } return 0;//沒踩到雷 } void open_mine(int x, int y)//坐標(biāo)周圍展開函數(shù) { if (real_mine[x - 1][y - 1]== '0') { show_mine[x - 1][y - 1] = count_mine(x - 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x - 1][y] == '0') { show_mine[x - 1][y] = count_mine(x - 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x - 1][y + 1] == '0') { show_mine[x - 1][y + 1] = count_mine(x - 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x][y - 1] == '0') { show_mine[x][y - 1] = count_mine(x, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x][y + 1] == '0') { show_mine[x][y + 1] = count_mine(x, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x + 1][y - 1] == '0') { show_mine[x + 1][y - 1] = count_mine(x + 1, y - 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x + 1][y] == '0') { show_mine[x + 1][y] = count_mine(x + 1, y) + '0';//顯示該坐標(biāo)周圍雷數(shù) } if (real_mine[x + 1][y + 1] == '0') { show_mine[x + 1][y + 1] = count_mine(x + 1, y + 1) + '0';//顯示該坐標(biāo)周圍雷數(shù) } } int count_show_mine()//判斷剩余未知區(qū)域的個(gè)數(shù),個(gè)數(shù)為雷數(shù)時(shí)玩家贏 { int count = 0; int i = 0; int j = 0; for (i = 1; i <= row - 2; i++) { for (j = 1; j <= col - 2; j++) { if (show_mine[i][j] == '*') { count++; } } } return count; }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Ubuntu 20.04 下安裝配置 VScode 的 C/C++ 開發(fā)環(huán)境(圖文教程)
這篇文章主要介紹了Ubuntu 20.04 下安裝配置 VScode 的 C/C++ 開發(fā)環(huán)境,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05