扫雷(C语言)

1.题目描述

问题描述:
扫雷游戏
基本要求:
(1)完成棋盘的初始化并在标准显示器中显示
(2)通过输入行列值确定用户输入
(3)游戏进行给出提示信息
(4)给出游戏的测试程序。

2.设计思路分析

在这里插入图片描述

3.设计程序流程图

用户会首先看到一个菜单,选择1进入游戏同时系统打印出10*10的扫雷界面,选择2退出游戏,如果进入游戏,用户输入所要选择的行列值,如果扫中雷,直接结束游戏,选中非雷区,则显示周围雷的个数,用户重复上述过程接着玩游戏,直至用户输或者赢。

扫雷逻辑比较简单,思路也很清晰,按照思路把各个功能封装到函数里,一个扫雷的程序就完成了。
在这里插入图片描述

4.数据结构定义

1、定义1212的二维数组用来存放雷区
2、定义10
10的二维数组显示用户所见的扫雷

5.源程序

5.1 自定义lei.h头文件

#ifndef _LEI_H_
#define _LEI_H_

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

#pragma warning(disable:4996)

#define ROW 12 //雷区行数
#define COL 12 //雷区列数
#define MINES 20

void Menu(); //用户菜单
void Game(); //游戏显示界面初始化
void SetMine(char mine_board[][COL], int row, int col); //布雷
void PlayGame(char show_board[][COL], char mine_board[][COL], int row, int col); //用户玩游戏
int GetMineNum(char mine_board[][COL], int i, int j); //计算雷的个数
void ShowBoard(char show_board[][COL], int row, int col); //扫雷显示界面

#endif

5.2 lei.c核心代码书写。

对定义的每个函数进行具体功能的编写,写之前明确各个函数实现的功能。

#define _CRT_SECURE_NO_WARNINGS 1
#include "lei.h"

void Menu() //用户菜单
{
	printf("###########################################\n");
	printf("##    1. Play                  2. Exit   ##\n");
	printf("###########################################\n");
	printf("Please Select:> ");
}

static int GetIndex(int start, int end) //得到雷坐标 static修饰 该函数只在该文件内部使用
{
	return rand() % (end - start + 1) + start; //rand()%(m) 随机生成0到m-1
}

void SetMine(char mine_board[][COL], int row, int col) //布雷
{
	srand((unsigned long)time(NULL)); //设置随机种子 time保证每一次都不一样
	int mine_num = MINES; //布雷个数MINES
	while (mine_num)
	{
		int i_index = GetIndex(1, col - 2); //在10*10内布雷
		int j_index = GetIndex(1, col - 2);
		if (mine_board[i_index][j_index] == '0') //判断雷位置合法性 此位置是否布雷
		{
			mine_board[i_index][j_index] = '1';
			mine_num--;
		}
	}
}

