C++实现扫雷游戏(控制台不闪屏版)

之前写了一个C++ 的控制台扫雷小游戏,但由于过度使用system("cls")刷屏,导致闪屏,因此重写了一个改善的不闪屏版本,并把逻辑重新捋了一遍。

map.h

#ifndef MAP_H_
#define MAP_H_

#define MAX_WID 18
#define MAX_LEN 32
#define UP_EDGE 1    //上边界
#define LEFT_EDGE 1   //左边界
#define RIGHT_EDGE _len //右边界
#define DOWN_EDGE _wid  //下边界

struct Position {    //用于表示位置
  short x;
  short y;
};

struct MapInfo {    //表示扫雷图的信息
  int n;       //-1表示地雷,0表示空格,1~8表示雷数
  bool flag;     //是否已经被打开
};

void gotoxy(short, short);    //光标移动函数

class Map {
private:
  int _len, _wid;        //图的长宽
  int _mines, _blanks;      //雷数和空格数
  Position pos;          //光标位置
  MapInfo data[MAX_WID][MAX_LEN]; //地图

public:
  void ChooseMode();       //选择游戏模式,初级,中级,高级
  void Draw();          //画出地图
  void InitMap();         //初始化地图信息
  void SetMine();         //设置地雷
  void SetNum();         //根据周围地雷数计算数字
  void Move();          //负责移动
  void OpenBlock();        //打开方块
  void OpenAll();         //如果触雷则全部打开
  void Play();          //提供游戏操作接口
  bool IfWin();          //判断输赢
  bool IfLose();
  // void show();
};

#endif

map类的实现

map.cpp

#include "map.h"
#include <iostream>
#include <cstdio>
#include <cstdlib>   //提供随机函数,rand(), srand()
#include <ctime>    //提供time()函数
#include <conio.h>   //提供不回显的输入函数getch()
#include <windows.h>  //提供system()内命令

#define GOTOXY( pos ) gotoxy( 2 * (pos).x - 1, (pos).y - 1 )
#define POSITION_POS _wid+1   //游戏信息的位置,这里是位置信息
#define POSITION_BLANKS _wid+2 //空格数位置
#define POSITION_TIMES _wid+3  //时间显示位置
#define POSITION_SITUATION _wid+4 //输赢状态位置

using std::cin;
using std::cout;

void gotoxy(short x, short y) {  //自行百度
  COORD pos = { x, y };
  HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleCursorPosition(hOut, pos);
}

void Map::ChooseMode() {
  system("cls");      //清屏
  cout << "Please choose the mode\n";
  cout << "1 : Beginner\n";
  cout << "2 : Intermediate\n";
  cout << "3 : Expert\nYour mode: ";

  char mode;
  cin >> mode;
  while (mode != '1' && mode != '2' && mode != '3') {  //只接受 1, 2, 3
    cout <<"\nWrong mode, please input 1, 2, or 3\nYour mode: ";
    cin >> mode;
  }
  switch (mode) {  //根据模式改变地图信息
    case '1': _len = _wid = 8; _mines = 10; break;
    case '2': _len = _wid = 16; _mines = 40; break;
    default : _len = 30; _wid = 16; _mines = 99;
  }
  _blanks = _len * _wid - _mines;  //更新空格数
}

void Map::Draw() {  //画出地图
  system("cls");
  SetConsoleOutputCP(437);   //自行百度,否则无法显现方块而是显现?
  for (int i = 1; i <= _wid; i++) {
    printf("|");
    for (int j = 1; j <= _len; j++)
      printf("%c", 219);    //219是方块
    printf("|\n");
  }
  gotoxy(0, POSITION_POS);
  printf("Position: ( %2d, %2d )\n", pos.x, pos.y);
  printf("Blanks: %2d", _blanks);
  GOTOXY(pos);   //查看 map.h 内说明
}

void Map::InitMap() {
  for (int i = 0; i <= _wid+1; i++)   //从0 ~ _wid+1 是因为可以假设地图边界的空格存在,且为0,后面计算
    for (int j = 0; j <= _len+1; j++) {
      data[i][j].flag = false;   //设置为没有被打开
      data[i][j].n = 0;      //全部设为空格
    }
  pos.x = pos.y = 1;
}

void Map::SetMine() {
  int mines = _mines;
  int x, y;
  Move();    //先执行move(),避免第一个空就触雷
  srand(time(NULL));
  while (mines) {
    x = rand() % _wid + 1;
    y = rand() % _len + 1;
    if (0 == data[x][y].n && (x != pos.x && y != pos.y)) {  //后面的条件可以避免第一个打开的空被设置为地雷,避免第一步就触雷
      data[x][y].n = -1;    //雷 设为 -1
      data[x][y].flag = true;  //设为雷的格子 flag 置为 true
      mines--;
    }
  }
}

