C/C++​​ - 扫雷​​

​​​​扫雷

        效果如上,本文用C/C++ 实现一下扫雷游戏,C++主要用cout 函数用来代替printf, 其他用C的结构体及函数等实现。

一、实现思路:

进入游戏,获取玩家输入

  1. 判断是否为雷:是,设置雷为点开;否,自动点开每个非雷区;
  2. 判断是否满足条件:1.雷被点开,输;2.当前剩下格子数等于总雷数 N,赢;3.都不满足继续游戏

函数

  • menu函数:获取玩家输入。
  • game函数操控进行一把游戏的整体逻辑。
  • ini 函数:函数初始化并设置随机雷。
  • show 函数:打印雷盘。
  • Click :函数玩家点击。(实际是获取输入坐标XD)
  • isOver 函数:判断游戏是否结束。
  • extend 函数:递归扩展非雷区。
  • cntBombs 函数 :雷区给周围区计数。

变量

Board  结构体 :用来存每一格的属性(包含是否点击,周围雷数)

        Bombs : -1表示当前格子炸弹>=0即为周围8格炸弹数

        isClick: 1表示点开,0表示未点开

typedef struct Board
{
	int Bombs;
	bool isClick;
}Board;

二、代码

        1.头文件game.h 用来定义一些宏变量,声明函数,结构体。

#pragma once
#define ROW 10
#define COL 10
#define n 10  //炸弹数
#define WIN 1
#define LOSE -1
#define SPACE_Y " | "
#define SPACE_X "——-"
#define SPACE   "   "

#include <iostream>
using namespace std;
typedef struct Board
{
	int Bombs;
	bool isClick;
}Board;


void ini(Board board[ROW][COL], int N, int row, int col);//N为炸弹数

void show(Board board[ROW][COL], int row, int col);

void Click(Board board[ROW][COL], int& x, int& y, int row, int col,int& left_cell);//输入的坐标,剩余格子都需要外部保存

int isOver(Board board[ROW][COL], int x, int y, int left_cell, int N);

void extend(Board board[ROW][COL], int x, int y, int row, int col, int& left_cell);

void cntBombs(Board board[ROW][COL], int x, int y, int row, int col);

        2.menu函数

void menu() {
	int input;
	do {
		cout << "1.Start game"<<endl<<"0.Quit game"<<endl;
		
		cin >> input;
		switch (input)
		{
		case 1:
			game();
			break;
		default:
			break;
		}
	} while (input);
}

        3.game函数

                局部变量 用了 left_cells 表示剩余格子数,x ,y 表示输入坐标,res为游戏结果用来判定

                用{0}给结构体数组board [ROW][COL] 初始化。

void game() {
	//每把设置一个新时间种子
	srand(time(NULL));

	//局部变量
	int x, y;//输入坐标
	int res;//结果
	int left_cells = ROW * COL;//记录当前剩余格子

	//初始化
	Board board[ROW][COL] = { 0 };
	ini(board, n, ROW, COL);
	show(board,ROW,COL);

	//玩家回合
	do {
		Click(board,x,y, ROW, COL,left_cells);
		show(board, ROW, COL);
		res = isOver(board, x, y, left_cells, n);
	} while (!res);//游戏未结束时循环
	switch (res)
	{
	case 1:
		cout << " WIN ! !" << endl;
		break;
	case -1:
		cout<<" BOOM ! ! !"<<endl;
		break;
		//如果想在结束后展现所有炸弹,需要一开始记录一串炸弹坐标的数组
	default:
		break;
	}

}

        4.ini函数 

                初始化布局 X * Y,并随机生成 N 个炸弹,以当前雷为参数,cntBombs累加周围炸弹

//随机生成{ N }个炸弹布局{ X * Y}根据炸弹计算每格数字 (初始化)  
void ini(Board board[ROW][COL], int N,int row,int col) {
	//全部 memset为0,直接在外初始化了,貌似不太好

	while (N--) {
		//随机生成N个 x,y 雷 循环判断是否重复雷
		int x, y;
		do {
			x = rand() % ROW;
			y = rand() % COL;
		} while (board[x][y].Bombs == -1);//如果随机到的xy是有炸弹的,重新生成
		//没有重复 设置为雷
		board[x][y].Bombs = -1;
		//炸弹周围格子++
		cntBombs(board, x, y, row, col);
	}
	
	
}

        5.show函数

