赞
踩
效果如上,本文用C/C++ 实现一下扫雷游戏,C++主要用cout 函数用来代替printf, 其他用C的结构体及函数等实现。
进入游戏,获取玩家输入
Board 结构体 :用来存每一格的属性(包含是否点击,周围雷数)
Bombs : -1表示当前格子为炸弹,>=0即为周围8格炸弹数。
isClick: 1表示点开,0表示未点开。
- typedef struct Board
- {
- int Bombs;
- bool isClick;
- }Board;
- #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);
- 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);
- }
局部变量 用了 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;
- }
-
- }
初始化布局 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);
- }
-
-
- }
- //打印(渲染)
- 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;
- }
- }
玩家输入一个坐标,直到合法,非炸弹就展开,雷设置点开。
- // 玩家点击
- 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;
-
- }
- //判定
- 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;
- }
递归打开非雷区,参考了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);
- }
- }
- }
-
- }
- //全部情况都跳过,说明是雷,啥也不用做
- }
让雷的周围格子增加雷计数,遍历周围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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。