int GetMineNum(char mine_board[][COL], int i, int j)//计算周围雷的个数
{
	return mine_board[i - 1][j - 1] + mine_board[i - 1][j] + \
		mine_board[i - 1][j + 1] + mine_board[i][j - 1] + \
		mine_board[i][j + 1] + mine_board[i + 1][j - 1] + \
		mine_board[i + 1][j] + mine_board[i + 1][j + 1] - 8 * '0'; 
	    //-8 * '0'是为了转化成可以直接使用的雷的个数
}
void ShowBoard(char show_board[][COL], int row, int col) //扫雷显示界面 
{
	int i = 1;
	int j = 1;
	printf("    ");
	//10*10 打印 1 2 3 4 5 6 7 8 9 10
	for (; i <= col - 2; i++){
		printf("%d   ", i);
	}
	printf("\n");
	for (i = 1; i <= col - 1; i++){
		printf("----");
	}
	printf("\n");

	for (i = 1; i <= row - 2; i++){
		printf("%2d|", i);
		for (j = 1; j <= col - 2; j++){
			printf(" %c |", show_board[i][j]);
		}
		printf("\n");
		int k = 1;
		for (; k <= col - 1; k++){
			printf("----");
		}
		printf("\n");
	}
}
void PlayGame(char show_board[][COL], char mine_board[][COL], int row, int col) //用户玩游戏
{
	int i = 0;
	int j = 0;
	int total = (ROW - 2)*(COL - 2);
	while (1){
		ShowBoard(show_board, row, col);
		printf("Please Enter Pos<x, y>: ");
		scanf("%d%d", &i, &j);
		if (i >= 1 && i <= row - 2 && j >= 1 && j <= col - 2) //输入是否合法
		{ 
			if (mine_board[i][j] == '0'){  //计算周围雷的个数
				int num = GetMineNum(mine_board, i, j);
				show_board[i][j] = num + '0'; //转化成字符
				total--;
			}
			else{ //扫中雷了
				ShowBoard(mine_board, row, col);
				printf("Game Over! You Lose :(!\n");
				break;
			}
		}
		else{
			printf("Enter Error, Try Again!\n");
			continue;
		}
		if (total == MINES){ //用户均未扫中雷 用户赢了
			printf("You Win:)\n");
			break;
		}
	}
}
void Game()//游戏显示界面初始化
{
	char show_board[ROW][COL]; //显示界面
	char mine_board[ROW][COL]; //雷界面
	memset(show_board, '*', sizeof(show_board));  //memset() 按字节为单位初始化
	memset(mine_board, '0', sizeof(mine_board));

	SetMine(mine_board, ROW, COL);  //布雷
	PlayGame(show_board, mine_board, ROW, COL); //用户玩游戏
}

5.3 main.c主函数的书写

#define _CRT_SECURE_NO_WARNINGS 1
#include "lei.h"

int main()
{
	int select = 0;
	int quit = 0;
	while (!quit){
		Menu(); //用户菜单
		scanf("%d", &select);
		switch (select){
		case 1:
			Game();
			break;
		case 2:
			printf("I Am Quit!\n");
			quit = 1;
			break;
		default:
			printf("Select Error! Try Again!\n");
			break;
		}

	}

	return 0;
}

5.4 注意

1、扫雷主要使用二维数组来定义数据结构,存储数据,增强了数组的应用熟练度。static int GetIndex(int start, int end) 函数被static修饰,表示该函数只在该文件内部使用。
2、

 srand((unsigned long)time(NULL));
   	    return rand() % (end - start + 1) + start;

程序中使用了srand和rand,srand 初始化随机种子,rand 生成随机数。

6.运行结果

1、运行程序的初始界面
在这里插入图片描述
2、选择1开始游戏
在这里插入图片描述
3、开始游戏,输入您想要点击的位置(行和列),知道赢得游戏,或者输掉游戏,然后选择1继续游戏,或者选择2退出游戏
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.存在问题分析

1.一旦游戏开始必须等用户输掉才可以结束游戏,要想退出必须关掉程序,用户体验不佳。

改进:程序中加入break,在每一次用户输入后加入if判断,提示用户是否要继续玩游戏,方便随时退出游戏。

2.游戏耗时太长,如果在用户运气特别好的情况下,要选择80次才可以胜利。

