IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言|扫雷游戏 -> 正文阅读

[C++知识库]C语言|扫雷游戏

#1 文件目录

---game.c? 游戏相关的函数定义

---game.h? 游戏相关的函数声明

---test.c? ? ?游戏运行相关的代码

#2 欢迎界面

游戏开始时,出现欢迎界面,可以在欢迎界面选择开始游戏或退出游戏。

相关代码实现:

void menu()
{
    printf("*******************************\n");
    printf("*********  1.play   ***********\n");
    printf("*********  0.exit   ***********\n");
    printf("*******************************\n");
}
scanf("%d", &input); // input为已声明的int类型变量
switch (input)
{
case 1:
    game();
    break;
case 0:
    break;
default:
    // 
    break;
}

以上代码可以实现简易的欢迎页面以及游戏初期的一些选择(虽然功能还没实现),但缺少重复游玩的功能,因此需要使用循环语句实现之,代码如下:

do
{
    //游戏相关代码
}while(input);

完成上面的工作后,应该先测试一下已写的代码,确定没问题后再继续。

#3 棋盘布局及雷的相关设定

在排查棋盘上一个点周围八个点有几个雷时,会遇到一个问题,当遇到棋盘四周或四个顶点时只能排查5个点或3个点的雷,这样排查函数较为难写,因此,在创建棋盘时,在9*9的棋盘外围再多加一圈全为‘0’的虚拟棋盘,也即棋盘数组大小为11*11.相关代码如下:

char mine[11][11] = {'0'}; // 存储雷的位置
char show[11][11] = {'0'}; // 存储周围雷个数

为了便于后续遍历,相关宏定义:

#define ROW 9 // 棋盘的真实大小
#define COL 9

#define ROWS ROW+9  // 数组的大小
#define COLS COL+9

#define MINE_NUM 10 // 雷的个数

所有操作可以分解为两步:第一布置雷,第二排查雷。

将雷所在位置设置为'1',不是雷的位置设置为'0'

一个数组存放布置雷的位置的信息;另一个数组存放排查雷的信息;

#3.1 初始化棋盘

mine数组初始化为‘0’,show数组初始化为‘*’(这里可能有人不懂,我解释一下,因为show数组是存放对应坐标周围一圈埋雷个数的,在游戏一开始,还没点任何一个点时,默认是不会显示出来的,因此初始化为‘*’,在我们点了任何一个点后,该点才显示数字。)

初始化函数为:

void 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 row, int col)
{
    int i = 0;
    int j = 0;
    for(j = 0; j <= col; j++)  // 打印列号
    {
        printf("%d ", j);
    }
    printf("\n");
    for(i = 1; i<=row; i++)
    {
        printf("%d ", i); // 打印行号
        for(j = 1; j <= col; j++)
        {
            printf("%c ", board[i][j]);
        }
        printf("\n");
    }
}

#4 布雷函数

void SetMind(char mine[ROWS][COLS], int row, int col)
{
    int count = 10;  // 设置十个雷
    while(count)
    {
        // 生成随机下标
        int x = rand() % (row + 1);
        int y = rand() % (col + 1);
        if(mine[x][y] == '0') // 判断该位置是否被设置过
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

为了产生0-100的随机值,需要在main()函数中加入下面代码:

srand((unsigned int)time(NULL));  // 需要stdlib.h 和 time.h 头文件

#5 排雷函数

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    int win = 0;
    
    while(win < row*col-MINE_NUM)
    {
        // 输入排查的坐标
        printf("请输入要排查的坐标:>");
        scanf("%d %d", &x, &y);
    
        // 判断坐标合法性
        if((x >= 1) && (x <= row) && (y >= 1) && (y <= col))
        {
            if(mine[x][y] == '1') // 检查坐标是否为雷
            {
                // 是雷 死了
                printf("很遗憾,你被炸死了\n");
                DisplayBoard(mine, row, col);
                break;
            }
            else
            {
                // 不是雷 返回坐标周围有几个雷
                int count = get_mine_count(mine, x, y);
                show[x][y] = count + '0'; // count+'0',将其转化为对应的数字字符
                DisplayBoard(show, row, col);
            }
        }
        else
        {
            printf("坐标不合法,请重新输入\n");
            continue;
        }
    }
    if(win == row*col-MINE_NUM)
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, row, col);
    }

}

