【C】扫雷之晋段赛

1、游戏介绍

  一款基于扫雷的新型排位游戏(ps:目前只有单机版,仅支持键盘输入数据),只要通过排位晋段赛,即可获得对应的称号。(ps:目前仅提供白银、黄金、钻石、大师等四个段位晋级赛,分别对应10颗、20颗、30颗、50颗雷)

2、玩法说明

  版权说明


  首先根据主菜单提示输入1开始游戏,


  接下来根据个人实力选择要冲击的段位,


  以冲击白银段位为例,下图所示的10x10棋盘中设置了10颗雷,


  根据提示选择是否标记当前位置的雷,或者排查当前位置下面是否有雷,


  选择排查地雷,并在10x10棋盘上任选一个坐标输入(4,7),


  这里实现了展开一片非雷区域的功能,由于坐标(3,10)上不再是*,这里再次输入坐标(3,10),会提示重新输入,


  由于(3,10)位置上数字为0,表示以(3,10)为中心,周围8个方向上没有雷,所以尝试输入坐标(2,10),


  发现(2,10)位置是1,表示周围有1个雷,多次尝试输入其他坐标排雷,直到可以确定某一位置必然是雷,


  由于(1,9)、(2,9)、(2,10)三个位置均为1,那么(1,10)必为雷,对其进行标记(ps:标记为?),


  当然,也支持取消标记,


  如果输入坐标不合法(ps:输入除数字外的字符),游戏因异常被终止,


  如果已经标记了某个位置(确定该位置就是雷),


  后来误操作,选择了排查雷,然后输入了上图中被标记位置的坐标(1,1),这仍然会被炸死,谁让你乱改呢?哈哈哈哈哈……


  省略若干步骤,直到标记所有的雷,游戏结束,并且打印雷的分布图,


  若输入坐标恰好是雷的坐标,则游戏结束,输出雷的分布图,并且蜂鸣器响,这里用蜂鸣器的响声模拟爆炸。


3、功能介绍

  a.为提升游戏体验,保证玩家第一次随机输入排雷坐标不被炸死;

  b.展开非雷区域;

  c.标记雷;

  d.取消标记雷;

  e.不小心踩雷,蜂鸣器响。

4、方案设计


  关于游戏主菜单、冲段等级菜单以及扫雷游戏菜单等这里就不一一叙述了,接下来讲讲怎么开发这款游戏。首先需要说明的是,游戏界面显示的是'*',真正存放雷的界面显示'0'和'1',标记用'?'表示,接下来为了方便讲解,先给出MineSweep.h,该头文件包含了一些宏定义以及函数的声明。

# ifndef __MINESWEEP_H__
# define __MINESWEEP_H__

//真正存放雷的行数
# define MINE_REAL_ROW 10
//真正存放雷的列数
# define MINE_REAL_COL 10
//真正存放雷的棋盘的行数
# define BOARD_REAL_ROW 10
//真正存放雷的棋盘的列数
# define BOARD_REAL_COL 10
//显示雷数组的行
# define MINE_DISPLAY_ROW MINE_REAL_ROW+2
//显示雷数组的列
# define MINE_DISPLAY_COL MINE_REAL_COL+2
//显示存放雷棋盘数组的行
# define BOARD_DISPLAY_ROW BOARD_REAL_ROW+2
//显示存放雷棋盘数组的列
# define BOARD_DISPLAY_COL BOARD_REAL_COL+2
//增加一行雷
# define MINE_ONE_ROW 1
//增加一列雷
# define MINE_ONE_COL 1


//白银段位设置10个雷
# define MINE_SILVER_COUNT 10
//黄金段位设置20个雷
# define MINE_GOLDEN_COUNT 20
//钻石段位设置30个雷
# define MINE_DIAMOND_COUNT 30
//大师段位设置50个雷
# define MINE_MASTER_COUNT 50


//随机设置雷的存放位置
void SetMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int rank_num);
//第一次遇到雷后移动该雷的位置到其他位置
void MoveMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int x, int y);
//显示雷的分布界面
void DisplayMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL]);
//显示游戏棋盘的界面
void DisplayBoard(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL]);
//统计雷的个数
int GetMineCount(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int x, int y);
//统计棋盘中标记地雷的个数
int GetSignCount(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL]);
//展开没有雷的区域
void SpreadNoMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y);
//标记雷
void SignMine(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y);
//取消标记雷
void NoSignMine(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y);
//玩扫雷游戏
void PlayGame(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int rank);

