扫雷自定义最难C语言,C语言实现扫雷小游戏(扩展版可选择游戏难度)

游戏简介

扫雷,是一款益智类小游戏。

游戏目标是找出所有没有地雷的方格,完成游戏;要是按了有地雷的方格,游戏失败;玩家可标记雷的位置。游戏以完成时间来评高低。有不同的游戏难度可选择。

实现的功能介绍

1.计时

2.初始化雷盘

3.打印雷盘

4.随机设置雷的分布,可选择游戏难易程度

5.统计坐标位置周围的雷数

6.第一次排雷不会被炸死

7.扩展式排雷,展开周围的非雷区

8.给所选坐标位置做标记,或取消标记

该程序分为三个文件:

1.game.h :包含头文件的引用、函数的声明和宏定义

2.game.c :包含游戏各功能函数的具体实现

3.pro.c :各功能函数的调用(程序的流程)

PS:文章末尾附完整代码 及 游戏效果图

e223ae01b1de5a60e887a9fc444ee1ed.png

因为排雷时要计算每个位置周围八个位置的雷数,所以在创建数组时要多一圈,即行列都要加2。给用户显示的数组不需要加。

f54888c88bb66efad9ae31fbdde1b052.png

游戏功能代码详解

1.计时

运用clock函数,该函数需要的头文件为 “time.h”

函数原型:clock_t clock(void);

功能:程序从启动到函数调用占用CPU的时间

这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间;若挂钟时间不可取,则返回-1。其中clock_t是用来保存时间的数据类型。

void set_time()//计时

{

printf("用时:%u 秒\n", clock() / CLOCKS_PER_SEC);

}

2.初始化雷盘

这里我用到的是memset函数,需要的头文件为“memory.h”或“string.h”

函数原型:void *memset(void *s, int ch, size_t n);

功能:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。在一段内存块中填充某个给定的值。

void init_board(char board[ROWS][COLS], int row, int col, char c)//初始化雷盘

{

memset(board, c, row*col*sizeof(board[0][0]));

}

3.打印雷盘

运用两个循环体实现雷盘数组的赋值、行号、列号的打印。正式游戏时可以加上system(“CLS”); 清屏语句,每次调用时都清屏一次,使游戏画面更简洁清晰。

我们把计时函数放在里面,每次打印雷盘时就可以显示所用的时间。

void disp_board(char board[ROWS][COLS], int row, int col)//打印雷盘

{

int i = 0;

int j = 0;

//system("CLS");//清屏

for (i = 0; i <= row; i++)

{

printf("%2d ", i);//打印行号

}

printf("\n");

for (i = 1; i <= row; i++)

{

printf("%2d ", i);//打印列号

for (j = 1; j <= col; j++)

{

printf("%2c ", board[i][j]);

}

printf("\n");

}

printf("\n");

set_time();//打印所用的时间

}

4.随机设置雷的分布,可选择游戏难易程度

放置雷必须是随机的,这里用到了rand函数,它和srand函数配合使用产生随机数。srand(time(NULL))放在主函数中调用一次,通过系统时间提供的种子值,使rand函数生成不同的伪随机数序列。

void set_mine(char board[ROWS][COLS], int row, int col,int count)//置雷

{

int x = 0;

int y = 0;

while (count)

{

x = rand() % row + 1;//随机位置范围1~row

y = rand() % col + 1;//随机位置范围1~col

if (board[x][y] == '0')//判断是否已有雷

{

board[x][y] = '1';//有雷的位置赋为1

count--;

}

}

}

5.统计坐标位置周围的雷数 及 未扫的位置的个数

当扫到一个没有雷的位置时,会显示这个位置周围一圈八个位置的含雷的总数,所以我们要写一个“数雷”函数来数。

int count_mine(char mine[ROWS][COLS], int x, int y)//数雷

{

return mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y] +

mine[x + 1][y - 1] + mine[x][y - 1] + mine[x - 1][y - 1] - 8 * '0';//数周围一圈八个位置的雷数

}

int count_print(char print[ROWS][COLS], int row, int col)//数未扫位置