get_mine_count()函数定义为:

int get_mine_count(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';
}

#6 优化

  1. 标记功能
  2. 如果不是雷,周围没有雷,展开一片 -- 递归实现
  3. 适时清屏

#6.1 标记功能

只需在输入是,多输入一个l(label标记)或者p(point指出)即可实现,相关代码如下:

printf("如果要标记,则输入格式为:l x坐标 y坐标(例如:l 1 2)\n");
printf("如果不是标记,则输入格式为:p x坐标 y坐标(例如:p 1 2)\n");
printf("请输入要排查的坐标:>");
scanf("%c %d %d", &function, &x, &y);
system("cls"); //清屏

// 判断坐标合法性
if ((x >= 1) && (x <= row) && (y >= 1) && (y <= col))
{
    if (function == 'p')
    {
        if (mine[x][y] == '1') // 检查坐标是否为雷
        {
            // 是雷 死了
            printf("很遗憾,你被炸死了\n");
            DisplayBoard(mine, row, col);
            break;
        }
        else
        {
            // 不是雷 返回坐标周围有几个雷
            int count = get_mine_count(mine, x, y);
            show[x][y] = count + '0'; // count+'0',将其转化为对应的数字字符
            DisplayBoard(show, row, col);
            win++;
        }
    }
    if (function == 'l')
    {
        show[x][y] = '?';
        DisplayBoard(show, row, col);
    }
}

#6.2?如果不是雷,周围没有雷,展开一片功能实现

首先判断输入坐标周围是否有雷,有雷则输出雷的个数;没雷,则输出0并一次判断周围八个坐标周围是否有雷,依次循环下去。相关代码:

void QuickClear(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col, int x, int y)
{
    int count = get_mine_count(mine, x, y);
    if (count == 0)
    {
        show[x][y] = '0';
        if (x - 1 > 0 && y - 1 > 0 && show[x - 1][y - 1] == '*') QuickClear(mine, show, row, col, x - 1, y - 1);
        if (x - 1 > 0 && show[x - 1][y] == '*') QuickClear(mine, show, row, col, x - 1, y);
        if (x - 1 > 0 && y + 1 <= col && show[x - 1][y + 1] == '*') QuickClear(mine, show, row, col, x - 1, y + 1);
        if (y - 1 > 0 && show[x][y - 1] == '*') QuickClear(mine, show, row, col, x, y - 1);
        if (y + 1 <= col && show[x][y + 1] == '*') QuickClear(mine, show, row, col, x, y + 1);
        if (x + 1 <= row && y - 1 > 0 && show[x + 1][y - 1] == '*') QuickClear(mine, show, row, col, x + 1, y - 1);
        if (x + 1 <= row && show[x + 1][y] == '*') QuickClear(mine, show, row, col, x + 1, y);
        if (x + 1 <= row && y + 1 <= col && show[x + 1][y + 1] == '*') QuickClear(mine, show, row, col, x + 1, y + 1);
    }
    else
    {
        show[x][y] = count + '0';
    }
}

#6.3 清屏功能实现

每输入一个坐标时,就清屏一次。

所需头文件stdlib.h

system("cls");

#7 完整代码附录

// game.h 文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2
#define MINE_NUM 10 // 设置雷的个数

// 游戏界面相关函数声明
void menu();

// 棋盘相关函数
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);

