实现框架:
首先,我们需要先把游戏的框架架构好,因为完成一个游戏的制作所需的代码是比较多的,所以我们采用分模块的方式来进行设计,将游戏代码分为text.c ,game.c , game.h 。在text.c中完成程序的框架,在game.c中方函数的主要实现,在game.h中放游戏实现所需要函数的声明。
一.text.c框架构造
首先,我们需要一个菜单界面,来控制游戏的进行,当游戏结束时退出程序,这时就要用到do...while循环,我们先用打印“扫雷”来代替游戏实现,看看是否正常运行。
这里有一个技巧,每完成一个代码,就测试代码是否能正常运行,这样更不容易出现bug
基本框架如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu(void)
{
printf("************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("************************\n");
}
void game(void)
{
printf("扫雷\n");
}
void text(void)
{
int choice = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break:
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (choice);
}
int main(void)
{
text();
return 0;
}
接下来进行游戏实现:
二.实现逻辑:
我们创建两个二维数组,一个用来确定雷的位置,一个用来展示。
大概逻辑如下:初始化棋盘->打印棋盘->布置雷->排查雷
在排查雷的时候我们会遇到判断数组是否越界的问题,而这将会是一个大工程,为了避免这个问题,我们在创建数组的时候,行数和列数都可以比需要显示的行数和列数多二,这样就避免了这一问题。
为什么不设置展示的棋盘为原来的行数和列数呢?
这样设置便于下标的操作!!
1.1创建棋盘和初始化棋盘
这里为了神秘感,在最开始时候将将show数组内的元素都初始化为'*',为了保持一致,将mine数组内的元素初始化为字符‘0’而不是‘0’,注意,这里用‘0‘表示不是雷,用’1‘表示雷是有妙用的,之后会说明。
下面是初始化和创建棋盘相关代码:
void init_board(char board[][COLS], int rows, int cols, char c)
{//c为初始化内容的指定,用此技巧可以一个函数完成两个初始化功能
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
board[i][j] = c;
}
}
void print_board(char board[][COLS], int row, int col)
{
int j = 0;
int i = 0;
printf("-----------扫雷----------\n");
for (i = 0; i <= row; i++)
{
if (i == 0)
printf(" ");
else
printf("%d ", i);
}
//这是为了让游戏可玩度更高,打印出了一个标识行
printf("\n");
for (i = 0; i <= row; i++)
{
printf("--");
if (i == 0)
printf("|");
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d |", i);
for (j = 1; j <= col; j++)
printf("%c ", board[i][j]);
printf("\n");
}
printf("-----------扫雷----------\n");
}
1.2 布置雷
总体逻辑就是让电脑随机生成你想要的雷数,可以把mine数组上对应坐标上的’0‘变成’1‘表示雷 ,生成随机数需要用到rand()和srand()函数,但是如果你只用了这两个函数,你会发现每次生成的雷的位置都一样,这样的话就不好玩了,所以可以将srand()函数的种子用time()函数返回值,time函数返回值是时间戳,可变的量,所以每次生成的雷的位置都是不一样的。
实现如下:
void set_mine(char board[][COLS], int row, int col)
{
int count = 0;
while (count < EASY_PATTERN)
{
int i = rand() % row + 1;
int j = rand() % col + 1;
if (board[i][j] != '1')
{
count++;
board[i][j] = '1';
}
}
}
现在再次进行测试,效果如下:
注意,因为是测试阶段,所以可以把雷区展示出来,真正玩的时候只是打印展示的棋盘而不打印雷区。
排查雷:
接下来就是排查雷了,这也是扫雷的重点,我们知道,扫雷中的数字代表周围8个中有多少个雷,那么要如何计算出周围有多少雷呢?
这里就需要用到用‘1’表示雷,而不是用其他符号表示雷的妙用了,我们知道,‘1’-‘0’就等于这个数字(这里要区分数字1和字符‘1’的区别),那么我们在计算周围有多少雷时就可以简单的把周围的元素都加起来再减去8个字符‘0’就可以得到周围雷的数量了。
另外,玩家在对指定坐标进行清扫时,还需要判断坐标是否合法,以及坐标是否被排查过,如果不考虑这两个因素,那么可能会导致bug的出现。
然后就剩下最后一个过程了,怎么判断玩家胜利呢?当已经排查的雷数==棋盘总位数-雷数时,玩家胜利,退出游戏。
代码实现:
int mine_num(char board[][COLS], int x, int y)
{
return board[x - 1][y - 1] + board[x - 1][y] + 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 mine_clear(char mine[][COLS], char show[][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
int win = 0;
while(win<ROW*COL-EASY_PATTERN)
{
print_board(show, ROW, COL);
//print_board(mine, ROW, COL);
printf("请输入要排查的坐标:>");
scanf("%d %d", &i, &j);
if (i >= 1 && i <= row && j >= 1 && j <= col)
{
if (show[i][j] == '*')
{
count = mine_num(mine, i, j);
show[i][j] = count + '0';
win++;
if (mine[i][j] == '1')
{
printf("你被炸死了,真惨!\n");
Sleep(500);
break;
}
}
else
{
printf("该坐标已经被排查过,请重新输入!\n");
Sleep(500);
}
}
else
{
printf("输入错误,请重新输入!\n");
Sleep(500);
}
system("cls");
}
if (win == ROW * COL - EASY_PATTERN)
{
printf("你真是个排雷大师呢!\n");
print_board(mine, ROW, COL);
}
else
{
printf("真遗憾,下次努力把。\n");
print_board(mine, ROW, COL);
}
}
到此为止,一个简易版的扫雷游戏就完成啦!!但是还有很多不足,需要一步步改进。
全部代码:
game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_PATTERN 10
//初始化棋盘
void init_board(char board[][COLS], int rows, int cols, char c);
//打印棋盘
void print_board(char board[][COLS], int row, int col);
//布置雷
void set_mine(char board[][COLS], int row, int col);
//排查雷
void mine_clear(char mine[][COLS], char show[][COLS], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void init_board(char board[][COLS], int rows, int cols, char c)
{//c为初始化内容的指定,用此技巧可以一个函数完成两个初始化功能
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
board[i][j] = c;
}
}
void print_board(char board[][COLS], int row, int col)
{
int j = 0;
int i = 0;
printf("-----------扫雷----------\n");
for (i = 0; i <= row; i++)
{
if (i == 0)
printf(" ");
else
printf("%d ", i);
}
//这是为了让游戏可玩度更高,打印出了一个标识行
printf("\n");
for (i = 0; i <= row; i++)
{
printf("--");
if (i == 0)
printf("|");
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d |", i);
for (j = 1; j <= col; j++)
printf("%c ", board[i][j]);
printf("\n");
}
printf("-----------扫雷----------\n");
}
void set_mine(char board[][COLS], int row, int col)
{
int count = 0;
while (count < EASY_PATTERN)
{
int i = rand() % row + 1;
int j = rand() % col + 1;
if (board[i][j] != '1')
{
count++;
board[i][j] = '1';
}
}
}
int mine_num(char board[][COLS], int x, int y)
{
return board[x - 1][y - 1] + board[x - 1][y] + 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 mine_clear(char mine[][COLS], char show[][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
int win = 0;
while(win<ROW*COL-EASY_PATTERN)
{
print_board(show, ROW, COL);
//print_board(mine, ROW, COL);
printf("请输入要排查的坐标:>");
scanf("%d %d", &i, &j);
if (i >= 1 && i <= row && j >= 1 && j <= col)
{
if (show[i][j] == '*')
{
count = mine_num(mine, i, j);
show[i][j] = count + '0';
win++;
if (mine[i][j] == '1')
{
printf("你被炸死了,真惨!\n");
Sleep(500);
break;
}
}
else
{
printf("该坐标已经被排查过,请重新输入!\n");
Sleep(500);
}
}
else
{
printf("输入错误,请重新输入!\n");
Sleep(500);
}
system("cls");
}
if (win == ROW * COL - EASY_PATTERN)
{
printf("你真是个排雷大师呢!\n");
print_board(mine, ROW, COL);
}
else
{
printf("真遗憾,下次努力把。\n");
print_board(mine, ROW, COL);
}
}
text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu(void)
{
printf("************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("************************\n");
}
void game(void)
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
set_mine(mine, ROW, COL);
system("cls");
mine_clear(mine, show, ROW , COL);
}
void text(void)
{
srand((unsigned int)time(NULL));
int choice = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (choice);
}
int main(void)
{
text();
return 0;
}
为了让界面更美观,这里运用了system(“cls")函数清空输出屏幕。
然而,这个游戏还有许多需要完善之处,比如:
1.能够像真正的游戏那样展开一片
2.标记和取消雷
3.显示剩余雷数
在之后,我一定会一点点用我多余的时间进行完善 ,希望有小伙伴看到这篇博客能够监督并且催促我哦,哈哈!