推箱子游戏是一款很有趣味的游戏,其开发过程有一定的技巧和方法,其中涉及软中断、二维数组、键盘操作以及图形化函数等方面的知识。本游戏的开发者需要基本掌握显示器中断寄存器的设置、二维数组及结构体的定义、键盘上键值的获取、图形方式下光标的显示和定位,以及部分图形函数的使用。在本章中,将使用C语言实现一个典型的推箱子游戏,向读者讲解其具体实现过程,并剖析技术核心和实现的算法。
项目规划分析
在软件项目开发过程,规划分析是首先需要完成的工作。在本节中,将详细讲解本推箱子游戏的项目规划分析方面的工作。
1.1.1 功能描述
本游戏一共4关,由易到难,每一关都有初始化、按键处理、重置及退出功能。
① 初始化包括屏幕初始化和每一关卡的初始化,屏幕被初始化为宽80像素,高25像素。
② 按键处理包括移动小人和移动箱子,通过移动上下左右键来控制小人的移动,从而推动箱子,以把箱子推到指定的目的地为过关。
③ 每一关都可以重置,按空格键可以重置当前关。
④ 按【Esc】键可以在任何时候退出游戏。
1.1.2 功能模块分析
本程序包括5个模块,分别是初始化模块、画图模块、移动箱子模块、移动小人模块和功能控制模块,如图1-1所示。各个模块的功能描述如下。
(1)初始化模块
该模块包括屏幕初始化和游戏第一关的初始化。屏幕初始化用于输出欢迎信息和操作提示,游戏每一关的初始化是构建每一关的关卡。
(2)画图模块
该模块主要是被其他模块调用,用于画墙、在空地画箱子、在目的地画箱子、画小人和画目的地。
(3)移动箱子模块
该模块用于移动箱子,包括目的地之间、空地之间和目的地与空地之间的箱子移动。
(4)移动小人模块
该模块用于控制小人移动,从而推动箱子到目的地。
(5)功能控制模块
该模块是几个功能函数的集合,包括屏幕输出功能、指定位置状态判断功能和关卡重置功能。
1.1.3 剖析执行流程
1.任务执行流程
游戏从第一关开始,按上下左右方向键控制小人移动来推动箱子,可以在游戏中的任何时候按【Esc】键退出。如果游戏无成功希望,可以按空格键回到当前任务的开始状态;如果成功完成当前关,则进入下一关,如果当前关是最后一关,则显示通关信息,提示游戏结束。图1-2显示了任务执行的流程。
图1-2 任务执行流程图
2.小人移动流程图
小人移动的方向有4个,move()函数(处理小人移动的函数)对这4个方向移动的处理都一致,只是调用函数时的参数有所不同。首先判断小人移动的方向,然后根据小人所处的当前状态、下一步状态或者下下一步状态进行适当的处理。处理过程如图1-3所示。
图1-3 小人移动流程图
经过游戏运作流程的分析和试玩过程可知,推箱子游戏是一款很有趣味的游戏,其开发过程需要一定的技巧和方法,其中涉及到软中断、二维数组、键盘操作以及图形化函数等方面的知识。本游戏的开发者需要基本掌握显示器中断寄存器的设置、二维数组及结构体的定义、键盘上键值的获取、图形方式下光标的显示和定位,以及部分图形函数的使用。
1.2 设计数据结构
1.设置全局变量
定义二维数组char status[20][20],用于记录屏幕一个点的状态。其中,“0”表示什么都没有,“b”表示箱子,“w”表示目的地,“i”表示箱子在目的地。首先将屏幕20×20范围内的状态初始化为0。然后根据具体情况,在画箱子时,将箱子所在点的状态改为“b”;在画墙壁时,将墙壁所在点的状态改为“w”;在画目的地时,将目的地所在点的状态改为“m”;当箱子被推到目的地时,箱子所在点的状态改为“i”,如果每一关中所有目的地的状态都为“i”,则说明该关已完成。
定义全局变量,char far *printScreen=(char far*)0xB8000000,用于在屏幕上输出字符。彩色显示器的字符缓冲区首地址为0xB8000000,每一个字符占2个字节(第一个字节为ASCII值,第二个字节为颜色值),字符模式下屏幕宽80像素,高25像素,一屏可以写80×25个字符。
2.定义结构体
定义结构体struct winer,用于判断每一关是否已完成。其中x用于存放目的地的横坐标,y用于存放目的地的纵坐标。如果所有表示目的地坐标对应的状态都为“i”,即箱子在目的地,则表示已经过关,可以进入下一关。该结构体的初始化在每一关的初始化时进行,具体代码如下所示。
typedef struct winer
{
int x;
int y;
struct winer *p;
}winer;
1.3 规划系统函数
系统函数是整个项目的灵魂,项目中的功能都是通过函数实现的,所以本阶段的工作十分重要。在本阶段要提前做好分析和规划工作,力争为后面的工作打好基础。
1.putoutChar()
(1)函数原型:void putoutChar(int y,int x,char ch,char fc,char bc)
(2)功能:putoutChar()函数用于在屏幕上的指定位置输出指定的字符。其中,x、y指明输出的位置,ch表示输出的字符,fc表示输出的字符颜色,bc表示背景色。
2.printWall()
(1)函数原型:void printWall(int x,int y)
(2)功能:printWall()函数用于画墙壁,传入参数x、y指明位置。该函数调用putoutChar()进行输出,以黑色为背景画绿色墙,用小方块表示墙(ASCII值为219)。
3.printBox()
(1)函数原型:void printBox(int x,int y)
(2)功能:printBox()函数用于在非目的地画箱子,传入参数x、y指明位置。该函数调用putoutChar()进行输出,以黑色为背景白色箱子,用ASCII值为10的字符表示箱子。
4.printBoxDes()
(1)函数原型:void printBoxDes(int x,int y)
(2)功能:printBoxDes()函数用于在目的地画箱子,传入参数x、y指明位置。该函数调用putoutChar()进行输出,以黑色为背景画黄色箱子,仍用ASCII值为10的字符表示箱子。
5.printDestination()
(1)函数原型:void printDestination(int x,int y)
(2)功能:printDestination()函数用于画目的地,传入参数x、y指明位置。该函数调用putoutChar()进行输出,以黑色为背景画黄色目的地,用心形表示(ASCII值为003)。
6.printDestination1()
(1)函数原型:void printDestination1(int x,int y,winer **win,winer **pw)
(2)功能:printDestination1()函数与printDestination()函数功能基本相同,都是画目的地函数,但是printDestination1()增加了记录每一个目的地位置的功能。其中x、y指明目的地的位置,每一关的所有目的地位置存放在结构体struct winer中,形成一条链表,**winer返回链表的头,**pw则指向链表的尾部。
7.printMan()
(1)函数原型:void printMan(int x,int y)
(2)功能:printMan()函数用于画小人,x、y指明画的位置。该函数通过软中断来实现,首先设置寄存器AX的高位和低位,设置高位0xa表示在光标位置显示字符;设置低位02(ASCII值),表示输出的字符;然后设置寄存器CX为01,表示重复输出的次数,这里只输出一次;最后产生类型为0x10的中断,表示显示器输出。
8.init()
(1)函数原型:void init()
(2)功能:init()函数用于初始化屏幕。该函数首先用两个for循环初始化屏幕20*20范围内的状态,初始化为0,以后根据实际情况重新赋值;然后设置屏幕输出状态,设置寄存器AX的高位为0,低位为3,表示以80*25的彩色方式显示;最后移动光标到指定的位置输出操作提示信息以及版权信息。
9.初始化游戏
(1)函数原型:winer *initStep1()、winer *initStep2()、winer *initStep3()、winer *initStep4()
(2)功能:这几个函数分别初始化游戏的第一关到第四关。这些函数的功能和实现步骤相似。首先根据需要在指定的位置画墙壁和画箱子,在这里可以设置游戏的难度,初始化的墙壁越复杂,箱子越多,则游戏就越难。游戏的第一关至第四关难度依次增加。然后分别调用printDestination1()和printMan()函数画目的地和小人。函数返回包含各个目的地位置的链表。
10.移动箱子
(1)函数原型:void moveBoxSpacetoSpace(int x,int y,char a)、void moveBoxDestoSpace(int x,int y, char a)、void moveBoxSpacetoDes(int x,int y,char a)、void moveBoxDestoDes(int x,int y,char a)
(2)功能:这几个函数实现的功能分别是人空地移动箱子到空地、从目的地移动箱子到空地、从空地移动箱子到目的地和中从目的地移动箱子到目的地。x、y指明小人当前所处的位置,字符a表示移动的方向,有“u”“d”“l”和“r”4个值,分别表示向上、下、左、右移动。这几个函数的实现原理大致相似。对于前面两个函数,首先判断移动的方向,从小人所在的位置沿着移动的方向移动一步画小人,移动两步画箱子(调用printBox()函数),并设置状态为“b”;对于后面两个参数,首先判断移动的方向,从小人所在的位置沿着移动方向移动一步画小人,移动两上在目的地画箱子(调用printBoxDes()函数),并设置状态为“i”,表明箱子在目的地上。
11.judge()
(1)函数原型:int judge(int x,int y)
(2)功能:judge()根据结构体struct[x][y]中存的值来判断该点的状态。
12.move()
(1)函数原型:void moid(int x,int y,char a)
(2)功能:Move()函数根据按下的键来处理小人的移动。整个项目的核心是实现推箱子处理,即通过函数move()来实现小人的移动。小人移动的方向有上(“u”)、下(“d”)、左(“l”)、右(“r”)4个,4个方向的处理方式一样。首先判断移动的方向,然后根据小人的当前位置、下一步位置以及下下一步位置所在的状态进行处理。
① 若下一步所在位置的状态为墙壁(“w”),则直接退出,不做任何处理。
② 若下一步所在位置的状态为目的地(“i”)或者什么都没有(“0”),则:
- 若当前位置的状态为目的地,则在当前位置画目的地(调用printDestination()函数)、在下一步位置画小人(调用printMan()函数)。
- 若当前位置的状态为非目的地,则输出空格清空当前位置的小人,并在下一步位置画小人(调用printMan()函数)。
③ 若下一步所在位置的状态为箱子(“b”),则:
- 如果下下一步位置的状态为“0”,则把箱子从空地移动到空地(调用moveBoxSpacetoSpace()函数),然后把光标移动到下一步位置(如果当前位置的状态为目的地,则应先画目的地(调用printDestinanion()函数))。
- 如果下下一步位置的状态为目的地,则把箱子从空地移动到目的地(调用moveBoxSpacetoDes()函数),然后把光标移动到下一步位置(如果当前位置的状态为目的地,则应先画目的地(调用printDestination()函数))。
- 其他情况则直接返回,不做任何处理。
④ 若下一步所在位置的状态为箱子在目的地(“i”),则:
- 如果下下一步位置的状态为“0”,则把箱子从目的地移动到空地(调用moveBoxDestoSpace()函数),然后把光标移动到下一步位置(如果当前位置的状态为目的地,则应先画目的地(调用printDestination()函数))。
- 如果下下一步位置的状态为目的地,则把箱子从目的地移动到目的地(调用moveBoxDestoDes()函数),然后把光标移动到下一步位置(如果当前位置的状态为目的地,则应先画目的地(调用printDestination()函数))。
- 其他情况则直接返回,不做任何处理。
13.reset()
(1)函数原型:void reset(int i)
(2)功能:reset()函数用于重置当前关。该函数首先判断当前关是第几关,然后调用init()函数和初始化当前关的函数进行重置。
14.主函数
主函数首先设置寄存器AX的高位和低位,显示器软中断,进行显示状态的设置,初始化屏幕,初始化第一关,并显示操作提示信息和版权信息。然后根据按下的键(bioskey(0)函数返回按下的键值)进行处理,处理过程由move()函数进行(如果按下【Esc】键,则退出程序)。对于每一关,如果所有的表示目的地的状态都由“m”变成了“i”,则表示通过该关,可以进入下一关。
1.4 具体编码
现在既有项目规划书,也有函数规划。有了这些资料,整个设计思路就十分清晰了,只需遵循规划书的方向,并参照规划函数即可轻松完成编码工作。
1.4.1 预处理
程序预处理部分包括加载头文件、定义全局变量和定义数据结构,并对它们进行初始化工作。具体实现代码如下所示。
/*加载头文件*/
#include <dos.h>
#include <stdio.h>
#include <ctype.h>
#include <conio.h>
#include <bios.h>
#include <alloc.h>
/*定义结构体,判断是否胜利*/
typedef struct winer
{
/*目的地的x和y坐标*/
int x,y;
struct winer *p;
}winer;
/*定义全局变量*/
/*记录屏幕上各点的状态*/
char status [20][20];
/*彩色显示器字符缓冲区的首地址为0xB8000000*/
char far *printScreen=(char far* )0xB8000000;
/*自定义原型函数*/
void putoutChar(int y,int x,char ch,char fc,char bc);
void printWall(int x, int y);
void printBox(int x, int y);
void printBoxDes(int x, int y);
void printDestination(int x, int y);
void printDestination1(int x,int y,winer **win,winer **pw);
void printMan(int x, int y);
void init();
winer *initStep1();
winer *initStep2();
winer *initStep3();
winer *initStep4();
void moveBoxSpacetoSpace(int x ,int y, char a);
void moveBoxDestoSpace(int x ,int y, char a) ;
void moveBoxSpacetoDes(int x, int y, char a);
void moveBoxDestoDes(int x, int y, char a);
int judge(int x, int y);
void move(int x, int y, char a);
void reset(int i);
1.4.2 初始化模块——递归算法
该模块主要用于对屏幕和关卡的初始化,初始化关卡时是调用画图模块中画图函数。该模块包括以下几个函数。
① void init():初始化屏幕的大小、显示方式、显示操作提示信息和版权信息。
② winer *initStep1():初始化游戏的第一关。
③ winer *initStep2():初始化游戏的第二关。
④ winer *initStep3():初始化游戏的第三关。
⑤ winer *initStep4():初始化游戏的第四关。
具体实现代码如下所示。
/*初始化全屏幕函数*/
void init()
{
int i,j;
for(i=0;i<20;i++)
for(j=0;j<20;j++)
/*屏幕20*20范围内的状态初始化为0*/
status[i][j]=0;
/*设置寄存器AX的低位,以80*25的色彩方式显示*/
_AL=3;
_AH=0;
geninterrupt(0x10);
/*移动光标到指定的位置输出屏幕信息*/
gotoxy(40,4);
printf("Welcome to the box world!");
gotoxy(40,6);
printf("You can use up, down, left,");
gotoxy(40,8);
printf("right key to control it, or");
gotoxy(40,10);
printf("you can press Esc to quit it.");
gotoxy(40,12);
printf("Press space to reset the game.");
gotoxy(40,14);
printf("Wish you have a good time !");
gotoxy(40,16);
printf("April , 2007");
}
/*初始化游戏第一关函数*/
winer *initStep1()
{
int x;
int y;
winer *win=NULL;
winer *pw;
/*在指定位置画墙,构建第一关*/
for(x=1,y=5;y<=9;y++)
printWall(x+4,y+10);
for(y=5,x=2;x<=5;x++)
printWall(x+4,y+10);
for(y=9,x=2;x<=5;x++)
printWall(x+4,y+10);
for(y=1,x=3;x<=8;x++)
printWall(x+4,y+10);
for(x=3,y=3;x<=5;x++)
printWall(x+4,y+10);
for(x=5,y=8;x<=9;x++)
printWall(x+4,y+10);
for(x=7,y=4;x<=9;x++)
printWall(x+4,y+10);
for(x=9,y=5;y<=7;y++)
printWall(x+4,y+10);
for(x=8,y=2;y<=3;y++)
printWall(x+4,y+10);
printWall(5+4,4+10);
printWall(5+4,7+10);
printWall(3+4,2+10);
/*在指定位置画箱子*/
printBox(3+4,6+10);
printBox(3+4,7+10);
printBox(4+4,7+10);
/*在指定位置画目的地*/
printDestination1(4+4,2+10,&win,&pw);
printDestination1(5+4,2+10,&win,&pw);
printDestination1(6+4,2+10,&win,&pw);
/*在指定位置画小人*/
printMan(2+4,8+10);
return win;
}
/*初始化游戏第二关函数*/
winer *initStep2()
{
int x;
int y;
winer *win=NULL;
winer *pw;
/*指定位置画墙,构建第二关*/
for(x=1,y=4;y<=7;y++)
printWall(x+4,y+10);
for(x=2,y=2;y<=4;y++)
printWall(x+4,y+10);
for(x=2,y=7;x<=4;x++)
printWall(x+4,y+10);
for(x=4,y=1;x<=8;x++)
printWall(x+4,y+10);
for(x=8,y=2;y<=8;y++)
printWall(x+4,y+10);
for(x=4,y=8;x<=8;x++)
printWall(x+4,y+10);
for(x=4,y=6;x<=5;x++)
printWall(x+4,y+10);
for(x=3,y=2;x<=4;x++)
printWall(x+4,y+10);
for(x=4,y=4;x<=5;x++)
printWall(x+4,y+10);
printWall(6+4,3+10);
/*在指定位置画箱子*/
printBox(3+4,5+10);
printBox(6+4,6+10);
printBox(7+4,3+10);
/*在指定位置画目的地*/
printDestination1(5+4,7+10,&win,&pw);
printDestination1(6+4,7+10,&win,&pw);
printDestination1(7+4,7+10,&win,&pw);
/*在指定位置画小人*/
printMan(2+4,6+10);
return win;
}
/*初始化游戏第三关函数*/
winer *initStep3()
{
int x;
int y;
winer *win=NULL;
winer *pw;
/*在指定位置画墙,构建第三关*/
for(x=1,y=2;y<=8;y++)
printWall(x+4,y+10);
for(x=2,y=2;x<=4;x++)
printWall(x+4,y+10);
for(x=4,y=1;y<=3;y++)
printWall(x+4,y+10);
for(x=5,y=1;x<=8;x++)
printWall(x+4,y+10);
for(x=8,y=2;y<=5;y++)
printWall(x+4,y+10);
for(x=5,y=5;x<=7;x++)
printWall(x+4,y+10);
for(x=7,y=6;y<=9;y++)
printWall(x+4,y+10);
for(x=3,y=9;x<=6;x++)
printWall(x+4,y+10);
for(x=3,y=6;y<=8;y++)
printWall(x+4,y+10);
printWall(2+4,8+10);
printWall(5+4,7+10);
/*在指定位置画箱子*/
printBox(6+4,3+10);
printBox(4+4,4+10);
printBox(5+4,6+10);
/*在指定位置画目的地*/
printDestination1(2+4,5+10,&win,&pw);
printDestination1(2+4,6+10,&win,&pw);
printDestination1(2+4,7+10,&win,&pw);
/*在指定位置画小人*/
printMan(2+4,4+10);
return win;
}
/*初始化游戏第四关函数*/
winer *initStep4()
{
int x;
int y;
winer *win=NULL;
winer *pw;
/*在指定位置画墙,构建第四关*/
for(x=1,y=1;y<=6;y++)
printWall(x+4,y+10);
for(x=2,y=7;y<=8;y++)
printWall(x+4,y+10);
for(x=2,y=1;x<=7;x++)
printWall(x+4,y+10);
for(x=7,y=2;y<=4;y++)
printWall(x+4,y+10);
for(x=6,y=4;y<=9;y++)
printWall(x+4,y+10);
for(x=3,y=9;x<=5;x++)
printWall(x+4,y+10);
for(x=3,y=3;y<=4;y++)
printWall(x+4,y+10);
printWall(3+4,8+10);
/*在指定位置画箱子*/
printBox(3+4,5+10);
printBox(4+4,4+10);
printBox(4+4,6+10);
printBox(5+4,5+10);
printBox(5+4,3+10);
printDestination1(3+4,7+10,&win,&pw);
printDestination1(4+4,7+10,&win,&pw);
printDestination1(5+4,7+10,&win,&pw);
printDestination1(4+4,8+10,&win,&pw);
printDestination1(5+4,8+10,&win,&pw);
/*在指定位置画小人*/
printMan(2+4,2+10);
return win;
}
1.4.3 画图模块——试探算法
该模块主要用于画图操作,包括画墙、画箱子、画目的地和画小人等。该模块包括以下几个函数。
① void printWall(int x,int y):用于画墙。
② void printBox(int x,int y):在空白地(非目的地)画箱子。
③ void printBoxDes(int x,int y):在目的地画箱子。
④ void printDestination(int x,int y):画目的地函数。
⑤ void printDestination1(int x,int y,winer **win,winer **pw):画目的地函数,并记录每个目的地的位置。
⑥ void printMan(int x,int y):画小人函数。
具体实现代码如下所示。
/*画墙函数*/
void printWall(int x,int y)
{
/*以黑色为背景画绿色墙,用小方块表示*/
putoutChar(y-1,x-1,219,GREEN,BLACK);
status[x][y]='w';
}
/*非目的地画箱子函数*/
void printBox(int x,int y)
{
/*以黑色为背景画白色箱子,用小方块表示*/
putoutChar(y-1,x-1,10,WHITE,BLACK);
status[x][y]='b';
}
/*画目的地函数,记录每个目的地的位置*/
void printDestination1(int x,int y,winer **win,winer **pw)
{
winer *qw;
/*以黑色为背景画黄色目的地,用心形表示*/
putoutChar(y-1,x-1,003,YELLOW,BLACK);
status[x][y]='m';
if(*win==NULL)
{
/*分配空间*/
*win=*pw=qw=(winer* )malloc(sizeof(winer));
(*pw)->x=x;
(*pw)->y=y;
(*pw)->p=NULL;
}
/*如果当前不是目的地的第一个点*/
else
{
qw=(winer* )malloc(sizeof(winer));
qw->x=x;
qw->y=y;
/*(*pw)的下一个点是qw */
(*pw)->p=qw;
(*pw)=qw;qw->p=NULL;
}
}
/*画目的地函数*/
void printDestination(int x,int y)
{
/*以黑色为背景画黄色目的地,用心形表示*/
putoutChar(y-1,x-1,003,YELLOW,BLACK);
status[x][y]='m';
}
void printMan(int x,int y)
{
gotoxy(y,x);
_AL=02;
_CX=01;
_AH=0xa;
geninterrupt(0x10);
}
/*在目的地画箱子函数*/
void printBoxDes(int x,int y)
{
/*以黑色为背景画黄色箱子,用小方块表示*/
putoutChar(y-1,x-1,10,YELLOW,BLACK);
status[x][y]='i';
}
1.4.4 移动箱子模块——试探、分治、递归、枚举算法
该模块是实现箱子的移动。根据游戏规则,箱子可以在空地之间、目的地之间、空地和目的地之间来回移动,因此,实现本模块共有以下4个函数。
① void moveBoxSpacetoSpace(int x,int y,char a):把箱子从空地移动到空地。
② void moveBoxDestoSpace(int x,int y,char a):把箱子从目的地移动到空地。
③ void moveBoxSpacetoDes(int x,int y,char a):把箱子从空地移动到目的地。
④ void moveBoxDestoDes(int x,int y,char a):把箱子从目的地移动到目的地。
具体实现代码如下所示。
/*从空地移动箱子到空地*/
void moveBoxSpacetoSpace(int x,int y,char a)
{
switch(a)
{
/*如果是向上键*/
case 'u':
status[x-1][y]=0;
printf(" ");
printBox(x-2,y);
printMan(x-1,y);
status[x-2][y]='b';
break;
/*如果是向下键*/
case 'd':
status[x+1][y]=0;
printf(" ");
printBox(x+2,y);
printMan(x+1,y);
status[x+2][y]='b';
break;
/*如果是向左键*/
case 'l':
status[x][y-1]=0;
printf(" ");
printBox(x,y-2);
printMan(x,y-1);
status[x][y-2]='b';
break;
/*如果是向右键*/
case 'r':
status[x][y+1]=0;
printf(" ");
printBox(x,y+2);
printMan(x,y+1);
status[x][y+2]='b';
break;
default:
break;
}
}
/*从目的地移动箱子到空地*/
void moveBoxDestoSpace(int x,int y,char a)
{
switch(a)
{
/*如果是向上键*/
case 'u':
status[x-1][y]='m';
printf(" ");
printBox(x-2,y);
printMan(x-1,y);
status[x-2][y]='b';
break;
/*如果是向下键*/
case 'd':
status[x+1][y]='m';
printf(" ");
printBox(x+2,y);
printMan(x+1,y);
status[x+2][y]='b';
break;
/*如果是向左键*/
case 'l':
status[x][y-1]='m';
printf(" ");
printBox(x,y-2);
printMan(x,y-1);
status[x][y-2]='b';
break;
/*如果是向右键*/
case 'r':
status[x][y+1]='m';
printf(" ");
printBox(x,y+2);
printMan(x,y+1);
status[x][y+2]='b';
break;
default:
break;
}
}
/*从空地移动箱子到目的地*/
void moveBoxSpacetoDes(int x,int y,char a)
{
switch(a)
{
/*如果是向上键*/
case 'u':
status[x-1][y]=0;
printf(" ");
printBoxDes(x-2,y);
printMan(x-1,y);
status[x-2][y]='i';
break;
/*如果是向下键*/
case 'd':
status[x+1][y]=0;
printf(" ");
printBoxDes(x+2,y);
printMan(x+1,y);
status[x+2][y]='i';
break;
/*如果是向左键*/
case 'l':
status[x][y-1]=0;
printf(" ");
printBoxDes(x,y-2);
printMan(x,y-1);
status[x][y-2]='i';
break;
/*如果是向右键*/
case 'r':
status[x][y+1]=0;
printf(" ");
printBoxDes(x,y+2);
printMan(x,y+1);
status[x][y+2]='i';
break;
default:
break;
}
}
/*从目的地移动箱子到目的地*/
void moveBoxDestoDes(int x,int y,char a)
{
switch(a)
{
/*如果是向上键*/
case 'u':
status[x-1][y]='m';
printf(" ");
printBoxDes(x-2,y);
printMan(x-1,y);
status[x-2][y]='i';
break;
/*如果是向下键*/
case 'd':
status[x+1][y]='m';
printf(" ");
printBoxDes(x+2,y);
printMan(x+1,y);
status[x+2][y]='i';
break;
/*如果是向左键*/
case 'l':
status[x][y-1]='m';
printf(" ");
printBoxDes(x,y-2);
printMan(x,y-1);
status[x][y-2]='i';
break;
/*如果是向右键*/
case 'r':
status[x][y+1]='m';
printf(" ");
printBoxDes(x,y+2);
printMan(x,y+1);
status[x][y+2]='i';
break;
default:
break;
}
}
1.4.5 移动小人模块——枚举、试探算法
移动小人模块是本程序的核心模块,仅由move()函数来实现。move()函数控制小人的移动,并调用画图模块、移动箱子模块中的函数来实现箱子的重画、移动等操作。其操作流程如图1-3所示,具体实现代码如下所示。
/*移动小人函数*/
void move(int x,int y,char a)
{
switch(a)
{
/*如果按向上键*/
case 'u':
/*如果(x-1,y)即小人的下一步状态为墙*/
if(!judge(x-1,y))
{
/*则跳转到(y,x),并跳出循环*/
gotoxy(y,x);
break;
}
/*如果小人的下一步状态为目的地或者什么都没有*/
else if(judge(x-1,y)==1||judge(x-1,y)==3)
{
/*如果当前状态为目的地*/
if(judge(x,y)==3)
{
/*画目的地*/
printDestination(x,y);
/*在新位置重新画小人*/
printMan(x-1,y);
break;
}
/*如果下一步状态为0*/
else
{
/*输出空字符,覆盖当前状态的小人*/
printf(" ");
/*在下一步重新画小人*/
printMan(x-1,y);
break;
}
}
/*如果下一步状态是箱子*/
else if(judge(x-1,y)==2)
{
/*如果下下一步为空*/
if(judge(x-2,y)==1)
{
/*则将箱子从空地向上移动到空地*/
moveBoxSpacetoSpace(x,y,'u');
if(judge(x,y)==3)
/*如果当前状态为目的地*/
printDestination(x,y);
gotoxy(y,x-1);
}
/*如果下下一步为目的地*/
else if(judge(x-2,y)==3)
{
/*则将箱子从空地向上移动到目的地*/
moveBoxSpacetoDes(x,y,'u');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x-1);
}
else
gotoxy(y,x);
break;
}
else if(judge(x-1,y)==4)
{
if(judge(x-2,y)==1)
{
moveBoxDestoSpace(x,y,'u');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x-1);
}
else if(judge(x-2,y)==3)
{
moveBoxDestoDes(x,y,'u');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x-1);
}
else
gotoxy(y,x);
break;
}
/*如果按向下键*/
case 'd':
if(!judge(x+1,y))
{
gotoxy(y,x);
break;
}
else if(judge(x+1,y)==1||judge(x+1,y)==3)
{
if(judge(x,y)==3)
{
printDestination(x,y);
printMan(x+1,y);
break;
}
else
{
printf(" ");
printMan(x+1,y);
break;
}
}
else if(judge(x+1,y)==2)
{
if(judge(x+2,y)==1)
{
moveBoxSpacetoSpace(x,y,'d');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x+1);
}
else if(judge(x+2,y)==3)
{
moveBoxSpacetoDes(x,y,'d');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x+1);
}
else
gotoxy(y,x);
break;
}
else if(judge(x+1,y)==4)
{
if(judge(x+2,y)==1)
{
moveBoxDestoSpace(x,y,'d');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x+1);
}
else if(judge(x+2,y)==3)
{
moveBoxDestoDes(x,y,'d');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y,x+1);
}
else
gotoxy(y,x);
break;
}
/*如果按向左键*/
case 'l':
if(!judge(x,y-1))
{
gotoxy(y,x);
break;
}
else if(judge(x,y-1)==1||judge(x,y-1)==3)
{
if(judge(x,y)==3)
{
printDestination(x,y);
printMan(x,y-1);
break;
}
else
{
printf(" ");
printMan(x,y-1);
break;
}
}
else if(judge(x,y-1)==2)
{
if(judge(x,y-2)==1)
{
moveBoxSpacetoSpace(x,y,'l');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y-1,x);
}
else if(judge(x,y-2)==3)
{
moveBoxSpacetoDes(x,y,'l');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y-1,x);
}
else
gotoxy(y,x);
break;
}
else if(judge(x,y-1)==4)
{
if(judge(x,y-2)==1)
{
moveBoxDestoSpace(x,y,'l');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y-1,x);
}
else if(judge(x,y-2)==3)
{
moveBoxDestoDes(x,y,'l');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y-1,x);
}
else
gotoxy(y,x);
break;
}
/*如果按向右键*/
case 'r':
if(!judge(x,y+1))
{
gotoxy(y,x);
break;
}
else if(judge(x,y+1)==1||judge(x,y+1)==3)
{
if(judge(x,y)==3)
{
printDestination(x,y);
printMan(x,y+1);
break;
}
else
{
printf(" ");
printMan(x,y+1);
break;
}
}
else if(judge(x,y+1)==2)
{
if(judge(x,y+2)==1)
{
moveBoxSpacetoSpace(x,y,'r');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y+1,x);
}
else if(judge(x,y+2)==3)
{
moveBoxSpacetoDes(x,y,'r');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y+1,x);
}
else
gotoxy(y,x);
break;
}
else if(judge(x,y+1)==4)
{
if(judge(x,y+2)==1)
{
moveBoxDestoSpace(x,y,'r');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y+1,x);
}
else if(judge(x,y+2)==3)
{
moveBoxDestoDes(x,y,'r');
if(judge(x,y)==3)
printDestination(x,y);
gotoxy(y+1,x);
}
else
gotoxy(y,x);
break;
}
default:
break;
}
}
1.4.6 功能控制模块——递归、分治算法
功能控制模块包括屏幕输出功能、关卡重置功能和坐标位置状态的判断功能。该模块包括以下几个函数。
① void putoutChar(int y,int x,char fc,char bc):在屏幕上指定的位置输出指定的字符。
② int judge(int x,int y):判断位置(x,y)处的状态,状态值可参见1.2节。
③ void reset (int i):重置关卡。
具体实现代码如下所示。
/*在屏幕指定位置输出指定的字符函数*/
void putoutChar(int y,int x,char ch,char fc,char bc)
{
/*屏幕输出字符ch*/
printScreen[(x*160)+(y<<1)+0]=ch;
/*指定字符颜色fc,背景颜色bc*/
printScreen[(x*160)+(y<<1)+1]=(bc*16)+fc;
}
/*判断特定坐标的状态函数*/
int judge(int x,int y)
{
int i;
/*根据status[x][y]中存的值来判断该点的状态*/
switch(status[x][y])
{
/*如果什么都没做*/
case 0:
i=1;
break;
/*如果该点表示墙*/
case 'w':
i=0;
break;
/*如果该点表示箱子*/
case 'b':
i=2;
break;
/*如果该点表示箱子在目的地*/
case 'i':
i=4;
break;
/*如果该点表示目的地*/
case 'm':
i=3;
break;
default:
break;
}
return i;
}
/*重置当前关函数*/
void reset(int i)
{
switch(i)
{
/*重置第一关*/
case 0:
init();
initStep1();
break;
/*重置第二关*/
case 1:
init();
initStep2();
break;
/*重置第三关*/
case 2:
init();
initStep3();
break;
/*重置第四关*/
case 3:
init();
initStep4();
break;
default:
break;
}
}
1.4.7 系统主函数——枚举、模拟、递归、试探算法
系统主函数main()用于实现整个程序的控制,其游戏操作流程如图1-2所示,具体代码如下所示。
void main()
{
/*记录按下的键*/
int key;
int x;
int y;
/*记录未被推到目的地的箱子个数*/
int s;
/*记录已经过了几关*/
int i=0;
winer *win;
winer *pw;
/*设置寄存器AX低位*/
_AL=3;
/*设置寄存器AX高位*/
_AH=0;
geninterrupt(0x10);
init();
win=initStep1();
do{
/*设置AH,读取光标位置*/
_AH=3;
geninterrupt(0x10);
/*读取光标所在的行,加1*/
x=_DH+1;
/*读取光标所在的列,加1*/
y=_DL+1;
/*bioskey(1)返回0,直到有键按下*/
while(bioskey(1)==0);
/*返回按下的键*/
key=bioskey(0);
switch(key)
{
/*如果按下向上键*/
case 0x4800:
move(x,y,'u');
break;
/*如果按下向下键*/
case 0x5000:
move(x,y,'d');
break;
/*如果按下向左键*/
case 0x4b00:
move(x,y,'l');
break;
/*如果按下向右键*/
case 0x4d00:
move(x,y,'r');
break;
/*如果按下空格键*/
case 0x3920:
reset(i);
break;
default:
break;
}
s=0;
pw=win;
/*如果指针非空*/
while(pw)