// 雷相关函数
void SetMind(char mine[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int get_mine_count(char mine[ROWS][COLS], int x, int y);
void QuickClear(char mine[ROWS][COLS], char show[ROWS][COLS], char state[ROWS][COLS], int x, int y);
int IsWin(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
// game.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void menu()
{
    printf("*******************************\n");
    printf("*********  1.play   ***********\n");
    printf("*********  0.exit   ***********\n");
    printf("*******************************\n");
}
void 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 row, int col)
{
    int i = 0;
    int j = 0;
    for (j = 0; j <= col; j++)  // 打印列号
    {
        printf("%d ", j);
    }
    printf("\n");
    for (i = 1; i <= row; i++)
    {
        printf("%d ", i); // 打印行号
        for (j = 1; j <= col; j++)
        {
            printf("%c ", board[i][j]);
        }
        printf("\n");
    }
}
void SetMind(char mine[ROWS][COLS], int row, int col)
{
    int count = MINE_NUM;  // 设置十个雷
    while (count)
    {
        // 生成随机下标
        int x = (rand() % row) + 1;
        int y = (rand() % col) + 1;
        if (mine[x][y] == '0') // 判断该位置是否被设置过
        {
            mine[x][y] = '1';
            count--;
        }
    }
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    char function = 0;
    char state[ROWS][COLS] = { '0' };

    while (IsWin(mine,show,ROW,COL)== 0)
    {
        getchar(); // 去掉一开始输入1后的换行符
        // 输入排查的坐标
        printf("如果要标记,则输入格式为:l x坐标 y坐标(例如:l 1 2)\n");
        printf("如果不是标记,则输入格式为:p x坐标 y坐标(例如:p 1 2)\n");
        printf("请输入要排查的坐标:>");
        scanf("%c %d %d", &function, &x, &y);
        system("cls"); //清屏

        // 判断坐标合法性
        if ((x >= 1) && (x <= row) && (y >= 1) && (y <= col))
        {
            if (function == 'p')
            {
                if (mine[x][y] == '1') // 检查坐标是否为雷
                {
                    // 是雷 死了
                    printf("很遗憾,你被炸死了\n");
                    DisplayBoard(mine, row, col);
                    break;
                }
                else
                {
                    // 不是雷 返回坐标周围有几个雷
                    int count = get_mine_count(mine, x, y);
                    show[x][y] = count + '0'; // count+'0',将其转化为对应的数字字符
                    QuickClear(mine, show, ROW, COL, x, y);
                    DisplayBoard(show, row, col);
                    //DisplayBoard(mine, ROW, COL);//
                }
            }
            if (function == 'l')
            {
                show[x][y] = '!';
                DisplayBoard(show, row, col);
            }
        }
        else
        {
            printf("坐标不合法,请重新输入\n");
            continue;
        }
    }
    if (IsWin(mine, show, ROW, COL)) // 排成功后执行
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, row, col);
    }

}
int get_mine_count(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';
}
void QuickClear(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col, int x, int y)
{
    int count = get_mine_count(mine, x, y);
    if (count == 0)
    {
        show[x][y] = '0';
        if (x - 1 > 0 && y - 1 > 0 && show[x - 1][y - 1] == '*') QuickClear(mine, show, row, col, x - 1, y - 1);
        if (x - 1 > 0 && show[x - 1][y] == '*') QuickClear(mine, show, row, col, x - 1, y);
        if (x - 1 > 0 && y + 1 <= col && show[x - 1][y + 1] == '*') QuickClear(mine, show, row, col, x - 1, y + 1);
        if (y - 1 > 0 && show[x][y - 1] == '*') QuickClear(mine, show, row, col, x, y - 1);
        if (y + 1 <= col && show[x][y + 1] == '*') QuickClear(mine, show, row, col, x, y + 1);
        if (x + 1 <= row && y - 1 > 0 && show[x + 1][y - 1] == '*') QuickClear(mine, show, row, col, x + 1, y - 1);
        if (x + 1 <= row && show[x + 1][y] == '*') QuickClear(mine, show, row, col, x + 1, y);
        if (x + 1 <= row && y + 1 <= col && show[x + 1][y + 1] == '*') QuickClear(mine, show, row, col, x + 1, y + 1);
    }
    else
    {
        show[x][y] = count + '0';
    }

}
int IsWin(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int i = 1;
    int j = 1;
    int count = 0;
    for (i = 1; i <= row; i++)
    {
        for (j = 1; j <= col; j++)
        {
            if (mine[i][j] == '1' || show[i][j] != '*')
                count++;
        }
    }
    if (count == ROW * COL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
// test.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

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

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


	do
	{
		// 进入欢迎界面
		menu();
		scanf("%d", &input); // 选择开始或结束
		
		switch (input)
		{
		case 1:
			InitBoard(mine, ROWS, COLS, '0');
			InitBoard(show, ROWS, COLS, '*');
			SetMind(mine, ROW, COL);
			DisplayBoard(show, ROW, COL);
			//DisplayBoard(mine, ROW, COL);
			FindMine(mine, show, ROW, COL);
			break;
		case 0:
			printf("退出\n");
			break;
		default:
			printf("输入有误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:02:42  更:2022-07-17 16:03:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:22:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码