前几天做了三子棋的游戏代码,今天弄一个扫雷游戏的代码。我觉得总的思路都是差不多的。上次三子棋是先做了头文件,之后按照顺序将源文件内的代码内容一一补充。我这次打算先编写源代码,编写完毕之后再写头文件。
Menu()
首先 制作刚开始的菜单页面,用printf来简略设定菜单页的外形。使用switch语句来选择,若输入1则开始游戏并进入之后的Game()部分,输入2退出游戏,输入其他显示输入错误。通过此部分设定,菜单页如下图:
main.c
#include "mine.h"
static void Menu()
{
printf("==================================\n");
printf("|| 欢迎来到扫雷 ||\n");
printf("==================================\n");
printf("|| 1.开始游戏 ||\n");
printf("==================================\n");
printf("|| 2.退出游戏 ||\n");
printf("==================================\n");
printf("请输入你的选择: ");
}
int main()
{
int quit = 0;
do {
int select = 0;
Menu();
scanf("%d", &select);
switch (select) {
case 1:
Game();
printf("当前游戏已经结束 ... 再来一把?\n");
break;
case 2:
quit = 1;
printf("再见 ... 欢迎下次来玩!\n");
break;
default:
printf("选择错误 ... 请重新选择\n");
break;
}
} while (!quit);
system("pause");
return 0;
}
mine.c
将此后的代码大致分为几部分,先是初始化面板,随机设定雷所在位置,展示游戏面板,得到某空格周围雷的数量,以及最后将所有代码联系起来的Game()部分。
InitBoard()
该部分使用自定义函数 InitBoard(),为方便修改面板大小以及雷数量,将其定义在头文件部分更为方便,此处不做具体赋值。使用for循环以及赋值语句初始化游戏面板,使面板内空格为0。
#include "mine.h"
void InitBoard(char board[ROW][COL], int row, int col, char elem)
{
int i = 0;
for (; i < row; i++) {
int j = 0;
for (; j < col; j++) {
board[i][j] = elem;
}
}
}
SetMine()
自定义函数,用rand随机选定位置于(row-2)(col-2)的空格中,若该位置为1,说明此处已设置了雷,继续;若该位置不为1,则将1赋值到该位置,并将n减一,目的是使在此(row-2)*(col-2)的格子中随机生成n个位置的雷(不重复),且用1赋值。
void SetMine(char mine[ROW][COL], int row, int col)
{
int n = NUM;
while (n)
{
int x = rand() % (row - 2) + 1;
int y = rand() % (col - 2) + 1;
if (mine[x][y] == '1') //若该位置为1 ,说明此位置有雷,继续
{
continue;
}
mine[x][y] = '1'; //若该位置不为1,则将1赋值到该位置
n--;
} //最后,使面板中1的数量与规定的NUM值一样
}
ShowBoard()
该部分自定义函数,展示初始化并赋值雷之后的游戏面板。用for循环来规定每个字符所在位置,以此来美化页面。
输出之后的游戏面板如下所示:
代码如下:
void ShowBoard(char board[ROW][COL], int row, int col)
{
int i = 1;
printf(" ");
for (; i <= col - 2; i++) {
printf(" %-2d|", i);
}
printf("\n");
for (i = 1; i <= col - 2; i++) {
printf("%3s", "----");
}
printf("---\n");
for (i = 1; i <= row - 2; i++) {
printf("%2d|", i);
int j = 1;
for (; j <= col - 2; j++) {
printf(" %-2c|", board[i][j]);
}
printf("\n");
for (j = 1; j <= col - 2; j++) {
printf("%3s", "----");
}
printf("---\n");
}
}
GetNum()
随机输入一个位置之后,返回值是它周围的八个格子内所含雷的个数
char GetNum(char mine[ROW][COL], 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';
}
Game()
首先初始化board和mine俩个页面,将board内全部赋值为“**”,而mine内全部赋值为“0”。放置雷的时候将雷随机放置在mine中面板中,即将mine中的数字0改为数字1的位置就是雷所存在的位置。
面板内非雷格子数赋值给count,展示board面板,之后输入要扫的位置(x y)。判定位置,第一种情况若x,y不在格子范围内,则显示错误;若所输入位置不为“*”,则说明该位置已被输入过,则输出该位置已被扫过;若输入某位置可执行,则同时将count减一;若输入某位置,该位置有雷,则游戏结束。
最后判定,若count大于0,这说明未把雷扫完,即扫雷失败;若count等于0,这说明将全部雷扫出来了,即扫雷成功。
void Game()
{
srand((unsigned long)time(NULL));
char board[ROW][COL];
char mine[ROW][COL];
InitBoard(board, ROW, COL, '*'); //'*'
InitBoard(mine, ROW, COL, '0'); //'0'
SetMine(mine, ROW, COL);
//ShowBoard(board, ROW, COL);
//ShowBoard(mine, ROW, COL);
int count = (ROW - 2) * (COL - 2) - NUM;
do {
system("cls"); //清屏
int x = 0;
int y = 0;
ShowBoard(board, ROW, COL);
printf("请输入你要扫的位置-> ");
scanf("%d %d", &x, &y);
if (x < 1 || x > 10 || y < 1 || y > 10) {//[1,10]
printf("你输入的位置是错误的,请重新输入...\n");
Sleep(1000);
continue;
}
if (board[x][y] != '*') { //避免重复输入同一位置
printf("你输入的位置已经被扫过,请重新输入...\n");
Sleep(1000);
continue;
}
if (mine[x][y] == '0') {
count--;
char num = GetNum(mine, x, y);
board[x][y] = num;
}
else {
printf("你输入的位置(%d, %d),有雷!\n", x, y);
break;
}
} while (count > 0);
char* result = NULL;
if (count > 0) {
result = "== 扫雷结束, 你被炸死了:< ==\n";
}
else {
result = "==扫雷结束,恭喜你,成功啦:> ==\n";
}
printf("==============================\n");
printf("%s", result);
printf("==============================\n");
ShowBoard(mine, ROW, COL);
}
至此,本代码的大部分内容就已经编写完毕了,最后,总结整个代码然后编写头文件。
mine.h
#ifndef _MINE_H_是防止mine.h被重复引用,之后引用库函数。宏定义扫雷游戏的ROW和COL以及雷的个数,在头文件中宏定义,便于之后修改游戏的难度。需要注意的是游戏中格子的个数是(ROW-2)X(COL-2),所以必须NUM要小于等于(ROW-2)X(COL-2),否则游戏将无法运行。之后声明源文件中的自定义函数。
#ifndef _MINE_H_
#define _MINE_H_
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#pragma warning(disable:4996)
#define ROW 12
#define COL 12
#define NUM 10
void Game();
//初始化面板
void InitBoard(char board[][COL], int row, int col, char elem);
//设置雷位置
void SetMine(char mine[][COL], int row, int col);
//展示面板
void ShowBoard(char board[][COL], int row, int col);
//得到周围雷的数量
char GetNum(char mine[][COL], int x, int y); //'0', '1', '2'
#endif
将头文件中宏定义的数据进行修改,修改之后,面板为6X6的格子,其中含有20个雷。如图所示:
接着输入要扫的位置就会显示数字或者扫雷失败。修改代码如下:
#define ROW 8
#define COL 8
#define NUM 20
最后,总体来说,都是现将制作游戏代码的整个过程捋一遍,将制作中需要用到的部分自定义出来,最后统筹规划在头文件中总的显示出来。最重要的还是细心的面面俱到吧,无论漏掉哪一部分都会导致游戏制作失败。
加油吧,以后做出来更加难而有意思的游戏!