赞
踩
要想实现扫雷的游戏我们要知道扫雷的规则是什么:
在一个99(初级),1616(中级),16*30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个)。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。
游戏主区域由很多个方格组成。使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷。
我们这以9*9的矩阵为例,要想实现这个游戏我选择如果写在一个文件里面会显得十分混乱,所以我们分为三个文件来完成:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include"game.h" #include<stdlib.h> #include<time.h> void game() { char showboard[ROWS][COLS] = { 0 }; char botboard[ROWS][COLS] = { 0 }; initboard(showboard, COLS, ROWS, '*'); initboard(botboard, COLS, ROWS, '0'); setMine(botboard, COL, ROW); char botboard2[ROWS][COLS]; copy(botboard2, botboard, COL, ROW); //displayboard(botboard, COL, ROW); //这个显示随机地雷埋在哪个位置,调试的是候可以使用 int x, y; int n = 0; while (1) { displayboard(showboard, COL, ROW); printf("请输入坐标值:"); scanf("%d%d", &x, &y); if (x > 0 && x <=COL && y > 0 && y <= COL) { n++; int ret = findMine(botboard, x, y); if (ret == 10) { showboard[x][y] = '#'; printf("你被炸死了!!\n"); break; } if (n == COL * ROW - 10) { printf("你获得了胜利\n"); break; } if (ret == 0) { fun(botboard2, showboard, botboard, x, y); } else { showboard[x][y] = ret + '0'; continue; } } printf("坐标错误!\n"); } displayboard(showboard, COL, ROW); //displayboard(botboard, COL, ROW); } void manu() //打印菜单 { printf("***************************\n"); printf("***************************\n"); printf("*******1 . play************\n"); printf("*******2 . excite**********\n"); printf("***************************\n"); printf("***************************\n"); } int main() { int n; srand((unsigned)time(NULL)); do { manu(); printf("玩家请选择:"); scanf("%d", &n); switch (n) { case 1: game(); break; case 2: n = 0; break; default: n = 0; break; } } while (n); }
在game()函数里设置了两个二维字符数组showboard()用来打印给玩家看到显示数组 和 botboard()用来储存雷的位置的数组,实际上我们下面函数在进行运算的时候使用的是botboard()来获得棋盘的信息。这里把数组设置成1111的数组但是我们只显示其中99的部分,原因下面分析findmine函数时会解析。
#pragma once #define COL 9 #define ROW 9 #define COLS 11 #define ROWS 11 #include<stdio.h> #include<stdlib.h> #include<time.h> //初始化棋盘 void initboard(char board[ROWS][COLS], int col, int row, char set); //展示棋盘 void displayboard(char board[ROWS][COLS], int col, int row); //设置地雷 void setMine(char board[ROWS][COLS], int col, int row); //排地雷 int findMine(char board[ROWS][COLS], int col, int row); //快速排除周围没有一个地雷的点 void fun(char board2[ROWS][COLS], char board1[ROWS][COLS], char board[ROWS][COLS], int x, int y); void copy(char board[ROWS][COLS], char board1[ROWS][COLS], int col, int row);
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include"game.h" void initboard(char board[ROWS][COLS], int col, int row, char set) { int i = 0, j = 0; for (i = 0; i < col; i++) { for (j = 0; j < row; j++) { board[j][i] = set; } } } void displayboard(char board[ROWS][COLS], int col, int row) { int i = 0, j = 0; for (i = 0; i <=col; i++) { printf("%d ", i); } printf("\n"); for (i = 1; i <=col; i++) { printf("%d ", i); for (j = 1; j <= row; j++) { printf("%c ", board[j][i]); } printf("\n"); } } void setMine(char board[ROWS][COLS], int col, int row) { int x, y; int n = 1; while (n<=10) { x = rand() % (col - 1) + 1; y = rand() % (row - 1) + 1; if (board[x][y] == '0') { board[x][y] = '1'; n++; } } } int findMine(char board[ROWS][COLS], int x, int y) { while (1) { if (board[x][y] == '1') { return 10; } else { return board[x - 1][y - 1] + board[x][y - 1] + board[x + 1][y - 1] + board[x - 1][y] + board[x + 1][y] + board[x - 1][y + 1] + board[x][y + 1] + board[x + 1][y + 1] - 8 * board[x][y]; } } } void fun(char board2[ROWS][COLS],char board1[ROWS][COLS],char board[ROWS][COLS], int x, int y) //fun(botboard, showboard, botboard, x, y); { int ret = findMine(board, x, y); if (ret == 0 && x > 0 && y > 0 && board2[x][y] != ' ') { board2[x][y] = ' '; fun(board2, board1, board, x - 1, y); fun(board2, board1, board, x + 1, y); fun(board2, board1, board, x, y + 1); fun(board2, board1,board, x, y - 1); } board1[x][y] = ret + '0'; } void copy(char board[ROWS][COLS], char board1[ROWS][COLS],int col,int row) { int i = 0, j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { board[i][j] = board1[i][j]; } } }
game.c函数的解析:
initboard 和 displayboard
两个函数 一个是用来初始化字符数组 一个是将字符数组打印在屏幕上,玩家输入的是坐标,而我们要输出的是该坐标周围有几个雷。用来实际计算的数组botboard我们将所有元素设为’0’,地雷设为0这样我们在后面计算地雷数时会方便不少,showboard全部设为’*’。
setMine
用来设置地雷数,我们这里是藏十个地雷,调用函数rand()生成随机数来随机取横纵坐标,随机坐标的值赋值为‘1’,但只在9*9的范围里赋值。
findMine
这里是一个有返回值的函数,这个函数的目的是先进行判断如果输入的那点值正好为’1’就返回一个特定的值,如果不是就返回周围’1’的个数也就是炸弹数。由于字符在内存中是以ASCII码的形式存储所以炸弹数等于周围八个坐标对应的值减去8*‘0’。这里还可以解释为什么要将数组设成1111的大小,如果输入的坐标在最后一行/列或第一行/列那么他周围只有3或5个坐标,如果分情况讨论会过于麻烦,不如设成1111多出两行两列,最边缘的点周围也有八个点,而且由于setmine只在9*9的范围里埋地雷顾多出来的行和列不影响结果。
递归函数fun
程序到上面其实就可以正常运行了,但在玩的时候你会发现如果你输入的点得到0即周围八个点没有地雷,那你还要再把周围八个点的坐标一个一个输入游戏才能继续,如果输出是零能自动展开周围的点该多好呀!
void fun(char board2[ROWS][COLS],char board1[ROWS][COLS],char board[ROWS][COLS], int x, int y) { int ret = findMine(board, x, y); if (ret == 0 && x > 0 && y > 0 && board2[x][y] != ' ') { board2[x][y] = ' '; fun(board2, board1, board, x - 1, y); fun(board2, board1, board, x + 1, y); fun(board2, board1, board, x, y + 1); fun(board2, board1,board, x, y - 1); } board1[x][y] = ret + '0'; } void copy(char board[ROWS][COLS], char board1[ROWS][COLS],int col,int row) { int i = 0, j = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { board[i][j] = board1[i][j]; } } }
fun(botboard2, showboard, botboard, x, y);
char botboard2[ROWS][COLS];
copy(botboard2, botboard, COL, ROW);
这里我们采用递归的方法这边我传进去了三个数组botboard2,botboard,showboard,由后两段代码我们可以知道其实botboard2和botboard是两个完全一样的数组但是地址不一样。
每一次递归我们先通过findmine函数的返回值得到ret,然后我们判断ret是否为0(即周围八个点是否都没有地雷) x > 0 && y > 0是判断是否到达边界防止递归越过9*9边界,board2[x][y] != ’ '判断字符串是否为我们已经检查过的点不在进入递归以防死循环(比如由于我们是向一个点的四个方向递归,第一个点右边的点进入下一次递归,第二层递归四个方向 向左边 的递归实际上又回到了第一层递归,但是board2[x][y] != ’ ‘的判断条件可以免除这种情况发生)。进入判断的第一件事就是将我们检查的点设成’ ‘,然后向该点的左右上下四个方向递归。
botboard2 作用就是将递归的点设成’ '防止死循环
botboard 作用是由于botboard2递归过的点值被修改会导致ret=findmine无法判断周围八个点是否为炸弹,故需要一个botboard2一模一样的数组来专门输入findmine函数
这就是递归函数的逻辑,这个函数是我自己想出来的,可能不是非常好但完成了目标,欢迎大家提出改进意见和自己的想法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。