#endif //__MINESWEEP_H__

关于棋盘选择10x10 以及 12x12

  10x10的数组存放雷的棋盘数组以及雷的数组,12x12的数组是在10x10的数组基础上增加了两圈,最外面一圈存放一堆数字,为了更好的显示各个位置的坐标,由于真正存放雷的数组的最外层显示其周围雷的数量不方便,这里选择增加一圈,便于对循环的控制。

关于初始化数组

  定义好数组后,用库函数memset(别忘了引头文件<string.h> or <memory.h>)对其初始化。

	//定义雷数组
	char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL] = {0};
	//定义游戏棋盘数组
	char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL] = {0};

	//初始化雷数组
	memset(mine, '0', sizeof(mine));
	//初始化游戏棋盘数组
	memset(board, '*', sizeof(board));

关于设置雷的存放位置

  利用随机数种子产生随机数存放到雷数组中,当然还得传入冲段等级对应的雷的数量。

/*
*	函数名称:SetMine
*
*	函数功能:随机设置雷的位置
*
*	入口参数:mine, rank_num
*
*	出口参数:void
*
*	返回类型:void
*/

void SetMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int rank_num)
{
	int x = 0;
	int y = 0;

	while (rank_num)
	{
		x = (rand() % MINE_REAL_ROW) + MINE_ONE_ROW;
		y = (rand() % MINE_REAL_COL) + MINE_ONE_COL;

		if ('0' == mine[x][y])
		{
			mine[x][y] = '1';
			rank_num--;
		}
		else
		{
			;
		}
	}

	return;
}

关于显示棋盘和雷的界面

  这里就不赘述了,详情见源代码。

关于第一次踩雷不被炸死

  注意:这里的第一次踩雷是指第一次输入排雷坐标(ps:在玩游戏过程中,应该设置一个标志位来检测是否为第一次输入排雷坐标),而不是说输入几次排雷坐标没被炸死,再次输入一个排雷坐标被炸死的情况。理解这点很重要,要不然每次游戏过程中的第一次踩雷都将被移动到非雷的位置,这是错误的。

  设计思路:如果当前位置是雷,那么将这个雷的位置移动到其他非雷的位置(ps:只移动一个雷的位置)。

/*
*	函数名称:MoveMine
*
*	函数功能:若第一次遇到雷,则移动该雷的位置到其他非雷的位置
*
*	入口参数:mine, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void MoveMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int x, int y)
{
	if ('1' == mine[x][y])
	{
		mine[x][y] = '0';
		while (1)
		{
			int new_x = (rand() % MINE_REAL_ROW) + MINE_ONE_ROW;
			int new_y = (rand() % MINE_REAL_COL) + MINE_ONE_COL;

			if ((new_x != x) && (new_y != y) && ('1' != mine[new_x][new_y]))
			{
				mine[new_x][new_y] = '1';
				break;
			}
			else
			{
				;
			}
		}
	}
	else
	{
		;
	}

	return;
}

关于统计雷的个数

  以当前位置为中心,查看其周围8个方向是否有雷存在,如果有,则计数器加1,反之则为0。而雷的显示界面只有'0'和'1',显然这是字符,要想得到数字,只需减去'0'即可。(ps: 想得到数字1,只需要'1' - '0'即可,要想得到数字2,只需要'2' - '0'即可,这是因为数字对应的ASCII码是连续的)

/*
*	函数名称:GetMineCount
*
*	函数功能:统计雷的个数
*
*	入口参数:mine, x, y
*
*	出口参数:void
*
*	返回类型:int
*/

int GetMineCount(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], 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 + 1][y + 1] + mine[x + 1][y]
		   + mine[x + 1][y - 1] + mine[x][y - 1] - 8 * '0');
}

关于统计棋盘中标记地雷的个数

  遍历真正存放雷的棋盘数组,若当前位置是雷,并且该雷被标记,则计数器加1。

/*
*	函数名称:GetSignCount
*
*	函数功能:统计棋盘中标记地雷的个数
*
*	入口参数:mine, board
*
*	出口参数:mark_count
*
*	返回类型:int
*/

