(5)、状态保存函数voidstatus_save(void)
将前一状态与第一点移动后产生的第一点新码值(change[])进行或运算,再与产生的被擦除点显示码值的反(eraser[])进行与运算,得到新的显示状态(除随机点)的码值。
(6)、第一点的操作过程记录函数voidrenew(unsignedcharin)
根据需要记录第一个点的N个运行过程(设为20)
(7)、吃点函数voideat(unsignedcharin)
将记录的随机点显示码值的数组font[]清零,让第一个点再向前操作一步,但不擦除最后一个点,用来实现吃点效果。
最后调用产生随机点函数重新产生一随机点。
(8)、结合函数voidjoin(void)
用于产生要显示的码值。
将表示状态的数组status[]与表示随机点的数组font[]进行或运算就可以实现。
(9)、测试函数unsignedchartest(unsignedchar*tester)
这个函数在程序中用于两个地方,一是在产生随机点的函数中,如果产生的随机点在“蛇”身上(返回值为2),则重新产生随机点;二是在第一个操作完一步后,判断此时第一点的位置,如果在“蛇”身上(返回值为2),则游戏失败。
(10)、液晶显示器显示函数voidprint_message(char*message)
用于显示字符串message。
(11)、函数voidstart(void)、void_win(void)、voidlose(void)用于在游戏开始时、胜利时、失败时的液晶显示、语音输出以及LED点阵输出。
(12)、难度选择函数voidchoose(void)
根据液晶显示的提示,选择游戏难度等级。
在按确认键后开中断。
程序流程图如下:
(13)、中断程序SIGNAL(SIG_OVERFLOW3)
用于限制游戏进行的时间,在规定时间内完成游戏任务,则关中断,中断程序不在执行,如果在规定时间没有完成,则调用失败函数(lose()),游戏失败。
程序流程图如下:
5.c程序代码:
/****************************************************
DOS游戏,贪吃蛇。
编辑器Win-TC,编译器TC2内核。
作者:
樊毅。
时间:
年10月10日。
注意:
贪吃蛇的数据结构要用双向链表实现,因为蛇没有吃到食物时,是在蛇头前加一个节点作为新的蛇头,把蛇尾删去,实现移动一格,同时,蛇长不变。
从尾部删除链表的一个节点就必须用双向链表了。
****************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
/*定义地图小格的边长*/
#defineSIZE10
#defineUP0x4800
#defineDOWN0x5000
#defineLEFT0x4B00
#defineRIGHT0x4D00
#defineENTER0x1C0D
voidfill(intx,inty,intxx,intyy,intcolor);
voidinitg();
voidwin_title();
intwin_menu();
voidblock(intx,inty,intcolor);
voiddraw_map();
voidinit_snake();
voidprints();
voidfree_snake();
voidsnake_move();
voidinit_map();
voidfood();
voidgame();
voidprint_scores();
voidpr_warn(intx,inty);
voidhelp();
enum{d_up,d_down,d_left,d_right}DIR=d_left;/*定义一个全局的方向类型的枚举变量DIR,初始方向为左。
其值前面加"d_"和全局变量的键值区分。
*/
structsnake/*定义蛇体的每个小方块的数据结构。
*/
{
charx;/*每个蛇体方块结构的x,y记录蛇体方块的方块坐标。
方块坐标就是以一个方块为单位的坐标。
*/
chary;
structsnake*pre;/*蛇体前指针,指向前一个蛇体方块结构的开始*/
structsnake*next;/*蛇体后指针,指向后一个蛇体方块结构的开始*/
}*HEAD=NULL,*TAIL=NULL;/*定义两个全局的结构体指针用于保存蛇头和蛇尾的位置*/
charOVER[]="GameOver!
";/*用于游戏失败输出的字符串。
*/
charWIN[]="YouWin!
";/*用于游戏胜利输出的字符串。
*/
charMAP[40][60];/*定义一个全局变量数组,用于标记地图方格属性.空地,蛇身,食物.*/
intGAME=0;/*定义一个全局变量GAME对游戏状态进行记录。
不结束游戏.1,结束游戏.*/
intLENGTH=3;/*定义一个全局变量LENGTH记录蛇体长度。
初始长度为.*/
intDIFF=0;/*定义游戏难度全局变量。
*/
intDELAY=20;/*定义延时时间全局变量。
单位是百分之一秒,即,0.01秒.初始值,表示延时.2秒。
*/
intS_SIZE=sizeof(structsnake);/*定义一个全局变量用来存放structsnake结构所占字节数,方便使用。
*/
/*****************************************
下面是,填充一定范围的函数,x,y是填充范围的左上角坐标,xx,yy是右下角坐标。
color是填充颜色。
bar()函数中,x,y
加,xx,yy减,是因为边框线有宽度,所以,要把填充范围往内缩一点。
*****************************************/
voidfill(intx,inty,intxx,intyy,intcolor)/*以(x+1,y+1)为左上角,以(xx-1,yy-1)为右下角,用指定颜色填充此范围。
*/
{
setfillstyle(SOLID_FILL,color);/*SOLID_FILL表示以单一的实体颜色填充,color是填充颜色。
*/
bar(x+1,y+1,xx-1,yy-1);/*以(x+1,y+1)为左上角,以(xx-1,yy-1)为右下角画条形。
*/
}
voidinitg()/*把显示器初始化成图像模式,屏幕的大小为640*480像素。
函数中还有错误检查程序。
*/
{
intgdriver,gmode,errorcode;
gdriver=VGA;/*图形驱动器*/
gmode=VGAHI;/*图形模式*/
registerbgidriver(EGAVGA_driver);/*该函数告诉连接程序在连接时把EGAVGA的驱动程序装入到用户的执行程序中*/
initgraph(&gdriver,&gmode,"");/*初始化图形系统*/
errorcode=graphresult();/*返回最后一次不成功的图形操作的错误代码,没有错误返回*/
if(errorcode!
=0)
{
printf("\n\terror:
\t%s\n",grapherrormsg(errorcode));/*grapherrormsg返回一个错误信息串的指针*/
getch();/*从控制台无回显地取一个字符,不需要回车。
就是为了等待用户按键以后,退出整个程序。
*/
exit
(1);
}
}
voidwin_title()/*绘制游戏主菜单页面的标题字符串。
*/
{
charstr1[]="DOSGameSnake";
charstr2[]="byFineFan";
setviewport(200,55,440,140,1);/*以(200,55)为左上角,(440,140)为右下角设置一个视口。
*/
settextjustify(0,2);/*设置字符串横向左对齐,纵向顶部对齐。
*/
settextstyle(0,0,2);/*设置字符串字体为第一个对应的字体,第二个表示水平输出,字体大小为。
*/
setcolor(WHITE);
outtextxy(10,10,str1);
outtextxy(20,50,str2);
}
voiddraw_menu(inti)/*根据i的取值,画出不同状态的菜单。
*/
{
intmidx,midy;
charstart[]="Start";
charexit[]="Exit";
charhelp[]="Help";
midx=getmaxx()/2;/*midx表示窗口的中心的X坐标,getmaxx()函数返回当前图像窗口的X坐标的最大值,下一行类似。
*/
midy=getmaxy()/2;
setviewport(midx-40,midy-30,midx+40,midy+60,0);
settextjustify(0,2);/*设置字符串横向左对齐,纵向顶部对齐。
*/
settextstyle(0,0,2);/*设置字符串字体为第一个对应的字体,第二个表示水平输出,字体大小为。
*/
switch(i)
{
case0:
clearviewport();
fill(0,0,80,30,YELLOW);/*i为时,把第一个选项"Start"高亮。
*/
setcolor(GREEN);/*将当前图形屏幕的当前画笔颜色置为绿色,“GREEN”必须全部大写。
*/
outtextxy(3,10,start);
outtextxy(10,40,help);
outtextxy(10,70,exit);
break;
case1:
clearviewport();
fill(0,30,80,60,YELLOW);
setcolor(GREEN);
outtextxy(3,10,start);
outtextxy(10,40,help);
outtextxy(10,70,exit);
break;
case2:
clearviewport();
fill(0,60,80,90,YELLOW);
setcolor(GREEN);
outtextxy(3,10,start);
outtextxy(10,40,help);
outtextxy(10,70,exit);
break;
}
}
intwin_menu()/*制作主菜单窗口,玩家选择"Start"返回,选择"Exit"返回。
*/
{
intkey,i=0;/*key记录键盘返回值,i根据按键变化,必须初始化为,因为后面的draw_menu()函数先要根据i值绘出菜单的初始画面。
退出后win_menu()函数返回i的值。
*/
do
{
draw_menu(i);
key=bioskey(0);/*bioskey(0)函数会使整个程序在此处暂停,直到用户按下键盘的任何一个键。
*/
if(key==UP)
{
if(i==0)continue;/*i等于时,已经是最小值了,不能再减小了,所以结束此次循环。
*/
i--;
}
if(key==DOWN)
{
if(i==2)continue;/*i等于时,已经是最大值了,不能再增大了,所以结束此次循环。
*/
i++;
}
if(key==ENTER)break;
}
while
(1);
returni;
}
voidblock(intx,inty,intcolor)/*填充小方块的函数.x,y是小方块的方块坐标。
*/
{
fill(x*SIZE,y*SIZE,x*SIZE+SIZE,y*SIZE+SIZE,color);/*此处本来应该将前一对坐标分别加,后一对坐标分别减,才能把小
方块之间的分隔线留出来,但是,fill()函数中已经做了这一步,所以此处不用做了。
用此函数画
蛇体的小方块时也一样,坐标不用再变化了,因为fill()函数已经把空隙留出来了。
*/
}
voidhelp()/*输出按键说明画面。
*/
{
charh1[]="KeyUptoturnup.";
charh2[]="KeyDowntoturndown.";
charh3[]="KeyLefttoturnleft.";
charh4[]="KeyRighttoturnright.";
charh5[]="KeyEntertoenterorreturn.";
setviewport(0,0,639,479,0);/*建立一个全屏视口,因为坐标从开始,所以,虽然横向有个像素,但,横坐标最大值是,纵坐标同理,横纵坐标一定不能超过最大值
639和479,否则,会出错,例如,建立的视口不是全屏,而是,只占据部分屏幕的视口。
*/
fill(0,0,639,479,LIGHTGRAY);
setcolor(RED);
setlinestyle(0,0,3);
rectangle(3,3,636,476);
settextjustify(0,2);/*设置字符串横向左对齐,纵向顶部对齐。
*/
settextstyle(0,0,2);/*设置字符串字体为第一个对应的字体,第二个表示水平输出,字体大小为。
*/
setcolor(BLUE);
outtextxy(30,30,h1);
outtextxy(30,70,h2);
outtextxy(30,110,h3);
outtextxy(30,150,h4);
outtextxy(30,190,h5);
while
(1)/*等待玩家按Enter键,返回主菜单。
*/
{
if(bioskey
(1))
{
if(bioskey(0)==ENTER)return;
}
}
}
voiddraw_map()/*绘制游戏的地图。
*/
{
charmap_title[]="GameSnakeScores:
";
inti=0,j=0;
cleardevice();/*清除全屏的显示内容。
*/
setviewport(20,40,620,80,0);/*建立地图标题栏的视口。
屏幕中心像素坐标为(320,240),由此可以算出setviewport()函数的前四个坐标参数的值。
*/
fill(0,0,600,40,YELLOW);/*将地图标题栏填充底色为黄色.*/
setcolor(WHITE);
rectangle(-3,-3,603,40);
settextjustify(0,2);/*设置字符串横向左对齐,纵向顶部对齐。
*/
settextstyle(0,0,2);/*设置字符串字体为第一个对应的字体,第二个表示水平输出,字体大小为。
*/
setcolor(LIGHTBLUE);/*设置地图标题文字的颜色。
*/
outtextxy(10,10,map_title);
setviewport(20,80,620,440,0);/*建立地图的方块部分的视口,大小为,600*360.可以划分成60*36个小方块.地图的方块部分应该重新建立视口,方便以后进行蛇体是否
出界的判断条件的设定,方便以后放置食物时,食物坐标的设定。
*/
setcolor(WHITE);
rectangle(-3,0,603,363);/*画地图边框线,*/
for(i=0;i<600;i+=SIZE)
for(j=0;j<360;j+=SIZE)
{
fill(i,j,i+10,j+10,LIGHTGRAY);
}
}
voidinit_snake()/*初始化蛇体,产生游戏刚开始时的三个蛇体方块的数据。
因为,有时要从尾部删除蛇尾节点,所以必须用双向链表。
*/
{/*最初的三个蛇体方块的方块坐标分别为(40,10)(41,10)(42,10)。
*/
structsnake*p;
HEAD=p=(structsnake*)malloc(S_SIZE);
p->x=40;
p->y=10;
p->pre=NULL;
MAP[10][40]=1;/*二维数组对应一块平面区域时,行坐标是纵坐标y,列坐标是横坐标x。
*/
p->next=(structsnake*)malloc(S_SIZE);
p->next->pre=p;
p=p->next;
p->x=41;
p->y=10;
MAP[10][41]=1;
p->next=(structsnake*)malloc(S_SIZE);
p->next->pre=p;
p=p->next;
p->x=42;
p->y=10;
MAP[10][42]=1;
p->next=NULL;
TAIL=p;
}
voidprints()/*显示蛇体的函数。
*/
{
structsnake*p;
if(HEAD==NULL)return;/*对蛇体为空的情况不予显示,否则,会显示错误,例如,在地图画面的左上角显示一个绿色小方块.*/
block(HEAD->x,HEAD->y,LIGHTBLUE);/*先把蛇头画出,画成淡蓝色。
*/
for(p=HEAD->next;p!
=NULL;p=p->next)
{
block(p->x,p->y,LIGHTGREEN);
}
}
voidfree_snake()/*释放蛇体所占的内存。
*/
{
structsnake*p1,*p2;
p1=HEAD;
HEAD=NULL;
for(;p1!
=NULL;)
{
p2=p1;
p1=p1->next;
free(p2);
}
}
voidpr_warn(intx,inty)/*形参x,y表示输出警告字符串的像素坐标。
此处的坐标是在地图方块部分内的相对像素坐标。
*/
{
charwarn1[]="Playgames,don'tbeplayedbygames!
";
charwarn2[]="Studynow!
!
!
!
Worknow!
!
!
!
";
fill(x-10,y-10,x+590,y+60,YELLOW);
settextjustify(0,2);/*设置字符串横向左对齐,纵向顶部对齐。
*/
settextstyle(0,0,2);/*设置字符串字体为第一个对应的字体,第二个表示水平输出,字体大小为。
*/
setcolor(RED);
outtextxy(x,y,warn1);
outtextxy(x,y+40,warn2);
}
voidsnake_move()/*蛇体移动函数,此函数内,完成了对蛇体是否撞墙,是否撞到自己,是否吃到食物的检测,并做相应操作.*/
{
structsnake*p;
p=HEAD;
HEAD=(structsnake*)malloc(S_SIZE);/*建立蛇体移动到下一步的蛇头HEAD,并暂时把当前蛇头的方块坐标赋值给下一步的蛇头。
*/
HEAD->x=p->x;/*此时,下一步的蛇头还没有和蛇体连接上。
*/
HEAD->y=p->y;
switch(DIR)/*根据方向,改变下一步的蛇头的方块坐标.*/
{
cased_up:
HEAD->y--;
break;
cased_down:
HEAD->y++;
break;
cased_left:
HEAD->x--;
break;
cased_right:
HEAD->x++;
break;
}
if(H