C语言程序设计——扫雷游戏

扫雷游戏是以前Windows自带的一款经典益智游戏,游戏玩法简单,不懂的朋友可以去百度然后尝试一下就知道了,现在我们就用C语言来实现一下这款游戏。

首先我们理清整体的思路,可以采用两个二维数组来作为游戏棋盘,一个用来埋雷(只有玩家通过游戏后显示一次),一个作为游戏棋盘,这里可以将埋雷棋盘初始化为全字符0,用字符1表示地雷,将游戏棋盘初始化为全'*'。(注:在确定数组大小时,我们可以在所需棋盘大小的基础上再在周围增加一圈棋盘,即行列都加2,这样便于后续统计扫雷位置周围地雷个数)。

除此之外,我们好需要通过数字、下划线以及一些其他符号来打印一个可视化的棋盘和一个选择菜单。

棋盘、菜单示例

效果比较粗糙,大家可以自己进行调整改进,使棋盘更加美观。

 代码实现:

void Menu()//选择菜单
{
	printf("#######################\n");
	printf("#  1.Play    2.Exit  ##\n");
	printf("#######################\n");
	printf("请输入你的选择: ");
}
void ShowBoard(char board[][COL], int row, int col)//打印棋盘
{
	printf("  ");
	for (int i = 1; i <= row - 2; i++){
		printf("%4d", i);
	}
	printf("\n   ");
	for (int i = 1; i <= row - 2; i++){
		printf(" ___");
	}
	for (int j = 1; j <= col - 2; j++){
		printf("\n");
		printf("%2d", j);
		printf(" |");

		for (int i = 1; i <= row - 2; i++){
			printf("_%c_|", board[j][i]);
		}
	}
	printf("\n");
	return;
}

随机埋雷

首先确定地雷个数,可以采用宏定义NUM,便于我们改变个数而不改变后续的逻辑,然后就可以利用rand函数来产生随机坐标进行埋雷了。代码实现:

void SetMines(char board[][COL], int row, int col){//随机埋雷
	int i = 0;
	while (i < NUM) {
		int _x = rand() % (row-2) + 1;
		int _y = rand() % (col-2) + 1;
		if (board[_x][_y] == BOOM) {
			continue;
		}
		board[_x][_y] = BOOM;
		i++;
	}
}

统计周围地雷个数

int CountMines(char board[][COL], int x, int y){// 统计附近地雷个数
	return board[x - 1][y - 1] + board[x - 1][y] + \
		board[x - 1][y + 1] + board[x][y - 1] + \
		board[x][y + 1] + board[x + 1][y - 1] + \
		board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//('1' + '0' + '0'...'0')- ('0'+'0')
}

判断游戏是否胜利

通过统计游戏棋盘上非字符’*‘的字符个数,若等于棋盘上格子总数减去地雷个数则游戏胜利返回1,反之游戏继续。代码实现:

int WinGame(char board[][COL], int row, int col) {//游戏胜利条件:胜利1 游戏继续0
	int total = (row - 2) * (col - 2) - NUM;
	int flag = 0;
	for (int i = 1; i < row; i++) {
		for (int j = 1; j < col; j++) {
			if ((board[i][j] != '*')) {
				flag++;
			}
		}
	}
	return (flag ==total ) ? 1 : 0;
}

展开安全区域

当我们扫了一个位置后,如果当前位置周围的地雷个数为0,则需要进行向四周展开直至附件有雷的格子,并在当前格子显示附件地雷的个数,增加游戏的可完性,根据需求特点我们可以利用函数递归来实现,那么递归的出口就有两个,一个就是向四周展开都找到了附近有雷的格子(边界除外),另一个就是展开了所有没雷的格子达到了游戏胜利的条件。代码实现:

int SafeBoard(char board[][COL], char mine_board[][COL], int row, int col, int x, int y) {//可展开的安全区域
	int tmp;
	tmp = CountMines(mine_board, x, y);
	if (x >= 1 && x <= row - 2 && y >= 1 && y <= col - 2) {
		if (tmp == 0) {
			board[x][y] = ' ';
			if (mine_board[x - 1][y - 1] != '1' && board[x - 1][y - 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x - 1, y - 1);
			}
			if (mine_board[x - 1][y + 1] != '1' && board[x - 1][y + 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x - 1, y + 1);
			}
			if (mine_board[x - 1][y] != '1' && board[x - 1][y] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x - 1, y);
			}
			if (mine_board[x][y + 1] != '1' && board[x][y + 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x, y + 1);
			}
			if (mine_board[x - 1][y - 1] != '1' && board[x][y - 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x, y - 1);
			}
			if (mine_board[x + 1][y] != '1' && board[x + 1][y] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x + 1, y);
			}
			if (mine_board[x + 1][y + 1] != '1' && board[x + 1][y + 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x + 1, y + 1);
			}
			if (mine_board[x + 1][y - 1] != '1' && board[x + 1][y - 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x + 1, y - 1);
			}
		}
		else {
			board[x][y] = tmp + '0';
			if (WinGame(board,ROW,COL)) {
				return 0;
			}
		}
	}
	return 1;
}

游戏主体

最后就是游戏的整个控制函数了,只需判断玩家输入的坐标是否合法进行对应的操作和提示即可。

代码实现:

int main()
{
	srand((unsigned long)time(NULL));
	int quit = 0;
	while (!quit) {
		Menu();
		int select = 0;
		scanf("%d", &select);
		switch (select) {
		case 1:
			Game();
			break;
		case 2:
			quit = 1;
			break;
		default:
			printf("输入有误, 重新选择!\n");
			break;
		}
	}
	printf("再见!\n");
	return 0;
}

void Game(){
	char show_board[ROW][COL];//游戏棋盘
	char mine_board[ROW][COL];//炸弹棋盘
	memset(show_board, '*', sizeof(show_board));//棋盘全‘*’初始化
	memset(mine_board, '0', sizeof(mine_board));
	SetMines(mine_board, ROW, COL);//随机埋雷
	while (1) {
		int x = 0;
		int y = 0;
		ShowBoard(show_board, ROW, COL);//打印棋盘
		printf("请输入坐标<x, y> ");
		scanf("%d %d", &x, &y);
		system("cls");
		if (!(x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2)) {
			printf("扫雷位置不合法,请重新输入!\n");
			continue;
		}
		if (show_board[x][y] != '*') {
			printf("扫雷的位置已经被排除,请重新输入!\n");
			continue;
		}
		if (mine_board[x][y] == '1') {
			printf("对不起,你被炸死了!\n");
			break;
		}
		int tmp = SafeBoard(show_board, mine_board, ROW, COL, x, y);
		if (tmp == 0) {
			ShowBoard(mine_board, ROW, COL);
			printf("恭喜你,游戏通过!\n");
			return ;
		}
		else {
			continue;
		}
	}
}

完整代码

MineSweeper.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#define ROW 12
#define COL 12

#define NUM 10
#define BOOM '1'

void Game();

game.c

int main()
{
	srand((unsigned long)time(NULL));
	int quit = 0;
	while (!quit) {
		Menu();
		int select = 0;
		scanf("%d", &select);
		switch (select) {
		case 1:
			Game();
			break;
		case 2:
			quit = 1;
			break;
		default:
			printf("输入有误, 重新选择!\n");
			break;
		}
	}
	printf("再见!\n");
	return 0;
}

MineSweeper.c

#include "MineSweeper.h"

void SetMines(char board[][COL], int row, int col){//随机埋雷
	int i = 0;
	while (i < NUM) {
		int _x = rand() % (row-2) + 1;
		int _y = rand() % (col-2) + 1;
		if (board[_x][_y] == BOOM) {
			continue;
		}
		board[_x][_y] = BOOM;
		i++;
	}
}