int GetSignCount(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL])
{
	int row = 0;
	int col = 0;
	int mark_count = 0;

	for (row=1; row<=BOARD_REAL_ROW; row++)
	{
		for (col=1; col<=BOARD_REAL_COL; col++)
		{
			if (('1' == mine[row][col]) && ('?' == board[row][col]))
			{
				mark_count++;
			}
			else
			{
				;
			}
		}
	}

	return mark_count;
}

关于展开没有雷的区域

  若该位置没有雷,则展开该位置一圈的非雷区域,横坐标从x-1遍历到x+1,纵坐标从y-1遍历到y+1。

/*
*	函数名称:SpreadNoMine
*
*	函数功能:展开没有雷的区域
*
*	入口参数:mine, board, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void SpreadNoMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
	int row = 0;
	int col = 0;
	char ret = GetMineCount(mine, x, y) + '0';

	if ('0' == ret)
	{
		for (row=x-1; row<=x+1; row++)
		{
			for (col=y-1; col<=y+1; col++)
			{
				board[row][col] = GetMineCount(mine, row, col) + '0';
			}
		}
	}
	else
	{
		;
	}

	return;
}

  上述展开只能实现一圈的展开,因此使用递归的方式去实现展开。

void SpreadNoMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
	if ((x >= 1) && (x <= MINE_REAL_ROW) && (y >= 1) && (y <= MINE_REAL_COL))
	{
		if (0 == GetMineCount(mine, x, y))
		{
			board[x][y] = '0';
			if ('*' == board[x - 1][y - 1])
			{
				SpreadNoMine(mine, board, x-1, y-1);
			}
			if ('*' == board[x - 1][y])
			{
				SpreadNoMine(mine, board, x-1, y);
			}
			if ('*' == board[x + 1][y + 1])
			{
				SpreadNoMine(mine, board, x+1, y+1);
			}
			if ('*' == board[x][y - 1])
			{
				SpreadNoMine(mine, board, x, y-1);
			}
			if ('*' == board[x][y + 1])
			{
				SpreadNoMine(mine, board, x, y+1);
			}
			if ('*' == board[x + 1][y - 1])
			{
				SpreadNoMine(mine, board, x+1, y-1);
			}
			if ('*' == board[x + 1][y])
			{
				SpreadNoMine(mine, board, x+1, y);
			}
			if ('*' == board[x + 1][y + 1])
			{
				SpreadNoMine(mine, board, x+1, y+1);
			}
		}
		else
		{
			;
		}
	}
	else
	{
		;
	}

	return;
}

关于标记雷和取消标记雷

  这个实现起来比较简单,只要替换掉游戏显示棋盘上的'*'即可。

/*
*	函数名称:SignMine
*
*	函数功能:标记雷
*
*	入口参数:board, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void SignMine(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
	if ('*' == board[x][y])
	{
		board[x][y] = '?';
	}
	else
	{
		;
	}

	return;
}

/*
*	函数名称:NoSignMine
*
*	函数功能:取消标记雷
*
*	入口参数:board, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void NoSignMine(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
	if ('?' == board[x][y])
	{
		board[x][y] = '*';
	}
	else
	{
		;
	}

	return;
}

关于玩扫雷游戏细节

  每次开始玩游戏前,先选择是否要标记雷或者排查雷,这里通过扫雷游戏菜单来进行选择,除此之外,对于重复输入的坐标,以及输入非数字的字符等进行了相应的处理,详情见源代码。

5、源代码

附上源代码,欢迎各位大佬交流,写的不好或者不对的地方麻烦给出改进意见,争取作出一款可玩性强的游戏。

MineSweep.c

#define _CRT_SECURE_NO_WARNINGS 1

/*
* Copyright (c) 2018, code farmer from sust
* All rights reserved.
*
* 文件名称:MineSweep.c
* 功能:扫雷游戏内部实现细节
*       1.第一次踩雷不炸死
*       2.周围没有雷实现展开
*       3.实现标记雷(用?标记)
* 当前版本:V3.0
* 作者:sustzc
* 完成日期:2018年4月13日12:44:33
*/

# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
# include "MineSweep.h"

/*
*	函数名称:SetMine
*
*	函数功能:随机设置雷的位置
*
*	入口参数:mine, rank_num
*
*	出口参数:void
*
*	返回类型:void
*/

void SetMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int rank_num)
{
	int x = 0;
	int y = 0;

	while (rank_num)
	{
		x = (rand() % MINE_REAL_ROW) + MINE_ONE_ROW;
		y = (rand() % MINE_REAL_COL) + MINE_ONE_COL;

		if ('0' == mine[x][y])
		{
			mine[x][y] = '1';
			rank_num--;
		}
		else
		{
			;
		}
	}

	return;
}

/*
*	函数名称:MoveMine
*
*	函数功能:若第一次遇到雷,则移动该雷的位置到其他非雷的位置
*
*	入口参数:mine, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void MoveMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], int x, int y)
{
	if ('1' == mine[x][y])
	{
		mine[x][y] = '0';
		while (1)
		{
			int new_x = (rand() % MINE_REAL_ROW) + MINE_ONE_ROW;
			int new_y = (rand() % MINE_REAL_COL) + MINE_ONE_COL;

			if ((new_x != x) && (new_y != y) && ('1' != mine[new_x][new_y]))
			{
				mine[new_x][new_y] = '1';
				break;
			}
			else
			{
				;
			}
		}
	}
	else
	{
		;
	}

	return;
}

/*
*	函数名称:DisplayMine
*
*	函数功能:显示雷的分布界面
*
*	入口参数:mine
*
*	出口参数:void
*
*	返回类型:void
*/

void DisplayMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL])  
{  
    int i = 0;  
    int j = 0;  

    printf("  ");

    for(i=1; i<=MINE_REAL_ROW; i++)
    {  
        printf(" %d ", i);  
    }  

    printf("\n");

    for(i=1; i<=MINE_REAL_ROW; i++)  
    {     
        printf("%2d", i);

        for(j=1; j<=MINE_REAL_COL; j++)  
        {  
            printf(" %c ", mine[i][j]);
        }  

        printf("\n");  
    }  

	return;
} 

/*
*	函数名称:DisplayBoard
*
*	函数功能:显示游戏棋盘界面
*
*	入口参数:board
*
*	出口参数:void
*
*	返回类型:void
*/

void DisplayBoard(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL])  
{  
    int i = 0;  
    int j = 0;  

    printf("  "); 

    for(i=1; i<=BOARD_REAL_ROW; i++)
    {  
        printf(" %d ", i);  
    }  

    printf("\n");

    for(i=1; i<=BOARD_REAL_ROW; i++)  
    {     
        printf("%2d", i);

        for(j=1; j<=BOARD_REAL_COL; j++)  
        {  
            printf(" %c ", board[i][j]);
        }  

        printf("\n");  
    }  

	return;
} 

/*
*	函数名称:GetMineCount
*
*	函数功能:统计雷的个数
*
*	入口参数:mine, x, y
*
*	出口参数:void
*
*	返回类型:int
*/

int GetMineCount(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], 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 + 1][y + 1] + mine[x + 1][y]
		   + mine[x + 1][y - 1] + mine[x][y - 1] - 8 * '0');
}

/*
*	函数名称:GetSignCount
*
*	函数功能:统计棋盘中标记地雷的个数
*
*	入口参数:mine, board
*
*	出口参数:mark_count
*
*	返回类型:int
*/

int GetSignCount(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL])
{
	int row = 0;
	int col = 0;
	int mark_count = 0;

	for (row=1; row<=BOARD_REAL_ROW; row++)
	{
		for (col=1; col<=BOARD_REAL_COL; col++)
		{
			if (('1' == mine[row][col]) && ('?' == board[row][col]))
			{
				mark_count++;
			}
			else
			{
				;
			}
		}
	}

	return mark_count;
}

