前言
之前课设做的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则继续
}