void ShowBoard(char board[][COL], int row, int col)//打印棋盘
{
	printf("  ");
	for (int i = 1; i <= row - 2; i++){
		printf("%4d", i);
	}
	printf("\n   ");
	for (int i = 1; i <= row - 2; i++){
		printf(" ___");
	}
	for (int j = 1; j <= col - 2; j++){
		printf("\n");
		printf("%2d", j);
		printf(" |");

		for (int i = 1; i <= row - 2; i++){
			printf("_%c_|", board[j][i]);
		}
	}
	printf("\n");
	return;
}

int CountMines(char board[][COL], int x, int y){// 统计附近地雷个数
	return board[x - 1][y - 1] + board[x - 1][y] + \
		board[x - 1][y + 1] + board[x][y - 1] + \
		board[x][y + 1] + board[x + 1][y - 1] + \
		board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';//('1' + '0' + '0'...'0')- ('0'+'0')
}

int WinGame(char board[][COL], int row, int col) {//游戏胜利条件:胜利1 游戏继续0
	int total = (row - 2) * (col - 2) - NUM;
	int flag = 0;
	for (int i = 1; i < row; i++) {
		for (int j = 1; j < col; j++) {
			if ((board[i][j] != '*')) {
				flag++;
			}
		}
	}
	return (flag ==total ) ? 1 : 0;
}

int SafeBoard(char board[][COL], char mine_board[][COL], int row, int col, int x, int y) {//可展开的安全区域
	int tmp;
	tmp = CountMines(mine_board, x, y);
	if (x >= 1 && x <= row - 2 && y >= 1 && y <= col - 2) {
		if (tmp == 0) {
			board[x][y] = ' ';
			if (mine_board[x - 1][y - 1] != '1' && board[x - 1][y - 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x - 1, y - 1);
			}
			if (mine_board[x - 1][y + 1] != '1' && board[x - 1][y + 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x - 1, y + 1);
			}
			if (mine_board[x - 1][y] != '1' && board[x - 1][y] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x - 1, y);
			}
			if (mine_board[x][y + 1] != '1' && board[x][y + 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x, y + 1);
			}
			if (mine_board[x - 1][y - 1] != '1' && board[x][y - 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x, y - 1);
			}
			if (mine_board[x + 1][y] != '1' && board[x + 1][y] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x + 1, y);
			}
			if (mine_board[x + 1][y + 1] != '1' && board[x + 1][y + 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x + 1, y + 1);
			}
			if (mine_board[x + 1][y - 1] != '1' && board[x + 1][y - 1] == '*') {
				SafeBoard(board, mine_board, ROW, COL, x + 1, y - 1);
			}
		}
		else {
			board[x][y] = tmp + '0';
			if (WinGame(board,ROW,COL)) {
				return 0;
			}
		}
	}
	return 1;
}

void Game(){
	char show_board[ROW][COL];//游戏棋盘
	char mine_board[ROW][COL];//炸弹棋盘
	memset(show_board, '*', sizeof(show_board));//棋盘全‘*’初始化
	memset(mine_board, '0', sizeof(mine_board));
	SetMines(mine_board, ROW, COL);//随机埋雷
	while (1) {
		int x = 0;
		int y = 0;
		ShowBoard(show_board, ROW, COL);//打印棋盘
		printf("请输入坐标<x, y> ");
		scanf("%d %d", &x, &y);
		system("cls");
		if (!(x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2)) {
			printf("扫雷位置不合法,请重新输入!\n");
			continue;
		}
		if (show_board[x][y] != '*') {
			printf("扫雷的位置已经被排除,请重新输入!\n");
			continue;
		}
		if (mine_board[x][y] == '1') {
			printf("对不起,你被炸死了!\n");
			break;
		}
		int tmp = SafeBoard(show_board, mine_board, ROW, COL, x, y);
		if (tmp == 0) {
			ShowBoard(mine_board, ROW, COL);
			printf("恭喜你,游戏通过!\n");
			return ;
		}
		else {
			continue;
		}
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hey小孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值