/*
*	函数名称:SpreadNoMine
*
*	函数功能:展开没有雷的区域
*
*	入口参数:mine, board, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void SpreadNoMine(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
    if ((x >= 1) && (x <= MINE_REAL_ROW) && (y >= 1) && (y <= MINE_REAL_COL))
    {
        if (0 == GetMineCount(mine, x, y))
        {
            board[x][y] = '0';
            if ('*' == board[x - 1][y - 1])
            {
                SpreadNoMine(mine, board, x-1, y-1);
            }
            if ('*' == board[x - 1][y])
            {
                SpreadNoMine(mine, board, x-1, y);
            }
            if ('*' == board[x + 1][y + 1])
            {
                SpreadNoMine(mine, board, x+1, y+1);
            }
            if ('*' == board[x][y - 1])
            {
                SpreadNoMine(mine, board, x, y-1);
            }
            if ('*' == board[x][y + 1])
            {
                SpreadNoMine(mine, board, x, y+1);
            }
            if ('*' == board[x + 1][y - 1])
            {
                SpreadNoMine(mine, board, x+1, y-1);
            }
            if ('*' == board[x + 1][y])
            {
                SpreadNoMine(mine, board, x+1, y);
            }
            if ('*' == board[x + 1][y + 1])
            {
                SpreadNoMine(mine, board, x+1, y+1);
            }
        }
        else
        {
            ;
        }
    }
    else
    {
        ;
    }

    return;
}

/*
*	函数名称:SignMine
*
*	函数功能:标记雷
*
*	入口参数:board, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void SignMine(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
	if ('*' == board[x][y])
	{
		board[x][y] = '?';
	}
	else
	{
		;
	}

	return;
}

/*
*	函数名称:NoSignMine
*
*	函数功能:取消标记雷
*
*	入口参数:board, x, y
*
*	出口参数:void
*
*	返回类型:void
*/

void NoSignMine(char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int x, int y)
{
	if ('?' == board[x][y])
	{
		board[x][y] = '*';
	}
	else
	{
		;
	}

	return;
}

/*
*	函数名称:GameMenu
*
*	函数功能:扫雷游戏菜单
*
*	入口参数:void
*
*	出口参数:select
*
*	返回类型:int
*/

int GameMenu(void)
{
	int select = 0;

	printf("***********************************\n");
	printf("**********  1 标记地雷  ***********\n");
	printf("**********  2 取消标记  ***********\n");
	printf("**********  3 排查地雷  ***********\n");
	printf("***********************************\n");
	printf("select>");

	assert(1 == scanf("%d", &select));

	return select;
}

/*
*	函数名称:PlayGame
*
*	函数功能:玩扫雷游戏
*
*	入口参数:mine, board, rank_num
*
*	出口参数:void
*
*	返回类型:void
*/

