爱学习的小妖精 发表于 2020-2-19 11:51

C++ 扫雷小游戏

本帖最后由 苏紫方璇 于 2020-2-19 13:54 编辑

还记得以前的扫雷游戏吗?
现在,我们就来一起回顾一下(小哥哥教我写的,感觉很不错,现在来分享给大家)
随便写写玩玩,嘻嘻嘻,大佬大大们不喜吻喷哟~~


Dev-C++写的
因为C语言中,存在文本属性,其实就是颜色属性,有背景色和前景色(就是字符的颜色)两类,每一类只提供三原色(红,绿,蓝)和加强色(灰色,可与其他颜色搭配使用,使颜色变亮)。最后还有一个反色。所以需要用三原色来搭配构造不同的颜色。

基本文本属性:
FOREGROUND_BLUE 蓝色
FOREGROUND_GREEN 绿色
FOREGROUND_RED 红色
FOREGROUND_INTENSITY 加强
BACKGROUND_BLUE 蓝色背景
BACKGROUND_GREEN 绿色背景
BACKGROUND_RED 红色背景
BACKGROUND_INTENSITY 背景色加强
COMMON_LVB_REVERSE_VIDEO 反色

不多说,下面我们直接来看看我改的源码:


大佬给的注释很多,理解起来应该不难,可以自己尝试着更改一些东西来摸索每一句的作用,Dev-C++上可以直接编译运行(我一直用的这个)

