用链表实现一个贪吃蛇

前言

之前课设做的C语言贪吃蛇代码,忽然想到贴上就不会丢了。
环境:dev-cpp
方法:建立一个文件夹EatingSnake,将“完整代码”中的.h文件命名为snake.h,将.c文件命名为start_game.c,另外创建(最高分保存.txt,游戏总时长记录.txt,作者信息与游戏版本.txt),均放如该文件夹,运行start_game.c即可。

2020-3-13
大一编写的代码,当时代码风格还不规范,其中用到了几个goto…还有缩进、符号的间隔…惨不忍睹。
最开始是用单链表写的,后来因为涉及到一些功能,稍微改了改就有了双链表版。

效果

在这里插入图片描述

核心代码

蛇身初始化
双向链表操作基本和单链表操作相同,只不过每个链节既指向下一个,也指向前一个,方便从后向前遍历。
蛇身初始化其实就是创建一个链表,初始化每个节点的坐标。

SNAKE *snake_init()	//初始蛇身长度为6 
{
	int size,body_x,body_y;
	int k=1;
	head=(SNAKE *)malloc(sizeof(SNAKE ));
	SNAKE *p,*q;
	head->len=6;
	srand(time(0));
	body_x=rand()%(width-9)+7; head->body_x=body_x;
	body_y=rand()%(height-9)+7; head->body_y=body_y;
	q=head;head->pre=NULL;
	while(k<=5)
	{
		p=(SNAKE *)malloc(sizeof(SNAKE ));
		p->body_x=--body_x;
		p->body_y=body_y;
		p->pre=q;
		p->next=NULL; 
		q->next=p;
		q=p;
		tail=p; 
		k++;
	}
	return head;
}

蛇身移动与吃食物
蛇身的移动:实质就是遍历链表,同时更新每个节点的值。
吃到食物:链表的尾插法,也就是我在《单链表的创建——从零开始》中使用的方法。
为什么不用头插法?
这个要具体问题具体解答,想象一下,贪吃蛇迟到一个食物之后,头部插入一个新节点似乎没什么问题。但是一旦这个食物出现在边界,正对着去吃这个食物直接就撞墙了。

void insert(SNAKE *m,int flag)
{
	m->next=head;
	m->pre=NULL;
	head->pre=m;
	head=m;
	if(flag){
		tail->pre->next=NULL;
		tail=tail->pre;	
	}
}
void move(char ch)
{
	int i,flag=1,grow=0;
	SNAKE *last,*m=(SNAKE *)malloc(sizeof(SNAKE ));
	m->body_x=head->body_x;
	m->body_y=head->body_y;
	m->len=head->len;
	m->pre=m->next=NULL;
	gotoxy(tail->body_x,tail->body_y);printf(" ");
	switch(ch)
	{
		case 'w':case 'W':m->body_y--;break;
		case 's':case 'S':m->body_y++;break;
		case 'a':case 'A':m->body_x--;break;
		case 'd':case 'D':m->body_x++;break;
	}
	if(head->body_x==food.x&&head->body_y==food.y)
	{
		grow=1;
		fresh_food();	//吃到食物就刷新
		
	}
	if(grow){
		flag=0;head->len++;m->len++;
		insert(m,flag);
	}
	else  insert(m,flag);
}

完整代码

(1)头文件部分

#ifndef _SNAKE_H_
#define _SNAKE_H_
typedef struct snake SNAKE;
SNAKE *snake_init();
static SNAKE *head,*p,*tail;
void HideCursor();
void fresh_food();
void welcome();
void ctrl_menu();
void move(char ch);
int judge(char ch);
void gotoxy(int x,int y);
void insert(SNAKE *m,int flag);
void show_author_info(char *str);
void draw_boundary(int speed,int best_score);
void show_time(double alltime,int max_score);
void print_end();
int isEnd();
int start(double alltime,int max_score,char *str);
struct snake{		//包括蛇的尺寸,每节蛇身的x y坐标 
	int len;
	int body_x,body_y;
	SNAKE *pre,*next; 
};
struct FOOD{
	int x;
	int y;
}food;
#endif

(2)主体代码部分