void PlayGame(char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL], char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL], int rank_num)
{
	int count = 0;
	int ret = 0;
	int select = 0;
	//第一次遇到雷
	int first_mine = 0;

	while(1)
	{
		select = GameMenu();

		switch(select)
		{
			case 1:
				{
					int x = 0;
					int y = 0;

					printf("请输入标记坐标x y(x,y之间用空格隔开):");
					//若没有输入数字,则异常退出
					assert(2 == (scanf("%d %d", &x, &y)));

					if (((1 <= x) && (x <= MINE_REAL_ROW)) && ((1 <= y) && (y <= MINE_REAL_COL)))
					{
						if ('?' == board[x][y])
						{
							printf("坐标重复,该位置已标记,请重新输入!\n");
						}
						else
						{
							SignMine(board, x, y);
							DisplayBoard(board);
						}
        		    }
					else
					{
						printf("输入坐标有误!\n");
					}
               }
				break;
			case 2:
				{
					int x = 0;
					int y = 0;

					printf("请输入取消标记坐标x y(x,y之间用空格隔开):");
					//若没有输入数字,则异常退出
					assert(2 == (scanf("%d %d", &x, &y)));

					if (((1 <= x) && (x <= MINE_REAL_ROW)) && ((1 <= y) && (y <= MINE_REAL_COL)))
					{
						if ('*' == board[x][y])
						{
							printf("坐标重复,已取消标记,请重新输入!\n");
						}
						else
						{
							NoSignMine(board, x, y);
							DisplayBoard(board);
						}
					}
					else
					{
						printf("输入坐标有误!\n");
					}
				}
				break;
			case 3:
			{
				int x = 0;
				int y = 0;

				//输入坐标
				printf("请输入坐标x y(x,y之间用空格隔开):");
				//若没有输入数字,则异常退出
				assert(2 == (scanf("%d %d", &x, &y)));

				//坐标合法性判断
				if (((1 <= x) && (x <= MINE_REAL_ROW)) && ((1 <= y) && (y <= MINE_REAL_COL)))
				{
					if ('1' == mine[x][y])
					{
						//保证第一次移动位置后就不会再移动雷的位置
						if ((0 == count) && (0 == first_mine))
						{
							MoveMine(mine, x, y);
							//DisplayMine(mine);
							DisplayBoard(board);
							//first_mine任意置为一个数字,只要不是0即可
							//确保不会再次移动雷的位置
							first_mine = 1;
						}
						else 
						{
							//游戏结束,蜂鸣器响
							printf("你被雷炸死了!\a\a\a\a\a\n");
							printf("雷的分布界面如下!\n");
							DisplayMine(mine);
							exit(0);
						}
					}
					else if (('*' != board[x][y]) && ('?' != board[x][y]))
						{
							printf("坐标重复,请重新输入!\n");
						}
						else 
						{
							//防止非第一次输入排雷坐标时移动某个雷的位置
							first_mine = 1;
							ret = GetMineCount(mine, x, y);
							board[x][y] = ret + '0';
							//展开非雷区域
							SpreadNoMine(mine, board, x, y);
							DisplayBoard(board);
							//被标记的个数
							count = GetSignCount(mine, board);
							//标记数等于设置的雷的数量
							if (rank_num == count)
							{
								printf("恭喜你,晋段成功!\n");
								printf("雷的分布界面如下!\n");
								DisplayMine(mine);
								exit(0);
							}
							else
							{
								;
							}
						}
				}
				else
				{
					printf("输入坐标有误!\n");
				}
			}
				break;
			default:
				printf("输入有误,请重新输入!\n");
				break;
		}
	}

	return;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

/*
* Copyright (c) 2018, code farmer from sust
* All rights reserved.
*
* 文件名称:test.c
* 功能:扫雷游戏主程序(支持段位选择)
*
* 当前版本:V3.0
* 作者:sustzc
* 完成日期:2018年4月13日12:44:20
*/

# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
# include <time.h>
# include "MineSweep.h"

/*
*	函数名称:MainMenu
*
*	函数功能:游戏主菜单显示
*
*	入口参数:void
*
*	出口参数:choose
*
*	返回类型:int
*/

int MainMenu(void)
{
	int choose = 0;

	printf("********************************\n");
	printf("******欢迎来到扫雷冲段大赛******\n");
	printf("********** 1 开始游戏 **********\n");
	printf("********** 2 版权说明 **********\n");
	printf("********** 0 退出游戏 **********\n");
	printf("********************************\n");
	printf("choose>");

	assert(1 == scanf("%d", &choose));

	return choose;
}

/*
*	函数名称:CopyRight
*
*	函数功能:游戏版权说明
*
*	入口参数:void
*
*	出口参数:void
*
*	返回类型:void
*/

void CopyRight(void)
{
	printf("**************************************************\n");
	printf("*** Copyright (c) 2018, code farmer from sust. ***\n");
	printf("************** All rights reserved. **************\n");
	printf("**************************************************\n");
	printf("\n");

	return;
}

/*
*	函数名称:RankMenu
*
*	函数功能:冲段等级菜单显示
*
*	入口参数:void
*
*	出口参数:select
*
*	返回类型:int
*/

int RankMenu(void)
{
	int select = 0;

	printf("***********************************\n");
	printf("******* 请选择你要冲击的段位 ******\n");
	printf("**********  1 白银段位  ***********\n");
	printf("**********  2 黄金段位  ***********\n");
	printf("**********  3 钻石段位  ***********\n");
	printf("**********  4 大师段位  ***********\n");
	printf("**********  0 返回上层  ***********\n");
	printf("***********************************\n");
	printf("select>");

	assert(1 == scanf("%d", &select));

	return select;
}

/*
*	函数名称:SilverGame
*
*	函数功能:白银晋段赛
*
*	入口参数:void
*
*	出口参数:void
*
*	返回类型:void
*/

void SilverGame(void)
{
	//定义雷数组
	char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL] = {0};
	//定义游戏棋盘数组
	char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL] = {0};

	//初始化雷数组
	memset(mine, '0', sizeof(mine));
	//初始化游戏棋盘数组
	memset(board, '*', sizeof(board));

	//设置雷
	SetMine(mine, MINE_SILVER_COUNT);

	//显示雷的分布界面
	//DisplayMine(mine);
	//显示扫雷游戏棋盘界面
	DisplayBoard(board);

	//白银晋段赛
	PlayGame(mine, board, MINE_SILVER_COUNT);

	return;
}