{

int count = 0;

int i = 0;

for (i = 1; i <= row; i++)

{

int j = 0;

for (j = 1; j <= col; j++)

{

if (print[i][j] == '@' ||print[i][j] == '*')

{

count++;

}

}

}

return count;

}

6.第一次排雷不会被炸死

为了增加游戏的可玩性,加入“第一次排雷不被炸死”这个函数。当第一次排就遇到雷时,我们把雷偷偷挪走,随机放在一个原本无雷的位置。

void safe_mine(char mine[ROWS][COLS],char print[ROWS][COLS],int x,int y,int row,int col)//第一次排雷不炸死

{

char ch = 0;

int ret = 1;

int number = 0;

if (mine[x][y] == '1')//第一次踩到雷后补救

{

mine[x][y] = '0';

char ch = count_mine(mine, x, y);

print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值

extend_board(mine, print, x, y);

while (ret)//在其余有空的地方设置一个雷

{

int x = rand() % row + 1;//产生1到row的随机数,在数组下标为1到10的范围内布雷

int y = rand() % col + 1;//产生1到col的随机数,在数组下标为1到10的范围内布雷

if (mine[x][y] == '0')//找不是雷的地方布雷

{

mine[x][y] = '1';

disp_board(print, row, col);

//disp_board(mine, row, col);

ret--;

break;

}

}

}

}

7.扩展式排雷,展开周围的非雷区

当游戏中排到一个周围一圈都无雷的位置时,运用递归,实现扩展展开周围的一片无雷区。

void extend_board(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y)//运用递归扩展周围

{

int n = 0;

n = count_mine(mine, x, y);

if (n == 0)//当该位置周围雷数为0时扩展

{

print[x][y] = ' ';//扩展的位置变为“空格”打印出来

if (mine[x - 1][y] == '0' && print[x - 1][y] == '@')

{

extend_board(mine, print, x - 1, y);//递归

}

if (mine[x + 1][y] == '0' && print[x + 1][y] == '@')

{

extend_board(mine, print, x + 1, y);

}

if (mine[x][y + 1] == '0' && print[x][y + 1] == '@')

{

extend_board(mine, print, x, y + 1);

}

if (mine[x - 1][y + 1] == '0' && print[x - 1][y + 1] == '@')

{

extend_board(mine, print, x - 1, y + 1);

}

if (mine[x + 1][y + 1] == '0' && print[x + 1][y + 1] == '@')

{

extend_board(mine, print, x + 1, y + 1);

}

if (mine[x][y - 1] == '0' && print[x][y - 1] == '@')

{

extend_board(mine, print, x, y - 1);

}

if (mine[x + 1][y - 1] == '0' && print[x + 1][y -1] == '@')

{

extend_board(mine, print, x + 1, y - 1);

}

if (mine[x - 1][y - 1] == '0' && print[x - 1][y - 1] == '@')

{

extend_board(mine, print, x - 1, y - 1);

}

}

else

print[x][y] = n + '0';

}

int find_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int row, int col,int count)//排雷

{

int x = 0;

int y = 0;

int number = 0;

int ret = 0;

while (1)

{

printf("输入坐标扫雷\n");

scanf("%d%d", &x, &y);//玩家输入扫雷的坐标位置

if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//判断输入坐标是否有误,输入错误重新输入

{

if (mine[x][y] == '0')//没踩到雷

{

number++;//记录扫雷的次数

char ch = count_mine(mine, x, y);//数雷数

print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值

extend_board(mine, print, x, y);

disp_board(mine, row, col);

disp_board(print, row, col);

if (count_print(print, row, col) == count)//剩余未扫位置=雷数 时胜利

{

return 0;

}

to_sign(print);//判断是否标记

disp_board(print, row, col);

}

else if (mine[x][y] == '1')//踩到雷

{

if (ret == 0 && number == 0)

{

ret++;

safe_mine(mine,print,x,y,row,col);

}

else

return 1;

}

}

else

{

printf("输入错误!请重新输入\n");

}

}

}

8.给所选坐标位置做标记,或取消标记

扫雷游戏还有一个功能:可以给你认为是雷的位置标记,或者取消标记。

我通过三个函数来实现,一个判断用户是否需要标记;一个实现标记功能,将@标记成* ;一个实现取消标记功能,将* 改回@。

