✍前言:
大家在学习C语言的时候是不是有时候会感觉很枯燥,觉得自己学的知识不能整合运用起来,那今天我给大家带来了一个特别适合小白新手的小游戏,这个小游戏也算是整合了一些简单的,分支循环,函数,数组等一些基础的知识,如果你是刚刚接触C语言,那么这个扫雷小游戏教程非常适合你哦 ! ! !
✈首先,在写游戏前,我们首先要了解一下扫雷游戏:
☀☀☀ 扫雷的玩法:在一个9×9(初级)的方块矩阵中随机布置一定量的地雷(初级为10个),再由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束,如果找到了躲避开所有地雷,那么游戏获胜。
注意:扫雷虽然是一个代码量不大的小游戏,但我们任然要保持好的习惯进行模块化分文件编程:
test.c (游戏测试文件)
game.c (代码定义文件)
game.h (函数声明文件)
我们可以看到扫雷的格子区域是一个 9*9 的矩阵,那么,我们也要同样去实现一个
实现的逻辑:
1.我们用键盘输出扫雷的坐标:
a.如果扫到了雷,那么游戏结束
b.如果没扫到雷,且围绕扫雷坐标旁边的8个坐标有雷,那么在此左边显示周边的雷的个数
c.如果没扫到雷,且围绕扫雷坐标旁边的8个坐标都没有雷,那么他将会展开.
怎么实现:
我们将会用两个 11*11 的两个二维数组(分为隐形棋盘和显形棋盘),在隐形棋盘上埋雷,寻找雷。再反馈给玩家看的显形棋盘,同时因为两个棋盘的大小和坐标是完全重叠的,所以通过两个棋盘的联动,就可以完成这个简单的小游戏了。
可能有人会问了,我们不是要做一个9*9的棋盘吗,为什么要去创建两个11*11的棋盘呢?看下图
11*11棋盘中x,y计算周边雷数(红色区域)的时候不用判定边界,黑色区域是我们创建初始化的,但是我们不会去操作黑色区域,蓝色区域是我们操作区域,打印,埋雷,找雷都在蓝色区域找
我现在创建了一个11*11 的棋盘,在后面我们因为要实现输入坐标后计算周边的雷数,如果我们创建一个9*9的棋盘,那么我们就需要去判断一下边界,并且玩家在输入坐标的时候,通常为把第一行第一列的坐标认为1,1。但是我们程序员知道如果创建9*9的棋盘后,第一个坐标是0.0,所以我们要创建大一圈的棋盘。当然了,创建大棋盘我们也需要处理一些具体的细节,※※※比如我们打印棋盘的时候,要注意从下标为1的元素开始打印,到下标为9的时候结束。埋雷找雷的时候都要从1-9中去找。
注意:不能去0和10埋雷找雷,我们把它认定为非法。
实现代码:
我们一般再写小游戏的时候,通常都会使用do while 语句 ,他会提前循环一次再让我们进行判断。
int main()
{
srand((size_t)time(NULL));
int input = 0;
do
{
meun();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误请重新选择\n");
break;
}
} while (input);
return 0;
}
再进行游戏前,我们肯定需要一个菜单栏并给玩家们提供选项,选择1进入游戏,选择0退出游戏并且结束循环,选择其他的任意数字,会重新进行选择
void meun()
{
printf("******************************\n");
printf("************ 扫雷 ************\n");
printf("***** 1.play 0.exit *****\n");
printf("******************************\n");
}
那让我们继续往下走,当我们选择了1的时候就会执行game()函数,如果选择0,就会退出,其他重新循环,看下图
我们可以可以看到,当我们选择1的时候走回走case 1这个通道,去执行game函数,在game函数中我们将写游戏的主体部分
void game()
{
char concealboard[ROWS][COLS] = { 0 };//隐藏的棋盘
char showboard[ROWS][COLS] = { 0 };//展示的棋盘
Initboard(concealboard, ROWS, COLS,'0');//初始化隐藏的棋盘,全部初始化为字符0
Initboard(showboard, ROWS, COLS,'*');//初始化显示的棋盘,全部初始化为字符*
Displayboard(showboard, ROW, COL);//打印给玩家看的棋盘
Setboard(concealboard, ROW, COL);//为隐藏的棋盘中设置雷
Findbomb(concealboard, showboard, ROW, COL);//在隐藏的棋盘中寻找雷,再通过联动显示的棋盘打印出来
}
再写游戏主体前我们先过一遍思路:
1.首先我们需要两个11*11的棋盘
2.初始化两个棋盘
3.打印出给玩家展示的棋盘
4.操作隐藏的棋盘,在隐藏的棋盘随机设置10个雷
5.在隐藏棋盘寻找雷:
a.输入坐标在隐藏的棋盘中找雷,踩到雷,游戏结束b.没踩到雷,那么将计算周边8个坐标的雷的个数,并在显示棋盘中显 示周边雷个数
c.没踩到雷,并且周边也没有雷,那么显示的棋盘将会以输出坐标为中 心展开
1.两个11*11棋盘
char concealboard[ROWS][COLS] = { 0 };//隐藏的棋盘
char showboard[ROWS][COLS] = { 0 };//展示的棋盘
2.初始化函数
void Initboard(char board[ROWS][COLS], int rows, int cols, char ch)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = ch;
}
}
}
3.打印函数
void Displayboard(char board[ROWS][COLS], int row, int col)
{
printf("-------------------\n");
for (int i = 0; i <= row; i++)
{
printf("%d ",i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------------------\n");
}
4.埋雷函数
void Setboard(char board[ROWS][COLS], int row, int col)//布置雷
{
int x = 0;
int y = 0;
int count = BOMB;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
※※※5.找雷的函数
我们可以看到找雷函数中又包括了几个功能函数,我解释一下:
1.如果踩到了雷,执行打印函数,给玩家打印出隐藏雷的棋盘示意玩家
2.如果不是雷执行展开函数(展开函数中同时会执行计算雷个数的函数)
3.判断胜利的函数,因为我们游戏有自动展开功能,所以判断胜利我们只需要计算 * 号的个数就可以,如果是10个 * 号 ,就判断赢
void Findbomb(char concealboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)//找雷 { int x = 0; int y = 0; while (1) { printf("请输入要找的坐标:"); scanf("%d%d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col && showboard[x][y] =='*') { if (concealboard[x][y] == '1')//如果是雷结束游戏 { printf("很遗憾,您被炸死了!\n"); Displayboard(concealboard, row, col); break; } else//如果不是,就计算一下周边雷的个数,和进行展开 { open(concealboard, showboard, x, y); system("cls"); Displayboard(showboard, row, col); if (Win(showboard, row, col))// { break; } } } else { printf("坐标不合法,请重新输入:\n"); } } if (Win(showboard, row, col)) { printf("恭喜你,成功排雷\n"); } } return; } }
下面的是展开函数,首先计算周边雷的个数,如果周边有雷,那么就在显示棋盘显示雷的个数,如果周边没有雷,并且不是空格符,那么就将此此坐标制成空格符号,并向周边递归展开
int get_count(char concealboard[ROWS][COLS],int x,int y)//计算周边8个坐标的雷数 { return (concealboard[x - 1][y - 1] + concealboard[x - 1][y] + concealboard[x - 1][y + 1] + concealboard[x][y - 1] + concealboard[x][y + 1] + concealboard[x + 1][y - 1] + concealboard[x + 1][y] + concealboard[x + 1][y + 1]) - 8 * '0'; } void open(char concealboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y)//排雷展开 { int count = 0; count=get_count(concealboard, x, y); if (count != 0)//不是雷,且周边有雷 { showboard[x][y] = count + '0'; return; } else if(showboard[x][y] != ' ')//不是雷,并且周边没雷 { showboard[x][y] = ' ';//把当前坐标制成空格符号,以防止死递归 for (int i = x-1; i <= x+1; i++) { for (int j =y-1;j<=y+1;j++) { open(concealboard, showboard, i, j);//对周边8个坐标展开 } } } else { return; } }
判断胜利的函数,很多人都会计算没踩雷的次数,当次数是row*col-bomb=71的时候获胜,如果是没有展开函数,那么就是正确的。但是如果实现了展开的功能,就不可以这样写,我自己也实际测试过,这样是无法停止的。
所以我认为可以计算显示棋盘的 * 号的数量 ,当* 号的数量正好是雷的个数,那么就获胜
bool Win(char showboard[ROWS][COLS], int row, int col) { int count = 0; for (int i = 1; i <=row; i++) { for (int j = 1; j <=row; j++) { if (showboard[i][j] == '*') { count++;//计算*号数量 } } } return count == BOMB;//如果*号为10个,说明找到了10个雷,获胜 }
✌✌✌游戏测试截图
上面模块化的功能我们已经实现,下面让我们看一下完整的代码
game.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
#include <Windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOMB 10
void Initboard(char board[ROWS][COLS], int rows, int cols, char ch);//初始化棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);//打印棋盘
void Setboard(char board[ROWS][COLS], int row, int col);//布置雷
void Findbomb(char concealboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col);//找雷
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Initboard(char board[ROWS][COLS], int rows, int cols, char ch)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = ch;
}
}
}
void Displayboard(char board[ROWS][COLS], int row, int col)
{
printf("-------------------\n");
for (int i = 0; i <= row; i++)
{
printf("%d ",i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------------------\n");
}
void Setboard(char board[ROWS][COLS], int row, int col)//布置雷
{
int x = 0;
int y = 0;
int count = BOMB;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int get_count(char concealboard[ROWS][COLS],int x,int y)//计算周边8个坐标的雷数
{
return (concealboard[x - 1][y - 1] + concealboard[x - 1][y] +
concealboard[x - 1][y + 1] + concealboard[x][y - 1] +
concealboard[x][y + 1] + concealboard[x + 1][y - 1] +
concealboard[x + 1][y] + concealboard[x + 1][y + 1]) - 8 * '0';
}
void open(char concealboard[ROWS][COLS], char showboard[ROWS][COLS], int x, int y)//排雷展开
{
int count = 0;
count=get_count(concealboard, x, y);
if (count != 0)//不是雷,且周边有雷
{
showboard[x][y] = count + '0';
return;
}
else if(showboard[x][y] != ' ')//不是雷,并且周边没雷
{
showboard[x][y] = ' ';//把当前坐标制成空格符号,以防止死递归
for (int i = x-1; i <= x+1; i++)
{
for (int j =y-1;j<=y+1;j++)
{
open(concealboard, showboard, i, j);//对周边8个坐标展开
}
}
}
else
{
return;
}
}
bool Win(char showboard[ROWS][COLS], int row, int col)
{
int count = 0;
for (int i = 1; i <=row; i++)
{
for (int j = 1; j <=row; j++)
{
if (showboard[i][j] == '*')
{
count++;//计算*号数量
}
}
}
return count == BOMB;//如果*号为10个,说明找到了10个雷,获胜
}
void Findbomb(char concealboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)//找雷
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入要找的坐标:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col && showboard[x][y] =='*')
{
if (concealboard[x][y] == '1')//如果是雷结束游戏
{
printf("很遗憾,您被炸死了!\n");
Displayboard(concealboard, row, col);
break;
}
else//如果不是,就计算一下周边雷的个数,和进行展开
{
open(concealboard, showboard, x, y);
system("cls");
Displayboard(showboard, row, col);
if (Win(showboard, row, col))//
{
break;
}
}
}
else
{
printf("坐标不合法,请重新输入:\n");
}
}
if (Win(showboard, row, col))
{
printf("恭喜你,成功排雷\n");
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void meun()
{
printf("******************************\n");
printf("************ 扫雷 ************\n");
printf("***** 1.play 0.exit *****\n");
printf("******************************\n");
}
void game()
{
char concealboard[ROWS][COLS] = { 0 };//隐藏的棋盘
char showboard[ROWS][COLS] = { 0 };//展示的棋盘
Initboard(concealboard, ROWS, COLS,'0');//初始化隐藏的棋盘,全部初始化为字符0
Initboard(showboard, ROWS, COLS,'*');//初始化显示的棋盘,全部初始化为字符*
Displayboard(showboard, ROW, COL);//打印给玩家看的棋盘
Setboard(concealboard, ROW, COL);//为隐藏的棋盘中设置雷
Findbomb(concealboard, showboard, ROW, COL);//在隐藏的棋盘中寻找雷,再通过联动显示的棋盘打印出来
}
int main()
{
srand((size_t)time(NULL));
int input = 0;
do
{
meun();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误请重新选择\n");
break;
}
} while (input);
return 0;
}
以上就是全部代码,还有很多需要优化的地方,我之后也会继续改良,如有错误和不足的地方。欢迎大家指正,完结撒花