分析需求
- 游戏主体构建
- 菜单实现进入退出游戏
- 地雷及提示生成
- 棋盘打印
- 开始扫雷
- 判断输赢
? ? ? ? 在进行分析时,我们发现,既要有一个数组用于存放地雷和周围雷数的提示(图一),也要有一个数组供玩家选择位置(图二)。
图一
?
图二
?
代码实现
1.游戏主体构建
????????创建头文件game.h用于声明函数和引用系统头文件,创建源文件mian.cpp用于存放游戏主体程序(game()函数),创建源文件game.cpp用于定义游戏有关函数。
?
2.菜单实现进入退出游戏
#include "game.h"
//菜单
void menu()
{
printf("************************\n");
printf("***** 1.开始游戏 *****\n");
printf("***** 0.退出游戏 *****\n");
printf("************************\n");
}
#include "game.h"
void game();
int main()
{
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
?
3.地雷及提示生成
? ? ? ? 首先,我们要创建一个数组用于存放地雷,这里使用到一个9*9的二维数组(也可以使用11*11的二维数组,后面在分析输赢的时候会方便,具体原因后文会讲到)。?再将地雷数组初始化。
//初始化数组
void initialization(char arr[ROW][COLUMN], char c)
{
int i = 0;
int j = 0;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
arr[i][j] = c;
}
}
}
#include "game.h"
//注意下面两行代码写在game.h中
#define ROW 9//define定义行和列方便后续更改
#define COLUMN 9
char position[ROW][COLUMN];
char mine[ROW][COLUMN];
void game();
int main()
{
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
void game()
{
initialization(mine, ' ');//地雷数组初始化
}
????????接下来就是生成地雷,这里要采用生成随机数的方法,实现每一轮地雷位置的随机,生成随机数的方法之前有写过博客介绍,可以通过链接跳转,这里不过多的解释。
//判断放置位置是否正确,正确返回1,错误返回0
//由于此代码专门为Minegeneration服务,所以使用static修饰,不用在game.h中进行声明
static int isture(char arr[ROW][COLUMN], int x, int y)
{
if (arr[x][y] == ' ')
{
return 1;
}
else
{
return 0;
}
}
//生成地雷
void Minegeneration(char arr[ROW][COLUMN])
{
int i = 0;
for (i = 0; i < 10; i++)//生成十个地雷
{
int x = rand() % 9;//生成0-8之间的数
int y = rand() % 9;
if (isture(arr, x, y) == 1)//生成成功
{
arr[x][y] = '@';//@表示雷
}
else//不成功则i--,重新生成
{
i--;
}
}
}
#include "game.h"
char position[ROW][COLUMN];
char mine[ROW][COLUMN];
void game();
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//使用时间戳作为种子,实现不断变化。
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
void game()
{
initialization(mine, ' ');//地雷数组初始化
Minegeneration(mine);//地雷生成
}
? ? ? ? 地雷生成完毕后,还需要写入周围有几个地雷的提示。注意由于我们生成的是9*9的数组,所以在判断周围地雷数量的时候要区分多种情况,而如果按前文所述,创建的是11*11的数组,则可以直接统一检查周围一圈八格(生成的数组是11*11,但是地雷的范围是中间的9*9,不包括外面一圈,检查时也是只检查中间9*9的位置,所以不会出现越位的情况)。
//判断周围地雷的数量
static char ishave(char arr[ROW][COLUMN], int i, int j)
{
if (i == 0)//位于第一行时,不用看上面一行
{
if (j == 0)//位于第一列时,不用看左边一列,第一行第一列即左上角
{
char num = '0';
if (arr[0][1] == '@')
{
num++;
}
if (arr[1][0] == '@')
{
num++;
}
if (arr[1][1] == '@')
{
num++;
}
if (num == '0')//如果周围没有炸弹,返回空格,有则返回数量
{
return ' ';
}
else
{
return num;
}
}
else if (j == 8)//位于最后一列时,不用看右边一列,第一行最后一列即右上角
{
char num = '0';
if (arr[0][7] == '@')
{
num++;
}
if (arr[1][7] == '@')
{
num++;
}
if (arr[1][8] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else//其他情况
{
char num = '0';
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (arr[i + 1][j + 1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
}
else if (i == 8) // 位于最后一行时,不用看下面一行
{
if (j == 0)//位于第一列时,不用看左边一列,最后一行第一列即左下角
{
char num = '0';
if (arr[7][0] == '@')
{
num++;
}
if (arr[7][1] == '@')
{
num++;
}
if (arr[8][1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else if (j == 8)//位于最后一列时,不用看右边一列,最后一行最后一列,即右下角
{
char num = '0';
if (arr[7][8] == '@')
{
num++;
}
if (arr[7][7] == '@')
{
num++;
}
if (arr[7][8] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else
{
char num = '0';
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i - 1][j - 1] == '@')
{
num++;
}
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j + 1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
}
else//位于其他行
{
if (j == 0)//位于第一列时,不用看左边一列
{
char num = '0';
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j + 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else if (j == 8)//位于最后一列时,不用看右边一列
{
char num = '0';
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j - 1] == '@')
{
num++;
}
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else//不靠边的情况
{
char num = '0';
if (arr[i - 1][j - 1] == '@')
{
num++;
}
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j + 1] == '@')
{
num++;
}
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (arr[i + 1][j + 1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
}
}
//若周围有地雷,生成数字提示
void Generateprompt(char arr[ROW][COLUMN])
{
int i = 0;
int j = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COLUMN; j++)
{
if (arr[i][j] != '@')//没有炸弹的地方生成提示,有炸弹就不处理
{
arr[i][j] = ishave(arr, i, j);
}
}
}
}
#include "game.h"
char position[ROW][COLUMN];
char mine[ROW][COLUMN];
void game();
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
void game()
{
initialization(mine, ' ');//地雷数组初始化
Minegeneration(mine);//地雷生成
Generateprompt(mine);//生成提示
}
?
4.棋盘打印
? ? ? ? 在进行分析时,我们讲到要创建一个二维数组用于玩家进行位置选择,同样的,我们选择使用一个9*9的二维数组(如果前面放置地雷和提示使用的是11*11的数组,这里也要创建11*11,处理方法和地雷数组相同,打印时也只打印中间9*9的位置)。
//初始化数组
void initialization(char arr[ROW][COLUMN], char c)
{
int i = 0;
int j = 0;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
arr[i][j] = c;
}
}
}
#include "game.h"
char position[ROW][COLUMN];
char mine[ROW][COLUMN];
void game();
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
void game()
{
initialization(mine, ' ');//地雷数组初始化
initialization(position, '*');//位置数组初始化
Minegeneration(mine);//地雷生成
Generateprompt(mine);//生成提示
}
? ? ? ? 初始化地雷数组和位置数组的函数相同,传入什么字符,数组的每一位就是什么字符,初始化完成后,就要打印棋盘,为了玩家选择位置时方便,我们可以在打印棋盘时加一个刻度。
//打印棋盘
void Chessboarddisplay(char arr[ROW][COLUMN])
{
int i = 1;
int j = 0;
int k = 0;
printf("0 1 2 3 4 5 6 7 8 9\n");
for (j = 0; j < ROW; j++)
{
printf("%d", i);
++i;
for (k = 0; k < COLUMN; k++)
{
printf(" %c", arr[j][k]);
}
printf("\n");
}
}
?
5.开始扫雷?及 6.判断输赢
? ? ? ? 扫雷的过程中,我们需要使用到两个数组,数组position用于玩家选择位置,数组mine用于判断玩家选择的位置是否存在地雷以及选择完成后现实周围有多少个地雷。
void Selectlocation(char mine[ROW][COLUMN], char position[ROW][COLUMN])
{
int a = 0;//行
int b = 0;//列
do//第一个do while循环用于重复排雷过程
{
do//第二个用于实现输入坐标错误后重新输入
{
printf("\n请输入位置坐标(先行后列):(例:1 1)\n");
scanf("%d", &a);
scanf("%d", &b);
if (a < 1 || a>9)//越位
{
printf("输入错误,请重新输入!\n");
continue;
}
if (b < 1 || b>9)//越位
{
printf("输入错误,请重新输入!\n");
continue;
}
if (position[a - 1][b - 1] != '*')//重复选择
{
printf("该位置已被选择!\n");
continue;
}
break;
} while (true);
printf("\n");
if (mine[a - 1][b - 1] == '@')//触发地雷
{
position[a - 1][b - 1] = mine[a - 1][b - 1];
Chessboarddisplay(position);//打印棋盘显示原因
printf("触发炸弹,游戏结束!\n\n");
break;
}
else//未触发地雷
{
position[a - 1][b - 1] = mine[a - 1][b - 1];
Chessboarddisplay(position);
//通过未选择位置'*'的数量,判断是否获胜
int i = 0;
int j = 0;
int num = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COLUMN; j++)
{
if (position[i][j] == '*')
{
num++;
if (num > 10)//总共10个地雷
{
break;
}
}
}
}
if (num == 10)//仅剩10个地雷
{
printf("游戏结束,玩家获胜!\n\n");
break;
}
}
} while (true);
}
总体代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COLUMN 9
void menu();//菜单
void initialization(char arr[ROW][COLUMN], char c);//初始化数组
void Minegeneration(char arr[ROW][COLUMN]);//生成地雷
void Generateprompt(char arr[ROW][COLUMN]);//若周围有地雷,生成数字提示
void Chessboarddisplay(char arr[ROW][COLUMN]);//打印棋盘
void Selectlocation(char mine[ROW][COLUMN], char position[ROW][COLUMN]);//开始排雷
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//菜单
void menu()
{
printf("************************\n");
printf("***** 1.开始游戏 *****\n");
printf("***** 0.退出游戏 *****\n");
printf("************************\n");
}
//初始化数组
void initialization(char arr[ROW][COLUMN], char c)
{
int i = 0;
int j = 0;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
arr[i][j] = c;
}
}
}
//判断放置位置是否正确,正确返回1,错误返回0
static int isture(char arr[ROW][COLUMN], int x, int y)
{
if (arr[x][y] == ' ')
{
return 1;
}
else
{
return 0;
}
}
//生成地雷
void Minegeneration(char arr[ROW][COLUMN])
{
int i = 0;
for (i = 0; i < 10; i++)//生成十个地雷
{
int x = rand() % 9;//生成0-8之间的数
int y = rand() % 9;
if (isture(arr, x, y) == 1)//生成成功
{
arr[x][y] = '@';//@表示雷
}
else//不成功则i--,重新生成
{
i--;
}
}
}
//判断周围地雷的数量
static char ishave(char arr[ROW][COLUMN], int i, int j)
{
if (i == 0)//位于第一行时,不用看上面一行
{
if (j == 0)//位于第一列时,不用看左边一列,第一行第一列即左上角
{
char num = '0';
if (arr[0][1] == '@')
{
num++;
}
if (arr[1][0] == '@')
{
num++;
}
if (arr[1][1] == '@')
{
num++;
}
if (num == '0')//如果周围没有炸弹,返回空格,有则返回数量
{
return ' ';
}
else
{
return num;
}
}
else if (j == 8)//位于最后一列时,不用看右边一列,第一行最后一列即右上角
{
char num = '0';
if (arr[0][7] == '@')
{
num++;
}
if (arr[1][7] == '@')
{
num++;
}
if (arr[1][8] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else//其他情况
{
char num = '0';
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (arr[i + 1][j + 1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
}
else if (i == 8) // 位于最后一行时,不用看下面一行
{
if (j == 0)//位于第一列时,不用看左边一列,最后一行第一列即左下角
{
char num = '0';
if (arr[7][0] == '@')
{
num++;
}
if (arr[7][1] == '@')
{
num++;
}
if (arr[8][1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else if (j == 8)//位于最后一列时,不用看右边一列,最后一行最后一列,即右下角
{
char num = '0';
if (arr[7][8] == '@')
{
num++;
}
if (arr[7][7] == '@')
{
num++;
}
if (arr[7][8] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else
{
char num = '0';
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i - 1][j - 1] == '@')
{
num++;
}
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j + 1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
}
else//位于其他行
{
if (j == 0)//位于第一列时,不用看左边一列
{
char num = '0';
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j + 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else if (j == 8)//位于最后一列时,不用看右边一列
{
char num = '0';
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j - 1] == '@')
{
num++;
}
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
else//不靠边的情况
{
char num = '0';
if (arr[i - 1][j - 1] == '@')
{
num++;
}
if (arr[i - 1][j] == '@')
{
num++;
}
if (arr[i - 1][j + 1] == '@')
{
num++;
}
if (arr[i][j - 1] == '@')
{
num++;
}
if (arr[i][j + 1] == '@')
{
num++;
}
if (arr[i + 1][j - 1] == '@')
{
num++;
}
if (arr[i + 1][j] == '@')
{
num++;
}
if (arr[i + 1][j + 1] == '@')
{
num++;
}
if (num == '0')
{
return ' ';
}
else
{
return num;
}
}
}
}
//若周围有地雷,生成数字提示
void Generateprompt(char arr[ROW][COLUMN])
{
int i = 0;
int j = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COLUMN; j++)
{
if (arr[i][j] != '@')//没有炸弹的地方生成提示,有炸弹就不处理
{
arr[i][j] = ishave(arr, i, j);
}
}
}
}
//打印棋盘
void Chessboarddisplay(char arr[ROW][COLUMN])
{
int i = 1;
int j = 0;
int k = 0;
printf("0 1 2 3 4 5 6 7 8 9\n");
for (j = 0; j < ROW; j++)
{
printf("%d", i);
++i;
for (k = 0; k < COLUMN; k++)
{
printf(" %c", arr[j][k]);
}
printf("\n");
}
}
//开始排雷
void Selectlocation(char mine[ROW][COLUMN], char position[ROW][COLUMN])
{
int a = 0;
int b = 0;
do
{
do
{
printf("\n请输入位置坐标(先行后列):(例:1 1)\n");
scanf("%d", &a);
scanf("%d", &b);
if (a < 1 || a>9)
{
printf("输入错误,请重新输入!\n");
continue;
}
if (b < 1 || b>9)
{
printf("输入错误,请重新输入!\n");
continue;
}
if (position[a - 1][b - 1] != '*')
{
printf("该位置已被选择!\n");
continue;
}
break;
} while (true);
printf("\n");
if (mine[a - 1][b - 1] == '@')//触发地雷
{
position[a - 1][b - 1] = mine[a - 1][b - 1];
Chessboarddisplay(position);
printf("触发炸弹,游戏结束!\n\n");
break;
}
else//未触发地雷
{
position[a - 1][b - 1] = mine[a - 1][b - 1];
Chessboarddisplay(position);
int i = 0;
int j = 0;
int num = 0;
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COLUMN; j++)
{
if (position[i][j] == '*')
{
num++;
if (num > 10)
{
break;
}
}
}
}
if (num == 10)
{
printf("游戏结束,玩家获胜!\n\n");
break;
}
}
} while (true);
}
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
char position[ROW][COLUMN];
char mine[ROW][COLUMN];
void game();
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
void game()
{
initialization(mine, ' ');//地雷数组初始化
initialization(position, '*');//位置数组初始化
Minegeneration(mine);//地雷生成
Generateprompt(mine);//生成提示
printf("地雷生成成功! 游戏开始!\n\n");
printf("棋盘如下:\n");
Chessboarddisplay(position);//打印棋盘
Selectlocation(mine, position);//开始排雷
}
总结
? ? ? ? 和三子棋一样,扫雷也是一个简单的C语言小游戏,在编程过程中更强调逻辑清晰,感兴趣的小伙伴还可以在其中加入递归的写法,实现选位时同时排出最外围一圈无雷区域。
?
|