C语言写扫雷游戏——扩展版
通过学习我们已经知道了如何写出一个单机版的扫雷游戏,这里对扫雷程序进行优化,主要有两个方面:1.实现第一次点击时不是雷;2.实现扫雷时的递归展开。
同之前的三子棋游戏一样,扫雷游戏我们依旧采用分文件形式书写,并继续加深对数组的认识。
扫雷规则如下:
1.数字是几就表示此数字位置邻接的八个方向有几个地雷
2.输入坐标
如果这个位置上的数字为1-8 即显示;
如果为数字为0(即空白) 自动延伸八个方向直到遇见数字(1-8)为止;
如果为地雷,游戏失败
4.当地图上的所有数字都点击完毕 游戏胜利
首先说一下对扫雷基本程序的编写:
我们同样利用一个数组来表示扫雷界面,扫雷中又多了一个数组来表示雷,并对它们初始化:
char mine_board[ROW][COL]; //雷的表
char show_board[ROW][COL]; //棋盘界面
memset(mine_board, '0', sizeof(mine_board)); //数组初始化
memset(show_board, '*', sizeof(show_board)); //数组初始化
main.c中写主体,在主函数中写出游戏的主体框架,主要是根据扫雷游戏的玩法来实现,并打印出菜单项;
game.h中写需要的库及几个函数的声明;
game.c中写各个函数的实现;
然后来说扩展的第一点,实现扫雷时输入的第一个坐标不是雷,
玩家输入第一个坐标时,如果是雷,首先确定是玩家第一次扫雷,因为布有20个雷(即NUM设为20),表格为100个,所以当times=80时就是第一次扫雷:
int times = 100 - NUM;
然后遍历周围的坐标,将周围每雷的位置与当前位置的值交换即可.:
需要在布雷函数中通过指针将找到的没雷坐标的值带出,
void SetMine(char board[][COL], int row, int col,int *x_p,int *y_p){...}
int i = 1;
for (; i <= 10; i++){
int j = 1;
for (; j <= 10; j++){
if (board[i][j] == '0'){
*x_p = i;
*y_p = j;
return;
}
}
}
在布雷时,传入没雷的两个参数:
int no_x;
int no_y;
SetMine(mine_board,ROW,COL,&no_x,&no_y); //布雷
然后再将当前位置设为0(即没雷),并将找到的没雷的位置设为雷:
if (times == 80){
mine_board[x][y] = '0';
mine_board[no_x][no_y] = '1';
}
最后来说扫雷时递归展开的实现,主要是对函数GetCount的改编,使用递归方法遍历一个做表周围雷的个数,并对其展开,实现如下:
//使用递归遍历周围雷的个数
int GetCount(char board[][COL],char show[][COL], int x, int y)
{
if (board[x][y] == '0')
{
int count = 0;
if (board[x - 1][y] == '1')
count++;
if (board[x - 1][y - 1] == '1')
count++;
if (board[x][y - 1] == '1')
count++;
if (board[x + 1][y - 1] == '1')
count++;
if (board[x + 1][y] == '1')
count++;
if (board[x + 1][y + 1] == '1')
count++;
if (board[x][y + 1] == '1')
count++;
if (board[x - 1][y + 1] == '1')
count++;
show[x][y] = (count + '0'); //确定是字符数字
}
if (show[x][y] == '0')
{
if (show[x - 1][y] == '*')
GetCount(board, show, x - 1, y);
if (show[x - 1][y - 1] == '*')
GetCount(board, show, x - 1, y - 1);
if (show[x][y - 1] == '*')
GetCount(board, show, x, y - 1);
if (show[x + 1][y - 1] == '*')
GetCount(board, show, x + 1, y - 1);
if (show[x + 1][y] == '*')
GetCount(board, show, x + 1, y);
if (show[x + 1][y + 1] == '*')
GetCount(board, show, x + 1, y + 1);
if (show[x][y + 1] == '*')
GetCount(board, show, x, y + 1);
if (show[x - 1][y + 1] == '*')
GetCount(board, show, x - 1, y + 1);
}
return board[x-1][y-1] + board[x-1][y] + board[x-1][y+1] + board[x][y-1] + \
board[x][y+1] + board[x+1][y-1] + board[x+1][y] + board[x+1][y+1] - 8 * '0';
}
然后在Game函数中判断当前坐标不为雷,且周围雷的个数为0时,调用GetCount函数,递归展开坐标周围区域,自动延伸八个方向直到遇见数字(1-8)为止,实现如下:
if (mine_board[x][y] == '0'){
GetCount(mine_board, show_board, x, y);
ShowBoard(mine_board, ROW, COL);
}
最终源代码实现如下:
main.c:
#include "game.h"
void Menu()
{
printf("################################\n");
printf("###### 欢迎来到扫雷游戏 ######\n");
printf("################################\n");
printf("###### 1.play 2.exit ######\n");
printf("################################\n");
printf("Please Select:> ");
}
int main()
{
int quit = 0;
while (!quit){
Menu();
int select = 0;
scanf("%d", &select);
switch (select){
case 1:
Game();
break;
case 2:
quit = 1; //退出
break;
default:
printf("你选择有误,重新输入!\n");
break;
}
}
printf("Bye Bye!\n");
system("pause\n");
return 0;
}
game.h
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#pragma warning(disable:4996)
#define ROW 12
#define COL 12
#define NUM 20
//布雷
void SetMine(char board[][COL], int row, int col, int *x_p, int *y_p);
//显示棋盘
void ShowBoard(char board[][COL], int row, int col);
//统计周围雷的个数
int GetCount(char board[][COL], char show[][COL], int x, int y);
void Game();
#endif
game.c
#include "game.h"
//布雷
void SetMine(char board[][COL], int row, int col,int *x_p,int *y_p)
{
int count = NUM;
while (count > 0){
int x = rand() % (row - 2) + 1;
int y = rand() % (col - 2) + 1;
//放雷
if (board[x][y] == '0'){
count--;
board[x][y] = '1';
}
}
int i = 1;
for (; i <= 10; i++){
int j = 1;
for (; j <= 10; j++){
if (board[i][j] == '0'){
*x_p = i;
*y_p = j;
return;
}
}
}
}
//显示棋盘界面
void ShowBoard(char board[][COL], int row, int col)
{
printf(" ");
int i = 1;
for (; i <= 10; i++){
printf(" %d ", i);
}
printf("\n");
for (i = 1; i <= 10; i++){
printf("----");
}
printf("---\n");
for (i = 1; i <= 10; i++){
printf("%2d|", i);
int j = 1;
for (; j <= 10; j++){
printf(" %c |", board[i][j]);
}
printf("\n");
int k = 1;
for (k = 1; k <= 10; k++){
printf("----");
}
printf("---\n");
}
}
//使用递归遍历周围雷的个数
int GetCount(char board[][COL],char show[][COL], int x, int y)
{
if (board[x][y] == '0')
{
int count = 0;
if (board[x - 1][y] == '1')
count++;
if (board[x - 1][y - 1] == '1')
count++;
if (board[x][y - 1] == '1')
count++;
if (board[x + 1][y - 1] == '1')
count++;
if (board[x + 1][y] == '1')
count++;
if (board[x + 1][y + 1] == '1')
count++;
if (board[x][y + 1] == '1')
count++;
if (board[x - 1][y + 1] == '1')
count++;
show[x][y] = (count + '0'); //确定是字符数字
}
if (show[x][y] == '0')
{
if (show[x - 1][y] == '*')
GetCount(board, show, x - 1, y);
if (show[x - 1][y - 1] == '*')
GetCount(board, show, x - 1, y - 1);
if (show[x][y - 1] == '*')
GetCount(board, show, x, y - 1);
if (show[x + 1][y - 1] == '*')
GetCount(board, show, x + 1, y - 1);
if (show[x + 1][y] == '*')
GetCount(board, show, x + 1, y);
if (show[x + 1][y + 1] == '*')
GetCount(board, show, x + 1, y + 1);
if (show[x][y + 1] == '*')
GetCount(board, show, x, y + 1);
if (show[x - 1][y + 1] == '*')
GetCount(board, show, x - 1, y + 1);
}
return board[x-1][y-1] + board[x-1][y] + board[x-1][y+1] + board[x][y-1] + \
board[x][y+1] + board[x+1][y-1] + board[x+1][y] + board[x+1][y+1] - 8 * '0';
}
void Game()
{
srand((unsigned long)time(NULL)); //埋随机种子
char mine_board[ROW][COL]; //雷的表
char show_board[ROW][COL]; //棋盘界面
memset(mine_board, '0', sizeof(mine_board)); //数组初始化
memset(show_board, '*', sizeof(show_board)); //数组初始化
//SetMine(mine_board, ROW, COL); //布雷
int no_x;
int no_y;
SetMine(mine_board,ROW,COL,&no_x,&no_y); //布雷
int x = 0;
int y = 0;
int times = 100 - NUM;
do{
system("cls");
ShowBoard(show_board, ROW, COL); //显示棋盘界面
printf("Please Enter<x,y>:");
scanf("%d %d", &x, &y);
//判断输入是否合法
if (x<1 || x>10 || y<1 || y>10){
printf("你输入的坐标有误,请重新输入!\n");
continue;
}
//判断输入位置是否被排过
if (show_board[x][y] != '*'){
printf("该位置已被排除,请重新输入!\n");
continue;
}
//检查是否有雷
if (mine_board[x][y] == '1'){
//判断第一次扫雷时
if (times == 80){
mine_board[x][y] = '0';
mine_board[no_x][no_y] = '1';
}
else{
printf("Game Over!\n");
ShowBoard(mine_board, ROW, COL);
break;
}
}
if (mine_board[x][y] == '0'){
GetCount(mine_board, show_board, x, y);
ShowBoard(mine_board, ROW, COL);
}
int count = GetCount(mine_board,show_board, x, y);//统计周围雷的个数
show_board[x][y] = count + '0';
times--;
} while (times > 0);
}
最后就是写完该游戏的总结了,具体实现在前面已经说了,收获点有几个:
1.认识到了一个初始化函数memset(),要在game.h中添加相应的库,可以通过Cplusplus.com手册中查看它的使用方法:
#include <string.h>
memset(mine_board, '0', sizeof(mine_board)); //数组初始化
memset(show_board, '*', sizeof(show_board)); //数组初始化
2.游戏的整个编写中都用到了二维数组的使用,继三子棋后继续加深对数组的认识;
3.埋随机种子,随机数的使用。
扫雷游戏还可以继续做更多的优化,比如闯关模式,雷的标记,MFC界面化实现等等,这些就需要我在多加学习之后再去实现了。
博客有什么问题希望大家可以在评论区提出。