//贪吃蛇第二版▇
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<math.h>
#include<windows.h>
#include<ctype.h>
#include"snake.h"
#define height 25
#define width 55
int main()
{
	FILE *fp1=fopen("最高分保存.txt","r+");
	FILE *fp2=fopen("游戏总时长记录.txt","r+");
	FILE *fp3=fopen("作者信息与游戏版本.txt","rt"); 
	double st,en,alltime;
	char ch,pre='d',str[100];
	int mark,t,speed,max_score,lenth,i=0;
	fscanf(fp1,"%d",&max_score);
	fscanf(fp2,"%lf",&alltime);
	fscanf(fp3,"%s",&str);
	rewind(fp3);
	while((ch=fgetc(fp3))!=EOF){
		str[i]=ch;i++;
	}
	st=clock();
	welcome(); 
C:	
	speed=start(alltime,max_score,str);
	snake_init();	//创建一条蛇 
	fresh_food();	//刷新食物
	draw_boundary(speed,max_score);
	while(ch=getch())
	{
		if(judge(ch)) continue;
		A:
		{
		mark=0;
		while(!kbhit())
		{
			if(abs(ch-pre)==4||abs(ch-pre)==3)	//如果执行操作与运动方向相反则不反馈 
			{
				mark=1;
				break;	//跳出本层while循环 
			}
			move(ch);
			if(isEnd()) goto B;	//如果结束了,直接跳出外层循环 
			draw_boundary(speed,max_score);	//画界面 
		}
		if(mark){	//若执行与运动方向相反,则重新循环运动,蛇身无反馈 
			ch=pre;	//本次输入无效,方向仍为之前的方向 
			goto A;
		}
		pre=ch;	
		}
	}
	B:{
		en=clock();
		alltime+=((en-st)/CLOCKS_PER_SEC);rewind(fp2);
		fprintf(fp2,"%lf",alltime);
		lenth=--head->len;
		gotoxy(65,27);
		printf("press 1 to begin a new game,2 to end:");
		rewind(fp1);
		if(lenth>max_score){
			fprintf(fp1,"%d",lenth);
			gotoxy(35,14);
			printf("Congratulations to New Record!:%d",lenth);
		}
		if(getch()=='1'){
			system("cls");goto C;
		}
	}
	return 0;
}
void gotoxy(int x,int y)	//锁定坐标 
{
	COORD coord={x,y};
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord);
}
void HideCursor()	//隐藏光标 
{
	CONSOLE_CURSOR_INFO cursor_info={1,0};
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info); 
}
int judge(char ch)
{
	if(ch=='w'||ch=='a'||ch=='s'||ch=='d') return 0;
	else
		if(ch=='W'||ch=='A'||ch=='S'||ch=='D') return 0;
	else return 1;
}
void show_time(double alltime,int max_score)
{
	gotoxy(35,14);printf("You have played for %lf s",alltime);
	gotoxy(35,15);printf("your best score:%d",max_score);
}
void show_author_info(char str[])
{
	gotoxy(35,15);printf("%s",str);
}
void ctrl_menu()
{
	printf("\t\t\t\t\t|                                     |\n");
}
void welcome()
{
	int i; 
	gotoxy(25,12);
	printf("It's time to begin the Gluttonous Snake,Press SPACE to pause");
	for(i=0;i<3;i++)
	{
		Sleep(500);
		printf(".");
	}
	sleep(3);
} 
int start(double alltime,int max_score,char str[])
{
	int i,j,speed;
	char ch;
	D:
	system("cls");
	for(i=1;i<=6;i++) printf("\n");
	printf("\t\t\t\t\t|------Please enter the option:-------|\n");ctrl_menu();ctrl_menu();
	printf("\t\t\t\t\t|             1.TURTLR                |\n");ctrl_menu();
	printf("\t\t\t\t\t|             2.EASY                  |\n");ctrl_menu();
	printf("\t\t\t\t\t|             3.NORMAL                |\n");ctrl_menu();
	printf("\t\t\t\t\t|             4.HARD                  |\n");ctrl_menu();
	printf("\t\t\t\t\t|             5.CRAZY                 |\n");ctrl_menu();
	printf("\t\t\t\t\t|             6.GAME RECORD           |\n");ctrl_menu();
	printf("\t\t\t\t\t|             7.AUTHOR'S INFO         |\n");ctrl_menu();ctrl_menu();
	printf("\t\t\t\t\t|-------------------------------------|\n\n");
	gotoxy(0,28);printf("\tUP:W/w DOWN:S/s LEFT:A/a RIGHT:D/d");
	HideCursor();//隐藏光标 
	ch=getch();
	switch(ch-'0')
	{
		case 1:case 2:case 3:case 4:case 5:speed=(10-(ch-'0')+1)*1;break;
		case 6:system("cls");show_time(alltime,max_score);sleep(5);goto D;break;
		case 7:system("cls");show_author_info(str);sleep(5);goto D;break;
		default:break;		
	}	
	system("cls");
	return speed;
}
SNAKE *snake_init()	//初始蛇身长度为6 
{
	int size,body_x,body_y;
	int k=1;
	head=(SNAKE *)malloc(sizeof(SNAKE ));
	SNAKE *p,*q;
	head->len=6;
	srand(time(0));
	body_x=rand()%(width-9)+7; head->body_x=body_x;
	body_y=rand()%(height-9)+7; head->body_y=body_y;
	q=head;head->pre=NULL;
	while(k<=5)
	{
		p=(SNAKE *)malloc(sizeof(SNAKE ));
		p->body_x=--body_x;
		p->body_y=body_y;
		p->pre=q;
		p->next=NULL; 
		q->next=p;
		q=p;
		tail=p; 
		k++;
	}
	return head;
}
void fresh_food()	//刷新食物 
{
	int x,y;
	A:{
		srand(time(NULL));
		x=rand()%(width-4)+2;
		y=rand()%(height-4)+2;
		food.x=x;food.y=y;
		for(p=head;p!=NULL;p=p->next)	//保证食物不产生于蛇身 
			if(x==p->body_x&&y==p->body_y) goto A;
	}
}
void insert(SNAKE *m,int flag)
{
	m->next=head;
	m->pre=NULL;
	head->pre=m;
	head=m;
	if(flag){
		tail->pre->next=NULL;
		tail=tail->pre;	
	}
}
void move(char ch)
{
	int i,flag=1,grow=0;
	SNAKE *last,*m=(SNAKE *)malloc(sizeof(SNAKE ));
	m->body_x=head->body_x;
	m->body_y=head->body_y;
	m->len=head->len;
	m->pre=m->next=NULL;
	gotoxy(tail->body_x,tail->body_y);printf(" ");
	switch(ch)
	{
		case 'w':case 'W':m->body_y--;break;
		case 's':case 'S':m->body_y++;break;
		case 'a':case 'A':m->body_x--;break;
		case 'd':case 'D':m->body_x++;break;
	}
	if(head->body_x==food.x&&head->body_y==food.y)
	{
		grow=1;
		fresh_food();	//吃到食物就刷新
		
	}
	if(grow){
		flag=0;head->len++;m->len++;
		insert(m,flag);
	}
	else  insert(m,flag);
}
void draw_boundary(int speed,int best_score)		//显示 
{
	int i,j,k,flag;
	Sleep(speed);
	for(i=0;i<width;i+=2)
	{
		gotoxy(i,0);
		printf("▇"); 
	}
	for(i=0;i<height-2;i++)
	{
		gotoxy(0,i+1);
		printf("▇");
		for(j=0;j<width-2;j++)
		{
			flag=1;
			if(i==food.y&&j==food.x)		//显示食物 
			{
				gotoxy(food.x,food.y);
				printf("*");
				flag=0;
			}
			for(p=head;p->next!=NULL;p=p->next)
			{
				if(i==p->body_y&&j==p->body_x)
				{
					gotoxy(j,i);
					printf("O");
				}
			}
			if(flag)
			{
				HideCursor();	
				printf(" ");	
			}
		}
		gotoxy(width-1,i+1);
		printf("▇");
	}
	for(i=0;i<width;i+=2)
	{
		gotoxy(i,height-2);
		printf("▇");	
	}
	printf("\nTo laugh is to risk apprearing the fool\n");
	printf("To weep is to risk being calling sentimental.");
	gotoxy(70,10);
	printf("your score:%d",head->len-1);
	gotoxy(70,14);
	printf("best score:%d",best_score);
}
void print_end()
{
	gotoxy(35,13);
	printf("YOU LOSE!");
	printf("You have got a grade of:%d\n ",head->len-1);
}
int isEnd()
{
	int i,j,flag=0;
	if(head->body_x==0||head->body_x==(width-2)||head->body_y==0||head->body_y==(height-2)){
		system("cls");
		gotoxy(35,12);
		printf("You konock your head against a brick wall!");print_end();flag=1;
	}
	for(p=head->next;p!=NULL;p=p->next)
		if(head->body_x==p->body_x&&head->body_y==p->body_y){
			system("cls");
			gotoxy(35,12);
			printf("You eat yourself!");
			print_end();flag=1;
		}
	return flag;//1代表结束,0则继续 
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值