一、简介

        扫雷是一款经典游戏,通过这几天学习也是成果编写出了一个简单的c程序实现。

        游戏代码逻辑主要分为:

1、创建棋盘(初始化)

        这里需要创建两个棋盘,一个用于存放雷的信息,一个用于展示给玩家。

2、棋盘打印

        创造一个能够将棋盘打印出来的函数,为了能使玩家看到棋盘,以及后续自己以及扫过的雷。

3、设置雷

        设置好雷的个数,通过随机生成两个数的方式产生雷的坐标,将该坐标设为‘雷’,并以此循环,直到雷的个数达到目标,中间不能重复埋雷。

4、排查雷

        这里就是玩家操作的模块了,玩家输入一个坐标,此函数在放好雷的棋盘中找到该坐标,如果为雷,则直接宣布失败,游戏结束;反之,将该坐标周围8格的雷数显示在此格中。这里有一个特殊情况,就是周围没有雷,如果周围没有雷,则进行整片排雷,就是直接将该坐标周围的格子翻开,显示他们周围的雷数,如果周围的格子中还存在“无雷”的格子,则继续连翻其周围的格子。这里要注意设置边界条件,不可超过棋盘字符数组的边界;还要判断翻的格子是否已经被翻过,如果被翻过,则跳过这个格子,不然会出现不停互翻的情况。

5、判断输赢

输赢判断也直接在其中进行,如果棋盘中剩余未排查的格子数正好等于设置的雷数时,玩家获胜。

二、代码模块(c语言)

        我创建了三个文件,包括头文件game.h,源文件game.c和test.c。其中game.c用来存放全局变量、声明函数以及调用函数库。test.c用来放主函数。game.c用来放实现不同功能的函数。 

        c语言实现简单扫雷游戏-LMLPHP

1、头文件代码:

#pragma once

#include <stdio.h>
#include <windows.h>
#include <time.h>


#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS ROW+2

#define EASY_COUNT 10

char InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

void DisplayBoard(char board[ROWS][COLS], int rows, int cols);

void SetMine(char board[ROWS][COLS], int rows, int cols);

void FindMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row , int col);

int get_mine_count(char mineboard[ROWS][COLS], int x, int y);

void WholeEx(char showboard[ROWS][COLS], char mineboard[ROWS][COLS], int x, int y);

2、主程序代码:

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"


void menu()
{
	printf("***************************\n");
	printf("******   1 . play   *******\n");
	printf("******   0 . exit   *******\n");
	printf("***************************\n");
}

void game()
{
	char mine[ROWS][COLS] = { 0 };     //存放布置好的雷的信息
	char show[ROWS][COLS] = { 0 };     //存放排查出的雷的信息
	//初始化数组的内容为指定的内容
	//mine 数组在没有布置雷的时候,都是'0'
	InitBoard(mine, ROWS, COLS, '0');
	//show 数组在没有排查雷的时候,都是'*'
	InitBoard(show, ROWS, COLS, '*');
	//棋盘打印
	DisplayBoard(show, ROWS, COLS);
	//设置雷
	SetMine(mine, ROW, COL);
	//排查雷
	FindMine(mine,show,ROW,COL);

}


int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;

	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		system("cls");
		switch (input)
		{
		case 1:
			game();
			
			break;
		case 0:
			printf("退出游戏:>\n");
			break;
		default:
			printf("输入错误,请重新输入:>\n");
			break;
		}
	} while (input);
	return 0;
}


3、函数代码:

#define _CRT_SECURE_NO_WARNINGS

#include "game.h"


char InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0;i < rows;i++)
	{
		for (j = 0;j < cols;j++)
		{
			board[i][j] = set;
		}
	}
}

void DisplayBoard(char board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	int j = 0;
	printf("--------------扫雷游戏---------------\n\n");
	printf("   ");
	for (j = 1;j < cols - 1;j++)
	{
		printf(" %d ", j);         //打印列号
	}
	printf("\n");
	printf("   ");
	for (j = 1;j < cols - 1;j++)
	{
		printf("---");         //打印列号
	}
	printf("\n");
	for (i = 1 ; i < rows-1 ; i++)
	{
		printf(" %d|", i);         //打印行号
		for (j = 1 ; j < cols-1 ; j++)
		{
			//这里如此打印行号和列号是不合理的,因为这样会改变本身board内的内容
			//if (i == 0)
			//{
			//	board[i][j] = j+48;
			//}
			//if (j == 0)
			//{
			//	board[i][j] = i+48;
			//}
			printf(" %c ", board[i][j]);
		}
		printf("|\n");
	}
	printf("   ");
	for (j = 1;j < cols - 1;j++)
	{
		printf("---");         //打印列号
	}
	printf("\n\n");
	printf("--------------扫雷游戏---------------\n");
}

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = EASY_COUNT;
	while (count)
	{
		do
		{
			i = rand() % row  + 1;
			j = rand() % col  + 1;
		} while (board[i][j]-48);
		board[i][j] = '1';
		count--;
	}
}

void FindMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)   
{
	int x = 0;
	int y = 0;
	int c = 0;
	int i = 0;
	int j = 0;
	int win = 0;  //胜利条件,剩余*数等于雷数
	
	while (1)
	{
		printf("请输入要排查的坐标:>\n");
		scanf("%d %d", &x, &y);
		system("cls");
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (showboard[x][y] != '*')
			{
				printf("坐标已经被排查过了。\n");
			}
			//如果是雷
			else if (mineboard[x][y] == '1')
			{
				DisplayBoard(mineboard, ROWS, COLS);
				printf("恒遗憾,你被炸死了\n");
				break;
			}
			//如果不是雷
			else
			{
				//统计坐标(x,y)周围有几个雷的函数
				c = get_mine_count(mineboard, x, y);
				showboard[x][y] = c + '0';          //转换成数字字符
				//连翻函数(如果排到雷数为0,直接将周围全翻了)
				if (c == 0)
				{
					showboard[x][y] == ' ';
					WholeEx(showboard, mineboard, x, y);
				}

				//棋盘打印
				DisplayBoard(showboard, ROWS, COLS);
			}
		}
		else
		{
			printf("坐标超出范围,请重新输入坐标:>\n");
		}
		win = 0;
		for (i = 1;i <= row;i++)
		{
			for (j = 1;j < col;j++)
			{
				if (showboard[i][j] == '*')
				{
					win++;
				}
			}
		}
		if (win == EASY_COUNT)
		{
			system("cls");
			DisplayBoard(mineboard, ROWS, COLS);
			printf("恭喜你,排雷成功!\n");
			break;
		}
	}

}


int get_mine_count(char mineboard[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	int c = 0;
	//这里可以不用循环,直接吧周围的字符加起来减去8倍的字符零的ASCII码大小,这样会更快
	for (i = x - 1;i <= x + 1;i++)
	{
		for (j = y - 1;j <= y + 1;j++)
		{
			if (mineboard[i][j] == '1')
			{
				c++;
			}
		}
	}
	return c;
}


void WholeEx(char showboard[ROWS][COLS], char mineboard[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	int c = 0;

	for (i = x - 1;i <= x + 1;i++)
	{
		for (j = y - 1;j <= y + 1;j++)
		{
			c = get_mine_count(mineboard, i, j);
			if (c == 0 && i >= 1 && i <= ROW && j >= 1 && j <= ROW && showboard[i][j] == '*')
			{
				showboard[i][j] = c + '0';
				WholeEx(showboard, mineboard, i, j);
			}
			else
			{
				showboard[i][j] = c + '0';
			}
			//showboard[i][j] = c + '0';
		}
	}
}
02-12 19:23