一、浅谈扫雷游戏的功能和实现
通过C语言,在命令台中,通过菜单显示,输入相应的序号实现相应的功能,
并且根据经典扫雷游戏,实现以下功能。
- 通过数组的下标输入,实现排雷的位置,如果周围有雷,那么排雷的位置将显示雷的数目。
- 如果踩到雷,将会显示其它雷并且结束游戏。
- 有标记功能,也能取消标记,并且限定标记的数目。
- 如果扫雷的位置周围没有雷,那么可以对整个位置制空。
二、扫雷功能的实现
1、测试页和头文件的准备
测试页Test.c,其中包括菜单和实现菜单里的部分功能。
//Test.c
#include"game.h"
void menu()
{
printf("**********************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("**********************\n");
}
//进入game的菜单,因为每次都要选择需要标记,还是扫雷,还是取消标记
void game()
{
char mine[ROWS][COLS] = { 0 }; //棋盘记录 雷('1')和非雷('0')的位置
char show[ROWS][COLS] = { 0 }; //显示的棋盘
char set[ROWS][COLS] = { 0 }; //记录的棋盘 记录有无排雷和有无标记
InitBoard(mine, '0');
InitBoard(show, '*');
InitBoard(set, '*');
SetMine(mine, ROW, COL);
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
int input = 0;
int win = 0;
int mark = 0; //记录标记数
do {
printf("【1、进入标记功能 2、进入排雷过程 3、取消标记功能 0、退出】\n\n");
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
Mark(show, set, ROW, COL, &mark);
break;
case 2:
DisplayBoard(show, ROW, COL); //先打印显示的扫雷盘
FindMine(show, mine, set, ROW, COL, &input); //然后进入扫雷功能
win = CheckNum(show, ROW, COL);
if (ROW * COL - EASY_COUNT == win)
{
printf("游戏获胜!\n");
input = 0;
}
break;
case 3:
DelMark(show, set, ROW, COL, &mark);
break;
case 0:
system("cls");
break;
default:
printf("输入错误,请重新输入。\n");
break;
}
} while (input);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do {
menu();
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出成功。\n");
break;
case 1:
game();
break;
default:
printf("输入错误,请重新输入。\n");
break;
}
} while (input);
return 0;
}
头文件game.h
//game.h
#include <stdio.h>
#include <time.h>
//显示的扫雷盘行列
#define ROW 9
#define COL 9
//实际的扫雷盘行列
#define ROWS ROW+2
#define COLS COL+2
//雷的数量
#define EASY_COUNT 10
//初始化数组
void InitBoard(char board[ROWS][COLS],char set);
//打印扫雷盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//设置雷的个数
void SetMine(char board[ROWS][COLS], int row, int col);
//如何找到雷以及找到雷的反应
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], char set[ROWS][COLS], int row, int col, int* input);
//扫雷位置周围没雷的制空
void set_blank(char show[ROWS][COLS], char set[ROWS][COLS], char mine[ROWS][COLS], int x, int y);
//标记功能
void Mark(char show[ROWS][COLS], char set[ROWS][COLS], int row, int col, int* mark);
//取消标记
void DelMark(char show[ROWS][COLS], char set[ROWS][COLS], int row, int col, int* mark);
//判断输赢的计数
int CheckNum(char show[ROWS][COLS], int x, int y);
下面实现具体的功能
2、初始化、打印和定义雷的数目功能实现
一个专门储存雷的数组雷盘,和一个供用户操作的数组雷盘
//game.c
//初始化
void InitBoard(char board[ROWS][COLS],char set)
{
int i = 0, j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
board[i][j] = set;
}
}
}
//打印扫雷盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("\n\n\t扫雷\n\n");
int i = 0, j = 0;
//为了方便看扫雷盘的横纵坐标位置,先打印一行的纵坐标数
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印一列的行坐标数
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]); //打印扫雷盘的字符
}
printf("\n");
}
printf("\n\n");
}
//设置雷的个数
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
3、排雷功能的实现
//game.c
//排雷功能的实现
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], char set[ROWS][COLS], int row, int col ,int* input)
{
int x = 0, y = 0;
printf("请输入坐标:");
scanf("%d%d", &x, &y);
while (show[x][y] != '*' && show[x][y]!='!' && x < 1 || y < 1 || x > row || y > col)
{
printf("输入错误,请重新输入.\n");
scanf("%d%d", &x, &y);
}
if (mine[x][y] == '1')
{
/*system("cls");*/
DisplayBoard(mine, ROW, COL);
printf("踩到雷了,游戏结束!\n");
(*input) = 0;
return;
}
//排雷的位置,以及展开
if (show[x][y] == '*' || show[x][y]=='!')//为*和已经标记了的位置!,都可以进行排雷
{
/*system("cls");*/
set_blank(show, set, mine, x, y);//进入制空
DisplayBoard(show, ROW, COL);
}
}
//在排雷的过程中,对制空和排雷位置计数的实现
void set_blank(char show[ROWS][COLS], char set[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
//防止越界
if (x<1 || x>ROW || y<1 || y>COL)
{
return;
}
//防止分布过多空白
if (mine[x][y] == '1')
{
return;
}
int i = 0, j = 0;
int count = get_mine_count(mine, x, y);
//如果周围八个不为0,这个点直接计雷数
if (count != 0)
{
show[x][y] = count + '0';
set[x][y] = '#'; //表示已经排过雷
}
//周围为0制空,以及递归遍历
if (count == 0)
{
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
//排过雷就跳过
if (set[i][j] == '#')
{
continue;
}
show[i][j] = ' ';
set[i][j] = '#';
set_blank(show, set, mine, i, j);
}
}
}
}
//因为是字符,所以在对排雷位置周围8个位置计数时,需要减去8个'0'得到int类型数。
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
mine[x][y - 1] + mine[x][y + 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
//输赢检查除雷数的其它字符个数
int CheckNum(char show[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = 1; i <= x; i++)
{
for (int j = 1; j <= y; j++)
{
if (show[i][j] != '*')
{
count++;
}
}
}
return count;
}
4、标记、和取消标记的实现
void Mark(char show[ROWS][COLS],char set[ROWS][COLS], int row, int col,int* mark)
{
if ((*mark) > EASY_COUNT)
{
printf("已超出最大标记数。\n");
return;
}
int x = 0, y = 0;
printf("请输入需要标记的位置:");
scanf("%d%d", &x, &y);
while(set[x][y] == '#')
{
printf("标记位置错误,请重新输入\n");
scanf("%d%d", &x, &y);
}
system("cls");
set[x][y] = show[x][y];//保存一下标记前的字符,取消的时候恢复
show[x][y] = '!';
(*mark)++;
DisplayBoard(show, ROW, COL);
}
void DelMark(char show[ROWS][COLS], char set[ROWS][COLS], int row, int col, int* mark)
{
if ((*mark) == 0)
{
printf("无标记。\n");
return;
}
int x = 0, y = 0;
printf("请输入需要取消标记的位置:");
scanf("%d%d", &x, &y);
while (show[x][y] != '!')
{
printf("选择取消标记位置错误,请重新输入\n");
scanf("%d%d", &x, &y);
}
system("cls");
show[x][y] = set[x][y];
set[x][y] = '*';
DisplayBoard(show, ROW, COL);
}
5、效果展示