原文地址:https://zouchanglin.github.io/2018/05/03/2018050302/
前言
之前在CSDN写了一个三子棋的小游戏,主要是考察数组的运用功底,接下来说说这个C语言版的扫雷小游戏,基于VisualStudio2013环境,在Github上的地址为:https://github.com/zouchanglin/FindMine !
- 首先我们需要两个数组,一个是开发者数组,一个是玩家数组。由于我们需要判断每一个数组元素周围的8个元素的状态,所以开发者数组肯定要比玩家看到的数组要大,至少大两行且两列
- 开发者数组存储那些是雷,哪些不是雷只是存储状态,而玩家数组存储那些已经被排过的坐标和周围雷的数量
- 这个小Demo分为3个文件,test.c文件是整个程序的主体框架,它包括了玩家菜单选择,排雷的开始和结束控制都在test.c中,game.c和与之对应的game.h中全是扫雷的具体逻辑实现,其中注释也比较详细
注意事项
- 把握好数组的边界以及站在玩家的角度确定数组的下标,避免越界访问
- 首次排雷不能被炸死,所以第一次排雷的时候要判断坐标是否正好落在雷上,如果刚好落在雷上则重新分布雷
- 游戏胜利的条件的判断,这个判断方式是多样的,我选择的是玩家数组中只有当 * 的数量等于雷的数量的时候就算玩家胜利
源码
game.h
#ifndef _GAME_H__
#define _GAME_H__
#define ROW 8
#define COL 8
#define ROWS ROW+2
#define COLS COL+2
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
//初始化
void InitBoard(char board[ROWS][COLS], int rows, int clos,char set);
//打印数组
void PrintBoard(char board[ROWS][COLS], int row, int col);
//设置雷的坐标
void SetMine(char board[ROWS][COLS], int row, int col, int count);
//排雷
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int x,int y);
//返回周围雷数目
int GetMineCount(char mine[ROWS][COLS], int x, int y);
//展开的方法
void Spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y);
//判断是否排完
int iswin(char show[ROWS][COLS], int row, int col,int mine_count);
#endif
game.c
#include "game.h"
//初始化通过传的参数分贝初始化两个数组
void InitBoard(char board[ROWS][COLS], int rows, int clos,char set){
int i = 0;
int j = 0;
for (i = 0; i < rows; i++){
for (j = 0; j < clos; j++){
board[i][j] = set;
}
}
}
//设置雷(开发者数组)
void SetMine(char board[ROWS][COLS], int row, int col, int count){
int i = 0;
for (i = 0; i < count; i++){
//设置count次雷
int x = rand() %(ROW);
int y = rand() % (COL);
while (board[x+1][y+1] == '0'){
board[x+1][y+1] = '1';//设置1的地方就是雷
}
}
}
//打印show(玩家)数组
void PrintBoard(char board[ROWS][COLS], int row, int col){
int i = 0;
int j = 0;
for (i = 0; i < ROWS-1; i++){
for (j = 0; j < COLS-1; j++){
if (i == 0){
printf("%d ", j);
}else if(j == 0){
printf("%d ",i);
}
else{
printf("%c ", board[i][j]);
}
}
printf("\n");
}
}
//排雷函数
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int x,int y){
if (mine[x][y] == '1'){
return -1;//return-1表示排雷失败
}
int mine_count = GetMineCount(mine, x, y);
show[x][y] = mine_count + '0';
return mine_count;
}
//返回坐标周围雷的数量
int GetMineCount(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');
}
//判断是否排完,排完返回1
int iswin(char show[ROWS][COLS],int row,int col,int mine_count){
int i = 0;
int j = 0;
int count = 0;
//逐个扫描统计*个数
for (i = 0; i < row; i++){
for (j = 0; j < col; j++){
if (show[i][j] == '*'){
count++;
}
}
}
//如果剩下的*和雷的数量相等,那就说明排雷成功
if (mine_count == count){
return 1;
}
return 0;
}
//坐标展开的方法(但是只是展开周围8个坐标)
void Spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y){
if (mine[x - 1][y - 1] == '0'){
show[x - 1][y - 1] = GetMineCount(mine,x - 1, y - 1) + '0';//显示该坐标周围雷数
}
if (mine[x - 1][y] == '0'){
show[x - 1][y] = GetMineCount(mine,x - 1, y) + '0';
}
if (mine[x - 1][y + 1] == '0')
{
show[x - 1][y + 1] = GetMineCount(mine,x - 1, y + 1) + '0';
}
if (mine[x][y - 1] == '0')
{
show[x][y - 1] = GetMineCount(mine,x, y - 1) + '0';
}
if (mine[x][y + 1] == '0')
{
show[x][y + 1] = GetMineCount(mine,x, y + 1) + '0';
}
if (mine[x + 1][y - 1] == '0')
{
show[x + 1][y - 1] = GetMineCount(mine, x + 1, y - 1) + '0';
}
if (mine[x + 1][y] == '0')
{
show[x + 1][y] = GetMineCount(mine, x + 1, y) + '0';
}
if (mine[x + 1][y + 1] == '0')
{
show[x + 1][y + 1] = GetMineCount(mine, x + 1, y + 1) + '0';
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "game.h"
#define TRUE 1
#define FALSE 0
void game(){
//定义二维数组
char show[ROWS][COLS] = { 0 };
char mine[ROWS][COLS] = { 0 };
//初始化显示界面数组
InitBoard(show, ROWS, COLS,'*');
InitBoard(mine, ROWS, COLS, '0');
//布雷
SetMine(mine,ROWS,COLS,3);
//打印数组
PrintBoard(show, ROWS, COLS);
printf("------------------------------\n");
PrintBoard(mine, ROWS, COLS);
int isFirstInput = TRUE;
while (1){
if (iswin(show,ROWS,COLS,3)){
printf("恭喜你,排雷完毕!\n");
break;
}
int x = 0;
int y = 0;
printf("请输入要排除的坐标>\n");
TRY_AGRGIN:
fflush(stdin);
scanf("%d%d", &x, &y);
if (isFirstInput){
if (mine[x][y] == '1'){
//重新布雷
InitBoard(mine, ROWS, COLS, '0');
SetMine(mine, ROWS, COLS, 3);
}
isFirstInput = FALSE;
}
//排查雷
if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL)){
int ret = FindMine(mine, show, x , y);
if (ret == -1){
printf("排雷失败!!!\n");
break;
}
//排雷成功则展开
Spread(show, mine, x, y);
PrintBoard(show, ROWS, COLS);
//这里是开发者看的开发者数组
printf("------------------------------\n");
PrintBoard(mine, ROWS, COLS);
}
else{
printf("输入有误,请重新输入>\n");
goto TRY_AGRGIN;
}
}
}
void menu(){
printf("--------------扫雷------------\n");
printf("-------1、play 0、exit-----\n");
printf("------------------------------\n");
}
int main(void){
//设置随机种子
srand((unsigned)time(NULL));
while (1){
int your_chosr = 0;
menu();
printf("请选择>");
fflush(stdin); //在输入之前先刷空缓冲区
scanf("%c", &your_chosr);
switch (your_chosr){
case '1':
game();
break;
case '0':
return 0;
default:
printf("输入有误,请重新输入>\n");
break;
}
}
system("pause");
return 0;
}