《掃雷》是一款大衆類的益智小遊戲,于1992年發行。遊戲目标是在最短的時間内根據點選格子出現的數字找出所有非雷格子,同時避免踩雷,踩到一個雷即全盤皆輸。
以上就是掃雷的介紹,來源百度百科。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLwUFVNpXSE5UMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzgTN0ADM0MjM2EDMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
我在網上找了幾個掃雷遊戲,對于這樣一個遊戲我們不僅僅要實作其基礎的功能,還要為了提升遊戲性,我們需要實作兩個功能
1.當第一個為炸彈時,我們需要将炸彈移動到别的地方,防止遊戲一開始就結束
2.當我們選擇的位置周邊不存在炸彈,同時以九宮格的方式擴撒,将周邊不存在炸彈的地方展開,這樣可以大大的減去不必要的遊戲過程,提高遊戲的效率與可玩性。
這是我在CSDN上實作的第三個小遊戲,這個遊戲與前幾個很相似,我們可以借用前幾個的界面和架構來完成這個遊戲
1.https://blog.csdn.net/qq_35423154/article/details/101311952猜數字遊戲
2.https://blog.csdn.net/qq_35423154/article/details/102490244三子棋遊戲
首先我們需要明确思路,我們可以用兩個棋盤來實作這個遊戲,一個用來存放炸彈,一個用來給玩家顯示炸彈的位置及周圍的炸彈數,這樣一個二維的棋盤我們可以使用二維數組來實作。
二維數組的建立及初始化
例如我們要使用一個9X9的棋盤,我們這時候應當建立一個11X11的即(N+2)x(N+2)的棋盤
這是我們顯示的9X9的棋盤
但是我們實際建立的是這樣一個棋盤
為什麼我們需要在每一個邊界擴大一行呢?因為如果當我們要判斷周圍的雷數時,如果不擴大,對于邊界的格子可能要判斷周圍兩個位置,四個位置,而不在邊界的要判斷八個位置,我們需要增加多種條件,為了簡化算法,我們可以采用整體擴大一圈,來保證每一個格子周圍都有八個格子,而邊上擴大的那一圈僅僅輔助計算,不顯示也不作用。
宏定義棋盤及炸彈
我們設定好行數和列數之後,再在其基礎上設定一個輔助棋盤既周圍擴大一圈的棋盤,分别在行數及列數的基礎上加二,然後我們設定炸彈數,用宏定義的好處是後期修改時友善
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOMBSNUM 10
#define SAFENUM ROW*COL-BOMBSNUM
初始化棋盤
我們需要兩個棋盤,一個給使用者進行遊戲,一個用來存放炸彈。給使用者遊戲的我們全部初始化為‘*’,存放炸彈的全部初始化為‘0’
void InitBoard(char Board[ROWS][COLS], int row, int col,char ch)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Board[i][j] = ch;
}
}
}
用字元0和1來代表炸彈可以友善我們後面計算周圍的炸彈
放置炸彈
void SetBombs(char Bombs[ROWS][COLS], int row, int col, int Bombsnum)
{
int x, y,count=0;
while( count < Bombsnum )
{
x = rand() % row+1;
y = rand() % col+1;
if (Bombs[x][y] != '1')
{
Bombs[x][y] = '1';
count++;
}
}
}
使用rand函數來擷取一個随機的坐标,當坐标不重複并且所處位置沒有炸彈時,将炸彈存放
選擇菜單
void GameMenu()
{
printf("*****************************************\n");
printf("********* 1.開始遊戲 *********\n");
printf("********* 0.結束遊戲 *********\n");
printf("*****************************************\n");
}
顯示棋盤
void ShowBoard(char Board[ROWS][COLS], int row, int col)
{
int i, j;
printf(" ");
for (i = 0; i < row+1; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 0; i < row; i++)
{
if (0 == i)
printf(" |");
else
printf("|");
printf("---");
}
printf("|\n");
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (0 == j)
{
printf(" ");
printf("%d ", i + 1);
printf("|");
}
printf(" %c |", Board[i+1][j+1]);
}
printf("\n");
for (j = 0; j < col; j++)
{
if ( 0 == j )
printf(" |");
printf("---|");
}
printf("\n");
}
}
用這個函數來向玩家展示棋盤,這裡用了大量的代碼其實是我為了讓棋盤更加美觀,可根據情況修改。
以上的幾個函數在上一個三子棋遊戲中都詳細講解過,在這裡就不再多提
計算周圍的炸彈數
因為我們之前初始化時将用字元1代表有炸彈,字元0代表沒有炸彈,是以在這一部我們可以利用這個規律,來計算周圍的炸彈數。
我們隻需要讓選擇的位置周邊的八個格子全部相加,然後減去8個字元0,即可得到整形的炸彈數,而後在主函數給其加上一個字元0将其将這個數字變為字元型的對應數字,存放與棋盤中
int BombsAround(char Bombs[ROWS][COLS], int row, int col)
{
return Bombs[row - 1][col] + Bombs[row + 1][col] + Bombs[row][col - 1] + Bombs[row][col + 1] + Bombs[row + 1][col + 1] + Bombs[row - 1][col - 1] + Bombs[row + 1][col - 1] + Bombs[row - 1][col + 1] - 8 * '0';
}
勝利條件
int WinGame(char Board[ROWS][COLS], int row, int col)
{
int i, j, flag=0;
for (i=1; i < row; i++)
{
for (j = 1; j < col; j++)
{
if (Board[i][j] == '*')
flag++;
}
}
return flag;
}
我們隻需要計算棋盤中剩餘的炸彈數,當剩餘炸彈數等于我們的放置炸彈數時,遊戲勝利
附加功能1.當第一次為炸彈時,将炸彈移動到别的位置
這個函數實作的方法很簡單,我們隻需要将那個位置的炸彈清空,再次調用SetBombs函數重新設定一個炸彈即可
int BombsAround(char Bombs[ROWS][COLS], int row, int col)
{
return Bombs[row - 1][col] + Bombs[row + 1][col] + Bombs[row][col - 1] + Bombs[row][col + 1] + Bombs[row + 1][col + 1] + Bombs[row - 1][col - 1] + Bombs[row + 1][col - 1] + Bombs[row - 1][col + 1] - 8 * '0';
}
附加功能2.當我們選擇的位置周邊不存在炸彈,同時以九宮格的方式擴撒,将周邊不存在炸彈的地方展開
這項功能是windows系統掃雷自帶的一個功能,當我們選擇一個周圍沒有炸彈的位置時,以九宮格擴撒,将不存在炸彈的格子顯示為空,直到有一個格子周圍存在炸彈。
這個很簡單我們就可以想到用遞歸函數來完成,我們隻需要用BombsAround函數來知道這個位置是否存在炸彈,如果不存在則顯示為‘ ’,如果存在則顯示炸彈數。我們可以用遞歸的方式,當位置合法時,對他周圍的八個格子進行判斷,那八個格子再對他們周圍的格子進行判斷
void SafeArea(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int flag;
flag = BombsAround(Bombs, row, col);
if (0 == flag)
{
Board[row][col] = ' ';
if (row - 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row - 1][col] == '*')
SafeArea(Board, Bombs, row-1, col);
if (row + 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row +1 ][col] == '*')
SafeArea(Board, Bombs, row+1, col);
if (row > 0 && row <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row][col - 1] == '*')
SafeArea(Board, Bombs, row, col-1);
if (row > 0 && row <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row][col + 1] == '*')
SafeArea(Board, Bombs, row, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row - 1][col - 1] == '*')
SafeArea(Board, Bombs, row-1, col-1);
if (row + 1 > 0 && row + 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row + 1][col + 1] == '*')
SafeArea(Board, Bombs, row+1, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row - 1][col + 1] == '*')
SafeArea(Board, Bombs, row-1, col+1);
if (row + 1 > 0 && row + 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row + 1][col - 1] == '*')
SafeArea(Board, Bombs, row+1, col-1);
}
else
{
Board[row][col] = BombsAround(Bombs, row, col) + '0';
}
}
排查炸彈
char FindBombs(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int x, y,count=1;
while(count)
{
printf("請輸入行和列:\n");
scanf("%d%d",&x,&y);
system("CLS");
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (Bombs[x][y] == '1')
{
if (1 == count)
{
MoveBomb(Bombs, x, y);
Board[x][y] = BombsAround(Bombs, x, y) + '0';
if (Board[x][y] == '0')
Board[x][y] = ' ';
SafeArea(Board, Bombs, x, y);
count++;
}
else
{
ShowBoard(Bombs, ROW, COL);
return 'F';
}
}
else if (Board[x][y] >= '0' && Board[x][y] <= '8' && Board[x][y] != '*' && Board[x][y] != ' ')
{
printf("該坐标已被輸入\n");
}
else
{
Board[x][y] = BombsAround(Bombs, x, y) + '0';
SafeArea(Board, Bombs, x, y);
count++;
if (WinGame(Board, ROW, COL) == BOMBSNUM)
return 'T';
ShowBoard(Board, ROW, COL);
}
}
else
{
printf("輸入錯誤,請重新輸入\n");
}
}
}
我們需要判斷輸入是否合法,輸入是否重複,如果錯誤則重新輸入,當輸入的位置為炸彈時,如果是第一次輸入,則将炸彈移動到别的位置,如果不是第一次輸入,則代表遊戲失敗,傳回’F’。每次輸入完一個位置,我們将那個位置周圍的炸彈數儲存到Board棋盤中,既玩家看到的棋盤,然後再使用展開函數來展開周圍不必要的格子,并且用WinGame函數判斷是否勝利
遊戲控制函數
void Game(char Board[ROWS][COLS],char Bombs[ROWS][COLS], int row, int col)
{
char flag;
InitBoard(Board, ROWS, COLS, '*');
InitBoard(Bombs, ROWS, COLS, '0');
SetBombs(Bombs, ROW, COL, BOMBSNUM);
ShowBoard(Board, ROW, COL);
printf("\n");
ShowBoard(Bombs, ROW, COL);
flag = FindBombs(Board, Bombs, ROW, COL);
if (flag == 'F')
{
printf("該位置為炸彈,遊戲結束\n");
}
else
{
printf("遊戲勝利\n");
}
}
這一步隻需調用前面寫完的函數即可
完整代碼
game.c
#include "head.h"
void GameMenu()
{
printf("*****************************************\n");
printf("********* 1.開始遊戲 *********\n");
printf("********* 0.結束遊戲 *********\n");
printf("*****************************************\n");
}
void InitBoard(char Board[ROWS][COLS], int row, int col,char ch)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Board[i][j] = ch;
}
}
}
//初始化
void SetBombs(char Bombs[ROWS][COLS], int row, int col, int Bombsnum)
{
int x, y,count=0;
while( count < Bombsnum )
{
x = rand() % row+1;
y = rand() % col+1;
if (Bombs[x][y] != '1')
{
Bombs[x][y] = '1';
count++;
}
}
}
//放置炸彈
void ShowBoard(char Board[ROWS][COLS], int row, int col)
{
int i, j;
printf(" ");
for (i = 0; i < row+1; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 0; i < row; i++)
{
if (0 == i)
printf(" |");
else
printf("|");
printf("---");
}
printf("|\n");
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (0 == j)
{
printf(" ");
printf("%d ", i + 1);
printf("|");
}
printf(" %c |", Board[i+1][j+1]);
}
printf("\n");
for (j = 0; j < col; j++)
{
if ( 0 == j )
printf(" |");
printf("---|");
}
printf("\n");
}
}
//界面
int BombsAround(char Bombs[ROWS][COLS], int row, int col)
{
return Bombs[row - 1][col] + Bombs[row + 1][col] + Bombs[row][col - 1] + Bombs[row][col + 1] + Bombs[row + 1][col + 1] + Bombs[row - 1][col - 1] + Bombs[row + 1][col - 1] + Bombs[row - 1][col + 1] - 8 * '0';
}
//算出周圍的炸彈
void MoveBomb(char Bombs[ROWS][COLS], int row, int col)
{
Bombs[row][col] = '0';
SetBombs(Bombs, ROW, COL, 1);
}
//防止玩家第一次選擇時遇到炸彈,将炸彈移動到别的位置
void SafeArea(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int flag;
flag = BombsAround(Bombs, row, col);
if (0 == flag)
{
Board[row][col] = ' ';
if (row - 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row - 1][col] == '*')
SafeArea(Board, Bombs, row-1, col);
if (row + 1 > 0 && row + 1 <= ROW && col > 0 && col <= COL && Board[row +1 ][col] == '*')
SafeArea(Board, Bombs, row+1, col);
if (row > 0 && row <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row][col - 1] == '*')
SafeArea(Board, Bombs, row, col-1);
if (row > 0 && row <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row][col + 1] == '*')
SafeArea(Board, Bombs, row, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row - 1][col - 1] == '*')
SafeArea(Board, Bombs, row-1, col-1);
if (row + 1 > 0 && row + 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row + 1][col + 1] == '*')
SafeArea(Board, Bombs, row+1, col+1);
if (row - 1 > 0 && row - 1 <= ROW && col + 1 > 0 && col + 1 <= COL && Board[row - 1][col + 1] == '*')
SafeArea(Board, Bombs, row-1, col+1);
if (row + 1 > 0 && row + 1 <= ROW && col - 1 > 0 && col - 1 <= COL && Board[row + 1][col - 1] == '*')
SafeArea(Board, Bombs, row+1, col-1);
}
else
{
Board[row][col] = BombsAround(Bombs, row, col) + '0';
}
}
int WinGame(char Board[ROWS][COLS], int row, int col)
{
int i, j, flag=0;
for (i=1; i < row; i++)
{
for (j = 1; j < col; j++)
{
if (Board[i][j] == '*')
flag++;
}
}
return flag;
}
char FindBombs(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col)
{
int x, y,count=1;
while(count)
{
printf("請輸入行和列:\n");
scanf("%d%d",&x,&y);
system("CLS");
if (x > 0 && x <= row && y > 0 && y <= col)
{
if (Bombs[x][y] == '1')
{
if (1 == count)
{
MoveBomb(Bombs, x, y);
Board[x][y] = BombsAround(Bombs, x, y) + '0';
SafeArea(Board, Bombs, x, y);
count++;
}
else
{
ShowBoard(Bombs, ROW, COL);
return 'F';
}
}
else if (Board[x][y] >= '0' && Board[x][y] <= '8' && Board[x][y] != '*' && Board[x][y] != ' ')
{
printf("該坐标已被輸入\n");
}
else
{
Board[x][y] = BombsAround(Bombs, x, y) + '0';
SafeArea(Board, Bombs, x, y);
count++;
if (WinGame(Board, ROW, COL) == BOMBSNUM)
return 'T';
ShowBoard(Board, ROW, COL);
}
}
else
{
printf("輸入錯誤,請重新輸入\n");
}
}
}
//排查炸彈
void Game(char Board[ROWS][COLS],char Bombs[ROWS][COLS], int row, int col)
{
char flag;
InitBoard(Board, ROWS, COLS, '*');
InitBoard(Bombs, ROWS, COLS, '0');
SetBombs(Bombs, ROW, COL, BOMBSNUM);
ShowBoard(Board, ROW, COL);
printf("\n");
flag = FindBombs(Board, Bombs, ROW, COL);
if (flag == 'F')
{
printf("該位置為炸彈,遊戲結束\n");
}
else
{
printf("遊戲勝利\n");
}
}
test.c
#include "head.h"
int main()
{
int n;
char board[ROWS][COLS] = { 0 };
char bombs[ROWS][COLS] = { 0 };
srand((int)time(NULL));
do
{
GameMenu();
printf("請輸入您的選擇:\n");
scanf("%d", &n);
switch (n)
{
case 1:
Game(board, bombs, ROWS, COLS);
break;
case 0:
printf("結束遊戲\n");
break;
default:
printf("輸入錯誤,請重新輸入\n");
break;
}
}while (n);
return 0;
}
head.h
#define _CRT_SECURE_NO_WARNINGS
#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 BOMBSNUM 10
void GameMenu();
void InitBoard(char Board[ROWS][COLS], int row, int col, char ch);
void SetBombs(char Bombs[ROWS][COLS], int row, int col, int Bombsnum);
void ShowBoard(char Board[ROWS][COLS], int row, int col);
int BombsAround(char Bombs[ROWS][COLS], int row, int col);
char FindBombs(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col);
void Game(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col);
void MoveBomb(char Bombs[ROWS][COLS], int row, int col);
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);
void SafeArea(char Board[ROWS][COLS], char Bombs[ROWS][COLS], int row, int col);
代碼連結:
https://github.com/HONGYU-LEE/test/tree/master/project/Mine%20sweeper