初学者学习了一些c语言基础用法以后需要尝试实战练习,扫雷小游戏就十分适合。因为他能用到很多以前学习的知识,用来巩固再好不过了
废话不多说 开干
我们首先要有一个清晰的思路
和其他游戏一样我们打开游戏第一眼一定会看到菜单。那么我们就先规划一下游戏流程
int main() { int input = 0; do { menu(); scanf("%d", &input); switch (input) //判断是否开始游戏 { case 1: printf("play game\n"); game(); break; case 0: printf("exit game\n"); break; default: printf("input error\n"); } } while (input); return 0; }
由以上代码可见 我们使用do while循环可以保证无论是否进行游戏 我们都会打印一次菜单。
然后我们用input来接收我们输入的值.如果我们输入1 则开始游戏,进入游戏函数。如果我们输入0则结束游戏。如果我们输入了奇怪的数字则提示我们输入错误,并重新输入。
那么我们来定义一下菜单函数吧
由于不需要返回值 菜单给了void类型 其他函数很多也是同理
void menu() { printf("****************************\n"); printf("****** 1.play ******\n"); printf("****** 2.exit ******\n"); printf("****************************\n"); }
测试一下
看来没什么问题
下一步 该进入游戏 我们输入1以后将进入 game() 函数,也就是我们的游戏本体
那么既然我们是扫雷游戏 那么我们就需要一个棋盘。那么我们设置一个9*9大小的棋盘。不过因为这个棋盘会在很多函数中都会用得到,那么定义一个变量的话,假设我们想改变棋盘的大小,那所有函数中的变量都要单独修改,十分不便。所以这里我们建议用宏定义棋盘的宽高。
#define ROW 9 //棋盘长为9 #define COL 9 //棋盘宽为9
在 game 函数中 我们每次开始游戏都要初始化一个新的棋盘来进行游戏,所以我们先对棋盘进行一次初始化。
不过在此之前 我们要知道 我们怎么用这个棋盘 实现这个游戏。
我们知道 扫雷的玩法其实就是点开一个格子 格子会告诉你周围有几个地雷。
那我们就要计算挖开的格子周围8个格子的地雷数量
比如点开2的位置 就会统计周围有的 雷的数目,然后打印
但是 如果点击这里呢?
周围格子是数组之外的数,统计地雷数量的时候回导致数组越界。那么该怎么解决呢?其实很简单
只要我们初始化数组的时候 棋盘设定的比实际显示的大一圈。
最外的一圈不打印 仅用来统计时填充无地雷的格子,即可解决问题。
那么也就是说我们初始化棋盘的时候 长宽要加 2 在这里 我们定义
#define ROW 9 #define COL 9 #define ROWS ROW + 2 //新增 #define COLS COL + 2 //新增
既然棋盘大小确定了 那么该开始初始化了
因为扫雷游戏中 我们是看不到雷的 所以我们要制作两个棋盘
一个用来放置地雷,一个用来给玩家看。
这里我们用 mine_borad 和 show_board 来放置数组
并设定一个函数 int_board 来初始化棋盘
void int_board(char arr[ROWS][COLS],char ret) { for (int a = 0; a < ROWS; a++) { for (int b = 0; b < COLS; b++) { arr[a][b] = ret; } } }
我们将数组名 和 想要初始化成的字符 放进函数,即可完成两个数组的初始化
int_board(mine, '0'); int_board(show, '*');
这里,我们用 '0' 来代表没有雷 。 但我们能看到的棋盘只能看到 *
为了便于调试,我们先打印两个棋盘便于观察。
不过要注意的是,我们要看到的是9*9的棋盘 而不是为了便于统计而初始化的11*11
所以打印的时候我们的下标从1开始 9结束
display_board(mine); display_board(show);
当然 我们要创建一个打印棋盘的函数
void display_board(char arr[ROW][COL]) { for (int a = 1; a <= ROW; a++) { for (int b = 1; b <= COL; b++) { printf("%c ",arr[a][b]); } printf("\n"); } }
在这里 我们输入数组名即可打印棋盘
测试一下
看起来没什么问题.....但是 怎么感觉坐标好难找。不如给他加上坐标吧
我们来修改打印棋盘的函数 display_board
void display_board(char arr[ROWS][COLS]) { for (int b = 0; b <= COL; b++) // { // 新 printf("%d ", b); // 增 } //先从0打印纵坐标 // 的 printf("\n"); // 代 for (int a = 1; a <= ROW; a++) { printf("%d ",a); // 码 //在每行前面打印行数 // for (int b = 1; b <= COL; b++) { printf("%c ",arr[a][b]); } printf("\n"); } }
测试一下
这下就舒服多了,一眼就能找到坐标
那么我们该放雷了 假设我们棋盘上有15个雷。
#define MINE 15 srand((unsigned int)time(NULL)); void set_mine(char arr[ROWS][COLS]) { int i = 0; while (MINE - i) { int a = rand() % ROW + 1; int b = rand() % COL + 1; if(arr[a][b] == '0') { arr[a][b] = '1'; i++; } } } //set_mine(mine);
如上 我们定义一个set_mine 函数,我们输入需要放雷的数组名和用来表示雷的字符即可随机生成指定数量的地雷。这里用字符 '0' '1' 来表示有没有雷 是为了和数字1和0更容易区分,不弄混
打印棋盘看看
很成功,已经放置好地雷了
使用库函数的时候 记得加头文件
rand 的头文件为 #include <stdlib.h>
time 的头文件为 #include <time.h>
雷既然放置好了 那么该开始排雷了
我们需要这样一个函数 每当我们翻开一个坐标,都会显示周围有几颗雷,并且打印棋盘让我们看见,所以我们需要用循环来完成。
大致流程:
有了思路 就可以去实现了
char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; int_board(mine, '0'); //初始化用来放置地雷的棋盘 int_board(show, '*'); //初始化用来显示的棋盘 set_mine(mine); //放置地雷 display_board(mine); //打印放雷的棋盘 仅用来便于测试 后续会删除 printf("\n"); display_board(show); //打印用来显示的棋盘 find_mine(mine, show); //开始扫雷
char get_mine(char mine[ROWS][COLS],int x,int y) { return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 7 * '0'; // - 8 * '0' + '0' } //该函数用来返回所选地区周围雷的数量 void find_mine(char mine[ROWS][COLS],char show[ROWS][COLS]) { int count = 0; int x = 0; int y = 0; while (ROW * COL - MINE - count) { scanf("%d %d", &x, &y); if (x > ROW || x<1 || y>COL || y < 1) { printf("坐标非法\n"); continue; } if (show[x][y] != '*') { printf("该位置已经被排查过了\n"); continue; } else if (mine[x][y] == '0') { show[x][y] = get_mine(mine, x, y); count++; if (ROW * COL == MINE + count) printf("恭喜扫雷成功\n"); } else if (mine[x][y] == '1') { display_board(mine); printf("game over\n"); break; } display_board(mine); printf("\n"); display_board(show); } }
这里因为 ASCII 码值中 '1' - '0' = 1 所以用8个元素的ASCII码减去 8个 '0'可以得到周围字符‘1’的个数 。
但得到的是个字符,依旧是个ASCII码值,所以要想让其变回数字需要再加上个'0'。
游戏已经可以运行了 但我不做演示了
我们删除用来测试的 打印放雷棋盘的函数。整理一下代码
完成了
test.c
#include "game.h"
int main()
{
int input = 0;
do {
menu();
scanf("%d", &input); //判断是否开始游戏
switch (input)
{
case 1:
printf("play game\n");
game();
break;
case 0:
printf("exit game\n");
break;
default:
printf("input error\n");
}
} while (input);
return 0;
}
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 15
void menu();
void game();
void int_board(char arr[ROWS][COLS], char ret);
void display_board(char arr[ROWS][COLS]);
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS]);
game.c
#include "game.h"
void menu()
{
printf("****************************\n");
printf("****** 1.play ******\n");
printf("****** 2.exit ******\n");
printf("****************************\n");
}
void int_board(char arr[ROWS][COLS],char ret)
{
for (int a = 0; a < ROWS; a++)
{
for (int b = 0; b < COLS; b++)
{
arr[a][b] = ret;
}
}
}
void display_board(char arr[ROWS][COLS])
{
for (int b = 0; b <= COL; b++)
{
printf("%d ", b);
} //先从0打印纵坐标
printf("\n");
for (int a = 1; a <= ROW; a++)
{
printf("%d ",a);
//在每行前面打印行数
for (int b = 1; b <= COL; b++)
{
printf("%c ",arr[a][b]);
}
printf("\n");
}
}
void set_mine(char arr[ROWS][COLS])
{
int i = 0;
while (MINE - i)
{
int a = rand() % ROW + 1;
int b = rand() % COL + 1;
if(arr[a][b] == '0')
{
arr[a][b] = '1';
i++;
}
}
}
char get_mine(char mine[ROWS][COLS],int x,int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] +
mine[x - 1][y + 1] + mine[x][y - 1] +
mine[x][y + 1] + mine[x + 1][y - 1] +
mine[x + 1][y] + mine[x + 1][y + 1] -
7 * '0'; // - 8 * '0' + '0'
}
void find_mine(char mine[ROWS][COLS],char show[ROWS][COLS])
{
int count = 0;
int x = 0;
int y = 0;
while (ROW * COL - MINE - count)
{
scanf("%d %d", &x, &y);
if (x > ROW || x<1 || y>COL || y < 1)
{
printf("坐标非法\n");
continue;
}
if (show[x][y] != '*')
{
printf("该位置已经被排查过了\n");
continue;
}
else if (mine[x][y] == '0')
{
show[x][y] = get_mine(mine, x, y);
count++;
if (ROW * COL == MINE + count)
printf("恭喜扫雷成功\n");
}
else if (mine[x][y] == '1')
{
display_board(mine);
printf("game over\n");
break;
}
//display_board(mine);
printf("\n");
display_board(show);
}
}
void game()
{
srand((unsigned int)time(NULL));
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
int_board(mine, '0');
int_board(show, '*');
set_mine(mine);
//display_board(mine);
printf("\n");
display_board(show);
find_mine(mine, show);
}