C语言实现扫雷小游戏

mac2025-10-31  0

C语言实现扫雷

之前闲来无趣,就自己模仿网上视频写了一个扫雷玩玩(主要是我女朋友让我去下一个扫雷,不晓得我怎么脑袋一热就说我给她写一个。。)这里记录分享一下,主要是用C语言进行,然后用了一点MFC的图形化界面窗口。 先分析一下,对于扫雷,我们应该要实现的是哪些东西? 首先,对于游戏地图的处理上,使用的是二维数组,然后对整个二维数组进行赋初值为0; 然后对于雷,我们用-1来进行表示,想要随机生成雷,那就直接用随机生成数的函数进行实现,用便利的方式把雷放到里面去就行。(先分小块进行讲,最后贴全部代码)

#include<iostream> #include<stdio.h> #include<stdlib.h> #include<time.h> #include<graphics.h> using namespace std; #define ROW 33 //列 #define COL 16 //行 #define NUM 99 //雷的个数 #define SIZE 30//图片的大小; int map[ROW + 2][COL + 2];//地图 void Initmap() { int n = 0; srand((unsigned int)time(NULL));//随机数种子; for (int i = 0; i < ROW + 2; i++) { for (int j = 0; j < COL + 2; j++) { map[i][j] = 0; } } while (n < NUM)//放置炸弹 { int r = rand() % ROW + 1;//用生成随机数函数实现生成随机数 int c = rand() % COL + 1; if (map[r][c] == 0)//放置雷; { map[r][c] = -1; n++; } } for (int i = 1; i <=ROW; i++)//遍历某个位置的九宫格,将该位置的数变成九宫格中雷的总数; { for (int j = 1; j <=COL; j++) { if (map[i][j] != -1) { for (int m = i - 1; m <= i + 1; m++) { for (int n = j - 1; n <= j + 1; n++) { if (map[m][n] == -1) { map[i][j]++; } } } } } }

那么对于一些特殊的位置该怎么处理呢?比如我们在玩扫雷的时候,对于某一个没有被翻起来的位置,我们鼠标左键单击之后,可能是空白,也可能是数字,那么这个数字是怎么得到的呢?我们讲一下关于扫雷的游戏规则: 如图: 当我们点击某一个没有被点击过的位置的时候,对于该位置的数字的求法,是以该位置为中心,周围形成的一个3x3的矩阵,然后便利除了他自己本身以外的格子,每遇到一个类,该位置的数字就加1,所以对应的数字就是这么得到的。但是在进行程序处理的时候,我们会发现一个问题,那如果我在边缘进行处理呢?也就是整个地图的最右上角或者左上角进行处理呢?不是越界了嘛?所以,我们在二维数组初始化的时候,将整个地图应该设置为(n+1)(m+1)的格式,后面进行地图更新和打印的时候,在nm内进行操作就行了;

for (int i = 1; i <=ROW; i++)//遍历某个位置的九宫格,将该位置的数变成九宫格中雷的总数; { for (int j = 1; j <=COL; j++) { if (map[i][j] != -1) { for (int m = i - 1; m <= i + 1; m++) { for (int n = j - 1; n <= j + 1; n++) { if (map[m][n] == -1) { map[i][j]++; } } } } }

然后我们再考虑一点,按照目前我们所对二位数组初始化的数据中,只有-1和0这两个数据,但是我们想要在鼠标点击之后显示1-8和小红旗(鼠标右击之后插红旗的动作)还有炸弹该如何处理呢?对于数字其实还好,我们可以直接进行判断,但是又有一个问题,在我鼠标没有任何动作的时候,地图所展现的每一个小方格都是没有被翻起的,就是空白的,那空白图片该怎么显示呢?还有小红旗该如何显示?那么我们可以在数据上进行一些操作,我这里是把所有的都加上20,使其范围变到19-28,然后凡是在这个范围内的都输出空白图片,也就是没有被翻起来,只要某一处被鼠标进行动作,比如被鼠标进行左击,假设是在进行翻起动作,就把当前该位置的值-20,得到的数字进行判断,是-1就显示雷(结束游戏),是数字就显示数字。再来说一下红旗的解决,同样的,当数据进行操作之后,我仅仅是对鼠标的左击进行了监控,而右击还没有,所以当鼠标在某个位置进行右击的时候,我再把这个位置的书加上30,让其变得更大,然后如果像取消小红旗,则就判断一下,如果该位置的值>50,那么就把这个位置的值-50即可。

