一:概要设计
程序功能:通过“w a s d”按键实现对蛇移动方向的控制,通过吃豆增加蛇身长度, 同时获得并记录游戏分数;game over之后可以选择结束或再次进行游戏
函数定义调用关系:
int main():主函数; 调用所有函数;
void setpos(int x,int y):坐标设置函数;
int HitWall():判断是否撞墙函数;调用void setpos(int x,int y);
int SelfBite():判断蛇身是否打结;调用void setpos(int x,int y);system();
int Move():移动函数;调用int HitWall();int SelfBite();void setpos(int x,int y);sleep();free();
void Welcome():初始界面函数;调用void setpos(int x,int y);
void DrawFrame():画边框函数;
void InitSnake():初始化蛇函数;
void CreateFood():创建豆函数;调用void setpos(int x,int y);
void PlayGame():游戏移动循环函数;调用void setpos(int x,int y);
void free_snake(Snake*head):释放资源函数;
char GetKeyBoard():键盘控制函数;
void Restart():重新开始函数;调用 void setpos(int x,int y);
程序流程:
将蛇身,食物,墙数据化: 蛇身用链表表示并用“@”打印,食物位置由随机数函数确认并用“?”打印,而墙的范围 可随意改变,确认一组长宽后,用“#”打印。
设定初始参数:
规定一个适当的初始速度,一个初始蛇身长度。
获取键位指示,模拟蛇的移动:
规定蛇初始的移动方向为向右方向,用GetAsyncKeyState()函数来判断键位指令判断其合理性,如果键位输入的命令是有效命令,则执行这条命令。蛇的运动过程则用链表头插法表示,同时去除蛇尾。
判断吃到食物和撞到自己和撞到墙:
吃食物:
通过比较蛇头下一次出现的位置和目标处的物体,使用if-else语句做出判断如果目标处是食物,则将其变为新的蛇头,蛇尾不变。
撞到墙:
比较蛇头下一次出现的位置和目标处的物体,使用if-else语句做出判断,如果目标处是墙或者自己身体则游戏结束。
细节处理和游戏引导:
在游戏开始和结束出给出相应文字引导,过程中给出分数等基本信息。
调用库函数:stdio.h;conio.h;stdlib.h;time.h;string.h;windows.h;
二:详细设计
地图初始化
void DrawFrame(){
int i=0;
for(i=0;i<60;i+=2)
{
setpos(i,0);
printf("#");
setpos(i,DOWN_WALL);
printf("#");
}
for(i=0;i<DOWN_WALL;i++)
{
setpos(0,i);
printf("#");
setpos(RIGHT_WALL,i);
printf("#");
}
return 0;
}
豆的创建
void CreateFood(){
srand(time(0));
flag:
while(1)
{foody=rand()%(DOWN_WALL-1+1-1)+1;
foodx=rand()%(RIGHT_WALL-2+1-3)+3;
if(foodx%2==0)
break;
}
p=head;
while(1)
{
if(p->x==foodx&&p->y==foody)
{goto flag;
}
if(p->next==NULL)
{break;
}
p=p->next;
}
setpos(foodx,foody);
printf("?");
}
控制蛇身移动
int Move()
{int ret;
if((key!='a')&&(key!='w')&&(key!='s')&&(key!='d'))
{return 0;
}
ret=HitWall();
ret+=SelfBite();
if(ret==1)
{
return 1;
}
p=(Snake*)malloc(sizeof(Snake));
p->next=head;
switch(key)
{case'd':
p->x=head->x+2;
p->y=head->y;
break;
case'w':
p->x=head->x;
p->y=head->y-1;
break;
case's':
p->x=head->x;
p->y=head->y+1;
break;
case'a':
p->x=head->x-2;
p->y=head->y;
break;
}
setpos(p->x,p->y);
printf("@");
head=p;
Sleep(speed);
if(p->x==foodx&&p->y==foody)
{
CreateFood();
score+=10;
setpos(60,7);
printf("得分%d",score);
}
else{
while(p->next->next!=NULL)
p=p->next;
setpos(p->x,p->y);
printf(" ");
free(p->next);
p->next=NULL;
p=head;
}
return 0;
}
判断game是否over
int HitWall(){//判断是否撞墙
if(head->x==0||head->x==RIGHT_WALL||head->y==0||head->y==DOWN_WALL)
{
setpos(DOWN_WALL,14);
printf("Game Over!");
return 1;
}
return 0;
}
int SelfBite(){//判断是否打结
while(p->next!=NULL)
{
p=p->next;
if(head->x==p->x&&head->y==p->y)
{setpos(DOWN_WALL,14);
printf("Game Over!");
return 1;
system("cls");
main();
}
}
return 0;
}
通过键盘控制
char GetKeyBoard() {//获取键盘输入
char key=0;
if(_kbhit()){
key=_getch();
}
return key;
}
三:功能展示
游戏开始
吃豆并获得分数
game over之后可以选择重开gg下一把或者摆烂不玩了
四:成员分工
@assassin_zzy :代码创建
@dlkd20:代码优化
@MySQL搬砖人:实验报告撰写
五:总结与建议
c语言功能之简陋实在是令人叹为观止,但不断编写新函数并使用也确实增强了我们的代码编写能力。在编写贪吃蛇的过程中,需要思考并逐步完成游戏的各项功能,并在不断修改调试之中对程序代码进行纠错与完善,无形之中提升了c语言能力,也给我们带来无与伦比的成就感与收获感。简而言之,获益良多。
六:于文末附上完整代码
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<windows.h>
#define DOWN_WALL 20
#define RIGHT_WALL 58
typedef struct s_snake
{
int x; //蛇头横坐标
int y;//蛇头纵坐标
struct s_snake*next ;
} Snake;
void setpos(int x,int y);
int HitWall();
int SelfBite();
int Move();
Snake *head, *end;
Snake *p;
int speed=310;
int level=0, score=0;
int foodx, foody;
char key;
void Welcome();
void DrawFrame();
void InitSnake();
void CreateFood();
void PlayGame();
void free_snake(Snake*head);
char GetKeyBoard();
void Restart();
int main(){
GetKeyBoard();
ConsoleSettings();
Welcome();
DrawFrame();
InitSnake();
CreateFood();
PlayGame();
Restart();
free_snake(head);
return 0;
}
void setpos( int x, int y){//设置光标
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),(COORD){
x,y
});
}
int HitWall(){
if(head->x==0||head->x==RIGHT_WALL||head->y==0||head->y==DOWN_WALL)
{
setpos(DOWN_WALL,14);
printf("Game Over!");
return 1;
}
return 0;
}
int SelfBite(){
while(p->next!=NULL)
{
p=p->next;
if(head->x==p->x&&head->y==p->y)
{setpos(DOWN_WALL,14);
printf("Game Over!");
return 1;
system("cls");
main();
}
}
return 0;
}
void DrawFrame(){
int i=0;
for(i=0;i<60;i+=2)
{
setpos(i,0);
printf("#");
setpos(i,DOWN_WALL);
printf("#");
}
for(i=0;i<DOWN_WALL;i++)
{
setpos(0,i);
printf("#");
setpos(RIGHT_WALL,i);
printf("#");
}
return 0;
}
void InitSnake()
{int i=0;
end=(Snake*)malloc(sizeof(Snake));
end->x=20;
end->y=15;
end->next=NULL;
for(i=1;i<=3;i++)
{
head=(Snake*)malloc(sizeof(Snake));
head->x=20+2*i;
head->y=15;
head->next=end;
end=head;
}
while(end->next!=NULL)
{setpos(end->x,end->y);
printf("@");
end=end->next;
}
}
void CreateFood(){
srand(time(0));
flag:
while(1)
{foody=rand()%(DOWN_WALL-1+1-1)+1;
foodx=rand()%(RIGHT_WALL-2+1-3)+3;
if(foodx%2==0)
break;
}
p=head;
while(1)
{
if(p->x==foodx&&p->y==foody)
{goto flag;
}
if(p->next==NULL)
{break;
}
p=p->next;
}
setpos(foodx,foody);
printf("?");
}
void PlayGame(){
int mv_ret=0;
key='d';
while(1)
{if((GetAsyncKeyState(0x57)&&0x8000)&&key!='s')
{key='w';
}
else if((GetAsyncKeyState(0x53)&&0x8000)&&key!='w')
{key='s';
}
else if((GetAsyncKeyState(0x44)&&0x8000)&&key!='a')
{key='d';
}
else if((GetAsyncKeyState(0x41)&&0x8000)&&key!='d')
{key='a';
}
else if (GetAsyncKeyState(0x50)&&0x8000)
{while(p->next!=NULL)
p=p->next;
setpos(p->x,p->y);
printf("@");
while(1)
{
Sleep(100);
if(GetAsyncKeyState(0x50)&&0x8000)
{break;
}
}
setpos(p->x,p->y);
printf(" ");
}
else if(GetAsyncKeyState(0x51)&&0x8000)
{return ;
}
if(score==level&&speed>10)
{speed-=10;
level+=30;
}
mv_ret=Move();
if(mv_ret==1)
{break;
}
}
}
int Move()
{int ret;
if((key!='a')&&(key!='w')&&(key!='s')&&(key!='d'))
{return 0;
}
ret=HitWall();
ret+=SelfBite();
if(ret==1)
{
return 1;
}
p=(Snake*)malloc(sizeof(Snake));
p->next=head;
switch(key)
{case'd':
p->x=head->x+2;
p->y=head->y;
break;
case'w':
p->x=head->x;
p->y=head->y-1;
break;
case's':
p->x=head->x;
p->y=head->y+1;
break;
case'a':
p->x=head->x-2;
p->y=head->y;
break;
}
setpos(p->x,p->y);
printf("@");
head=p;
Sleep(speed);
if(p->x==foodx&&p->y==foody)
{
CreateFood();
score+=10;
setpos(60,7);
printf("得分%d",score);
}
else{
while(p->next->next!=NULL)
p=p->next;
setpos(p->x,p->y);
printf(" ");
free(p->next);
p->next=NULL;
p=head;
}
return 0;
}
void Welcome()
{DrawFrame();
setpos(25,8);
printf("press any key to start!");
while((getch())==1)
continue ;
system("cls");
}
char GetKeyBoard() {//获取键盘输入
char key=0;
if(_kbhit()){
key=_getch();
}
return key;
}
void ConsoleSettings(){//设置控制台字体
CONSOLE_CURSOR_INFO cursor;
cursor.bVisible=FALSE;
cursor.dwSize=sizeof(cursor);
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize=sizeof cfi;
cfi.nFont=0;
cfi.dwFontSize.X=20;
cfi.dwFontSize.Y=20;
cfi.FontFamily=FF_DONTCARE;
cfi.FontWeight=FW_NORMAL;
wcscpy_s(cfi.FaceName,_countof(cfi.FaceName),L"Raster");
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE),FALSE,&cfi);
}
void free_snake(Snake*head)
{if(head==NULL||head->next==NULL){
return;
}
while((p=head)!=NULL)
{head=head->next;
free(p);
}
}
void Restart(){
while(1)
{
setpos(60,8);
printf("Try again? y/n");
flag:
getch();
if(getch()=='y')
{
system("cls");
score=0;
main();
}
if(getch()=='n')
{ exit (0);
}
else
{setpos(60,9);
printf("please choose one right key");
goto flag;
}
}
}
感谢观看!take care of yourself and be well!