C语言实现扫雷
目录结构和上一个五子棋一样一样的,参考五子棋程序。
代码流程
- 用户输入,选择游戏难度,此处使用了枚举常量PLAY1和PLAY2与SWitch语句进行搭配使用,可以让代码清晰明了。
- 进入游戏的流程是根据用户输入的难度选择,创建不同大小的数组空间(棋盘),这里本应该用malloc动态申请内存来做,但是偷懒,我用符号常量定义了一个大空间ROW行和COL列,在选择处做一个判断,分别传入不同的row和col。并且定义了EASY_COUNT 和MIDDLE_COUNT两个不同难度的雷数。
- 完成随机布雷,展示棋盘,玩家排雷,以及最重要的棋盘展开函数。
重点难点注意点
怎么判断是胜利,还是爆炸?
如果玩家输入的坐标是雷,那么就原地爆炸,如果玩家把所有尚未点开的块都点开了并且没有遇到雷,那么则是胜利,
if(show[i][j]=='*' && board[i][j]=='0')
这句话意思是如果还有尚未点开的地图并且这里没有雷,那么就还不算胜利,这个在FindMine函数里。棋盘展开函数
其实就是一个深度优先遍历搜索,从当前坐标开始不断的向周围四个方向进行探索,如果碰到show矩阵有数字的地方就停止展开(或者碰到边界),把走过的show矩阵全部置为空格,这样就达到了爆炸的效果。需要注意的是,深搜需要一个used数组,和棋盘一样大小,记录已经走过的格子,不然走过一边又走一遍会无限死循环的。
代码重用性
我们用一个show数组表示显示的棋盘,一个board数组表示雷的分布,初始化函数多传入一个 set 参数,能避免多写一遍代码。而且两个数组都是char型,很方便的在 ‘*’ ‘数字’ ‘空格’,之间转化,记得算雷数时减去周围的8个’0’ 的ascii码值就可以了。
运行结果
game.h
#ifndef __GAME_H__
#define __GAME_H__
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
enum OPTION
{
EXIT,
PLAY,
PLAY2,
};
#define EASY_COUNT 5 //7x7
#define MIDDLE_COUNT 90 //10x10
#define ROW 30
#define COL 30
void InitBoard(char board[ROW][COL], int row, int col, char set);
void Display(char board[ROW][COL], int row, int col);
void SetMine(char board[ROW][COL], int row, int col, int mine);
void SpreadMine(char board[ROW][COL],char show[ROW][COL],int used[ROW][COL], int row, int col, int x, int y);
int FindMine(char board[ROW][COL],char show[ROW][COL], int row, int col);
int PlayerMove(char board[ROW][COL], char show[ROW][COL], int row, int col,int mine, int step);
#endif //__GAME_H__
game.c
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col, char set){
memset(board,set,row*col*sizeof(char));
}
void Display(char board[ROW][COL], int row, int col){
int i = 0;
int j = 0;
for(i=1; i<row; i++){
if(i == 1){
for(j=1; j<col;j++){
if(1 == j){
printf(" ");
}
printf("|%2d|",j);
}
printf("\n");
for(j=1; j<col;j++){
if(1 == j){
printf(" ");
}
printf(" ---");
}
printf("\n");
}
for(j=1; j<col;j++){
if(1 == j){
printf("%2d|",i);
}
printf("| %c ",board[i][j]);
}
printf("|\n");
for(j=1; j<col;j++){
if(1 == j){
printf(" ");
}
printf(" ---");
}
printf("\n");
}
printf("\n");
}
void SetMine(char board[ROW][COL], int row, int col, int mine){
int x = 0;
int y = 0;
int i = 0;
for(i=0; i<mine; i++){
x = rand()%(row-1)+1; //因为传的row是8 如果7X7生成1~7的随机数
y = rand()%(col-1)+1;
while(board[x][y] == '1'){ //该位置已经有雷,重新选位置。
x = rand()%(row-1)+1;
y = rand()%(col-1)+1;
}
board[x][y] = '1';
}
}
int FindMine(char board[ROW][COL],char show[ROW][COL], int row, int col){
//正常展开返回0,没有可扫的雷了返回1
int flag = 0;
int i = 0, j = 0;
for(i=1; i<row; i++){
for(j=1; j<col;j++){
if(show[i][j]=='*' && board[i][j]=='0'){ //还有没扫出来的雷
flag = 1;
break;
}
}
}
if(!flag){
return 1;
}
return 0;
}
void SpreadMine(char board[ROW][COL],char show[ROW][COL],int used[ROW][COL], int row, int col, int x, int y){
int tmp = 0;
int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
int tmpx = 0, tmpy = 0;
int i;
if(x<1||y<1||x>=row||y>=col||used[x][y]== -1){ //row是8,到下标为8就到边界了
return;
}
used[x][y] = -1; //访问过了
if(board[x][y]=='0'){
show[x][y]= ' ';
}
tmp = board[x-1][y-1]+board[x-1][y+0]+board[x-1][y+1]+board[x+0][y-1]+board[x+0][y+1]+board[x+1][y-1]+board[x+1][y+0]+board[x+1][y+1]- 8*'0';
if(tmp > 0 ){
show[x][y] = tmp + '0';
return;
}
for(i=0; i<4; i++){
tmpx = x +dir[i][0];
tmpy = y +dir[i][1];
SpreadMine(board, show, used, row, col, tmpx, tmpy);
}
}
int PlayerMove(char board[ROW][COL], char show[ROW][COL], int row, int col,int mine, int step){
int x = 0;
int y = 0;
int i = 0, j = 0;
int used[ROW][COL]= {0};
while(1){
printf("请输入你要排雷的坐标(x,y)->\n");
scanf("%d%d",&x,&y);
if(0 == step && board[x][y] == '1'){
//第一步是雷则把雷移开
for(i=1; i<row; i++){
for(j=1; j<col; j++){
if(board[i][j]=='0'){
board[i][j]='1';
board[x][y]='0';
break;
}
}
}
}
if(x>=row|| y>=col|| x<=0|| y<=0){
printf("输入非法的位置!\n");
continue;
}
else if(show[x][y] != '*'){
printf("这个位置你已经知道啦!\n");
continue;
}
else if(board[x][y]=='1'){
Display(board, row, col);
printf("************你炸了**********\n");
return 1;
}
break; //合法输入
}
SpreadMine(board, show, used, row, col, x, y);
return 0;
}
test.c
#include <stdio.h>
#include "game.h"
#define _CRT_SECURE_NO_WARNINGS 1
void menu(){
printf("**************************************\n");
printf("********来一盘惊险刺激的扫雷吧********\n");
printf("********1.入门级别********************\n");
printf("********2.进阶级别********************\n");
printf("********0.退出游戏********************\n");
printf("**************************************\n");
}
void game(int choice){
char board[ROW][COL];
char show[ROW][COL];
int ret = 0; //玩家走一步之后的输赢结果
int row = 0;
int col = 0;
int step = 0; //已走步数
int mine = 0; //雷数
if(choice == 1){
row = 8;
col = 8;
mine = EASY_COUNT;
}
else {
row = 11;
col = 11;
mine = MIDDLE_COUNT;
}
InitBoard(board, ROW, COL, '0'); //初始化雷阵
InitBoard(show, ROW, COL, '*'); //初始化显示矩阵
srand((unsigned int)time(NULL));
SetMine(board, row, col, mine);
// Display(board, row, col);
Display(show, row, col);
while(1){
ret = PlayerMove(board,show, row, col, mine, step);
if(1 == ret){
break;
}
ret = FindMine(board, show, row, col);
if(1 == ret){
Display(show, row, col);
Display(board, row, col);
printf("**************革命成功了!*******\n");
break;
}
//Display(board, row, col);
Display(show, row, col);
step ++;
}
}
int main(){
int choice = 0;
do{
menu();
printf("请输入->");
scanf("%d",&choice);
switch(choice){
case PLAY:game(choice);
continue;
case PLAY2:game(choice);
continue;
case EXIT:printf("退出游戏");
break;
default:printf("没有这个选项,请重新输入\n");
continue;
}
}while(choice);
}