void Map::SetNum() {
  for (int i = 1; i <= _wid; i++) {
    for (int j = 1; j <= _len; j++) {  //逐个计算格子周围的 8 个格子的雷数
      if (-1 == data[ i ][ j ].n) continue;
      if (-1 == data[i-1][j-1].n) data[i][j].n++;
      if (-1 == data[i-1][ j ].n) data[i][j].n++;
      if (-1 == data[i-1][j+1].n) data[i][j].n++;
      if (-1 == data[ i ][j-1].n) data[i][j].n++;
      if (-1 == data[ i ][j+1].n) data[i][j].n++;
      if (-1 == data[i+1][j-1].n) data[i][j].n++;
      if (-1 == data[i+1][ j ].n) data[i][j].n++;
      if (-1 == data[i+1][j+1].n) data[i][j].n++;
    }
  }
  OpenBlock();  //与SetMine()配套,这时才正好打开用户要打开的第一个空,避免第一步就触雷
}

void Map::Move() {
  char mv;
  while (1) {
    mv = getch();
    if (mv == ' ') break;  //如果是 ‘ '(空格),那么就结束移动,打开方块
    if (mv != 'w' && mv != 'a' && mv != 's' && mv != 'd') continue; //移动只接受 w a s d 四个键
    switch (mv) {
      case 'w':
        if (pos.y != UP_EDGE)  pos.y--;
        break;
      case 's':
        if (pos.y != DOWN_EDGE) pos.y++;
        break;
      case 'a':
        if (pos.x != LEFT_EDGE) pos.x--;
        break;
      default :
        if (pos.x != RIGHT_EDGE) pos.x++;
    }
    gotoxy(12, POSITION_POS); //12,可以不用重新输入覆盖已经存在的 "Position: ",而是接着输入
    printf("%2d, %2d", pos.x, pos.y);
    GOTOXY(pos); //回到用户所指的位置
  }
}

#define IF_IN_EDGE(p) ((p.x >= LEFT_EDGE && p.x <= RIGHT_EDGE) && (p.y >= UP_EDGE && p.y <= DOWN_EDGE)) //判断是否越界
#define IF_PUSHIN_STACK(p) (data[p.y][p.x].flag == false && IF_IN_EDGE(p))     //判断是否入栈,条件是:不越界且格子未被打开
#define PUSHIN_STACK(p) { stack[++top] = p; data[p.y][p.x].flag = true; _blanks--; } //入栈,并设置为已打开,并减少空格数,用于定是否获胜

void Map::OpenBlock() {
  if (data[pos.y][pos.x].flag == true) return ;  //如果格子打开过,就跳出函数
  int num, top = 0;
  Position stack[_len * _wid << 1];  //栈,用于存位置
  Position temp;
  stack[top] = pos;
  data[pos.y][pos.x].flag = true;  //要打开的第一个格子设置为打开
  _blanks--;
  while (top != -1) {
    temp = stack[top--];
    GOTOXY(temp);
    num = data[temp.y][temp.x].n;
    if (0 == num) {
      printf(" ");      //如果是0,那么输出空格,并且判断一下周围8个是否要打开,如果不是地雷就打开
      temp.y--; temp.x--;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)  //格子周围8个都要判断
      temp.x++;                    //因为空格周围的都要打开
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
      temp.x++;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
      temp.y++;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
      temp.y++;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
      temp.x--;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
      temp.x--;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
      temp.y--;
      if (IF_PUSHIN_STACK(temp)) PUSHIN_STACK(temp)
    }
    else {
      printf("%d ", num);  //不是0,也即不是空格,不存在连开
    }
  }
  gotoxy(8, POSITION_BLANKS);  //更新空格数
  printf("%2d", _blanks);
  GOTOXY(pos);  //回到用户所指的位置
}

void Map::OpenAll() {
  SetConsoleOutputCP(437);
  gotoxy(0,0);
  for (int i = 1; i <= _wid; i++) {
    printf("|");
    for (int j = 1; j <= _len; j++) {
      switch (data[i][j].n) {
        case 0 : printf("%c", 219); break;
        case -1: printf("* "); break;
        default: printf("%d ", data[i][j].n);
      }
    } printf("|\n");
  }
  GOTOXY(pos);
  printf("X");
}

