提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、大体框架的构建
- 二、构建棋盘
- 三、布置雷
- 四、排雷
- 五、递归展开
- 六、判断输赢
- 总结
前言
扫雷是一个比较经典的游戏,而通过c语言将游戏做出来,是对c语言函数,循环,库函数,数组知识点的一个很好的考验。
本篇文章主要讲解如何用C语言实现扫雷游戏的一个逻辑运行,其中主要准备采用多文件执行,即game.h、game.c、test.c分别用于三子棋游戏的函数声明、游戏函数的实现、测试三子棋游戏。。
一、大体框架的构建
这个比较简单,就直接上代码了
二、构建棋盘
1、为了实现扫雷,我们选择一个双棋盘的模式,其中一个(mine)是放雷的隐藏棋盘,另一个是(show)存放排查出来的雷的信息。
2、初始化棋盘,mine我们选择初始化为0,然后雷为1:show我们选择初始化为*,排雷后根据周围雷的数量输入具体数字。
void init_board(char board[ROWS][COLS],int rows,int cols,char sz)
{
int a = 0;
int b = 0;
for (a = 0; a < rows; a++)
{
for (b = 0; b < cols; b++)
{
board[a][b] =sz;
}
}
}
3、打印棋盘
void prfint(char board[ROWS][COLS], int row, int col)
{
int a = 1;
int b = 1;
int c = 1;
for (c = 0; c <= row; c++)
{
printf("%d ", c);
}//打印列标号
printf("\n");
for (a = 1; a <= row; a++)
{
printf("%d ", a);//打印行标号
for (b = 1; b <= col; b++)
{
printf("%c ", board[a][b]);
}
printf("\n");
}
}
运行效果
三、布置雷
9*9的棋盘上我们准备布置10个雷,那么怎么完成这十个雷的随机布置?我们采取的是用时间戳一直再变化的特性来完成随机布雷。
void hide_mine(char board[ROWS][COLS], int row, int col)
{
int count = 10;//计算数量
while (count)
{
int a = rand() % 9 + 1;//一个数%9的范围为0-8,+1后为1-9,符合棋盘
int b = rand() % 9 + 1;
if ((a >= 1) & (a <= 9) & (b >= 1) & (b <= 9))
{
if (board[a][b] == '0')
{
board[a][b] = '1';
count--;
}
}
}
}
运行效果
四、排雷
排雷我们主要是通过输入坐标,然后在mine棋盘上排查坐标周围雷的数量,在将数量传给show棋盘中的对应坐标。
int rem_mines_mine(char mine[ROWS][COLS], int a, int b)//排查mine棋盘中坐标a b周围雷的数量
{
return (mine[a - 1][b - 1] + mine[a][b - 1]
+ mine[a + 1][b - 1] + mine[a - 1][b]
+ mine[a + 1][b] + mine[a - 1][b + 1]
+ mine[a][b + 1] + mine[a + 1][b + 1]
-8*'0');
}
void rem_mines(char mine[ROW][COL], char show[ROW][COL], int row, int col)
{
int a = 0;
int b = 0;
while (1)
{
printf("请输入坐标:");
scanf("%d %d", &a, &b);
if ((a >= 1) & (a <= 9) & (b >= 1) & (b <= 9))
{
if (mine[a][b] == '1')
{
printf("你被炸死了\n");
break;
}
else
{
int c = rem_mines_mine(mine, a, b);
show[a][b] = c + '0';
prfint(mine, ROW, COL);//打印棋盘
prfint(show, ROW, COL);//打印棋盘
}
}
else
printf("坐标违法,请重新输入\n");
}
}
运行效果
五、递归展开
通过递归展开达到下图效果
点一下橙色方块,就会展开整个红色区域
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int a, int b, int* win)
{
if ((a >= 1) && (a <= ROW) && (b >= 1) && (b <= COL) )//防止越界
{
int c = rem_mines_mine(mine, a, b);//坐标周围雷的数量
if (c == 0)//没有雷开始
{
show[a][b] = ' ';
int i = 0;//对坐标周围八个坐标进行递归
for (i = a - 1; i <= a + 1; i++)
{
int j = 0;
for (j = b - 1; j <= b + 1; j++)
{
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else
{
show[a][b] = c + '0';
}
(*win)++;
}
}
运行效果如下
运用递归后的玩家排雷代码
int rem_mines_mine(char mine[ROWS][COLS], int a, int b)//排查mine棋盘中坐标a b周围雷的数量
{
return (mine[a - 1][b - 1] + mine[a][b - 1]
+ mine[a + 1][b - 1] + mine[a - 1][b]
+ mine[a + 1][b] + mine[a - 1][b + 1]
+ mine[a][b + 1] + mine[a + 1][b + 1]
-8*'0');
}
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int a, int b, int* win)
{
if ((a >= 1) && (a <= ROW) && (b >= 1) && (b <= COL) )//防止越界
{
int c = rem_mines_mine(mine, a, b);//坐标周围雷的数量
if (c == 0)//没有雷开始
{
show[a][b] = ' ';
int i = 0;//对坐标周围八个坐标进行递归
for (i = a - 1; i <= a + 1; i++)
{
int j = 0;
for (j = b - 1; j <= b + 1; j++)
{
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else
{
show[a][b] = c + '0';
}
(*win)++;
}
}
void rem_mines(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int a = 0;
int b = 0;
int win=0;
while (win<ROW*COL-10)
{
printf("请输入坐标:");
scanf("%d %d", &a, &b);
if ((a >= 1) && (a <= ROW) && (b >= 1) & (b <= COL))
{
if ((show[a][b] == '*'))
{
if (mine[a][b] == '1')
{
prfint(mine, ROW, COL);
printf("你被炸死了\n");
break;
}
else
{
expand(mine, show, a, b, &win);//递归展开
prfint(mine, ROW, COL);
prfint(show, ROW, COL);//打印棋盘
}
}
else
{
printf("该坐标已被排查过,请重新输入\n");
}
}
else
printf("坐标违法,请重新输入\n");
}
if (win == ROW * COL - 10)
{
printf("恭喜你获得胜利\n");
prfint(mine, ROW, COL);//打印棋盘
}
}
六、判断输赢
因为有十个雷,所以我们可以通过判断每次输入坐标,建立变量win,show棋盘每输入一个值,win++,当win大于71(棋盘总数-雷数)时则跳出循环胜利。
int rem_mines_mine(char mine[ROWS][COLS], int a, int b)//排查mine棋盘中坐标a b周围雷的数量
{
return (mine[a - 1][b - 1] + mine[a][b - 1]
+ mine[a + 1][b - 1] + mine[a - 1][b]
+ mine[a + 1][b] + mine[a - 1][b + 1]
+ mine[a][b + 1] + mine[a + 1][b + 1]
-8*'0');
}
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int a, int b, int* win)
{
if ((a >= 1) && (a <= ROW) && (b >= 1) && (b <= COL) )//防止越界
{
int c = rem_mines_mine(mine, a, b);//坐标周围雷的数量
if (c == 0)//没有雷开始
{
show[a][b] = ' ';
int i = 0;//对坐标周围八个坐标进行递归
for (i = a - 1; i <= a + 1; i++)
{
int j = 0;
for (j = b - 1; j <= b + 1; j++)
{
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else
{
show[a][b] = c + '0';
}
(*win)++;
}
}
void rem_mines(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int a = 0;
int b = 0;
int win=0;
while (win<ROW*COL-10)
{
printf("请输入坐标:");
scanf("%d %d", &a, &b);
if ((a >= 1) && (a <= ROW) && (b >= 1) & (b <= COL))
{
if ((show[a][b] == '*'))
{
if (mine[a][b] == '1')
{
prfint(mine, ROW, COL);
printf("你被炸死了\n");
break;
}
else
{
expand(mine, show, a, b, &win);//递归展开
prfint(mine, ROW, COL);
prfint(show, ROW, COL);//打印棋盘
}
}
else
{
printf("该坐标已被排查过,请重新输入\n");
}
}
else
printf("坐标违法,请重新输入\n");
}
if (win == ROW * COL - 10)
{
printf("恭喜你获得胜利\n");
prfint(mine, ROW, COL);//打印棋盘
}
}
运行效果
总结
test.c
测试三子棋游戏
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("******************************\n");
printf("******** 1:开始游戏 ********\n");
printf("******** 0:退出游戏 ********\n");
printf("******************************\n");
}
void play()
{
printf("开始扫雷!!\n");
char mine[ROWS][COLS];
char show[ROWS][COLS];
init_board(mine, ROWS, COLS, '0');//初始化,最后一个字符‘0’,是要初始化的值
init_board(show, ROWS, COLS, '*');//初始化,最后一个字符‘*’,是要初始化的值
prfint(show, ROW, COL);//打印棋盘
hide_mine(mine, ROW, COL);
prfint(mine, ROW, COL);
rem_mines(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int intput = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &intput);
switch (intput)
{
case 1:
play();
break;
case 0:
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (intput);//用input是因为如果输出是0则退出,其他的无论是1还是其他都会重新进入
return 0;
}
game.c
游戏函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void init_board(char board[ROWS][COLS],int rows,int cols,char sz)
{
int a = 0;
int b = 0;
for (a = 0; a < rows; a++)
{
for (b = 0; b < cols; b++)
{
board[a][b] =sz;
}
}
}
void prfint(char board[ROWS][COLS], int row, int col)
{
int a = 1;
int b = 1;
int c = 1;
for (c = 0; c <= row; c++)
{
printf("%d ", c);
}//打印列标号
printf("\n");
for (a = 1; a <= row; a++)
{
printf("%d ", a);//打印行标号
for (b = 1; b <= col; b++)
{
printf("%c ", board[a][b]);
}
printf("\n");
}
}
void hide_mine(char board[ROWS][COLS], int row, int col)
{
int count = 10;//计算数量
while (count)
{
int a = rand() % 9 + 1;//一个数%9的范围为0-8,+1后为1-9,符合棋盘
int b = rand() % 9 + 1;
if (board[a][b] == '0'&&board[a][b]!='1')
{
board[a][b] = '1';
count--;
}
}
}
int rem_mines_mine(char mine[ROWS][COLS], int a, int b)//排查mine棋盘中坐标a b周围雷的数量
{
return (mine[a - 1][b - 1] + mine[a][b - 1]
+ mine[a + 1][b - 1] + mine[a - 1][b]
+ mine[a + 1][b] + mine[a - 1][b + 1]
+ mine[a][b + 1] + mine[a + 1][b + 1]
-8*'0');
}
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int a, int b, int* win)
{
if ((a >= 1) && (a <= ROW) && (b >= 1) && (b <= COL) )//防止越界
{
int c = rem_mines_mine(mine, a, b);//坐标周围雷的数量
if (c == 0)//没有雷开始
{
show[a][b] = ' ';
int i = 0;//对坐标周围八个坐标进行递归
for (i = a - 1; i <= a + 1; i++)
{
int j = 0;
for (j = b - 1; j <= b + 1; j++)
{
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else
{
show[a][b] = c + '0';
}
(*win)++;
}
}
void rem_mines(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int a = 0;
int b = 0;
int win=0;
while (win<ROW*COL-10)
{
printf("请输入坐标:");
scanf("%d %d", &a, &b);
if ((a >= 1) && (a <= ROW) && (b >= 1) & (b <= COL))
{
if ((show[a][b] == '*'))
{
if (mine[a][b] == '1')
{
prfint(mine, ROW, COL);
printf("你被炸死了\n");
break;
}
else
{
expand(mine, show, a, b, &win);//递归展开
prfint(mine, ROW, COL);
prfint(show, ROW, COL);//打印棋盘
}
}
else
{
printf("该坐标已被排查过,请重新输入\n");
}
}
else
printf("坐标违法,请重新输入\n");
}
if (win == ROW * COL - 10)
{
printf("恭喜你获得胜利\n");
prfint(mine, ROW, COL);//打印棋盘
}
}
game.h
函数声明
#pragma once
#include <stdio.h>
#include <stdlib.h>//srand函数的头文件
#include <time.h>//time函数的头文件
#define ROW 9
#define COL 9
#define ROWS ROW+2//设置11*11的数组,打印9*9的棋盘,这样就可以避免棋盘边缘的越界情况的出现
#define COLS COL+2
//初始化数组
void init_board(char board[ROWS][COLS], int rows, int cols, char sz);
//打印棋盘
void prfint(char board[ROW][COL], int row, int col);
//布置雷
void hide_mine(char board[ROWS][COLS], int row, int col);
//排雷
void rem_mines(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);