当前位置:   article > 正文

C++扫雷(附代码和原理)_c++扫雷代码

c++扫雷代码

目录

玩法

代码

注释

框架

重点-定义翻开区域的函数

游戏截图


玩法

wasd控制指针移动,空格键翻开,f键标记,p键自爆(直接输)

代码

  1. #include <bits/stdc++.h>
  2. #include <windows.h>
  3. #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
  4. using namespace std;
  5. string mines0[81]; // 平铺的雷(用于随机埋雷)
  6. string status[9][9]; // 雷区状态(用于判断)
  7. string outstatus[9][9]; // 显示状态(用于显示)
  8. string out = ""; // 会在fan()函数前面注释解释
  9. int s; // 此区域周围雷数(用于标数字)
  10. int x = 1; // 指针坐标x
  11. int y = 1; // 指针坐标y
  12. int win = 0; // 0为正在游戏,1为胜利,-1为失败,-2为自爆
  13. int mine = 0; // 剩余雷数
  14. map<string, int> col = { // 颜色名称对应的颜色代码
  15. {"dblue", 1},
  16. {"dgreen", 2},
  17. {"dteal", 3},
  18. {"dred", 4},
  19. {"dpink", 5},
  20. {"yellow", 6},
  21. {"dwhite", 7},
  22. {"grey", 8},
  23. {"lblue", 9},
  24. {"lgreen", 10},
  25. {"lteal", 11},
  26. {"lred", 12},
  27. {"lpink", 13},
  28. {"lyellow", 14},
  29. {"lwhite", 15}
  30. };
  31. map<string, string> codesty = { // 状态代码对应的显示图标
  32. {"no", "□"},
  33. {"1", " 1"},
  34. {"2", " 2"},
  35. {"3", " 3"},
  36. {"4", " 4"},
  37. {"5", " 5"},
  38. {"6", " 6"},
  39. {"7", " 7"},
  40. {"8", " 8"},
  41. {"mine", "¤"},
  42. {"flag", " F"},
  43. {"nfan", "■"},
  44. {"point", "♂"}
  45. };
  46. map<string, string> codecol = { // 状态代码对应的颜色名称
  47. {"no", "grey"},
  48. {"1", "lblue"},
  49. {"2", "dgreen"},
  50. {"3", "lred"},
  51. {"4", "dblue"},
  52. {"5", "dpink"},
  53. {"6", "lteal"},
  54. {"7", "dteal"},
  55. {"8", "lgreen"},
  56. {"mine", "dred"},
  57. {"flag", "lyellow"},
  58. {"nfan", "lwhite"},
  59. {"point", "dwhite"}
  60. };
  61. void clearbuf() { // 清屏
  62. system("cls");
  63. }
  64. void color(string a) { // 设置文本颜色
  65. SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), col[a]);
  66. }
  67. void say(int a) { // 输出 a=1正常 a=0直接把所有内部都输出
  68. color("lwhite");
  69. cout << " 1 2 3 4 5 6 7 8 9" << endl;
  70. for (int i = 1; i <= 9; i++) {
  71. color("lwhite");
  72. cout << i << " ";
  73. for (int j = 1; j <= 9; j++) {
  74. if (i == y && j == x) {
  75. color(codecol["point"]);
  76. cout << codesty["point"];
  77. } else {
  78. if (a == 0) {
  79. color(codecol[status[i - 1][j - 1]]);
  80. cout << codesty[status[i - 1][j - 1]];
  81. } else {
  82. color(codecol[outstatus[i - 1][j - 1]]);
  83. cout << codesty[outstatus[i - 1][j - 1]];
  84. }
  85. }
  86. }
  87. cout << endl;
  88. }
  89. color("lwhite");
  90. cout << "剩余雷数: ";
  91. color("lred");
  92. cout << mine << endl;
  93. cout << endl;
  94. }
  95. bool w() { // 判断输赢
  96. for (int i = 0; i < 9; i++) {
  97. for (int j = 0; j < 9; j++) {
  98. if (outstatus[i][j] == "nfan" && status[i][j] != "mine") {
  99. return false;
  100. } else if (outstatus[i][j] == "flag" && status[i][j] != "mine") {
  101. return false;
  102. }
  103. }
  104. }
  105. return true;
  106. }
  107. /*
  108. 最重要的部分,连续翻开一大片 的函数(前提是点到了周围八个空均没有雷的方块)
  109. 思路如下:
  110. 首先假设是这个图案且点到了中心[2, 2](m是雷)
  111. 0 0 0 1 m
  112. 1 1 0 1 1
  113. m 1 0 0 0
  114. 1 1 1 1 1
  115. 0 0 1 m 1
  116. 看一下如果out里面有(a,b) 就 return
  117. 先把那一块3*3的区域划下来:
  118. 1 0 1
  119. 1 0 0
  120. 1 1 1
  121. 从左到右,从上到下判断是否为0,
  122. 如果是,重新以这个坐标为中心,把3*3的区域划下来(递归):
  123. 0 0 1
  124. 1 0 1
  125. 1 0 0
  126. 之后把判断的每一个位置都要outstatus[x][y] = status[x][y];然后把(x,y)添加到out,防止重复
  127. 角边要特殊处理!!!
  128. */
  129. void fan(int a, int b) {
  130. outstatus[a][b] = status[a][b];
  131. if (out.find("(" + to_string(a) + "," + to_string(b) + ")") != string::npos) {
  132. return;
  133. }
  134. out += ("(" + to_string(a) + "," + to_string(b) + ")");
  135. if (status[a][b] != "no") return;
  136. if (a == 0 && b == 0) { // 左上角
  137. fan(a, b + 1); // 递归
  138. fan(a + 1, b);
  139. fan(a + 1, b + 1);
  140. } else if (a == 0 && b == 8) { // 右上角
  141. fan(a, b - 1);
  142. fan(a + 1, b - 1);
  143. fan(a + 1, b);
  144. } else if (a == 8 && b == 0) { // 左下角
  145. fan(a - 1, b);
  146. fan(a - 1, b + 1);
  147. fan(a, b + 1);
  148. } else if (a == 8 && b == 8) { // 右下角
  149. fan(a - 1, b - 1);
  150. fan(a - 1, b);
  151. fan(a, b - 1);
  152. } else if (a == 0 || b == 0 || a == 8 || b == 8) { // 边上
  153. if (a == 0) { // 上边;
  154. fan(a, b - 1);
  155. fan(a, b + 1);
  156. fan(a + 1, b - 1);
  157. fan(a + 1, b);
  158. fan(a + 1, b + 1);
  159. } else if (b == 8) { // 右边
  160. fan(a - 1, b - 1);
  161. fan(a - 1, b);
  162. fan(a, b - 1);
  163. fan(a + 1, b - 1);
  164. fan(a + 1, b);
  165. } else if (a == 8) { // 下边
  166. fan(a - 1, b - 1);
  167. fan(a - 1, b);
  168. fan(a - 1, b + 1);
  169. fan(a, b - 1);
  170. fan(a, b + 1);
  171. } else if (b == 0) { // 左边
  172. fan(a - 1, b);
  173. fan(a - 1, b + 1);
  174. fan(a, b + 1);
  175. fan(a + 1, b);
  176. fan(a + 1, b + 1);
  177. }
  178. } else { // 中间
  179. fan(a - 1, b - 1);
  180. fan(a - 1, b);
  181. fan(a - 1, b + 1);
  182. fan(a, b - 1);
  183. fan(a, b + 1);
  184. fan(a + 1, b - 1);
  185. fan(a + 1, b);
  186. fan(a + 1, b + 1);
  187. }
  188. }
  189. int main() {
  190. // 随机埋雷
  191. for (int i = 0; i < 81; i++) {
  192. mines0[i] = "no";
  193. }
  194. for (int i = 0; i <= 10; i++) {
  195. mines0[i] = "mine";
  196. }
  197. srand(time(NULL));
  198. random_shuffle(mines0, mines0 + 81);
  199. for (int i = 1; i <= 9; i++) {
  200. for (int j = 1; j <= 9; j++) {
  201. status[i - 1][j - 1] = mines0[i* j - 1];
  202. outstatus[i - 1][j - 1] = "nfan";
  203. if (status[i-1][j-1] == "mine") mine++;
  204. }
  205. }
  206. // 标数字
  207. if (status[0][0] == "no") { // 左上角
  208. s = 0;
  209. if (status[0][1] == "mine") s++;
  210. if (status[1][0] == "mine") s++;
  211. if (status[1][1] == "mine") s++;
  212. if (s != 0) status[0][0] = to_string(s);
  213. }
  214. if (status[0][8] == "no") { // 右上角
  215. s = 0;
  216. if (status[0][7] == "mine") s++;
  217. if (status[1][7] == "mine") s++;
  218. if (status[1][8] == "mine") s++;
  219. if (s != 0) status[0][8] = to_string(s);
  220. }
  221. if (status[8][0] == "no") { // 左下角
  222. s = 0;
  223. if (status[7][0] == "mine") s++;
  224. if (status[7][1] == "mine") s++;
  225. if (status[8][1] == "mine") s++;
  226. if (s != 0) status[8][0] = to_string(s);
  227. }
  228. if (status[8][8] == "no") { // 右下角
  229. s = 0;
  230. if (status[7][7] == "mine") s++;
  231. if (status[7][8] == "mine") s++;
  232. if (status[8][7] == "mine") s++;
  233. if (s != 0) status[8][8] = to_string(s);
  234. }
  235. for (int i = 1; i <= 7; i++) { // 上边
  236. if (status[0][i] != "no") continue;
  237. s = 0;
  238. if (status[0][i - 1] == "mine") s++;
  239. if (status[0][i + 1] == "mine") s++;
  240. if (status[1][i - 1] == "mine") s++;
  241. if (status[1][i] == "mine") s++;
  242. if (status[1][i + 1] == "mine") s++;
  243. if (s != 0) status[0][i] = to_string(s);
  244. }
  245. for (int i = 1; i <= 7; i++) { // 右边
  246. if (status[i][8] != "no") continue;
  247. s = 0;
  248. if (status[i - 1][7] == "mine") s++;
  249. if (status[i - 1][8] == "mine") s++;
  250. if (status[i][7] == "mine") s++;
  251. if (status[i + 1][7] == "mine") s++;
  252. if (status[i + 1][8] == "mine") s++;
  253. if (s != 0) status[i][8] = to_string(s);
  254. }
  255. for (int i = 1; i <= 7; i++) { // 下边
  256. if (status[8][i] != "no") continue;
  257. s = 0;
  258. if (status[i - 1][8] == "mine") s++;
  259. if (status[i + 1][8] == "mine") s++;
  260. if (status[i - 1][7] == "mine") s++;
  261. if (status[i][7] == "mine") s++;
  262. if (status[i + 1][7] == "mine") s++;
  263. if (s != 0) status[8][i] = to_string(s);
  264. }
  265. for (int i = 1; i <= 7; i++) { // 左边
  266. if (status[i][0] != "no") continue;
  267. s = 0;
  268. if (status[i - 1][0] == "mine") s++;
  269. if (status[i - 1][1] == "mine") s++;
  270. if (status[i][1] == "mine") s++;
  271. if (status[i + 1][0] == "mine") s++;
  272. if (status[i + 1][1] == "mine") s++;
  273. if (s != 0) status[i][0] = to_string(s);
  274. }
  275. for (int i = 0; i < 8; i++) { // 中间
  276. for (int j = 0; j < 8; j++) {
  277. if (status[i][j] != "no") {
  278. continue;
  279. }
  280. s = 0;
  281. if (status[i - 1][j - 1] == "mine") s++;
  282. if (status[i - 1][j] == "mine") s++;
  283. if (status[i - 1][j + 1] == "mine") s++;
  284. if (status[i][j - 1] == "mine") s++;
  285. if (status[i][j + 1] == "mine") s++;
  286. if (status[i + 1][j - 1] == "mine") s++;
  287. if (status[i + 1][j] == "mine") s++;
  288. if (status[i + 1][j + 1] == "mine") s++;
  289. if (s != 0) status[i][j] = to_string(s);
  290. }
  291. }
  292. //开始运行(一直判断键盘是否按下并执行操作)
  293. while (win == 0) {
  294. say(1);
  295. if (KEY_DOWN('W') && y > 1) {
  296. y -= 1;
  297. } else if (KEY_DOWN('A') && x > 1) {
  298. x -= 1;
  299. } else if (KEY_DOWN('S') && y < 9) {
  300. y += 1;
  301. } else if (KEY_DOWN('D') && x < 9) {
  302. x += 1;
  303. } else if (KEY_DOWN('F')) { //插旗旗
  304. if (outstatus[y - 1][x - 1] == "nfan") {
  305. outstatus[y - 1][x - 1] = "flag";
  306. mine--;
  307. }
  308. } else if (KEY_DOWN(' ')) { // 翻方块
  309. if (status[y - 1][x - 1] == "mine") {
  310. win = -1;
  311. } else if (outstatus[y - 1][x - 1] == "flag") {
  312. mine++;
  313. fan(y - 1, x - 1);
  314. } else {
  315. fan(y - 1, x - 1);
  316. }
  317. } else if (KEY_DOWN('P')) { // 自爆
  318. win = -2;
  319. }
  320. // 判断输赢 - 没有空地(如果有检查空地是否mine)且所有flag都在mine上
  321. if (w()) {
  322. win = 1;
  323. }
  324. clearbuf(); // 清屏
  325. }
  326. say(0); // 输出内部
  327. cout << endl;
  328. if (win == -1 || win == -2) { // 判断输赢(输出)
  329. color("lred");
  330. if (win == -1) {
  331. cout << "你无了" << endl;
  332. } else {
  333. cout << "你自爆了" << endl;
  334. }
  335. color("dwhite");
  336. } else {
  337. color("lgreen");
  338. cout << "你排掉了所有的雷" << endl;
  339. color("dwhite");
  340. }
  341. system("pause");
  342. return 0;
  343. }

