一、相关知识点
程序员必备资源,值得收藏!点击下载
1、键盘监听
在扫雷游戏中,当用户输入是否开始游戏的 Y/N 时,程序能够自动监听,当用户输入完成后,不用回车,程序立即做出反应,这就用到了键盘监听。
所谓键盘监听,就是用户按下某个键时系统做出相应的处理,本章讲到的输入输出函数也是键盘监听函数的一种,例如 getchar()、getche()、getch() 等。下面的代码演示了 getche() 函数的使用:
#include <stdio.h> #include <conio.h> int main(){ char ch; int i = 0; //循环监听,直到按Esc键退出 while(ch = getch()){ if(ch == 27){ break; }else{ printf("Number: %d\n", ++i); } } return 0; }
这段代码虽然达到了监听键盘的目的,但是每次都必须按下一个键才能执行 getch() 后面的代码,也就是说,getch() 后面的代码被阻塞了。
阻塞式键盘监听用于用户输入时一般没有任何问题,用户输入完数据再执行后面的代码往往也符合逻辑。然而在很多小游戏中,阻塞式键盘监听会带来很大的麻烦,用户要不停按键游戏才能进行,这简直就是灾难,所以在小游戏中一般采用非阻塞式键盘监听:用户输入数据后程序可以捕获,用户不输入数据程序也可以继续执行。
在 Windows 系统中,conio.h头文件中的kbhit()函数就可以用来实现非阻塞式键盘监听。
❝
conio.h 是 Windows 下特有的头文件,所以 kbhit() 也只适用于 Windows,不适用于 Linux 和 Mac OS。
❞
用户每按下一个键,都会将对应的字符放到输入缓冲区中,kbhit() 函数会检测缓冲区中是否有数据,如果有的话就返回非 0 值,没有的话就返回 0 值。但是 kbhit() 不会读取数据,数据仍然留在缓冲区,所以一般情况下我们还要结合输入函数将缓冲区种的数据读出。请看下面的例子:
#include <stdio.h> #include <windows.h> #include <conio.h> int main(){ char ch; int i = 0; //循环监听,直到按Esc键退出 while(1){ if(kbhit()){ //检测缓冲区中是否有数据 ch = getch(); //将缓冲区中的数据以字符的形式读出 if(ch == 27){ break; } } printf("Number: %d\n", ++i); Sleep(1000); //暂停1秒 } return 0; }
每次循环,kbhit() 会检测用户是否按下某个键(也就是检测缓冲区中是否有数据),没有的话继续执行后面的语句,有的话就通过 getch() 读取,并判断是否是 Esc,是的话就退出循环,否则继续循环。
扫雷完整代码。点击下载
2、改变输出文本的颜色
在扫雷游戏中,当用户输入是否开始游戏的 Y/N 时,程序能够自动监听,当用户输入完成后,不用回车,程序立即做出反应,这就用到了键盘监听。
C语言不总是“黑底白字”,它也可以是彩色的,可以调用Windows.h头文件下的SetConsoleTextAttribute函数改变文字和背景颜色。
调用形式为:
SetConsoleTextAttribute( HANDLE hConsoleOutput, WORD wAttributes );
hConsoleOutput表示控制台缓冲区句柄,可以通过GetStdHandle(STD_OUTPUT_HANDLE)来获得;wAttributes表示文字颜色和背景颜色。
0~F 分别代表的颜色如下:
❝
0 = 黑色 8 = 灰色 1 = 淡蓝 9 = 蓝色 2 = 淡绿 A = 绿色 3 = 湖蓝 B = 淡浅绿
C = 红色 4 = 淡红 5 = 紫色 D = 淡紫
6 = 黄色 E = 淡黄 7 = 白色 F = 亮白
❞
例如,将背景设置为淡绿色,文字设置为红色:
#include <stdio.h> #include <windows.h> int main(){ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole, 0x2C ); puts("c语言与cpp编程"); return 0; }
如果只希望设置文字颜色,背景保持黑色,那么也可以只给出一位16进制数,例如:
SetConsoleTextAttribute(hConsole, 0xC ); //将文字颜色设置为红色
SetConsoleTextAttribute(hConsole, 0xF ); //将文字颜色设置为白色
再来看一个例子:
#include <stdio.h> #include <windows.h> int main(){ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole, 0xC ); puts("红色文字"); SetConsoleTextAttribute(hConsole, 0xF ); puts("白色文字"); SetConsoleTextAttribute(hConsole, 2 ); puts("淡绿色文字"); return 0; }
3、获取随机数
扫雷游戏中,雷区会随机出现在整个区域的任意位置,没有任何规律,这就要求程序能够生成随机数值,由随机数设置雷区的位置。
在 C 语言中,我们一般使用<stdlib.h> 头文件中的 rand() 函数来生成随机数,它的用法为:
int rand (void);
void 表示不需要传递参数。C语言中还有一个 random() 函数可以获取随机数,但是 random() 不是标准函数,不能在 VC/VS 等编译器通过,所以比较少用。
rand() 会随机生成一个位于 0 ~ RAND_MAX 之间的整数。
RAND_MAX 是 <stdlib.h> 头文件中的一个宏,它用来指明 rand() 所能返回的随机数的最大值。C语言标准并没有规定 RAND_MAX 的具体数值,只是规定它的值至少为 32767。在实际编程中,我们也不需要知道 RAND_MAX 的具体值,把它当做一个很大的数来对待即可。
下面是一个随机数生成的实例: 纯文本复制
#include <stdio.h> #include <stdlib.h> int main(){ int a = rand(); printf("%d\n",a); return 0; }
生成一定范围内的随机数
在实际开发中,我们往往需要一定范围内的随机数,过大或者过小都不符合要求,那么,如何产生一定范围的随机数呢?我们可以利用取模的方法:
int a = rand() % 10; //产生0~9的随机数,注意10会被整除
如果要规定上下限:
int a = rand() % 51 + 13; //产生13~63的随机数
分析:取模即取余,rand()%51+13我们可以看成两部分:rand()%51是产生 0~50 的随机数,后面+13保证 a 最小只能是 13,最大就是 50+13=63。
最后给出产生 13~63 范围内随机数的完整代码:
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ int a; srand((unsigned)time(NULL)); a = rand() % 51 + 13; printf("%d\n",a); return 0; }
二、扫雷游戏初始化
扫雷游戏初始化过程中,我们使用到了 3 个二维数组:mine、show、mineDow:
mine 数组用于初始化扫雷游戏。具体做法是:首先默认整个二维数组中没有雷区,全部设为安全区域(用 0 表示),然后在二维数组中随机安插一定数量的雷区;
show 数组用于每次将结果输出给用户。当用户输入完成后,show 数组会根据用户的输入对存储的数据做适当的更新,然后输出给用户;
mineDow 数组中每个数据表示的,该位置相邻的周围雷区的数量,此数组的建立为的是实现“点击一个位置,开出一片安全区域”的效果 提示:在源代码的 game 函数中,在 mine_sweep() 函数之前的所有工作,都是初始化工作。
三、扫雷功能的实现
扫雷功能的实现,整体思路是:
判断用户输入坐标处是否是雷区;
更新 show 数组:如果是雷区,将 show 数组中该坐标位置上由字符 ‘*’ 改为表示雷区的字符 ‘o’;如果不是雷区,借助 mineDow数组中存储的信息,使用递归的方式,找到其他符合条件的非雷区区域(show_deal 函数的作用),将找到的所有区域一并更新到 show 数组中;
将新的 show 数组以一定的格式输出,反馈给用户;
提示:show_deal 函数找的是该区域既不是雷区,其周围也没有雷区的区域,将其全部更新到 show 数组中。一旦遇到周围有雷区的区域(这部分区域也会被更新到 show 数组中),则递归结束。
扫雷功能的具体实现,可见源代码中的 mine_sweep 函数。
四 扫雷界面的优化
扫雷界面,实际上是将 show 数组中存储的数据换了一种方式输出出来。
例如,show 数组中存储有字符 ‘*’,在输出时,统一换为“■”;表示雷区的字符 ‘o’,统一换为 ‘●’,等等。
注意:用于替换的字符,并不是普通的字符,它们并不在 ASCII 码范围内,是宽字符,占用两个字节。
采用此种方式,再配以合适的颜色(采用Windows API),可以将二维数组以如下的这种形式反馈给用户:
扫雷界面的详细优化代码,可见原代码中的 display_board() 函数的实现。