//------------------------------------------------------------------------------------------- for (int i = 1; i <= ROW; i++)//对每个数据进行加20,对数据进行优化,方便后面对空白,红旗的处理; { for (int j = 1; j <= COL; j++) { map[i][j] += 20; } } //----------------------------------------------------------------------------------------- int PlayGame()//对鼠标动作进行监控 { int r ; int c ; MOUSEMSG msg = { 0 };//定义一个鼠标消息; while(1) { msg = GetMouseMsg(); switch (msg.uMsg) { case WM_LBUTTONDOWN://翻开空白图片; r = msg.x / SIZE + 1; c = msg.y / SIZE + 1; if (map[r][c] >= 19 && map[r][c] <= 28) { if (map[r][c] == 20) { OpenZ(r, c); } else { map[r][c] -= 20; counts++; } } return map[r][c]; break; case WM_RBUTTONDOWN://标志小红旗 r = msg.x / SIZE + 1; c = msg.y / SIZE + 1; if (map[r][c] >= 19 && map[r][c] <= 28)//放置小红旗 { map[r][c] += 50; } else if(map[r][c] > 30)//取消放置的小红旗 { map[r][c] -= 50; } return map[r][c]; break; } } }

最后还有一个小问题,就是在游玩扫雷的时候,你会发现有时候你点击了某一处,可能显示的不仅仅是你那个3x3的小方格的所有内容,可能还显示了其他的一些,这个就涉及到如果你点击的位置是空白,也就是周围一个雷都没有。那么也就是说,如果你点击到了一个周围一个类都没有的矩阵中心,然后就去遍历这个矩阵中其他小方格,看是否有也为0的,然后再以其为中心遍历其自身的矩阵。很明显,递归。

void OpenZ(int r,int c)//递归实现输出对应能输出的空位 { map[r][c] -= 20; counts++; for (int m = r - 1; m <= r + 1; m++) { for (int n = c - 1; n <= c + 1; n++) { if (m >= 1 && m <= ROW && n >= 1 && n <= COL)//保证在游戏区 { if (map[m][n] >=19 && map[m][n] <=28)//保证遍历的是空白的,也就是没有被翻起的 { if (map[m][n] == 20)//如果遍历到的位置是空白的话,进行递归 { OpenZ(m, n); } else//如果不是,那么就把他翻起来,然后把监控翻起来数值的变量加1,这里不用考虑雷的问题,因为本身遍历的这个矩阵中心是0,所以这个矩阵中没有雷 { map[m][n] -= 20; counts++; } } } } } }

最后我把整体代码贴一下:

#include<iostream> #include<stdio.h> #include<stdlib.h> #include<time.h> #include<graphics.h> using namespace std; #define ROW 33 //列 #define COL 16 //行 #define NUM 99 //雷的个数 #define SIZE 30//图片的大小; int map[ROW + 2][COL + 2];//地图 int counts = 0;//判断游戏胜利的条件; IMAGE img[12];//存放对应数字的图片和对应雷和红旗的图片; void OpenZ(int r,int c)//递归实现输出对应能输出的空位 { map[r][c] -= 20; counts++; for (int m = r - 1; m <= r + 1; m++) { for (int n = c - 1; n <= c + 1; n++) { if (m >= 1 && m <= ROW && n >= 1 && n <= COL)//保证在游戏区 { if (map[m][n] >=19 && map[m][n] <=28)//保证遍历的是空白的,也就是没有被翻起的 { if (map[m][n] == 20)//如果遍历到的位置是空白的话,进行递归 { OpenZ(m, n); } else//如果不是,那么就把他翻起来,然后把监控翻起来数值的变量加1,这里不用考虑雷的问题,因为本身遍历的这个矩阵中心是0,所以这个矩阵中没有雷 { map[m][n] -= 20; counts++; } } } } } } void Initmap() { int n = 0; srand((unsigned int)time(NULL));//随机数种子; for (int i = 0; i < ROW + 2; i++) { for (int j = 0; j < COL + 2; j++) { map[i][j] = 0; } } while (n < NUM)//放置炸弹 { int r = rand() % ROW + 1; int c = rand() % COL + 1; if (map[r][c] == 0)//放置雷; { map[r][c] = -1; n++; } } for (int i = 1; i <=ROW; i++)//遍历某个位置的九宫格,将该位置的数变成九宫格中雷的总数; { for (int j = 1; j <=COL; j++) { if (map[i][j] != -1) { for (int m = i - 1; m <= i + 1; m++) { for (int n = j - 1; n <= j + 1; n++) { if (map[m][n] == -1) { map[i][j]++; } } } } } } for (int i = 1; i <= ROW; i++)//对每个数据进行加20,对数据进行优化,方便后面对雷,空白,红旗的处理; { for (int j = 1; j <= COL; j++) { map[i][j] += 20; } } } int PlayGame()//对鼠标动作进行监控 { int r ; int c ; MOUSEMSG msg = { 0 };//定义一个鼠标消息; while(1) { msg = GetMouseMsg(); switch (msg.uMsg) { case WM_LBUTTONDOWN://翻开空白图片; r = msg.x / SIZE + 1; c = msg.y / SIZE + 1; if (map[r][c] >= 19 && map[r][c] <= 28) { if (map[r][c] == 20) { OpenZ(r, c); } else { map[r][c] -= 20; counts++; } } return map[r][c]; break; case WM_RBUTTONDOWN://标志小红旗 r = msg.x / SIZE + 1; c = msg.y / SIZE + 1; if (map[r][c] >= 19 && map[r][c] <= 28)//放置小红旗 { map[r][c] += 50; } else if(map[r][c] > 30)//取消放置的小红旗 { map[r][c] -= 50; } return map[r][c]; break; } } } void Showmap() { for (int i = 1; i <= ROW; i++) { for (int j = 1; j <= COL; j++) { printf("%2d ", map[i][j]); if (map[i][j] == -1) { putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[9]);//打印雷的图片 } else if (map[i][j] >= 0 && map[i][j] <= 8) { putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[map[i][j]]);//打印数字图片 } else if (map[i][j] >= 19 && map[i][j] <= 28) { putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[10]);//打印空白图片 } else if (map[i][j] > 30) { putimage((i - 1)*SIZE, (j - 1)*SIZE, &img[11]);//打印小红旗; } } cout << endl; } } int main() { HWND hwnd = initgraph(ROW*SIZE, COL*SIZE); loadimage(&img[0], "0.jpg", SIZE, SIZE); loadimage(&img[1], "1.jpg", SIZE, SIZE); loadimage(&img[2], "2.jpg", SIZE, SIZE); loadimage(&img[3], "3.jpg", SIZE, SIZE); loadimage(&img[4], "4.jpg", SIZE, SIZE); loadimage(&img[5], "5.jpg", SIZE, SIZE); loadimage(&img[6], "6.jpg", SIZE, SIZE); loadimage(&img[7], "7.jpg", SIZE, SIZE); loadimage(&img[8], "8.jpg", SIZE, SIZE); loadimage(&img[9], "9.jpg", SIZE, SIZE); loadimage(&img[10], "10.jpg", SIZE, SIZE); loadimage(&img[11], "11.jpg", SIZE, SIZE); STAR: Initmap(); while (1) { Showmap(); if (PlayGame() == -1) { Showmap(); int result=MessageBox(hwnd, "踩到雷啦!游戏失败!是否需要重新开始呢?", "", MB_YESNO); if (result == IDYES) { MessageBox(hwnd, "您选择了重新开始!", "", MB_OK); counts = 0; goto STAR; break; } else { MessageBox(hwnd, "那么感谢您的游玩!", "", MB_OK); } break; } if (ROW*COL - NUM == counts) { int result=MessageBox(hwnd, "很棒哦!恭喜你找出了所有雷!是否需要再来一局呢?", "", MB_YESNO); if (result == IDYES) { MessageBox(hwnd, "那么在此预祝你本次游玩愉快!", "", MB_OK); counts = 0; goto STAR; break; } else { MessageBox(hwnd, "那么感谢您的游玩!", "", MB_OK); } break; } } closegraph(); system("pause"); return 0; }

实现的样子: 注:关于图片,图片好似只能用.jpg格式,然后要存放到项目目录下(就是放.cpp的里面),不然显示不出来。还有游戏胜利的判断是用一个变量记录所有翻起来的格子数,当变量的值=总的数-雷的数,那就是胜利了。

最新回复(0)