注释

框架

1-5行 预处理器和namespace std; 第三行定义判断键盘是否按下的函数(可以直接背)

7-15行 定义需要的变量

17-66行 定义map

68-70行 定义清屏函数

72-74行 定义颜色函数

76-103行 定义输出函数

105-116行 定义判断输赢函数

140-199行 定义翻开区域的函数(重点)

主程序:

203-217行 随机埋雷(Error:不知道为什么埋的雷数也是随机的)

220-304行 标区域周围雷的个数-标数字

while(正在游戏){

308行 输出当前状态

309-316行 用wasd控制指针移动

317-321行 f键插旗

322-330行 空格翻方块

331-333行 p键自爆

336-338行 判断输赢

340行 清屏(不断刷新)

}

342-343行 输出内部情况

344-356行 输赢的输出(也可以算做一个结束界面)

357-360行 请按任意键继续...

重点-定义翻开区域的函数

思路如下:
首先假设是这个图案且点到了中心[3, 3](m是雷)
0 0 0 1 m
1 1 0 1 1
m 1 0 0 0
1 1 1 1 1
0 0 1 m 1

检测out里是否有点到区域的坐标,如果是,直接return;

显示翻开的区域,把翻开区域坐标添加到字符串out用来防重复

框下周围八个方块(角边特殊处理):

1 0 1
1 0 0
1 1 1

依次重新翻开,如果翻开的区域是0:框下周围八个方块...(就是递归)

游戏截图

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/90921
推荐阅读
相关标签
  

闽ICP备14008679号