void to_sign(char board[ROWS][COLS])//判断是否标记

{

int chose_b = 0;

int x = 0;

int y = 0;

printf("是否需要标记/取消标记:>\n(1.标记 ;2.取消标记 ;3.跳过该步骤) :>");

scanf("%d", &chose_b);

do{

switch (chose_b)

{

case 1:

{

printf("请输入需要标记的位置坐标:>\n");

scanf("%d%d", &x, &y);

sign(board, x, y);

break;

}

case 2:

{

printf("请输入取消标记的位置坐标:>\n");

scanf("%d%d", &x, &y);

unsign(board, x, y);

break;

}

case 3:

{

printf("跳过此步骤。\n");

chose_b = 0;

break;

}

default:

{ printf("输入错误!\n");

chose_b = 0;

break;

}

}

chose_b = 0;

} while (chose_b);

}

void sign(char board[ROWS][COLS], int x, int y)//用‘*'标记雷

{

if (board[x][y] == '@')

{

board[x][y] = '*';

}

}

void unsign(char board[ROWS][COLS], int x, int y)//取消标记

{

if (board[x][y] == '*')

{

board[x][y] = '@';

}

}

附:完整代码

game.h

#ifndef _GAME_H_

#define _GAME_H_

//用到的头文件

#include

#include

#include

#include

#include

//定义打印的雷盘行、列

#define _ROW 9

#define _COL 9

#define ROW 16

#define COL 16

//定义数组的行、列

#define _ROWS _ROW+2

#define _COLS _COL+2

#define ROWS ROW+2

#define COLS COL+2

//定义难、易程度雷数

#define EASY_COUNT 10

#define HARD_COUNT 40

//定义游戏中的函数

void init_board(char board[ROWS][COLS],int row, int col, char c);//初始化

void disp_board(char board[ROWS][COLS],int row,int col);//打印

void set_mine(char board[ROWS][COLS], int row, int col,int count);//置雷

void safe_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y, int row, int col);//第一次排雷不炸死

int find_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int row, int col,int count);//排雷

int count_mine(char mine[ROWS][COLS], int x, int y);//数雷

void extend_board(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y);//扩展

void to_sign(char board[ROWS][COLS]);//判断是否标记

void sign(char board[ROWS][COLS], int x, int y);//标记

void unsign(char board[ROWS][COLS], int x, int y);//取消标记

int count_print(char print[ROWS][COLS], int row, int col);//数未扫位置

#endif//_GAME_H_

game.c

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

void set_time()//计时

{

printf("用时:%u 秒\n", clock() / CLOCKS_PER_SEC);

}

void init_board(char board[ROWS][COLS], int row, int col, char c)//初始化雷盘

{

memset(board, c, row*col*sizeof(board[0][0]));

}

void disp_board(char board[ROWS][COLS], int row, int col)//打印雷盘

{

int i = 0;

int j = 0;

system("CLS");//清屏

for (i = 0; i <= row; i++)//加行号

{

printf("%2d ", i);

}

printf("\n");

for (i = 1; i <= row; i++)//加列号

{

printf("%2d ", i);

for (j = 1; j <= col; j++)

{

printf("%2c ", board[i][j]);

}

printf("\n");

}

printf("\n");

set_time();//打印所用的时间

}

void set_mine(char board[ROWS][COLS], int row, int col,int count)//置雷

{

int x = 0;

int y = 0;

while (count)

{

x = rand() % row + 1;//随机位置范围1~row

y = rand() % col + 1;//随机位置范围1~col

if (board[x][y] == '0')//判断是否已有雷

{

board[x][y] = '1';

count--;

}

}

}

void safe_mine(char mine[ROWS][COLS],char print[ROWS][COLS],int x,int y,int row,int col)//第一次排雷不炸死

{

char ch = 0;

int ret = 1;

int number = 0;

if (mine[x][y] == '1')//第一次踩到雷后补救

{

mine[x][y] = '0';

char ch = count_mine(mine, x, y);

print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值

extend_board(mine, print, x, y);

while (ret)//在其余有空的地方设置一个雷

{

int x = rand() % row + 1;//产生1到row的随机数,在数组下标为1到10的范围内布雷

int y = rand() % col + 1;//产生1到col的随机数,在数组下标为1到10的范围内布雷

if (mine[x][y] == '0')//找不是雷的地方布雷

{

mine[x][y] = '1';

disp_board(print, row, col);

//disp_board(mine, row, col);

ret--;

break;

}

}

}

}