/*
*	函数名称:GoldenGame
*
*	函数功能:黄金晋段赛
*
*	入口参数:void
*
*	出口参数:void
*
*	返回类型:void
*/

void GoldenGame(void)
{
	//定义雷数组
	char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL] = {0};
	//定义游戏棋盘数组
	char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL] = {0};

	//初始化雷数组
	memset(mine, '0', sizeof(mine));
	//初始化游戏棋盘数组
	memset(board, '*', sizeof(board));

	//设置雷
	SetMine(mine, MINE_GOLDEN_COUNT);

	//显示雷的分布界面
	//DisplayMine(mine);
	//显示扫雷游戏棋盘界面
	DisplayBoard(board);

	//黄金晋段赛
	PlayGame(mine, board, MINE_GOLDEN_COUNT);

	return;
}

/*
*	函数名称:DiamondGame
*
*	函数功能:钻石晋段赛
*
*	入口参数:void
*
*	出口参数:void
*
*	返回类型:void
*/

void DiamondGame(void)
{
	//定义雷数组
	char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL] = {0};
	//定义游戏棋盘数组
	char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL] = {0};

	//初始化雷数组
	memset(mine, '0', sizeof(mine));
	//初始化游戏棋盘数组
	memset(board, '*', sizeof(board));

	//设置雷
	SetMine(mine, MINE_DIAMOND_COUNT);

	//显示雷的分布界面
	//DisplayMine(mine);
	//显示扫雷游戏棋盘界面
	DisplayBoard(board);

	//钻石晋段赛
	PlayGame(mine, board, MINE_DIAMOND_COUNT);

	return;
}

/*
*	函数名称:MasterGame
*
*	函数功能:大师晋段赛
*
*	入口参数:void
*
*	出口参数:void
*
*	返回类型:void
*/

void MasterGame(void)
{
	//定义雷数组
	char mine[MINE_DISPLAY_ROW][MINE_DISPLAY_COL] = {0};
	//定义游戏棋盘数组
	char board[BOARD_DISPLAY_ROW][BOARD_DISPLAY_COL] = {0};

	//初始化雷数组
	memset(mine, '0', sizeof(mine));
	//初始化游戏棋盘数组
	memset(board, '*', sizeof(board));

	//设置雷
	SetMine(mine, MINE_MASTER_COUNT);

	//显示雷的分布界面
	//DisplayMine(mine);
	//显示扫雷游戏棋盘界面
	DisplayBoard(board);

	//大师晋段赛
	PlayGame(mine, board, MINE_MASTER_COUNT);

	return;
}

/*
*	函数名称:main
*
*	函数功能:游戏主程序
*
*	入口参数:void
*
*	出口参数:0
*
*	返回类型:int
*/

int main(void)
{
	int choose = 0;
	int select = 0;
	srand((unsigned int)time(NULL));

	do
	{
		choose = MainMenu();

last:	switch(choose)
		{
			case 0:
				printf("退出游戏!\n");
				break;
			case 1:
				select = RankMenu();

				switch (select)
				{
					case 1:
						SilverGame();
						break;
					case 2:
						GoldenGame();
						break;
					case 3:
						DiamondGame();
						break;
					case 4:
						MasterGame();
						break;
					case 0:
						break;
					default:
						printf("输入有误,请重新输入!\n");
					    goto last;
						break;
				}
				break;
			case 2:
				CopyRight();
				exit(1);
			default:
				printf("输入有误,请重新输入!\n");
				break;
		}
	
	}while(choose);

	return 0;
}

5、个人感触

  目前该游戏更新到了第三版,也仅仅实现了一丢丢的功能,并且仍有很多bug。以后有机会的话,想利用MFC或者QT开发个图形界面玩一玩,起码鼠标可以用,黑框框的程序实在是不忍直视啊!!!关于后续版本,想尝试下统计每一局比赛的积分,当然这需要一套算法(ps:连胜次数越多,加分越多),并且加入惩罚机制(ps:比如需要在规定时间内排查雷,如果超时,会缩短每次排雷的时间,时间到了也算游戏结束),除此之外加入网络对战也可以哦!(ps:补充点socket编程的知识,一方布置雷,另一方排雷)。

猜你喜欢

转载自blog.csdn.net/sustzc/article/details/79943715
今日推荐