void Map::Play() {
  char op;
  float end, start;

  start = clock();  //计时用
  while (!IfWin()) {  //如果还未获胜
    Move();  //当 Move() 跳出即代表用户输入空格
    if (IfLose()) { OpenAll(); break; } //如果触雷则跳出,并打开全图
    OpenBlock();  //开格子
  }
  end = clock();  //计时用
  gotoxy(0, POSITION_TIMES);
  printf("Times: %.2f s\n\n", (end-start)/CLK_TCK);
}

bool Map::IfWin() {
  return _blanks == 0;
}

bool Map::IfLose() {
  return -1 == data[pos.y][pos.x].n;
}

主函数

mineweeper.cpp

#include <cstdio>
#include <cstdlib>
#include <conio.h>
#include "map.h"

int main() {
  Map game;
  char ch;
  while (1) {
    game.ChooseMode();  //模式选择
    game.InitMap();   //初始化
    game.Draw();     //画出地图
    game.SetMine();   //布置地雷
    game.SetNum();    //计算数字
    game.Play();     //扫雷
    if (game.IfWin())   //判定输赢
      printf("You Win\n");
    else
      printf("You Lose\n");
    printf("\nInput q to quit or c to continue : ");  //是否继续
    ch = getch();
    while (ch != 'q' && ch != 'c') {
      ch = getch();
    }
    if (ch == 'q') break;
  }
  system("cls");
  printf("~Bye~\n\n");
  system("pause");
  return 0;

游戏截图

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C++实现简单扫雷游戏

    扫雷是一个经典的电脑小游戏,用C++来编一下,效果自己试一下 #include<stdio.h> #include<Windows.h> #define YELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY #define CYAN FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY #define ORANGE FOREGROUND_RED |

  • C++实现一个扫雷小游戏

    本文实例为大家分享了C++实现扫雷小游戏的具体代码,供大家参考,具体内容如下 目前的版本是0.98版本,可以提出增加新功能意见哦 代码如下: #include<bits/stdc++.h> #include<windows.h> using namespace std; long long int c,dev,m,k,cnt,d,e,jie=10,z,abc,n,b[1000][1000],a[1000][1000],cc,cd,ce,def; //c是随机行,k是随机列 bool

  • 利用c++和easyx图形库做一个低配版扫雷游戏

    游戏界面 由于这个游戏是我抱着玩一玩的心态做出来的,所以没有过多的去设计界面,也没有去找游戏的资源(图片.游戏音效等).仅使用了不同颜色的方块来表示游戏中方块的状态和种类.(绿色为初始状态(未翻转的状态),黄色为翻转后的背景颜色,蓝色表示已插旗的方块,红色代表地雷) 图1 游戏主菜单界面 图二 模式一的游戏界面(20*20 40个雷) 图三 模式二的游戏界面(10*10 20个雷) 图四 游戏成功界面 图五 游戏失败界面 2.全部代码 #include<graphics.h> #include

  • C++实现扫雷经典小游戏

    用C++复现经典扫雷,供大家参考,具体内容如下 主要是dfs实现打开一片的操作,数字带有颜色,很真实. windows扫雷中鼠标左右键同时按也实现了,即试探. 先上图,详见下面代码: 代码中有详细注释,编译无任何错误警告. Ps.有bug请评论指出,谢谢啦~ 另外我觉得代码比较臃肿,有什么可以优化的也请提出~ #include<cstdio> #include<cstring> #include<algorithm> #include<conio.h> #i

  • C++实现简单的扫雷游戏(控制台版)

    C++新手的代码,请各位多包涵. 用C++写的一个简单的控制台版扫雷游戏.玩家通过输入方块的坐标来翻开方块. 只是一个雏形,能够让玩家执行翻开方块的操作并且判断输赢,还未添加标记方块.游戏菜单.记录游戏时间.重新开一局等等的功能. 玩家输入坐标的方式来翻开方块只适用于小型的"雷区",若"雷区"大了,用坐标会变得很不方便. 代码片段扫雷V1.1 #include<stdio.h> #include<Windows.h> #define YELL

  • C++实现扫雷小游戏

    本文实例为大家分享了C++实现扫雷游戏的具体代码,供大家参考,具体内容如下 #include<stdio.h> #include<windows.h> #include<stdlib.h> #include<time.h> #include<conio.h> #include<queue> #include<ctype.h> #define A 17 //地图的高 #define B 17 //地图的宽 #define C

  • C++基于EasyX实现简单扫雷游戏

    本文实例为大家分享了C++ EasyX实现简单扫雷游戏的具体代码,供大家参考,具体内容如下 [实现代码] #include <cmath> #include <time.h> #include <easyx.h> #include <conio.h> using namespace std; #define Size 500 //定义窗口大小 #define SquareSize 50 //定义格子大小 #define BackGroundColor LIG

  • C++实现扫雷游戏

    本文实例为大家分享了C++实现扫雷游戏的具体代码,供大家参考,具体内容如下 直接上代码 #include<stdio.h> #include<windows.h> #include<stdlib.h> #include<time.h> #include<conio.h> #include<queue> #include<ctype.h> #define A 17 //地图的高 #define B 17 //地图的宽 #de

  • C++实现扫雷游戏(控制台版)

    本文实例为大家分享了C++实现扫雷游戏的具体代码,供大家参考,具体内容如下 需要开一个map.txt  写入地图 地图中 *表示空地   ; x表示地雷 ********** ********** **x******* ********** ********** ********** ********** ********** ********** 然后就是扫雷的控制台代码了,只简单的检测了一下 #include <stdio.h> #include <string.h> #def

  • C++实现扫雷游戏(控制台不闪屏版)

    之前写了一个C++ 的控制台扫雷小游戏,但由于过度使用system("cls")刷屏,导致闪屏,因此重写了一个改善的不闪屏版本,并把逻辑重新捋了一遍. map.h #ifndef MAP_H_ #define MAP_H_ #define MAX_WID 18 #define MAX_LEN 32 #define UP_EDGE 1 //上边界 #define LEFT_EDGE 1 //左边界 #define RIGHT_EDGE _len //右边界 #define DOWN_ED

  • java实现扫雷游戏控制台版

    本文实例为大家分享了java实现扫雷游戏控制台版,供大家参考,具体内容如下 扫雷游戏 a.游戏的分析 在游戏中需要存在对象包含哪些. 格子对象(Grid): 属性:内容(content).状态(type) b.工程架构 设计工程包结构 bean:存放实体类 core:游戏核心类(游戏操作) test:扫雷玩的测试类 c.编写类 Grid(属性:content.type) Core类–游戏核心类(雷区创建.显示雷区.布雷.踩雷) Test类–测试类 d.核心类设计 1.格子对象创建方法定义 2.显

  • C++控制台实现扫雷游戏

    本文实例为大家分享了C++控制台实现扫雷游戏的具体代码,供大家参考,具体内容如下 花了一下午写出来的控制台扫雷,主要通过修改和打印数组来实现. 主要的问题点: 1.在显示地图的过程中,既要显示数字,又要显示雷和符号,所以的用string类型的二维向量,vector<vector<string.>>;中间要利用ASCII码将int型的数字转化为字符串.2.生成地图的时候,雷是随机的,我这里采用的做法是取余生成雷,举个例子,如果雷数是格子数的十分之一,那我遍历整个二维数组,在rand(

  • C++控制台版扫雷游戏

    本文实例为大家分享了C++控制台版扫雷游戏的具体代码,供大家参考,具体内容如下 先说思路,其实挺简单的. (1) 随机生成10个雷,标记到二维数组里,然后计算八个方向的雷的总数记录下来,这是预处理阶段. (2)输入要翻开的位置的坐标,如果是数字直接显示,是空白的话,这里采用bfs即宽度优先搜索解决,搜到最外层是数字(仅一层)时结束,具体详见代码. // 扫雷程序  #include <iostream> #include <string> #include <queue>

  • C++代码实现扫雷游戏

    前言 提示:本文是基于easyX图形库实现的,还有部分功能可以添加,仅适合新手参考. 提示:以下是本篇文章正文内容,下面案例可供参考 一.扫雷游戏模式 在确定大小的矩形雷区中随机布置一定数量的地雷,玩家需要尽快找出雷区中的所有不是地雷的方块,而不许踩到地雷. 游戏的基本操作包括左键单击和右键单击.其中左键用于打开安全的格子,推进游戏进度:右键用于标记地雷,以辅助判断. 左键单击:在判断出不是雷的方块上按下左键,可以打开该方块.如果方块上出现数字,则该数字表示其周围3×3区域中的地雷数(一般为8个

  • 基于C语言实现的扫雷游戏代码

    本文详细讲述了基于C语言实现的扫雷游戏代码,代码中备有比较详细的注释,便于读者阅读和理解.希望对学习游戏开发的朋友能有一点借鉴价值. 完整的实例代码如下: /* 模拟扫雷游戏 */ #include <graphics.h> #include <math.h> #include <stdio.h> #include <dos.h> #include <stdlib.h> #include <conio.h> #include <

  • Android实现闪屏效果

    在登陆一些页面时,通常能看见"一闪而过"效果并进入页面.下面看看是怎样实现这样的效果的吧 首先,在布局里(可以说和平常没有什么不同),划线部分是进度条: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width=&q

随机推荐