C語言實(shí)現(xiàn)經(jīng)典windows游戲掃雷的示例代碼
1. 前言
大家好,我是努力學(xué)習(xí)游泳的魚。今天我們會用C語言實(shí)現(xiàn)一個(gè)經(jīng)典的windows小游戲:掃雷。掃雷是一款單機(jī)小游戲,我上中學(xué)時(shí)特喜歡在電腦課上玩,研究應(yīng)對各種情況的思路,每次通關(guān)最高難度的關(guān)卡都會開心好一陣?,F(xiàn)在學(xué)會了C語言,總算可以自己實(shí)現(xiàn)掃雷了。話不多說,咱們開始吧。
2. 準(zhǔn)備工作
我們新建一個(gè)項(xiàng)目,并創(chuàng)建三個(gè)文件:
- test.c - 負(fù)責(zé)測試游戲代碼。
- game.c - 負(fù)責(zé)游戲功能的具體實(shí)現(xiàn)。
- game.h - 負(fù)責(zé)頭文件的包含,符號的定義,函數(shù)的聲明。
在test.c和game.c里都要#include "game.h"
測試游戲時(shí),玩一把肯定不過癮,會想要再來一把,這就需要用到do while循環(huán)。
void menu() { printf("****************************\n"); printf("******** 1. play *******\n"); printf("******** 0. exit *******\n"); printf("****************************\n"); } int main() { int input = 0; do { menu(); printf("請選擇(1/0):>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戲\n"); break; default: printf("選擇錯(cuò)誤,重新選擇!\n"); break; } } while (input); return 0; }
3. 設(shè)計(jì)思路
接下來講解掃雷游戲(game函數(shù))實(shí)現(xiàn)的思路。
1.布置雷 - 10個(gè)。
2.掃雷 - 玩法如下:
輸入坐標(biāo),此時(shí)分兩種情況:
- 是雷 – 就被炸了,游戲結(jié)束。
- 不是雷 – 就告訴你這個(gè)坐標(biāo)周圍8個(gè)坐標(biāo)上總共有多少個(gè)雷。
直到把所有非雷的位置全部都找出來,游戲結(jié)束,掃雷成功。
我們假設(shè)掃雷的棋盤是9×9的。我們布置雷的信息要想全部存起來,就需要使用9×9的二維數(shù)組。
怎么布置雷呢?假設(shè)要布置10個(gè)雷,我們就隨機(jī)生成10個(gè)坐標(biāo),把數(shù)組的這10個(gè)位置都置成1,其余位置存儲0。實(shí)際排查的時(shí)候,只需要顯示周圍8個(gè)坐標(biāo)有幾個(gè)1就行了。
但是這樣設(shè)計(jì)有一個(gè)問題,假設(shè)有一個(gè)位置周圍只有1個(gè)雷,那就顯示1。我們?nèi)绾闻袛噙@個(gè)1是表示雷的1,還是顯示周圍有一個(gè)雷的1呢?這就有歧義了。
如何解決這個(gè)問題呢?我們可以再搞一個(gè)一樣大的數(shù)組。兩個(gè)數(shù)組,一個(gè)放布置好的雷的信息,另一個(gè)放排查出的雷的信息。對于后者,如果某個(gè)位置沒有排查過,就存儲星號,以保持神秘感;如果排查過了,并且不是雷,就存儲周圍雷的個(gè)數(shù)。由于星號是一個(gè)字符,為了保持類型的統(tǒng)一,雷的個(gè)數(shù)也要用數(shù)字字符來存儲(如某位置周圍有3個(gè)雷,就存儲字符3),那存儲排查出的雷的信息的數(shù)組就是一個(gè)9×9的char類型的數(shù)組。還是為了保持類型的統(tǒng)一,存儲雷的信息的數(shù)組中,我們用字符0表示非雷,字符1表示雷,該數(shù)組也是一個(gè)9×9的char類型的數(shù)組。
階段總結(jié)一下:
- char mine[9][9] 負(fù)責(zé)存儲布置好的雷的信息,字符1表示雷,字符0表示非雷。
- char show[9][9] 負(fù)責(zé)存儲排查出的雷的信息,星號表示未排查,數(shù)字字符表示已排查。
但是,這樣設(shè)計(jì)仍然有問題。如果我們要排查數(shù)組邊上或角上的位置,我們需要訪問該位置周圍的8個(gè)位置,就有可能越界訪問了。
如何解決這個(gè)問題呢?我們可以把存放雷的信息的數(shù)組開大一圈,這樣訪問時(shí),排查邊上或角上的數(shù)據(jù)就不會越界了。為了保持兩個(gè)數(shù)組的一一對應(yīng),另一個(gè)數(shù)組也開大一圈。經(jīng)過以上的分析,如果實(shí)際使用的棋盤大小是9×9的,兩個(gè)數(shù)組就應(yīng)該定義為11×11的。
4. 定義數(shù)組
為了以后修改這點(diǎn)方便,我們定義幾個(gè)宏,ROW和COL為實(shí)際使用的大小(9×9),ROWS和COLS為實(shí)際定義數(shù)組的大?。?1×11)。
#define ROW 9 #define COL 9 #define ROWS (ROW + 2) #define COLS (COL + 2)
接著定義兩個(gè)數(shù)組。
char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 };
5. 初始化
mine數(shù)組由于要存儲雷的信息,沒布置雷前,我們想把這個(gè)數(shù)組全部初始化成0。show數(shù)組用于存放排查出來的雷的信息,一開始應(yīng)全部初始化成星號。所以我們需要一個(gè)初始化函數(shù)。由于兩個(gè)數(shù)組初始化的內(nèi)容不一樣,所以我們設(shè)計(jì)函數(shù)時(shí),需要把初始化的內(nèi)容當(dāng)做參數(shù)傳過去。
init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*');
具體的實(shí)現(xiàn),只需遍歷數(shù)組就行了。
void init_board(char arr[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (; i < rows; ++i) { int j = 0; for (; j < cols; ++j) { arr[i][j] = set; } } }
6. 打印
對兩個(gè)數(shù)組進(jìn)行初始化后,我們想把它們打印出來看看。
初始化時(shí),我們需要初始化整個(gè)數(shù)組(11×11),但是打印以及后面的操作,我們基本只關(guān)心中間的9×9,周圍的一圈只是為了防止越界。
show_board(mine, ROW, COL); show_board(show, ROW, COL);
具體的實(shí)現(xiàn),也是遍歷數(shù)組除去最外面一圈的元素,那下標(biāo)應(yīng)從1開始,最大是row或col。
void show_board(char arr[ROWS][COLS], int row, int col) { int i = 0; for (i = 1; i <= row; ++i) { int j = 0; for (j = 1; j <= col; ++j) { printf("%c ", arr[i][j]); } printf("\n"); } }
打印出來效果如下:
但是這樣打印不夠完美,我們每次還要去數(shù)某個(gè)位置是第幾行第幾列,所以最好把行標(biāo)和列標(biāo)也打印出來。
每次打印一行前,我們都打印下行號printf("%d ", i);
在所有信息打印前,我們把列標(biāo)打印出來。
for (i = 0; i <= col; ++i) { printf("%d ", i); } printf("\n");
當(dāng)然,我們可以在打印的最前和最后加上分割行。printf("------------掃雷------------\n");
下面是打印函數(shù)完整的代碼。
void show_board(char arr[ROWS][COLS], int row, int col) { int i = 0; printf("------------掃雷------------\n"); for (i = 0; i <= col; ++i) { printf("%d ", i); } printf("\n"); for (i = 1; i <= row; ++i) { printf("%d ", i); int j = 0; for (j = 1; j <= col; ++j) { printf("%c ", arr[i][j]); } printf("\n"); } printf("------------掃雷------------\n"); }
打印效果如下:
7. 布置雷
我們需要在mine數(shù)組里布置雷。set_mine(mine, ROW, COL);
假設(shè)雷的個(gè)數(shù)是EASY_COUNT。#define EASY_COUNT 10
具體的實(shí)現(xiàn),我們需要寫一個(gè)循環(huán),每次隨機(jī)生成一個(gè)坐標(biāo),如果這個(gè)位置不是雷,就在這個(gè)位置放雷,知道把所有的雷放完為止。
void set_mine(char mine[ROWS][COLS], int row, int col) { int count = EASY_COUNT; int x = 0; int y = 0; while (count) { x = rand() % row + 1; y = rand() % col + 1; if ('0' == mine[x][y]) { mine[x][y] = '1'; // 布置雷 --count; } } }
不要忘記在調(diào)用rand函數(shù)之前要調(diào)用srand函數(shù),并給srand函數(shù)傳遞用time函數(shù)生成的時(shí)間戳。srand((unsigned int)time(NULL));rand函數(shù)和srand函數(shù)需要引用頭文件stdlib.h,time函數(shù)需要引用頭文件time.h。
我們可以把生成雷的信息打印出來。show_board(mine, ROW, COL);
8. 排查雷
布置好雷后,就開始排查雷。排查雷需要同時(shí)操作兩個(gè)數(shù)組。find_mine(mine, show, ROW, COL);
排查雷時(shí),可以通過一個(gè)循環(huán)反復(fù)獲取坐標(biāo),并對坐標(biāo)進(jìn)行判斷。
- 首先判斷坐標(biāo)的合法性,橫縱坐標(biāo)都必須在1到row(col)之間。
- 如果坐標(biāo)合法,再看這個(gè)坐標(biāo)有沒有排查過,如果show數(shù)組在該位置還是星號,說明沒有排查過。
- 如果坐標(biāo)還沒排查過,再看是不是雷,是雷的話游戲結(jié)束,不是雷的話就顯示該坐標(biāo)周圍有幾個(gè)雷。
循環(huán)會在兩種情況下結(jié)束,
一種是踩到雷了,直接break出去,
另一種是找到所有非雷的位置,就排雷成功了??偣灿衦ow×col個(gè)位置,一共有EASY_COUNT個(gè)雷,那非雷的位置個(gè)數(shù)就是兩者相減。判斷是否排雷成功,可以在循環(huán)條件中判斷。
void find_mine(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("請輸入要排查的坐標(biāo):>"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if ('*' == show[x][y]) { if ('1' == mine[x][y]) { printf("很遺憾,你被炸死了\n"); show_board(mine, row, col); break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; show_board(show, row, col); ++win; } } else { printf("該坐標(biāo)已被排查\n"); } } else { printf("坐標(biāo)非法,重新輸入\n"); } } if (row * col - EASY_COUNT == win) { printf("恭喜你,排雷成功\n"); show_board(mine, row, col); } }
我們用get_mine_count函數(shù)來獲取某個(gè)位置(坐標(biāo)為x,y)周圍8個(gè)坐標(biāo)雷的個(gè)數(shù)。由于字符1的ASCII碼值減去字符0的ASCII碼值是1,而mine數(shù)組里存放的就是字符1和字符0。所以我們只需要把mine數(shù)組中,該位置周圍八個(gè)坐標(biāo)存儲的字符加起來,再減去字符0的ASCII碼值的八倍,就能算出一共有多少個(gè)雷了。由于get_mine_count函數(shù)只在fine_mine函數(shù)中使用,所以加上static。
static int get_mine_count(char mine[ROWS][COLS], int x, int y) { return 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'; }
到此為止,整個(gè)掃雷游戲就寫完啦。
9. 完整代碼
game.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 9 #define COL 9 #define ROWS (ROW + 2) #define COLS (COL + 2) #define EASY_COUNT 10 // 初始化 void init_board(char arr[ROWS][COLS], int rows, int cols, char set); // 打印 void show_board(char arr[ROWS][COLS], int row, int col); // 布置雷 void set_mine(char mine[ROWS][COLS], int row, int col); // 排查雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void init_board(char arr[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (; i < rows; ++i) { int j = 0; for (; j < cols; ++j) { arr[i][j] = set; } } } void show_board(char arr[ROWS][COLS], int row, int col) { int i = 0; printf("------------掃雷------------\n"); for (i = 0; i <= col; ++i) { printf("%d ", i); } printf("\n"); for (i = 1; i <= row; ++i) { printf("%d ", i); int j = 0; for (j = 1; j <= col; ++j) { printf("%c ", arr[i][j]); } printf("\n"); } printf("------------掃雷------------\n"); } void set_mine(char mine[ROWS][COLS], int row, int col) { int count = EASY_COUNT; int x = 0; int y = 0; while (count) { x = rand() % row + 1; y = rand() % col + 1; if ('0' == mine[x][y]) { mine[x][y] = '1'; // 布置雷 --count; } } } static int get_mine_count(char mine[ROWS][COLS], int x, int y) { return 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'; } void find_mine(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("請輸入要排查的坐標(biāo):>"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if ('*' == show[x][y]) { if ('1' == mine[x][y]) { printf("很遺憾,你被炸死了\n"); show_board(mine, row, col); break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; show_board(show, row, col); ++win; } } else { printf("該坐標(biāo)已被排查\n"); } } else { printf("坐標(biāo)非法,重新輸入\n"); } } if (row * col - EASY_COUNT == win) { printf("恭喜你,排雷成功\n"); show_board(mine, row, col); } }
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void menu() { printf("****************************\n"); printf("******** 1. play *******\n"); printf("******** 0. exit *******\n"); printf("****************************\n"); } void game() { // 掃雷游戲的具體實(shí)現(xiàn) // 存儲布置好的雷的信息 char mine[ROWS][COLS] = { 0 }; // 存放排查出來的雷的信息 char show[ROWS][COLS] = { 0 }; // 初始化棋盤 init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*'); // 打印棋盤 //show_board(mine, ROW, COL); // 布置雷 set_mine(mine, ROW, COL); show_board(show, ROW, COL); // 排查雷 find_mine(mine, show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("請選擇(1/0):>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戲\n"); break; default: printf("選擇錯(cuò)誤,重新選擇!\n"); break; } } while (input); return 0; }
以上就是C語言實(shí)現(xiàn)經(jīng)典windows游戲掃雷的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C語言掃雷游戲的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言連續(xù)生成多個(gè)隨機(jī)數(shù)實(shí)現(xiàn)可限制范圍
這篇文章主要介紹了C語言連續(xù)生成多個(gè)隨機(jī)數(shù)實(shí)現(xiàn)可限制范圍,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01C++數(shù)據(jù)結(jié)構(gòu)關(guān)于棧迷宮求解示例
這篇文章主要為大家介紹了C++數(shù)據(jù)結(jié)構(gòu)關(guān)于棧的迷宮求解示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-11-11C語言實(shí)現(xiàn)3*3數(shù)組對角線之和示例
今天小編就為大家分享一篇C語言實(shí)現(xiàn)3*3數(shù)組對角線之和示例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12C語言使用單鏈表實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言使用單鏈表實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11C語言中數(shù)據(jù)結(jié)構(gòu)之鏈?zhǔn)交鶖?shù)排序
這篇文章主要介紹了C語言中數(shù)據(jù)結(jié)構(gòu)之鏈?zhǔn)交鶖?shù)排序的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09