目錄
掃雷的介紹
部分效果展示
游戲實作
開始準備
開始界面
初始化棋盤
列印棋盤
隨機生成雷
排雷程序
排雷函式主體
資訊函式
計算周圍雷數的函式
雷區標記函式
判斷輸贏
完整代碼實作
掃雷的介紹
掃雷就是要把所有非地雷的格子揭開即勝利;踩到地雷格子就算失敗, 游戲主區域由很多個方格組成, 使用滑鼠左鍵隨機點擊一個方格,方格即被打開并顯示出方格中的數字;方格中數字則表示其周圍的8個方格隱藏了幾顆雷,

這里我們用c語言模擬掃雷游戲,當然,由于作者水平有限,暫時不能做出圖形,也暫時不能使用滑鼠操作,只能用鍵盤操作
部分效果展示

游戲實作
開始準備
開始界面
我們可以首先列印選單,提醒玩家是否開始游戲,通過while回圈控制,可以達到一次玩完不過癮還能接著玩的目的
int main()
{
int input = 0;
do
{
menu();
printf("請選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戲開始!\n");
game();//封裝的游戲主體,后文介紹
break;
case 0:
printf("退出游戲\n");
system("pause");
break;
default:
printf("選擇錯誤,重新選擇!\n");
break;
}
} while (input);
return 0;
}
選單函式如下
void menu()
{
printf("*********************************\n");
printf("*****歡迎游玩掃雷游戲!**********\n");
printf("*****made by 東條希爾薇**********\n");
printf("*******1. play***************\n");
printf("*******0. exit***************\n");
printf("*********************************\n");
}
列印效果如下

初始化棋盤
我們在這里需要定義兩個二維陣列,一個陣列用于展示給玩家,并儲存排雷資訊,一個陣列用于在后臺隨機生成雷并儲存,用兩個陣列儲存可以有效的防止排雷程序中產生的歧義(例如,把雷設為‘1’,那么就不清楚‘1’到底是給玩家的提示還是放置的雷),
當然,我們最好將雷設為‘1’,至于為什么這樣設定,見下文
void make_board(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
在初始化的時候
char show[ROWS][COLS];//ROWS已經在#define中定義為11,ROW,已定義為9
char mine[ROWS][COLS];
make_board(show, ROWS, COLS, '*');//將*設定為不知道資訊的區域
make_board(mine, ROWS, COLS, '0');//0來初始化,設為非雷區
我們模擬的是簡單模式的9*9,那為什么要把陣列初始化為11*11的呢?
為了防止陣列越界
畫圖示意

列印棋盤
我們只需要列印中間的9*9區域即可,而且只需要列印show陣列,存盤雷的陣列不需要顯示給玩家
void display_board(char board[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i < rows-1; i++)//列印行數,方便玩家操作
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i < rows - 1; i++)
{
printf("%d ", i);//列印列數
for (j = 1; j < cols - 1; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
效果如下

隨機生成雷
我們可以使用stdlib.h中的rand函式來隨機生成雷的坐標,為了能保證每次游戲的隨機生成結果不同,可以在main函式中使用srand和time函式,
void set_mine(char board[ROWS][COLS], int row, int col)
{
int count = COUNT;//COUNT已用#define定義,方便后期維護,使用簡單難道的雷數
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;//只有在坐標沒被占用時才會放置,防止最后生成的雷數小于10個
}
}
}
準備作業分界線
排雷程序
排雷函式主體
需要玩家選擇排雷的坐標,并將該坐標周圍的資訊反饋給玩家,當然,玩家可能會不慎選擇不合法的坐標,所以可以通過回圈讓玩家反復選擇,直到選擇到合法的坐標為止
void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count1 = 0;
while (1)
{
printf("請選擇要排的坐標\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//合法的坐標范圍
{
if (mine[x][y] == '1')
{
printf("很遺憾,你被炸死了!\n");//玩家踩到雷了
display_board(mine, ROWS, COLS);
system("pause");
break;
}
else
{
if (show[x][y] != '*')
{
printf("坐標被占用!重新輸入\n");//玩家輸入的坐標已經被開過了
}
else
{
open_board(mine,show, ROW, COL, x, y);//后文會提到的資訊函式
display_board(show, ROWS, COLS);//將每次更新的資訊呈現給玩家
printf("你是否需要標記?1是 or 0否\n");//后文將會提到的標記函式
int ret = 0;
scanf("%d", &ret);
if (ret == 1)
{
printf("開始標記\n");
count1 = flag(mine, show, row, col);
if (count1 == COUNT)
{
printf("恭喜,你贏了!\n");
break;
}
}
else
printf("繼續\n");
}
}
}
else
{
printf("坐標非法!重新輸入!\n");//輸入坐標超出棋盤范圍
}
}
}
資訊函式
這里我們使用了函式的遞回,可以實作以下的效果,能有效防止反復操作的問題

void open_board(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == ' ')
{
return;//遞回的停止條件
}
else if (count_mine(mine, x, y) != 0)//判斷周圍是否有雷,不用直接遍歷判斷,有就呈現資訊
{
show[x][y] = count_mine(mine, x, y) + '0';//后文提到的計算雷數的函式
}
else
{
show[x][y] = ' ';//標記,防止重復呼叫,造成遞回死回圈
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
open_board(mine, show, row, col, i, j);//查看以該坐標中心的周圍8格區域
}
}
}
}
}
計算周圍雷數的函式
這里將‘1’設為雷的優越性就體現出來了,
可以直接把‘1’減去字符0,將其轉化為數字相加,在呈現資訊的時候再加上‘0’轉化為字符,
int count_mine(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
count += (mine[i][j] - '0');
}
}
return count;
}
呈上效果圖

