基于C語言掃雷游戲的設(shè)計(jì)與實(shí)現(xiàn)
1 引言
伴隨著信息技術(shù)的快速發(fā)展,近年來,人們的生活已經(jīng)離不開計(jì)算機(jī)。生活?yuàn)蕵穾缀醵际窃谟?jì)算機(jī)上進(jìn)行的。其中的掃雷游戲就是之一。掃雷游戲是微軟公司在1992年在windows系統(tǒng)上發(fā)布的一款益智類小游戲,直到今天這款小游戲依然存在,可見此款游戲的成功。本文將用Visual Studio 2019作為開發(fā)工具來模擬實(shí)現(xiàn)掃雷游戲。經(jīng)過大一第一學(xué)期的學(xué)習(xí),我們對(duì)C語言的理論及其相關(guān)知識(shí)有了一定的認(rèn)識(shí)和了解。本文可以把我們所學(xué)的理論知識(shí)和實(shí)踐動(dòng)手能力相結(jié)合,另外也可以對(duì)我所學(xué)過的知識(shí)進(jìn)行復(fù)習(xí)鞏固。通過探索windows的掃雷小游戲,我們可以巧妙地和這學(xué)期學(xué)過的C語言相結(jié)合起來模擬實(shí)現(xiàn)無界面化簡(jiǎn)易版的掃雷小游戲。
2 相關(guān)工作
準(zhǔn)備Visual Studio 2019開發(fā)工具,了解數(shù)據(jù)類型,會(huì)使用選擇語句、循環(huán)語句、函數(shù)、數(shù)組等內(nèi)容。
了解ASCII碼值、隨機(jī)數(shù)的生成。了解二維坐標(biāo)相關(guān)的數(shù)學(xué)知識(shí)。
3 本文方法
3.1玩家游戲思路
進(jìn)入菜單,進(jìn)行選擇是否進(jìn)入,或退出游戲。
進(jìn)入游戲模塊,輸入坐標(biāo)進(jìn)行掃雷。掃到雷,結(jié)束游戲,沒掃到雷,進(jìn)行標(biāo)記該位置周圍相鄰八個(gè)位置有多少個(gè)雷,直到玩家失敗或者贏掉此游戲。
3.2游戲構(gòu)思細(xì)節(jié)
由于本游戲玩家看到的界面是由9* 9大小的方格構(gòu)成并用字符* 對(duì)B雷盤進(jìn)行覆蓋的畫面,但當(dāng)我們計(jì)算一個(gè)非雷的位置周圍八個(gè)相鄰位置是否有雷時(shí),這樣就會(huì)導(dǎo)致數(shù)組越界,為了防止越界,我們將雷盤設(shè)置為11乘11。因?yàn)槭亲址? 覆蓋,所以對(duì)B雷盤創(chuàng)建用的是字符類型的二維數(shù)組,同時(shí)為了方便實(shí)現(xiàn)一次聲明,兩次調(diào)用等操作。我們把a(bǔ)雷盤也用字符類型的二維數(shù)組進(jìn)行創(chuàng)建。
3.3 游戲設(shè)計(jì)實(shí)現(xiàn)
3.3.1 游戲分三個(gè)模塊,test.c,game.c和game.h。
(1)以模塊化的函數(shù)思想進(jìn)行設(shè)計(jì),使游戲整體思路更加清晰。首先打開Visual Studio 2019,創(chuàng)建掃雷游戲的空項(xiàng)目,創(chuàng)建test.c源文件,和game.c源文件,一個(gè)game.h頭文件。c語言中頭文件中一般定義了函數(shù)的聲明、結(jié)構(gòu)體的定義、宏定義。(常量和全局變量最好放到源文件中)。C語言源文件中我們放置一些函數(shù)。來將游戲的具體實(shí)現(xiàn)。 源文件(test.c)里面放主函數(shù)和游戲的整體功能。
3.3.2 在頭文件中進(jìn)行準(zhǔn)備工作
(1)#define進(jìn)行宏定義,進(jìn)行 InitMine,SetMine,DisplayMine,F(xiàn)indMine等函數(shù)的聲明。
//宏定義便于后期的更改 #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROWS ROW + 2 //設(shè)置11*11的格子防止數(shù)組越界。 #define COLS COL + 2 #define ROW 9 #define COL 9 //以下是對(duì)函數(shù)的聲明 void InitMine(char mine[ROWS][COLS], int row, int col, char set); void SetMine(char mine[ROWS][COLS], int row, int col); void DisplayMine(char show[ROWS][COLS], int row, int col); void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //設(shè)置雷的個(gè)數(shù) #define minecount 10
3.3.3 test.c中游戲大致功能的實(shí)現(xiàn)
(1)生成菜單欄
對(duì)于玩家來說,游戲的開始之前需要一個(gè)菜單欄進(jìn)行選擇游戲的開始或者退出,這時(shí)候就要用do語句生成菜單欄,并在其中使用switch語句進(jìn)行選擇進(jìn)入或退出游戲。
test() { //用rand()函數(shù)之前需要先用srand函數(shù)。 srand((unsigned int)time(NULL)); int n = 0; //游戲進(jìn)去之后需要先有個(gè)菜單所以用上do語句 do { //打印菜單 menu(); scanf("%d", &n); switch (n) { case 1: game(); break; case 0: printf("退出游戲\n"); break; default: printf("請(qǐng)重新選擇\n"); break; } } while (n); } //程序的入口 int main() { test(); return 0; }
(2)調(diào)用game函數(shù)
我們將A盤有mine數(shù)組表示,B盤用show數(shù)組表示。
在switch中調(diào)用game函數(shù)進(jìn)行char mine[ROWS][COLS] = { 0 };char show[ROWS][COLS] = { 0 };等數(shù)組的創(chuàng)建。用InitMine(mine, ROWS, COLS, ‘0');InitMine(show, ROWS, COLS, ' * );進(jìn)行雷盤的初始化。我們?cè)O(shè)置雷為字符1,非雷為字符0。首先,把A盤初始化為非0,即全都不是雷,把B盤初始化為*號(hào)。接著用SetMine(mine, ROW, COL);DisplayMine(show, ROW, COL)運(yùn)用隨機(jī)數(shù)生成函數(shù),向A盤隨機(jī)布置雷;//用B盤進(jìn)行展示雷的信息和結(jié)果,接著用FindMine(mine, show, ROW, COL);進(jìn)行排雷
game() { system("cls");//對(duì)畫面進(jìn)行清屏,使頁(yè)面簡(jiǎn)潔。 char mine[ROWS][COLS] = { 0 };//創(chuàng)建11*11數(shù)組 char show[ROWS][COLS] = { 0 }; //第一次傳mine數(shù)組名 InitMine(mine, ROWS, COLS, '0'); //第二次傳show數(shù)組名 InitMine(show, ROWS, COLS, '*'); //在A雷盤布置雷,所以需要傳mine數(shù)組名 SetMine(mine, ROW, COL); //用B盤進(jìn)行展示雷的信息和結(jié)果 DisplayMine(show, ROW, COL); //進(jìn)行排雷 FindMine(mine, show, ROW, COL); }
3.3.4game.c中功能具體實(shí)現(xiàn)
(1)InitMine運(yùn)用雙重for循環(huán),初始化棋盤。
(2)SetMine運(yùn)用隨機(jī)數(shù)的生成,rand函數(shù)()函數(shù),布置雷。
(3)DisplayMine用雙重for循環(huán)遍歷,展示棋盤
(4)FindMine用while循環(huán)和if的嵌套,期間關(guān)鍵在于調(diào)用SpreadMine函數(shù)進(jìn)行遞歸,再調(diào)用is_win函數(shù)進(jìn)行判斷輸贏。
關(guān)鍵部分代碼(遞歸)
void SpreadMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { //調(diào)用CountMine函數(shù)進(jìn)行查找周圍八個(gè)位置雷的個(gè)數(shù)。 int count = CountMine(mine, x, y); if (0 == count) { show[x][y] = ' '; if (show[x][y + 1] == '*') SpreadMine(mine, show, x, y + 1); if (show[x][y - 1] == '*') SpreadMine(mine, show, x, y - 1); if (show[x + 1][y] == '*') SpreadMine(mine, show, x + 1, y); if (show[x + 1][y - 1] == '*') SpreadMine(mine, show, x + 1, y - 1); if (show[x + 1][y + 1] == '*') SpreadMine(mine, show, x + 1, y + 1); if (show[x - 1][y] == '*') SpreadMine(mine, show, x - 1, y); if (show[x - 1][y + 1] == '*') SpreadMine(mine, show, x - 1, y + 1); if (show[x - 1][y - 1] == '*') SpreadMine(mine, show, x - 1, y - 1); } else { //如果該位置是雷,則將雷的個(gè)數(shù)顯示在此位置 show[x][y] = count + '0'; //由于‘0'的的ASCII碼值為48,‘1'的ASCII碼值是49. // 所以 //1 + '0' = '1' //2 + '0' = '2' } }
4 結(jié)果與分析
本文實(shí)現(xiàn)了掃雷游戲的基本功能,根據(jù)提示,輸入坐標(biāo)從而實(shí)現(xiàn)掃雷。如果踩到雷就會(huì)退出游戲。如果剩下的*號(hào)等于雷數(shù),則贏得游戲。如果輸入坐標(biāo)不對(duì),則重新輸入。最后通過遞歸實(shí)現(xiàn)了掃雷一片展開的功能。待有提高的是實(shí)現(xiàn)圖形界面化和用鼠標(biāo)操作界面可以學(xué)習(xí)EasyX來進(jìn)一步優(yōu)化。
5 總結(jié)
通過對(duì)此掃雷游戲的設(shè)計(jì)和實(shí)現(xiàn),我們回顧了C語言的大半部分知識(shí)并對(duì)其中的細(xì)節(jié)進(jìn)行了剖析。學(xué)習(xí)了C語言編程風(fēng)格,了解了一些較常用的知識(shí)。我們對(duì)二維數(shù)組的應(yīng)用,函數(shù)的聲明,定義,和調(diào)用,以及if,switch選擇語句和for,do,while循環(huán)語句有了更深刻的理解,提升了我們的邏輯思維能力。同時(shí)對(duì)ASCII碼,隨機(jī)數(shù)生成的應(yīng)用也有了一些了解。另外,也提升了我們的模塊化思維能力,比如我們用三個(gè)模塊來構(gòu)建此游戲。并且游戲中每個(gè)功能的實(shí)現(xiàn)都是由函數(shù)模塊構(gòu)成的。
GitHub源碼鏈接:
https://github.com/CaoCoder/C_code
整體代碼
頭文件
//宏定義便于后期的更改 #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROWS ROW + 2 //設(shè)置11*11的格子防止數(shù)組越界。 #define COLS COL + 2 #define ROW 9 #define COL 9 //以下是對(duì)函數(shù)的聲明 void InitMine(char mine[ROWS][COLS], int row, int col, char set); void SetMine(char mine[ROWS][COLS], int row, int col); void DisplayMine(char show[ROWS][COLS], int row, int col); void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //設(shè)置雷的個(gè)數(shù) #define minecount 10
源文件
#define _CRT_SECURE_NO_WARNINGS #include "12_9game.h"http://引用自己創(chuàng)建的頭文件用雙引號(hào) //對(duì)9*9雷區(qū)遍歷查*的數(shù)量和minecount進(jìn)行比較。 int is_win(char show[ROWS][COLS], int row, int col) { int i = 0; int j = 0; int c = 0; for (i = 1; i <= row; i++) { for (j = 1; j <= col; j++) { if ('*' == show[i][j]) { c++; } } } return c; } //用遞歸,進(jìn)行實(shí)現(xiàn)一片展開的功能 void SpreadMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) { //調(diào)用CountMine函數(shù)進(jìn)行查找周圍八個(gè)位置雷的個(gè)數(shù)。 int count = CountMine(mine, x, y); if (0 == count) { show[x][y] = ' '; if (show[x][y + 1] == '*') SpreadMine(mine, show, x, y + 1); if (show[x][y - 1] == '*') SpreadMine(mine, show, x, y - 1); if (show[x + 1][y] == '*') SpreadMine(mine, show, x + 1, y); if (show[x + 1][y - 1] == '*') SpreadMine(mine, show, x + 1, y - 1); if (show[x + 1][y + 1] == '*') SpreadMine(mine, show, x + 1, y + 1); if (show[x - 1][y] == '*') SpreadMine(mine, show, x - 1, y); if (show[x - 1][y + 1] == '*') SpreadMine(mine, show, x - 1, y + 1); if (show[x - 1][y - 1] == '*') SpreadMine(mine, show, x - 1, y - 1); } else { //如果該位置是雷,則將雷的個(gè)數(shù)顯示在此位置 show[x][y] = count + '0'; //由于‘0'的的ASCII碼值為48,‘1'的ASCII碼值是49. // 所以 //1 + '0' = '1' //2 + '0' = '2' } } //查找非雷位置周圍八個(gè)位置有多少雷 int CountMine(char mine[ROWS][COLS], int x, int y) { return (mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x - 1][y] + mine[x + 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] - 8 * '0'); } //掃雷 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int c = row * col - minecount; while (1) { printf("請(qǐng)輸入坐標(biāo)(x,y):"); scanf("%d%d", &x, &y); //將輸入的坐標(biāo)限制在9*9的雷區(qū) if (x <= row && x >= 1 && y <= col && y >= 1) { if (mine[x][y] == '0') { SpreadMine(mine, show, x, y); system("cls"); DisplayMine(show, ROW, COL); if (is_win(show, ROW, COL) == minecount) { printf("恭喜你贏了!\n"); DisplayMine(mine, ROW, COL); break; } } else { printf("很遺憾你被炸死了~\n"); DisplayMine(mine, ROW, COL); break; } } else { printf("輸入坐標(biāo)越界,請(qǐng)重新輸入:"); } } } //初始化棋盤,一個(gè)函數(shù),兩次調(diào)用。 void InitMine(char mine[ROWS][COLS], int row, int col, char set) { int i = 0; int j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { mine[i][j] = set; } } } //布置雷 void SetMine(char mine[ROWS][COLS], int row, int col) { int c = 0; while (c != minecount) { int x = rand() % row + 1; //用rand()函數(shù)對(duì)9求余得到0~9這十個(gè)數(shù),再加一就得到1~10. int y = rand() % col + 1; if (mine[x][y] == '0') { //如果此位置為‘0',即非雷 //則設(shè)置為‘1',即雷 mine[x][y] = '1'; c++; } } } //運(yùn)用雙重循環(huán)打印9*9的B雷區(qū) void DisplayMine(char show[ROWS][COLS], int row, int col) { int i = 0; int j = 0; for (i = 0; i <= row; i++) { printf("%d ", i); for (j = 1; j <= col; j++) { if (0 == i) { printf("%d ", j); } else { printf("%c ", show[i][j]); } } printf("\n"); } }
源文件
#define _CRT_SECURE_NO_WARNINGS #include "12_9game.h" game() { system("cls");//對(duì)畫面進(jìn)行清屏,使頁(yè)面簡(jiǎn)潔。 char mine[ROWS][COLS] = { 0 };//創(chuàng)建11*11數(shù)組 char show[ROWS][COLS] = { 0 }; //第一次傳mine數(shù)組名 InitMine(mine, ROWS, COLS, '0'); //第二次傳show數(shù)組名 InitMine(show, ROWS, COLS, '*'); //在A雷盤布置雷,所以需要傳mine數(shù)組名 SetMine(mine, ROW, COL); //用B盤進(jìn)行展示雷的信息和結(jié)果 DisplayMine(show, ROW, COL); //進(jìn)行排雷 FindMine(mine, show, ROW, COL); } menu() { printf("┏━━━━━━┓━━━━━━━━━━━━━━━━━━━━┓━━━━━━━━━━━┓\n"); printf("┣━━━━━━┫━━━━━━1.PLAY━━━━━━━━┫━━━━━━━━━━━┫\n"); printf("┣━━━━━━┫━━━━━━1.EXIT━━━━━━━━┫━━━━━━━━━━━┫\n"); printf("┗━━━━━━┛━━━━━━━━━━━━━━━━━━━━┛━━━━━━━━━━━┛\n"); } test() { //用rand()函數(shù)之前需要先用srand函數(shù)。 srand((unsigned int)time(NULL)); int n = 0; //游戲進(jìn)去之后需要先有個(gè)菜單所以用上do語句 do { //打印菜單 menu(); scanf("%d", &n); switch (n) { case 1: game(); break; case 0: printf("退出游戲\n"); break; default: printf("請(qǐng)重新選擇\n"); break; } } while (n); } //程序的入口 int main() { test(); return 0; }
到此這篇關(guān)于基于C語言掃雷游戲的設(shè)計(jì)與實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C語言掃雷游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言簡(jiǎn)明講解隊(duì)列的實(shí)現(xiàn)方法
隊(duì)列(Queue)與棧一樣,是一種線性存儲(chǔ)結(jié)構(gòu),它具有如下特點(diǎn):隊(duì)列中的數(shù)據(jù)元素遵循“先進(jìn)先出”(First?In?First?Out)的原則,簡(jiǎn)稱FIFO結(jié)構(gòu)。在隊(duì)尾添加元素,在隊(duì)頭刪除元素2022-04-04基于C語言實(shí)現(xiàn)簡(jiǎn)單的掃雷小游戲
這篇文章主要為大家詳細(xì)介紹了基于C語言實(shí)現(xiàn)簡(jiǎn)單的掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11探討register關(guān)鍵字在c語言和c++中的差異
建議不要用register關(guān)鍵字定義全局變量,因?yàn)槿肿兞康纳芷谑菑膱?zhí)行程序開始,一直到程序結(jié)束才會(huì)終止,而register變量可能會(huì)存放在cpu的寄存器中,如果在程序的整個(gè)生命周期內(nèi)都占用著寄存器的話,這是個(gè)相當(dāng)不好的舉措2013-10-10詳解C語言中strcpy函數(shù)與memcpy函數(shù)的區(qū)別與實(shí)現(xiàn)
這篇文章主要介紹了C語言中字符串拷貝函數(shù)(strcpy)與內(nèi)存拷貝函數(shù)(memcpy)的不同及內(nèi)存拷貝函數(shù)的模擬實(shí)現(xiàn),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-12-12C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
這篇文章主要介紹了C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例,并且轉(zhuǎn)換后會(huì)統(tǒng)計(jì)二進(jìn)制1的個(gè)數(shù),實(shí)例簡(jiǎn)單明了,需要的朋友可以參考下2014-06-06