这是一篇介绍扫雷游戏的博客,采用C语言实现,主要作为数组的简单实践,包括有游戏实现的简单思路、游戏代码,以及笔者的一些小经验,希望可以给同为小白的同志一些参考,如有错误,还望大佬们不吝赐教。 此博客与上一篇三子棋有不少相似处,各位也可一看:三子棋C语言实现
一、实现思路
1、布置雷
- 定义二维字符数组mine,用以存放雷,以‘1’表示雷,‘0’表示无雷;
- 定义二维字符数组show,用以保存扫雷需要的信息;
2、排雷
以输入坐标的形式排雷,可能出现四种结果:
- 坐标非法;
- 踩到雷,游戏结束;
- 踩到处周边有雷,显示周边雷的数量;
- 踩到处周边无雷,将周边无雷的区域展开;
3、检查雷是否被排完
排完则游戏成功,否则游戏继续。
二、函数代码实现
1、“雷”数组的初始化以及打印“雷区”
- 初始化: 将数组中的元素全部初始化为set;
//初始化数组
void Initarr(char arr[ROWS][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
arr[i][j] = set;
}
}
}
- 打印雷区: 利用分割线分开每一次排雷,打印行标与列标以便选择位置。
//打印“雷区”,由于第一行、最后一行、第一列与最后一列作为辅助作用,
//因此不予打印,只打印中间9行。
void Display(char arr[ROWS][COLS], int row, int col)
{
printf("------- 扫雷 ------\n");
printf("0 1 2 3 4 5 6 7 8 9\n");//打印列标
for (int i = 1; i < row + 1; i++)
{
printf("%d ", i);//打印行标
for (int j = 1; j < col + 1; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("------- 扫雷 ------\n");
}
2、布置雷与扫雷
- 布置雷 以count表示游戏难度,即布置雷的数量,通过传参保证函数脱离全局变量后依旧可用;
//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
while (count)
{
//mine为11×11大小,首行末行与首列末列均不布置雷
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
- 扫雷,根据出现的情况进行分类 在判断输赢时采用了循环检查雷是否排查完成,效率较低;
//扫雷可能出现的情况:
// 1、坐标非法->重新输入
// 2、坐标合法->A、踩雷->游戏结束
// B、未踩雷->a、周边有雷->显示雷的数量
// b、周边无雷->大面积展开
// C、该位置已经被扫过->坐标非法
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
int x = 0, y = 0;
while (1)
{
printf("请输入坐标:\n");
scanf("%d %d", &x, &y);
if (x < 1 || x > row || y < 1 || y > col)
{
printf("坐标非法,请重新输入:\n");
}
else if (mine[x][y] == '1')
{
printf("此处有雷,游戏结束\n");
Display(mine, row, col);
break;
}
else if (show[x][y] != '*')
{
printf("该位置已被扫过,请重新输入:\n");
}
else
{
//周边无雷
if (get_mine(mine, x, y) == 0)
{
unfold(mine, show, x, y);
Display(show, row, col);
}
//周边有雷
else
{
show[x][y] = get_mine(mine, x, y) + '0';
Display(show, row, col);
}
}
//判断是否完成
int win = 0;
for (int i = 1; i < row + 1; i++)
{
for (int j = 1; j < col + 1; j++)
{
if (show[i][j] == '*')
win++;
}
}
if (win == EASY)
{
printf("扫雷成功!\n");
Display(mine, row, col);
break;
}
}
}
- 递归展开无雷处 采用递归,依次判断周边的八个位置,达到展开效果;
void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (x == 0 || x == ROW + 1 || y == 0 || y == COL + 1)
return;
if (show[x][y] == '*')
{
int ret = get_mine(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
unfold(mine, show, x -1, y -1);
unfold(mine, show, x - 1, y);
unfold(mine, show, x - 1, y + 1);
unfold(mine, show, x, y - 1);
unfold(mine, show, x, y + 1);
unfold(mine, show, x + 1, y - 1);
unfold(mine, show, x + 1, y);
unfold(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = ret + '0';
}
}
}
- 获取周边雷的数量 检查周边8个位置的雷个数,利用两层嵌套循环检查;
//获取周边的雷的个数
int get_mine(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = -1; i < 2; i++)
{
for(int j = -1; j < 2; j++)
if (mine[x + i][y + j] == '1')
{
count++;
}
}
return count;
}
三、小tips
- 定义二维数组存放雷时,将数组定义大两个,9×9的棋盘采用11×11的数组,如此,即便是判断边缘格子时也可以采用一样的函数,不用担心越界;
- 定义游戏难度时,可将相关内容都放在头文件中,以#define定义的方式,方便以后修改;
- 定义函数时,可将只会在本.c文件中用到的函数前加上static,防止其他文件调用,同时也让在其他.c文件中定义函数时更为方便;
- 书写代码时,须首先严谨思考,考虑到各种结果,先思考再敲代码,会更有效率。
四、代码汇总
头文件game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS 11
#define COLS 11
//游戏难度
#define EASY 10
//初始化数组,初始arr数组中的值为set
void Initarr(char arr[ROWS][COLS], int row, int col, char set);
//打印“雷区”
void Display(char arr[ROWS][COLS], int row, int col);
//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count);
//扫雷
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);
函数文件game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//具体函数的实现
//选择菜单
void menu()
{
printf("******************\n");
printf("***** 1、开始 ****\n");
printf("***** 0、退出 ****\n");
printf("******************\n");
}
//初始化数组
void Initarr(char arr[ROWS][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
arr[i][j] = set;
}
}
}
//打印“雷区”,由于第一行、最后一行、第一列与最后一列作为辅助作用,因此不予打印,只打印中间9行
void Display(char arr[ROWS][COLS], int row, int col)
{
printf("------- 扫雷 ------\n");
printf("0 1 2 3 4 5 6 7 8 9\n");//打印列标
for (int i = 1; i < row + 1; i++)
{
printf("%d ", i);//打印行标
for (int j = 1; j < col + 1; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
printf("------- 扫雷 ------\n");
}
//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
while (count)
{
//mine为11×11大小,首行末行与首列末列均不布置雷
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//获取周边的雷的个数
static int get_mine(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = -1; i < 2; i++)
{
for(int j = -1; j < 2; j++)
if (mine[x + i][y + j] == '1')
{
count++;
}
}
return count;
}
//展开函数
static void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (x == 0 || x == ROW + 1 || y == 0 || y == COL + 1)
return;
if (show[x][y] == '*')
{
int ret = get_mine(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
unfold(mine, show, x -1, y -1);
unfold(mine, show, x - 1, y);
unfold(mine, show, x - 1, y + 1);
unfold(mine, show, x, y - 1);
unfold(mine, show, x, y + 1);
unfold(mine, show, x + 1, y - 1);
unfold(mine, show, x + 1, y);
unfold(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = ret + '0';
}
}
}
//扫雷可能出现的情况:
// 1、坐标非法->重新输入
// 2、坐标合法->A、踩雷->游戏结束
// B、未踩雷->a、周边有雷->显示雷的数量
// b、周边无雷->大面积展开
// C、该位置已经被扫过->坐标非法
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
int x = 0, y = 0;
while (1)
{
printf("请输入坐标:\n");
scanf("%d %d", &x, &y);
if (x < 1 || x > row || y < 1 || y > col)
{
printf("坐标非法,请重新输入:\n");
}
else if (mine[x][y] == '1')
{
printf("此处有雷,游戏结束\n");
Display(mine, row, col);
break;
}
else if (show[x][y] != '*')
{
printf("该位置已被扫过,请重新输入:\n");
}
else
{
//周边无雷
if (get_mine(mine, x, y) == 0)
{
unfold(mine, show, x, y);
Display(show, row, col);
}
//周边有雷
else
{
show[x][y] = get_mine(mine, x, y) + '0';
Display(show, row, col);
}
}
//判断是否完成
int win = 0;
for (int i = 1; i < row + 1; i++)
{
for (int j = 1; j < col + 1; j++)
{
if (show[i][j] == '*')
win++;
}
}
if (win == EASY)
{
printf("扫雷成功!\n");
Display(mine, row, col);
break;
}
}
}
void game()
{
// mine数组用以存放雷
char mine[ROWS][COLS] = { 0 };
// show数组用以存放扫出的信息
char show[ROWS][COLS] = { 0 };
//初始化mine数组为字符0,show数组为字符'*'
Initarr(mine, ROWS, COLS, '0');
//Display(mine, ROW, COL);//检测初始化效果
Initarr(show, ROWS, COLS, '*');
//Display(show, ROW, COL);//检测初始化效果
Setmine(mine, ROW, COL, EASY);
//Display(mine, ROW, COL);//查看雷的布置位置
Display(show, ROW, COL);
Sweep(mine, show, ROW, COL, EASY);
}
- 测试文件test.h
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int main()
{
int x = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &x);
switch (x)
{
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("游戏退出\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (x);
}
标签:游戏,show,int,mine,C语言,扫雷,printf,col,row
From: https://blog.51cto.com/u_15423682/6064527