```
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<queue>
#include<ctype.h>
#define A 20      //地图的高
#define B 20      //地图的宽
#define C 30      //雷的总数
using namespace std;

//全局变量
DWORD a,b;
char map,news,spare;
int BoomTotalNum,floatx,floaty,flag,flagnum,mode,slect,game;

//定义颜色属性

const WORD FORE_BLUE   = FOREGROUND_BLUE;         //蓝色文本属性
const WORD FORE_GREEN= FOREGROUND_GREEN;          //绿色文本属性
const WORD FORE_RED   =   FOREGROUND_RED;      //红色文本属性
const WORD FORE_WHITE=FORE_BLUE | FORE_RED | FORE_GREEN;      //白色文本属性
const WORD BACK_GRAY   = BACKGROUND_INTENSITY;      //灰色背景属性

//定义地图结构体
struct node {
      int x;
      int y;
};
queue <node> dui;

//打印位置
void position(int x,int y) {
      COORD pos={x,y};
      HANDLE Out=GetStdHandle(STD_OUTPUT_HANDLE);
      SetConsoleCursorPosition(Out,pos);
}

//隐藏光标
void Hide() {
      HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
      CONSOLE_CURSOR_INFO CursorInfo;
      GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
      CursorInfo.bVisible = false; //隐藏控制台光标
      SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态   
}

//初始化
void Beginning() {
      while(!dui.empty()) {
                dui.pop();
      }
      game=1;
      //BoomTotalNum=C;
      floatx=A/2;
      floaty=B/2;
      flagnum=0;
      BoomTotalNum=C;
      mode=0;
      HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄
    CONSOLE_SCREEN_BUFFER_INFO csbi;                        //定义窗口缓冲区信息结构体
    GetConsoleScreenBufferInfo(handle_out, &csbi);          //获得窗口缓冲区信息
    int x,y;
      srand((unsigned)time(0));
      for(int i=0;i<A;i++) for(int j=0;j<B;j++) {
                map=' ';
                flag=0;
                slect=0;
      }
      while(BoomTotalNum) {
                x=rand()%A;
                y=rand()%B;
                if(map==' ') {
                        map='@';
                        BoomTotalNum--;
                }
      }
      SetConsoleTextAttribute(handle_out, BACK_GRAY);
      for(int i=0;i<A;i++) {
                for(int j=0;j<B;j++) printf("█");
                printf("\n");
      }
      position(floaty*2,floatx);
      SetConsoleTextAttribute(handle_out, FORE_RED);
      printf("开");      //光标位置
      position(44,9);
      printf("扫雷模式");
      position(44,5);
      printf("剩余雷数:%d ",C-flagnum);
      SetConsoleTextAttribute(handle_out, BACK_GRAY);
      position(5,22);
      printf("按“空格”切换标记模式");
      position(5,23);
      printf("按“Enter”确认");
      position(5,24);
      printf("按“方向键”选择方块");
      
}

//打印地图的一块儿
void Lump(int xx,int yy) {
      switch(map) {
                case '1' : printf("①");break;      //周围雷的数量(下同)
                case '2' : printf("②");break;
                case '3' : printf("③");break;
                case '4' : printf("④");break;
                case '5' : printf("⑤");break;
                case '6' : printf("⑥");break;
                case '7' : printf("⑦");break;
                case '8' : printf("⑧");break;
                case ' ' :
                        if(xx==floatx&&yy==floaty) {
                              if(flag==0) {
                                        if(mode%2==0) printf("开");
                                        else printf("标");
                              }
                              else printf("?");
                        }
                        else {
                              if(flag==0) printf("█");
                              else printf("?");
                        }
                        break;
                case '@' :
                        if(xx==floatx&&yy==floaty) {
                              if(flag==0) {
                                        if(mode%2==0) printf("开");
                                        else printf("标");
                              }
                              else printf("?");
                        }
                        else {
                              if(flag==0) printf("█");
                              else printf("?");
                        }
                        break;
                case 'x' : if(floatx==xx&&floaty==yy) printf(""); else printf("");break;      //已经挖开的空白
      }
}

//移动光标
void Move() {
      HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄
    CONSOLE_SCREEN_BUFFER_INFO csbi;                        //定义窗口缓冲区信息结构体
    GetConsoleScreenBufferInfo(handle_out, &csbi);          //获得窗口缓冲区信息
    int xxx,yyy;
    xxx=floatx;
    yyy=floaty;
      switch(news) {
                case 72 : floatx--;break;      //上
                case 80 : floatx++;break;      //下
                case 75 : floaty--;break;      //左
                case 77 : floaty++;break;      //右
      }
      if(floatx==-1) floatx=A-1; floatx%=A;      //两端穿模处理
      if(floaty==-1) floaty=B-1; floaty%=B;
      
      position(yyy*2,xxx);
      SetConsoleTextAttribute(handle_out, BACK_GRAY);
      Lump(xxx,yyy);      //删除原位置
      
      if(map=='x') {
                position(floaty*2,floatx);
                printf("");
      }
      
      position(floaty*2,floatx);
      SetConsoleTextAttribute(handle_out, FORE_WHITE);
      Lump(floatx,floaty);      //更新新位置
}

//插旗和排雷模式切换
void Mode() {
      HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄
    CONSOLE_SCREEN_BUFFER_INFO csbi;                        //定义窗口缓冲区信息结构体
    GetConsoleScreenBufferInfo(handle_out, &csbi);          //获得窗口缓冲区信息
      mode++;
      SetConsoleTextAttribute(handle_out, FORE_WHITE);
      position(floaty*2,floatx);
      if(mode%2==0) printf("开");
      else printf("标");
      
      position(44,9);
      if(mode%2==0) {
                SetConsoleTextAttribute(handle_out, FORE_WHITE);
                printf("扫雷模式");
      }
      else {
                SetConsoleTextAttribute(handle_out, FORE_RED);
                printf("插旗模式");
      }
}

//该点周围地雷数
int Boomnum(int xx,int yy) {
      int num=0;
      if((xx-1>=0)&&(yy-1>=0)&&(map=='@')) num++;
      if((xx-1>=0)&&(yy+0>=0)&&(map=='@')) num++;
      if((xx-1>=0)&&(yy+1<B) &&(map=='@')) num++;
      if((xx+0>=0)&&(yy-1>=0)&&(map=='@')) num++;
      if((xx+0>=0)&&(yy+1<B) &&(map=='@')) num++;
      if((xx+1<A)&&(yy-1>=0) &&(map=='@')) num++;
      if((xx+1<A)&&(yy+0>=0) &&(map=='@')) num++;
      if((xx+1<A)&&(yy+1<B)&&(map=='@')) num++;
      return num;
}

//更新地图
void Open() {
      node c;
      node d;
      while(!dui.empty()) {
                dui.pop();
      }
      c.x=floatx;
      c.y=floaty;
      dui.push(c);
      slect=1;
      while(!dui.empty()) {
                c=dui.front();
                dui.pop();
                if(Boomnum(c.x,c.y)!=0) {
                        map=(Boomnum(c.x,c.y)+48);
                        continue;
                }
                else {
                        map='x';                                                                                                                                                                                                                                                                                                                                                                                                                                    
                        if((c.x-1>=0)&&(c.y-1>=0)&&(map==' ')&&(slect==0)) {
                              d.x=c.x-1;
                              d.y=c.y-1;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x-1>=0)&&(c.y-0>=0)&&(map==' ')&&(slect==0)) {
                              d.x=c.x-1;
                              d.y=c.y-0;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x-1>=0)&&(c.y+1<B)&&(map==' ')&&(slect==0)) {
                              d.x=c.x-1;
                              d.y=c.y+1;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x-0>=0)&&(c.y-1>=0)&&(map==' ')&&(slect==0)) {
                              d.x=c.x-0;
                              d.y=c.y-1;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x-0>=0)&&(c.y+1<B)&&(map==' ')&&(slect==0)) {
                              d.x=c.x-0;
                              d.y=c.y+1;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x+1<A)&&(c.y-1>=0)&&(map==' ')&&(slect==0)) {
                              d.x=c.x+1;
                              d.y=c.y-1;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x+1<A)&&(c.y-0>=0)&&(map==' ')&&(slect==0)) {
                              d.x=c.x+1;
                              d.y=c.y-0;
                              dui.push(d);
                              slect=1;
                        }
                        if((c.x+1<A)&&(c.y+1<B)&&(map==' ')&&(slect==0)) {
                              d.x=c.x+1;
                              d.y=c.y+1;
                              dui.push(d);
                              slect=1;
                        }
                }
      }
}

int main() {
      freopen("排名.txt","r",stdin);
      Relife:      //重玩处
      HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄
    CONSOLE_SCREEN_BUFFER_INFO csbi;                        //定义窗口缓冲区信息结构体
    GetConsoleScreenBufferInfo(handle_out, &csbi);          //获得窗口缓冲区信息
   
      Hide();                //隐藏光标
      Beginning();//初始化地图
      a=GetTickCount();
      while(1) {
                if(kbhit()!=0) {
                        spare=getch();
                        
                        //按其他
                        if((spare!=(-32))&&(spare!=13)&&(spare!=' ')) continue;//跳过
                        
                        //按Enter
                        if(spare==13) {      //确认
                              //排雷
                              if(mode%2==0) {
                                        if(map=='@'&&flag==0) {
                                                break;      //触雷
                                                game=0;
                                        }
                                       
                                        if(flag==1) continue;      //有旗跳过
                                        Open();
                                        position(0,0);
                                        SetConsoleTextAttribute(handle_out, BACK_GRAY);
                                        for(int i=0;i<A;i++) {
                                                for(int j=0;j<B;j++) Lump(i,j);
                                                printf("\n");
                                        }
                                        position(floaty*2,floatx);
                                        SetConsoleTextAttribute(handle_out, FORE_WHITE);
                                        Lump(floatx,floaty);
                              }
                              
                              //插拔旗
                              else {
                                       
                                        //不能插旗的地方
                                        if(map=='x'||(map>'0'&&map<'9'))
                                                continue;      //跳过
                                       
                                        //插旗
                                        if(flag==0) {
                                                flagnum++;
                                                flag=1;
                                                position(floaty*2,floatx);
                                                SetConsoleTextAttribute(handle_out, FORE_WHITE);
                                                Lump(floatx,floaty);
                                        }
                                       
                                        //拔旗
                                        else {
                                                flagnum--;
                                                flag=0;
                                                position(floaty*2,floatx);
                                                SetConsoleTextAttribute(handle_out, FORE_WHITE);
                                                Lump(floatx,floaty);
                                        }
                              }
                        }
                        
                        //按空格
                        if(spare==' ') Mode();      //切换模式
                        
                        //按方向键
                        if(spare==-32) {
                              news=getch();
                              Move();      //移动光标
                        }
                        for(int i=0;i<A;i++) for(int j=0;j<B;j++) if(map=='x'||(map>'0'&&map<'9')) game++;
                        if(game==A*B-C+1) break;
                        else game=1;
                        SetConsoleTextAttribute(handle_out, FORE_RED);
                        position(44,5);
                        printf("剩余雷数:%d ",C-flagnum);
                }
                else Sleep(10);
                b=GetTickCount();
                SetConsoleTextAttribute(handle_out, FORE_RED);
                position(44,7);
                printf("用时:");      //用时
                if((b-a)/60000<10) printf("0");
                printf("%d:",(b-a)/60000);
                if(((b-a)/1000)%60<10) printf("0");
                printf("%d:",((b-a)/1000)%60);
                if(((b-a)/10)%100<10) printf("0");
                printf("%d",((b-a)/10)%100);
      }
      SetConsoleTextAttribute(handle_out, FORE_RED);
      position(5,5);
      if(game==1) printf("游戏结束!");
      else printf("恭喜通关!");
      position(5,8);
      printf("任意键重玩");
      scanf("%c%c",&spare,&spare);
      system("cls");
      position(0,0);
      goto Relife;
}
```