雷區標記函式
我們可以模擬掃雷游戲中的標記功能,供玩家標記他們認為有可能有雷的坐標,當然,如果標記錯了,可以隨時取消
int flag(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int input = 0;
int count = 0;
int x = 0;
int y = 0;
do
{
printf("請標記你認為的雷的位置,輸入已經標記的坐標已取消\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//判斷坐標是否合法
{
if (show[x][y] == '*')
{
show[x][y] = '$';//用$作為旗幟標記
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (show[i][j] == '$' && mine[i][j] == '1')
{
count++;//下文提到的判斷輸贏的方式
}
}
}
display_board(show, ROWS, COLS);//每次標記完后列印棋盤
}
else if (show[x][y] == '$')
{
show[x][y] = '*';//實作取消標記的功能
}
else
{
printf("坐標被占用!\n");//坐標已經被開過或者已經被標記過
}
}
else
{
printf("坐標非法,重新輸入!\n");
}
printf("是否繼續?1是 or 0否\n");
scanf("%d", &input);
} while (input);//可以讓玩家一直標記,直到玩家主動停止標記
return count;
}
判斷輸贏
我們上文中的flag函式有個回傳值,回傳show陣列中的旗幟$和mine陣列中的雷重合的個數,判斷,如果重合個數等于了COUNT就判斷贏了,如果踩到雷,就判斷輸了
count1 = flag(mine, show, row, col);
if (count1 == COUNT)
{
printf("恭喜,你贏了!\n");
break;
}
if (mine[x][y] == '1')
{
printf("很遺憾,你被炸死了!\n");
display_board(mine, ROWS, COLS);
system("pause");
break;
}
為了驗證我們邏輯的正確性,將雷數設定為1,來測驗一下我們的小游戲

完整代碼實作
main.c檔案
#include"game.h"
void game()
{
char show[ROWS][COLS];
char mine[ROWS][COLS];
make_board(show, ROWS, COLS, '*');
make_board(mine, ROWS, COLS, '0');
set_mine(mine, ROW, COL);
display_board(show, ROWS, COLS);
//display_board(mine, ROWS, COLS);
find_mine(show, mine, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("請選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戲開始!\n");
game();
break;
case 0:
printf("退出游戲\n");
system("pause");
break;
default:
printf("選擇錯誤,重新選擇!\n");
break;
}
} while (input);
return 0;
}
game.c檔案
#include"game.h"
void menu()
{
printf("*********************************\n");
printf("*****歡迎游玩掃雷游戲!**********\n");
printf("*****made by 東條希爾薇**********\n");
printf("*******1. play***************\n");
printf("*******0. exit***************\n");
printf("*********************************\n");
}
void make_board(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void display_board(char board[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i < rows-1; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i < rows - 1; i++)
{
printf("%d ", i);
for (j = 1; j < cols - 1; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void set_mine(char board[ROWS][COLS], int row, int col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count1 = 0;
while (1)
{
printf("請選擇要排的坐標\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遺憾,你被炸死了!\n");
display_board(mine, ROWS, COLS);
system("pause");
break;
}
else
{
if (show[x][y] != '*')
{
printf("坐標被占用!重新輸入\n");
}
else
{
open_board(mine,show, ROW, COL, x, y);
display_board(show, ROWS, COLS);
printf("你是否需要標記?1是 or 0否\n");
int ret = 0;
scanf("%d", &ret);
if (ret == 1)
{
printf("開始標記\n");
count1 = flag(mine, show, row, col);
if (count1 == COUNT)
{
printf("恭喜,你贏了!\n");
break;
}
}
else
printf("繼續\n");
}
}
}
else
{
printf("坐標非法!重新輸入!\n");
}
}
}
int count_mine(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
count += (mine[i][j] - '0');
}
}
return count;
}
int flag(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int input = 0;
int count = 0;
int x = 0;
int y = 0;
do
{
printf("請標記你認為的雷的位置,輸入已經標記的坐標已取消\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
show[x][y] = '$';
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (show[i][j] == '$' && mine[i][j] == '1')
{
count++;
}
}
}
display_board(show, ROWS, COLS);
}
else if (show[x][y] == '$')
{
show[x][y] = '*';
}
else
{
printf("坐標被占用!\n");
}
}
else
{
printf("坐標非法,重新輸入!\n");
}
printf("是否繼續?1是 or 0否\n");
scanf("%d", &input);
} while (input);
return count;
}
void open_board(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == ' ')
{
return;
}
else if (count_mine(mine, x, y) != 0)//判斷周圍是否有雷,不用直接遍歷判斷
{
show[x][y] = count_mine(mine, x, y) + '0';
}
else
{
show[x][y] = ' ';//標記,防止重復呼叫
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
open_board(mine, show, row, col, i, j);
}
}
}
}
}
game.h檔案
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define COUNT 10
void menu();
void make_board(char board[ROWS][COLS], int rows, int cols, char set);
void display_board(char board[ROWS][COLS], int rows, int cols);
void set_mine(char board[ROWS][COLS], int row, int col);
void find_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);
int count_mine(char mine[ROWS][COLS], int x, int y);
void open_board(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int x,int y);
int flag(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
由于作者水平有限,代碼中如有任何的bug,不足之處在所難免!希望各位大佬提出你們寶貴的意見,筆芯~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/290971.html
標籤:其他
上一篇:一位小白的自我介紹
下一篇:C語言三子棋 +(看完包你會)