改进:布雷布多一点或者用户选择10次直接胜利。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
扫雷是一种经典的游戏,可以使用C语言来实现。以下是一个简单的扫雷程序的实现示例: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #define ROWS 10 // 行数 #define COLS 10 // 列数 #define MINES 10 // 雷的数量 void init_game(char board[][COLS], int rows, int cols); // 初始化游戏面板 void print_board(char board[][COLS], int rows, int cols); // 打印游戏面板 void set_mines(char board[][COLS], int rows, int cols, int mines); // 布雷 void get_neighbors(int row, int col, int rows, int cols, int neighbors[]); // 获取邻居格子 int count_mines(char board[][COLS], int row, int col, int rows, int cols); // 计算某个格子周围的雷数 void reveal(char board[][COLS], int row, int col, int rows, int cols); // 翻开某个格子 int game_over(char board[][COLS], int rows, int cols); // 判断游戏是否结束 int main() { char board[ROWS][COLS]; int row, col; srand((unsigned)time(NULL)); // 设置随机数种子 init_game(board, ROWS, COLS); // 初始化游戏面板 set_mines(board, ROWS, COLS, MINES); // 布雷 print_board(board, ROWS, COLS); // 打印游戏面板 while (1) { printf("请输入坐标(行 列): "); scanf("%d %d", &row, &col); if (row < 0 || row >= ROWS || col < 0 || col >= COLS) { printf("输入错误,请重新输入\n"); continue; } if (board[row][col] == '*') { // 点到雷了 printf("游戏结束!你输了\n"); board[row][col] = 'X'; // 标记点到的雷 print_board(board, ROWS, COLS); break; } else { reveal(board, row, col, ROWS, COLS); // 翻开格子 print_board(board, ROWS, COLS); } if (game_over(board, ROWS, COLS)) { // 判断游戏是否结束 printf("游戏结束!你赢了\n"); break; } } return 0; } // 初始化游戏面板 void init_game(char board[][COLS], int rows, int cols) { int i, j; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { board[i][j] = '-'; } } } // 打印游戏面板 void print_board(char board[][COLS], int rows, int cols) { int i, j; printf(" "); for (j = 0; j < cols; j++) { printf(" %d", j); } printf("\n"); for (i = 0; i < rows; i++) { printf("%d ", i); for (j = 0; j < cols; j++) { printf("%c ", board[i][j]); } printf("\n"); } printf("\n"); } // 布雷 void set_mines(char board[][COLS], int rows, int cols, int mines) { int count = 0, i, j; while (count < mines) { i = rand() % rows; j = rand() % cols; if (board[i][j] == '-') { board[i][j] = '*'; count++; } } } // 获取邻居格子 void get_neighbors(int row, int col, int rows, int cols, int neighbors[]) { int i, j, k = 0; for (i = row - 1; i <= row + 1; i++) { for (j = col - 1; j <= col + 1; j++) { if (i >= 0 && i < rows && j >= 0 && j < cols && (i != row || j != col)) { neighbors[k++] = i * cols + j; } } } } // 计算某个格子周围的雷数 int count_mines(char board[][COLS], int row, int col, int rows, int cols) { int neighbors[8], count = 0, i; get_neighbors(row, col, rows, cols, neighbors); for (i = 0; i < 8; i++) { if (board[neighbors[i] / cols][neighbors[i] % cols] == '*') { count++; } } return count; } // 翻开某个格子 void reveal(char board[][COLS], int row, int col, int rows, int cols) { int count; if (board[row][col] != '-') { // 已经翻开了 return; } count = count_mines(board, row, col, rows, cols); if (count > 0) { // 周围有雷 board[row][col] = '0' + count; } else { // 周围没有雷 board[row][col] = ' '; reveal(board, row - 1, col, rows, cols); reveal(board, row + 1, col, rows, cols); reveal(board, row, col - 1, rows, cols); reveal(board, row, col + 1, rows, cols); } } // 判断游戏是否结束 int game_over(char board[][COLS], int rows, int cols) { int i, j, count = 0; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { if (board[i][j] == '-' || board[i][j] == '*') { count++; } } } return count == MINES; } ``` 在这个示例程序中,我们使用二维字符数组来表示游戏面板。其中,'-'表示未翻开的格子,'*'表示雷,' '表示周围没有雷的格子,'1'-'8'表示周围有1-8个雷的格子,'X'表示点到的雷。我们使用了递归算法来实现翻开格子的功能,当某个格子周围没有雷时,我们会递归地翻开它周围的格子,直到所有周围有雷的格子都被标记为'1'-'8'。 这只是一个简单的示例程序,还有很多可以改进的地方。比如,可以添加计时器和计分系统,以及更加友好的界面等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值