带你快速实现【C语言】之扫雷(未优化版)
目录
一.游戏底层逻辑
要写出一款游戏,首先得要知道游戏的底层原理。
在扫雷这个游戏中,首先我们得要有一个雷盘,棋盘中要有N个雷,输入坐标后如果踩到雷,游戏结束。反之,则显示该坐标周围的雷数。
二.菜单
设计任何游戏都得有一个菜单方便用户选择,和三子棋一样,我们使用do while结构,让菜单不论什么情况都能打印一次。
int main()
{
srand((unsigned int)time(NULL)); //rand函数初始化
int p = 0;
do {
menu();
printf("n请选择->");
scanf("%d", &p);
switch (p)
{
case 1:
game(); //选择1进入游戏,转到game函数
break;
case 0:
printf("n再见n"); //退出游戏
break;
default: //选择错误
printf("n选择错误,请重新选择n");
break;
}
} while(p);
}
void menu()
{
printf("|---欢迎来到扫雷---|n");
printf("|------------------|n");
printf("|------1.play------|n");
printf("|------0.exit------|n");
printf("|------------------|n");
}
三. 游戏设计
1.雷盘设置.
这里要注意的是,如果使用1个棋盘,它又要负责设置雷,又要负责打印雷盘,又要负责标记已经扫过的雷,这显然会使得太过繁琐复杂。所以我们用两个雷盘,一个用于设置雷,一个用于展示雷盘。这里我们顺便把他初始化,第一个全部初始化为字符0,第二个全部初始化成字符*
然后,如果雷盘我们选择9*9的矩阵,但这里我们不能设置成9*9的数组,因为我们后续扫雷的会检查周围8个坐标的雷数,如果玩家刚好输入下标为9 9的坐标,去检查雷数的话会导致下标访问越界,所以这里我们把矩阵上下左右各自延展1,也就是设置成11*11的数组,这样就不会存在访问越界的问题了。
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define mine 10
void game()
{
char board[ROWS][COLS] = { 0 }; //设置雷
char show[ROWS][COLS] = { 0 }; //展示
init(board, ROWS, COLS,'0');
init(show, ROWS, COLS,'*');
}
void init(char board[ROWS][COLS], int r, int c, int set)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
board[i][j] = set; //‘0’或者‘*’
}
}
}
2.打印雷盘.
初始化后,我们来检查检查是否完成,设计一个函数print打印,这里不需要把11*11的数组全部打印,因为我们玩家玩的是9*9的雷盘,只需要展示9*9的数组就好。
另外,为了帮助玩家更加直观的输入坐标,我们在第一行,第一列加上坐标。
void print(char board[ROWS][COLS], int r, int c)
{
int i = 0;
int j = 0;
//列标
for (j = 0; j <= c; j++)
printf("%d ", j);
printf("n");
//行标
for (i = 1; i <= r; i++)
{
printf("%d ", i);
for (j = 1; j <= c; j++)
{
printf("%c ", board[i][j]);
}
printf("n");
}
}
3.设置雷.
这里我们设置10颗雷,雷用字符‘1’表示,注意不是数字1。(后面会讲为什么用字符1)
随机生成使用rand函数,rand函数要使用srand初始化。
void set(char board[ROWS][COLS], int r, int c)
{
int count = mine;
while (count) // count为0跳出循环
{
int x = rand() % r + 1; //表示只在下标为1-9的地方生成
int y = rand() % c + 1; //表示只在下标为1-9的地方生成
if (board[x][y] == '0')
{
board[x][y] = '1';
count--; // 每成功生成一次count--
}
}
}
看看效果图
4.找雷.
找雷,玩家输入坐标得满足2个条件:1.该坐标在1-9之间。2.该坐标未被重复扫雷。
满足条件后,如果踩到了雷,游戏结束,否则,游戏继续。
而游戏继续中,我们要把该坐标附近8个坐标的雷数打印出来,所以又需要一个函数mine_num来计算。在这里用字符1的好处就体现出来了,我们先找8个坐标中有几个字符1,
假设找到了8个字符1 ,我们减去8*字符0,就得到了数字8。
所以我们要返回一个数字,就得用算出的字符1减去8*字符0,得到数字N返回给函数。
最后这个游戏不能一直进行,如果一直没踩到雷的话,它结束的条件应该是除了雷的所有格子被扫过了,即win=r*c-mine的时候游戏结束
void findmine(char board[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{
int win = 0;
while (win<r*c-mine)
{
int x = 0;
int y = 0;
printf("n请输入坐标->");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= r && y >= 1 && y <= c)
{
if (show[x][y] == '*')
{
if (board[x][y] == '1')
{
printf("n恭喜你,踩到雷了,游戏失败n");
break;
}
else
{
int num = mine_num(board, x, y);
show[x][y] = num + '0';
win++;
}
}
else
{
printf("n该地已被排查n");
}
if (win == r * c - mine)
{
printf("nWINn");
print(board, r, c);
break;
}
}
else
{
printf("坐标非法,请重新输入n");
}
print(show, r, c);
}
}
int mine_num(char board[ROWS][COLS], int x, int y)
{
return ((board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1] + board[x][y - 1] +
board[x][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1])-8 * '0');
}
四.总代码
1.game.h
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define mine 10
void menu();
void game();
//初始化棋盘
void init(char board[ROWS][COLS], int r,int c,int set);
//打印棋盘
void print(char board[ROW][COL], int r, int c);
//设置雷
void set(char board[ROWS][COLS], int r, int c);
//找雷
void findmine(char board[ROWS][COLS], char[ROWS][COLS], int r, int c);
2.game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu()
{
printf("|---欢迎来到扫雷---|n");
printf("|------------------|n");
printf("|------1.play------|n");
printf("|------0.exit------|n");
printf("|------------------|n");
}
void init(char board[ROWS][COLS], int r, int c,int set)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
board[i][j] = set;
}
}
}
void print(char board[ROWS][COLS], int r, int c)
{
int i = 0;
int j = 0;
//列标
for (j = 0; j <= c; j++)
printf("%d ", j);
printf("n");
//行标
for (i = 1; i <= r; i++)
{
printf("%d ", i);
for (j = 1; j <= c; j++)
{
printf("%c ", board[i][j]);
}
printf("n");
}
}
void set(char board[ROWS][COLS], int r, int c)
{
int count = mine;
while (count)
{
int x = rand() % r + 1;
int y = rand() % c + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int mine_num(char board[ROWS][COLS], int x, int y)
{
return ((board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1] + board[x][y - 1] +
board[x][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1])-8 * '0');
}
void findmine(char board[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{
int win = 0;
while (win<ROW*COL-mine)
{
int x = 0;
int y = 0;
printf("n请输入坐标->");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= r && y >= 1 && y <= c)
{
if (show[x][y] == '*')
{
if (board[x][y] == '1')
{
printf("n恭喜你,踩到雷了,游戏失败n");
break;
}
else
{
int num = mine_num(board, x, y);
show[x][y] = num + '0';
win++;
}
}
else
{
printf("n该地已被排查n");
}
if (win == ROW * COL - mine)
{
printf("nWINn");
print(board, r, c);
break;
}
}
else
{
printf("坐标非法,请重新输入n");
}
print(show, r, c);
}
}
3.test.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void game()
{
char board[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
init(board, ROWS, COLS,'0');
init(show, ROWS, COLS,'*');
//print(board, ROW, COL);
print(show, ROW, COL);
set(board, ROW, COL);
print(board, ROW, COL);
findmine(board, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int p = 0;
do {
menu();
printf("n请选择->");
scanf("%d", &p);
switch (p)
{
case 1:
game();
break;
case 0:
printf("n再见n");
break;
default:
printf("n选择错误,请重新选择n");
break;
}
} while(p);
}