int count_mine(char mine[ROWS][COLS], int x, int y)//数雷

{

return mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y] +

mine[x + 1][y - 1] + mine[x][y - 1] + mine[x - 1][y - 1] - 8 * '0';//数周围一圈八个位置的雷数

}

int count_print(char print[ROWS][COLS], int row, int col)//数未扫位置

{

int count = 0;

int i = 0;

for (i = 1; i <= row; i++)

{

int j = 0;

for (j = 1; j <= col; j++)

{

if (print[i][j] == '@' ||print[i][j] == '*')

{

count++;

}

}

}

return count;

}

void extend_board(char mine[ROWS][COLS], char print[ROWS][COLS], int x, int y)//运用递归扩展周围

{

int n = 0;

n = count_mine(mine, x, y);

if (n == 0)//当该位置周围雷数为0时扩展

{

print[x][y] = ' ';//扩展的位置变为“空格”打印出来

if (mine[x - 1][y] == '0' && print[x - 1][y] == '@')

{

extend_board(mine, print, x - 1, y);//递归

}

if (mine[x + 1][y] == '0' && print[x + 1][y] == '@')

{

extend_board(mine, print, x + 1, y);

}

if (mine[x][y + 1] == '0' && print[x][y + 1] == '@')

{

extend_board(mine, print, x, y + 1);

}

if (mine[x - 1][y + 1] == '0' && print[x - 1][y + 1] == '@')

{

extend_board(mine, print, x - 1, y + 1);

}

if (mine[x + 1][y + 1] == '0' && print[x + 1][y + 1] == '@')

{

extend_board(mine, print, x + 1, y + 1);

}

if (mine[x][y - 1] == '0' && print[x][y - 1] == '@')

{

extend_board(mine, print, x, y - 1);

}

if (mine[x + 1][y - 1] == '0' && print[x + 1][y -1] == '@')

{

extend_board(mine, print, x + 1, y - 1);

}

if (mine[x - 1][y - 1] == '0' && print[x - 1][y - 1] == '@')

{

extend_board(mine, print, x - 1, y - 1);

}

}

else

print[x][y] = n + '0';

}

int find_mine(char mine[ROWS][COLS], char print[ROWS][COLS], int row, int col,int count)//排雷

{

int x = 0;

int y = 0;

int number = 0;

int ret = 0;

while (1)

{

printf("输入坐标扫雷\n");

scanf("%d%d", &x, &y);//玩家输入扫雷的坐标位置

if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//判断输入坐标是否有误,输入错误重新输入

{

if (mine[x][y] == '0')//没踩到雷

{

number++;//记录扫雷的次数

char ch = count_mine(mine, x, y);//数雷数

print[x][y] = ch + '0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值

extend_board(mine, print, x, y);

// disp_board(mine, row, col);

disp_board(print, row, col);

if (count_print(print, row, col) == count)//剩余未扫位置=雷数 时胜利

{

return 0;

}

to_sign(print);//判断是否标记

disp_board(print, row, col);

}

else if (mine[x][y] == '1')//踩到雷

{

if (ret == 0 && number == 0)

{

ret++;

safe_mine(mine,print,x,y,row,col);

}

else

return 1;

}

}

else

{

printf("输入错误!请重新输入\n");

}

}

}

void sign(char board[ROWS][COLS], int x, int y)//用‘*'标记雷

{

if (board[x][y] == '@')

{

board[x][y] = '*';

}

}

void unsign(char board[ROWS][COLS], int x, int y)//取消标记

{

if (board[x][y] == '*')

{

board[x][y] = '@';

}

}

void to_sign(char board[ROWS][COLS])//判断是否标记