//打印(渲染) 
void show(Board board[ROW][COL],int row, int col) {
	system("cls");
	for (int i = -1; i < row; i++) {//-1是为了打印轴
		for (int j = -1; j < col; j++) {
            //打印横纵轴
			//打印首行横坐标 第j列
			if (i==-1) {
				if (j==-1)	{
					cout << "  ";//开头空两格
					continue;
				}
				cout << SPACE << j << SPACE;//输出横坐标
				continue;
			}
			//当j已经不是第一行,每行首打印纵坐标  第i行
			if (j == -1) { 
				cout << i << " "; 
				continue;
			}
			//打印横纵轴↑

			//判断是否点击 打印
			Board tmpBoard= board[i][j];
			if (tmpBoard.isClick) {	
				switch (tmpBoard.Bombs)
				{
				case -1://炸弹
					cout <<SPACE_Y<< "X" << SPACE_Y;
					break;
				case 0://周围没有炸弹
					cout << SPACE_Y << " " << SPACE_Y;
					break;
				default:
					cout << SPACE_Y << tmpBoard.Bombs <<SPACE_Y;
					break;
				}
			}
			else
				cout << SPACE_Y << "*"<<SPACE_Y;
		}
		cout << endl<< endl;
	}
}

        click函数

                玩家输入一个坐标,直到合法,非炸弹就展开,雷设置点开。

// 玩家点击
void Click(Board board[ROW][COL],int &x,int &y, int row, int col,int& left_cell) {
    //是否合法循环判断
	while (1) {
		cin >> y>> x;//实际游戏x,y是反的,所以调了顺序
		//判断y,x是否合法
		if (x <0 || x >= row || y < 0 || y >= col) {
			cout << "非法坐标"<<endl;
			continue;
		}
		if (board[x][y].isClick) {
			cout << "重复了" << endl;
			continue;
		}
		break;
	}
	//合法,当点击的坐标不是雷时,扩展
	if (board[x][y].Bombs != -1)
		extend(board, x, y, row, col,left_cell);
    //是雷,把雷点开
	else
		board[x][y].isClick = 1;

}

        isOver函数

//判定
int isOver(Board board[ROW][COL], int x, int y,int left_cell,int N) {
	//输了,雷格子的状态为点击
	if (board[x][y].isClick == 1 && board[x][y].Bombs == -1)
		return LOSE;

	//赢了,剩余格子数等于雷的数量——需要提前统计剩余格子数
	if (left_cell == N)
		return WIN;

	//没输没赢,继续进行
	return 0;
}

        extend函数

                递归打开非雷区,参考了c语言扫雷递归展开非雷位置tangke121的博客-CSDN博客

//扩展非雷
void extend(Board board[ROW][COL],int x,int y, int row, int col,int &left_cell) {
    //因为是向外扩展的,有可能是炸弹,只有不是雷才能设置Click为1
	//每次迭代,看有无点击过;
	//不是炸弹就点开自己:有若干雷不扩展;周围0个雷时拓展;当自己是炸弹时 啥也不做

	//是否点击过,点过跳过
	if (board[x][y].isClick)	return;
	//没点过,且不是雷
	if (board[x][y].Bombs >= 0)	{
		board[x][y].isClick = 1;
		left_cell--;//剩余减少一格
	//非雷向各方位拓开
		if (!board[x][y].Bombs) {
			for (int i = x - 1; i <= x + 1; i++) {
				for (int j = y - 1; j <= y + 1; j++) {
					if (i >= 0 && i < row && j>=0 && j < col)
						extend(board, i, j, row, col,left_cell);
				}
			}
		}

	}
	//全部情况都跳过,说明是雷,啥也不用做
}

        cntBombs函数

                让雷的周围格子增加雷计数,遍历周围8个让其属性Bombs自增1。

//计数周围的雷
void cntBombs(Board board[ROW][COL], int x, int y, int row, int col) {
	for(int i = x - 1; i <= x + 1; i++) {
		for (int j = y - 1; j <= y + 1; j++) {
			if (i == x && j == y)	continue;//跳过自己 不能让自己++
			if (i >= 0 && i < row && j >= 0 && j < col)//界限判断
				board[i][j].Bombs++;//每个周围++
		}
	}
}

三、结果

         这里我把雷数设置成了2,方便演示。

WIN

LOSE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值