爱学习的小妖精 发表于 2020-2-21 15:40

CLX 发表于 2020-2-19 12:21
我是初学者,之前尝试做过,但...一言难尽呀,敢问大佬有源码么?

哈哈哈,没事没事,慢慢来,可以的,上面那些就是源码可以成功运行

爱学习的小妖精 发表于 2020-3-14 14:33

铁狼 发表于 2020-2-19 12:24
太多了吧,不过可以观摩一下,毕竟我也学C++

加油!C很重要哒!好好学相信你可以的

15157519997 发表于 2020-2-19 12:10

好长的天书

jefel 发表于 2020-2-19 12:15

不错,可以观察。

無--- 发表于 2020-2-19 12:15

相信自己,永不放弃

yhzh 发表于 2020-2-19 12:16

能不能附件发源码

loner888 发表于 2020-2-19 12:18

表示看不懂~~

CLX 发表于 2020-2-19 12:21

我是初学者,之前尝试做过,但...一言难尽呀,敢问大佬有源码么?

wwhtm 发表于 2020-2-19 12:23

我就是写不出来,只能举手{:1_921:}

铁狼 发表于 2020-2-19 12:24

太多了吧,不过可以观摩一下,毕竟我也学C++

散场的拥抱 发表于 2020-2-19 12:37

牛逼,厉害
页: [1] 2 3 4
查看完整版本: C++ 扫雷小游戏