{

int chose_b = 0;

int x = 0;

int y = 0;

printf("是否需要标记/取消标记:>\n(1.标记 ;2.取消标记 ;3.跳过该步骤) :>");

scanf("%d", &chose_b);

do{

switch (chose_b)

{

case 1:

{

printf("请输入需要标记的位置坐标:>\n");

scanf("%d%d", &x, &y);

sign(board, x, y);

break;

}

case 2:

{

printf("请输入取消标记的位置坐标:>\n");

scanf("%d%d", &x, &y);

unsign(board, x, y);

break;

}

case 3:

{

printf("跳过此步骤。\n");

chose_b = 0;

break;

}

default:

{ printf("输入错误!\n");

chose_b = 0;

break;

}

}

chose_b = 0;

} while (chose_b);

}

pro.c

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"

void menu()

{

printf("+---------------------------------+\n");

printf("+ Welcome to 扫雷世界 ! +\n");

printf("+ ο(=>ω<=)ρ⌒☆ +\n");

printf("+ 1、play +\n");

printf("+ 0、exit +\n");

printf("+---------------------------------+\n");

}

void game()

{

char mine[ROWS][COLS] = { 0 };

char print[ROWS][COLS] = { 0 };

int chose_m = 0;

int ret = 0;

printf("请选择模式(1、简单 2、困难):>");//选择游戏难易程度,产生不同大小的棋盘和雷数

scanf("%d", &chose_m);

switch (chose_m)

{

case 1:

{

init_board(mine, ROWS, COLS, '0');//初始化雷盘

init_board(print, ROWS, COLS, '@');

set_mine(mine, _ROW, _COL, EASY_COUNT);//布雷

// disp_board(mine, _ROW, _COL);//打印雷盘

disp_board(print, _ROW, _COL);

int ret = find_mine(mine, print, _ROW, _COL, EASY_COUNT);//扫雷,踩到雷返回1,没有踩到雷返回0

while (1)//循环扫雷

{

if (ret == 0)//若返回0则胜利

{

disp_board(print, _ROW, _COL);

printf("WOW~ YOU WIN!\n\n");

break;

}

if (ret)//若返回1则失败

{

disp_board(mine, _ROW, _COL);//打印雷盘

printf("GAME OVER!\n");

break;

}

disp_board(print, _ROW, _COL);//打印玩家棋盘

}

break;

}

case 2:

{

init_board(mine, ROWS, COLS, '0');//初始化雷盘

init_board(print, ROWS, COLS, '@');

set_mine(mine, ROW, COL, HARD_COUNT);//布雷

// disp_board(mine, ROW, COL);//打印雷盘

disp_board(print, ROW, COL);

while (1)//循环扫雷

{

int ret = find_mine(mine, print, ROW, COL, HARD_COUNT);//扫雷,踩到雷返回1,没有踩到雷返回0

if (ret == 0)//若返回0胜利

{

disp_board(print, ROW, COL);

printf("WOW~ YOU WIN!\n\n");

break;

}

if (ret)//若返回1失败

{

disp_board(mine, ROW, COL);//打印雷盘

printf("GAME OVER!\n");

break;

}

disp_board(print, ROW, COL);//打印玩家棋盘

}

break;

}

default:

{

printf("输入错误!\n");

break;

}

}

}

void text()

{

srand((unsigned int)time(NULL));//产生随机值发生器

int chose = 0;//选择是否开始游戏

do

{

menu();//菜单

printf("请选择:>");

scanf("%d", &chose);

switch (chose)

{

case 1:

game();//开始游戏

break;

case 0:

printf("退出游戏\n");

break;

default:

printf("输入错误,没有该选项\n");

break;

}

} while (chose);

}

int main()

{

text();

system("pause");

return 0;

}

游戏效果图

①开始选择菜单、难易模式选择

cbd92edbf45e4144a2b1a9c084b33e7e.png

②两种难度扫雷

↓9×9雷盘 10颗雷

ae97dea525e3ee5079bad27f26fcb50a.png

↓16×16雷盘 40颗雷

7808e981ddd84fecb7b3da61c834d35b.png

③演示标记

243ddb2dbaae0772b76b661465211779.png

cf4c9d0c77ce88f39b9dfab65ab078d0.png

④GAME OVER 玩家失败演示

4c21cdd8175d4ecf2a7c0057066795bf.png

⑤WIN 玩家成功演示

8dd16e8c2f7d4cbc042ca650f2055ae2.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值