要想實作掃雷的游戲我們要知道掃雷的規則是什么:
在一個99(初級),1616(中級),16*30(高級),或自定義大小的方塊矩陣中隨機布置一定量的地雷(初級為10個,中級為40個,高級為99個),由玩家逐個翻開方塊,以找出所有地雷為最終游戲目標,如果玩家翻開的方塊有地雷,則游戲結束,
游戲主區域由很多個方格組成,使用滑鼠左鍵隨機點擊一個方格,方格即被打開并顯示出方格中的數字;方格中數字則表示其周圍的8個方格隱藏了幾顆雷,
我們這以9*9的矩陣為例,要想實作這個游戲我選擇如果寫在一個檔案里面會顯得十分混亂,所以我們分為三個檔案來完成:

test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
#include<stdlib.h>
#include<time.h>
void game()
{
char showboard[ROWS][COLS] = { 0 };
char botboard[ROWS][COLS] = { 0 };
initboard(showboard, COLS, ROWS, '*');
initboard(botboard, COLS, ROWS, '0');
setMine(botboard, COL, ROW);
char botboard2[ROWS][COLS];
copy(botboard2, botboard, COL, ROW);
//displayboard(botboard, COL, ROW); //這個顯示隨機地雷埋在哪個位置,除錯的是候可以使用
int x, y;
int n = 0;
while (1)
{
displayboard(showboard, COL, ROW);
printf("請輸入坐標值:");
scanf("%d%d", &x, &y);
if (x > 0 && x <=COL && y > 0 && y <= COL)
{
n++;
int ret = findMine(botboard, x, y);
if (ret == 10)
{
showboard[x][y] = '#';
printf("你被炸死了!!\n");
break;
}
if (n == COL * ROW - 10)
{
printf("你獲得了勝利\n");
break;
}
if (ret == 0)
{
fun(botboard2, showboard, botboard, x, y);
}
else
{
showboard[x][y] = ret + '0';
continue;
}
}
printf("坐標錯誤!\n");
}
displayboard(showboard, COL, ROW);
//displayboard(botboard, COL, ROW);
}
void manu() //列印選單
{
printf("***************************\n");
printf("***************************\n");
printf("*******1 . play************\n");
printf("*******2 . excite**********\n");
printf("***************************\n");
printf("***************************\n");
}
int main()
{
int n;
srand((unsigned)time(NULL));
do
{
manu();
printf("玩家請選擇:");
scanf("%d", &n);
switch (n)
{
case 1:
game();
break;
case 2:
n = 0;
break;
default:
n = 0;
break;
}
} while (n);
}
在game()函式里設定了兩個二維字符陣列showboard()用來列印給玩家看到顯示陣列 和 botboard()用來儲存雷的位置的陣列,實際上我們下面函式在進行運算的時候使用的是botboard()來獲得棋盤的資訊,這里把陣列設定成1111的陣列但是我們只顯示其中99的部分,原因下面分析findmine函式時會決議,
game.c:
#pragma once
#define COL 9
#define ROW 9
#define COLS 11
#define ROWS 11
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//初始化棋盤
void initboard(char board[ROWS][COLS], int col, int row, char set);
//展示棋盤
void displayboard(char board[ROWS][COLS], int col, int row);
//設定地雷
void setMine(char board[ROWS][COLS], int col, int row);
//排地雷
int findMine(char board[ROWS][COLS], int col, int row);
//快速排除周圍沒有一個地雷的點
void fun(char board2[ROWS][COLS], char board1[ROWS][COLS], char board[ROWS][COLS], int x, int y);
void copy(char board[ROWS][COLS], char board1[ROWS][COLS], int col, int row);
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
void initboard(char board[ROWS][COLS], int col, int row, char set)
{
int i = 0, j = 0;
for (i = 0; i < col; i++)
{
for (j = 0; j < row; j++)
{
board[j][i] = set;
}
}
}
void displayboard(char board[ROWS][COLS], int col, int row)
{
int i = 0, j = 0;
for (i = 0; i <=col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <=col; i++)
{
printf("%d ", i);
for (j = 1; j <= row; j++)
{
printf("%c ", board[j][i]);
}
printf("\n");
}
}
void setMine(char board[ROWS][COLS], int col, int row)
{
int x, y;
int n = 1;
while (n<=10)
{
x = rand() % (col - 1) + 1;
y = rand() % (row - 1) + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
n++;
}
}
}
int findMine(char board[ROWS][COLS], int x, int y)
{
while (1)
{
if (board[x][y] == '1')
{
return 10;
}
else
{
return board[x - 1][y - 1] +
board[x][y - 1] +
board[x + 1][y - 1] +
board[x - 1][y] +
board[x + 1][y] +
board[x - 1][y + 1] +
board[x][y + 1] +
board[x + 1][y + 1] - 8 * board[x][y];
}
}
}
void fun(char board2[ROWS][COLS],char board1[ROWS][COLS],char board[ROWS][COLS], int x, int y) //fun(botboard, showboard, botboard, x, y);
{
int ret = findMine(board, x, y);
if (ret == 0 && x > 0 && y > 0 && board2[x][y] != ' ')
{
board2[x][y] = ' ';
fun(board2, board1, board, x - 1, y);
fun(board2, board1, board, x + 1, y);
fun(board2, board1, board, x, y + 1);
fun(board2, board1,board, x, y - 1);
}
board1[x][y] = ret + '0';
}
void copy(char board[ROWS][COLS], char board1[ROWS][COLS],int col,int row)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = board1[i][j];
}
}
}
game.c函式的決議:
initboard 和 displayboard
兩個函式 一個是用來初始化字符陣列 一個是將字符陣列列印在螢屏上,玩家輸入的是坐標,而我們要輸出的是該坐標周圍有幾個雷,用來實際計算的陣列botboard我們將所有元素設為’0’,地雷設為0這樣我們在后面計算地雷數時會方便不少,showboard全部設為’*’,
setMine
用來設定地雷數,我們這里是藏十個地雷,呼叫函式rand()生成亂數來隨機取橫縱坐標,隨機坐標的值賦值為‘1’,但只在9*9的范圍里賦值,
findMine
這里是一個有回傳值的函式,這個函式的目的是先進行判斷如果輸入的那點值正好為’1’就回傳一個特定的值,如果不是就回傳周圍’1’的個數也就是炸彈數,由于字符在記憶體中是以ASCII碼的形式存盤所以炸彈數等于周圍八個坐標對應的值減去8*‘0’,這里還可以解釋為什么要將陣列設成1111的大小,如果輸入的坐標在最后一行/列或第一行/列那么他周圍只有3或5個坐標,如果分情況討論會過于麻煩,不如設成1111多出兩行兩列,最邊緣的點周圍也有八個點,而且由于setmine只在9*9的范圍里埋地雷顧多出來的行和列不影響結果,
遞回函式fun
程式到上面其實就可以正常運行了,但在玩的時候你會發現如果你輸入的點得到0即周圍八個點沒有地雷,那你還要再把周圍八個點的坐標一個一個輸入游戲才能繼續,如果輸出是零能自動展開周圍的點該多好呀!
void fun(char board2[ROWS][COLS],char board1[ROWS][COLS],char board[ROWS][COLS], int x, int y)
{
int ret = findMine(board, x, y);
if (ret == 0 && x > 0 && y > 0 && board2[x][y] != ' ')
{
board2[x][y] = ' ';
fun(board2, board1, board, x - 1, y);
fun(board2, board1, board, x + 1, y);
fun(board2, board1, board, x, y + 1);
fun(board2, board1,board, x, y - 1);
}
board1[x][y] = ret + '0';
}
void copy(char board[ROWS][COLS], char board1[ROWS][COLS],int col,int row)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = board1[i][j];
}
}
}
fun(botboard2, showboard, botboard, x, y);
char botboard2[ROWS][COLS];
copy(botboard2, botboard, COL, ROW);
這里我們采用遞回的方法這邊我傳進去了三個陣列botboard2,botboard,showboard,由后兩段代碼我們可以知道其實botboard2和botboard是兩個完全一樣的陣列但是地址不一樣,
每一次遞回我們先通過findmine函式的回傳值得到ret,然后我們判斷ret是否為0(即周圍八個點是否都沒有地雷) x > 0 && y > 0是判斷是否到達邊界防止遞回越過9*9邊界,board2[x][y] != ’ '判斷字串是否為我們已經檢查過的點不在進入遞回以防死回圈(比如由于我們是向一個點的四個方向遞回,第一個點右邊的點進入下一次遞回,第二層遞回四個方向 向左邊 的遞回實際上又回到了第一層遞回,但是board2[x][y] != ’ ‘的判斷條件可以免除這種情況發生),進入判斷的第一件事就是將我們檢查的點設成’ ‘,然后向該點的左右上下四個方向遞回,
botboard2 作用就是將遞回的點設成’ '防止死回圈
botboard 作用是由于botboard2遞回過的點值被修改會導致ret=findmine無法判斷周圍八個點是否為炸彈,故需要一個botboard2一模一樣的陣列來專門輸入findmine函式
這就是遞回函式的邏輯,這個函數是我自己想出來的,可能不是非常好但完成了目標,歡迎大家提出改進意見和自己的想法
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/281723.html
標籤:其他
