这是我C语言学习过程中第二个趣味小程序,相较井字棋,扫雷更加复杂,也花费了我更长的时间。
首先依旧是开始菜单和main函数,这和井字棋没有任何不同
void menu()//菜单
{
printf("*****************************************\n");
printf("*****************************************\n");
printf("*********** 开始游戏输入1 ************\n");
printf("*********** 退出游戏输入0 ************\n");
printf("*****************************************\n");
printf("*****************************************\n");
}
int main()
{
int i = 0;
for (; i == 0;)
{
menu();
scanf("%d", &i);
if (i == 0)
{
printf("游戏结束\n");
return 0;
}
if (i != 1 && i != 0)
{
i = 0;
printf("输入错误\n");
}
}
//是否开始游戏
printf("开始游戏\n");
game();
system("pause");
return 0;
}
之后就要实现扫雷的功能了,为了实现扫雷,我们要创建两个棋盘,一个是玩家看到的,被打印出来的棋盘,还有一个是真正,埋有地雷的棋盘。期中真正的棋盘要比显示的棋盘大一圈,对接下来的程序运行有利。
void initboard_real(char board[row+2][col+2], int ROL,int COL)//初始化不显示的棋盘
{
int i = 0;
int j = 0;
for (i = 1; i < row+1; i++)
{
for (j = 1; j < col+1; j++)
{
board[i][j] = ' ';
}
}
}
void initboard_show(char board[row][col], int ROL, int COL)//初始化显示的棋盘
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = '#';
}
}
}
然后就要埋地雷,我这里依然采用了真随机。
void setmine(char board[row+2][col+2], int ROW, int COL)//设置地雷
{
int i = 0;
int a = 0;
int b = 0;
for (i = 0; i < 8 ;)
{
a = randNext(1, 10);
b = randNext(1, 10);
if (board[a][b] == ' ')
{
i++;
board[a][b] = '*';
}
}
}
//返回[left, right]的随机数
int randNext(int left, int right)
{
static unsigned int seed = 0;
seed++;
srand((unsigned)time(NULL) + seed * seed);
return rand() % (right - left + 1) + left;
}
打印棋盘和井字棋没什么区别
void printboard(char board[row][col], int ROW, int COL)//打印显示的棋盘
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" ""%c"" |", board[i][j]);
}
printf("\n");
if (i < row - 1)
{
printf("---|---|---|---|---|---|---|---|---|---|\n");
}
}
}
然后就要设计玩家行动函数。首先玩家行动要判断踩到的是不是雷,还要显示出周围的地雷数量。
int player_move(char board1[row+2][col+2],char board2[row][col], int a, int b)//玩家行动
{
for (;;)
{
if (board1[a+1][b+1] == ' ')
{
char ch = count_mine(board1,a+1, b+1);
board2[a][b] = ch+'0';
return 1;//没踩到雷返回1
}
else
{
return 0;//踩到雷返回0
}
break;
}
}
int count_mine(char board[row+2][col+2], int x, int y)//检测周围8个区域雷的个数
{
int count = 0;
if (board[x - 1][y - 1] == '*')
count++;
if (board[x - 1][y] == '*')
count++;
if (board[x - 1][y + 1] == '*')
count++;
if (board[x][y - 1] == '*')
count++;
if (board[x][y + 1] == '*')
count++;
if (board[x + 1][y - 1] == '*')
count++;
if (board[x + 1][y] == '*')
count++;
if (board[x + 1][y + 1] == '*')
count++;
return count;
}
然后我们要设计判断胜负的函数。胜利条件为未揭示的格子为地雷数,即为胜利。同时踩到雷会显示出整个棋盘。
int result(char board[row][col])//判断是否胜利
{
int i = 0;
int j = 0;
int count = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (board[i][j] == '#')//如果未点开,计数器加一
count++;
}
}
if (count == 8)//如果未点开的格子等于雷数,胜利
return 1;
else return 0;
}
void endboard(char board[row+2][col+2], int ROW, int COL)//踩到雷后展示棋盘
{
int i = 1;
int j = 1;
for (i = 1; i < row+1; i++)
{
for (j = 1; j < col+1; j++)
{
printf(" ""%c"" |", board[i][j]);
}
printf("\n");
if (i < row - 1)
{
printf("---|---|---|---|---|---|---|---|---|---|\n");
}
}
}
其实现在扫雷就已经基本完成了,但如果要和真正扫雷一模一样,还要加上周围如果一颗雷都没有,自动展开周围格子的功能。这是本程序的难点,也是我编写时间最长的一个点。这个函数通过递归实现了无限展开周围没有雷的格子。
void showmine(char board1[row + 2][col + 2], char board2[row][col], int a, int b)//展开0颗雷附近所有格
{
if (board2[a][b] == '0')//如果周围为0颗雷,显示周围所有格子
{
int i = -1;
int j = -1;
for (i=-1; i < 2; i++)
{
for (j=-1;j < 2; j++)
{
if (board2[a + i][b + j] == '#' && a + i >= 0 && a + i < 10 && b + j >= 0 && b + j < 10)//如果格子未揭示且未超出范围就检测他附近的地雷
{
char ch = count_mine(board1, a + i+1, b + j+1);
board2[a+i][b+j] = ch + '0';
showmine(board1, board2, a + i, b + j);
}
}
}
}
}
然后再游戏函数里将这些函数串起来,但我这里将判断玩家下的格子是否符合规范的功能整合在了游戏函数中,因为如果写在玩家行动函数中需要对整个代码进行修改(懒得改了)
void game()//游戏主程序
{
char realboard[row + 2][col + 2] = {};
char showboard[row][col];
initboard_real(realboard, row, col);
initboard_show(showboard, row, col);//初始化棋盘
setmine(realboard, row, col);//设置雷
printboard(showboard, row, col);//打印棋盘
int i = 0;
for (; i == 0;)
{
int a = 0;
int b = 0;
printf("请输入坐标(坐标格式为a b 请勿输入标点符号)\n");
scanf(" %d %d", &a, &b);
a--;
b--;
if (a < 10 && a >= 0 && b < 10 && b >= 0&&showboard[a][b]=='#')//判断输入的坐标是否正确
{
if (player_move(realboard, showboard, a, b))
{
showmine(realboard, showboard, a, b);//没踩到雷显示周围所有空白格
printboard(showboard, row, col);//打印棋盘
if (result(showboard))
{
printf("你胜利了");
break;
}
//判断是否胜利
}
else
{
i = 1;
printf("踩到雷了,你输了\n");
endboard(realboard, row, col);
}
}
else printf("输入错误,请输入正确的坐标\n");
}
}