c学习------贪吃蛇实现

转载自: https://blog.csdn.net/qq_40953281/article/details/79315254
[其中对蛇倒退、stop时蛇会消失,共两处作出调整,以下运行环境为window10 vistual c++6.0]

#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <Windows.h>
#include <time.h>

#define up 'w'
#define down 's'
#define left 'a'
#define right 'd'
#define stop 'p'

//------------------------------------------------------------------------------
void welcome();  //开始界面
void gotoxy(int x, int y);  //设置光标函数
void creatgraph();  //play主界面画面[上下边框、蛇]
void gotoprint(int x, int y);  //光标设置
void creatfood(); //随机数生成食物
int judge(); //判断蛇是否与墙壁发生碰撞、蛇头是否和身体重叠在一块
void Finish(); //游戏失败后打印分数
int ClickControl(); //监听点击事件
void MovingBody(); //蛇移动
void Eating(); //蛇吃东西
void gotdelete(int x, int y); //清空■[用"  "代替]
void ChangeBody(int a, int b); //蛇移动后,得到蛇新的链表
void if_change_click(); //判断蛇是否能改变前进方向

typedef struct Snake{  //蛇
	int x;
	int y;
	Snake *Next;
}snake;

struct Food{  //食物
	int x;
	int y;
};

//--------------------------全局变量定义--------------------------
Food food;
snake *head, *tail;
char name[20];   //玩家名字
int score = 0;   //玩家分数
char click = 1;  
char tmp_for_click = 1; //记录每次键盘敲击的字符
char way_before_stop = 1; //记录敲下stop之前蛇的运行方向
int speed;       //蛇的前进速度


//----------------------------------------------------函数----------------------------------------------------
int main(){
	system("color 0B"); //设置背景
	welcome(); //展示欢迎界面
	creatgraph(); //展示play界面
	creatfood(); //创造食物
	if(ClickControl() == 0){
		return 0;
	}
	return 0;
}

//======================欢迎界面======================
void welcome(){
	gotoxy(15,10);
	printf("/==============================================/");
	gotoxy(15, 20);
	printf("/==============================================/");
	gotoxy(20, 13);
	printf("WELCOME TO THE GAME OF RETRO SNAKE");
	gotoxy(14, 16);
	printf("请在英文输入法中操作,wasd控制p暂停");
	gotoxy(20, 18);
	printf("PLEASE ENTER YOUR NAME:");
	scanf("%s", &name);
	system("cls");
}

//======================play主界面样式======================
void creatgraph(){
	//[ 0]  [2]               [58]   
	//    ■   ■■■...■■■  . ■      ■
	//[ 1]
	//    ■                       ■ 
	//[ 2].                       .
	//[ 3].                       .
	//[ 4].                       .
	//
	//[29]
	//     ■   ■■■...■■■ . ■      ■
	//
	//-------呈正方形,各边30个-------
	int i;
	for(i=0; i<60; i+=2){ //因为一个■[宽占2,高占1]
		gotoprint(i, 0);  //上边框
		gotoprint(i, 29); //下边框
	}
	for(i=1; i<29; i++){  
		gotoprint(0, i);  //左边框
		gotoprint(58, i); //右边框
	}
	gotoxy(63, 10);    //右侧屏幕记录打印
	printf("hello %s, welcome to play", name); 
	gotoxy(63, 15);
	printf("your score is: %d 分", score);
    
	//蛇初始化[位置、长度(用链表)]
	head = (snake*)malloc(sizeof(snake)); //蛇头
	head->x = 16;
	head->y = 15;

	tail = (snake*)malloc(sizeof(snake)); //尾端[此tail不是蛇身体一部分,仅作为蛇链表结尾]
	tail ->Next = NULL;
	tail->x = 4;
	tail->y = 2;

	snake *p = (snake*)malloc(sizeof(snake)); //蛇初始身体;
	snake *q = (snake*)malloc(sizeof(snake));
	p->x = 16;
	p->y = 16;
	q->x = 16;
	q->y = 17;
	head->Next = p;  //蛇各部分连接
	p->Next = q;
	q->Next = tail;
}


//======================坐标相关======================
void gotoxy(int x, int y){
	COORD pos;  //结构体[记录一个字符在控制台屏幕上的坐标]
	HANDLE hOutput; //资源句柄
	pos.X = x;
	pos.Y = y;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);  //GetStdHandle: 获得输入输出屏幕句柄
	SetConsoleCursorPosition(hOutput, pos);  //根据pos定位光标
}


void gotoprint(int x, int y){
	gotoxy(x, y);
	printf("■");
}

void gotodelete(int x, int y){
	gotoxy(x, y);
	printf("  ");
}

//======================食物相关======================
void creatfood(){
	//srand: 随机数发生器初始化函数  time(NULL): 系统时间[单位秒]
	//这样写是为了每次运行都产生不同的随机数
	srand((int)time(NULL)); 
	lable:
	food.x = rand()%54+2;  //2-56
	food.y = rand()%27+1; //1-28
	if(food.x % 2 != 0){
		food.x = food.x+1; //因为食物占两格,所以食物只能出现在x%2=0的位置上
	}
	snake *judge = head; //遍历, 排除蛇身和食物在同一位置
	while(true){
		if(judge->Next == NULL){
			break;
		}
		if(food.x == judge->x && food.y == judge->y){ //如果食物和身体重复,就重新生成食物
			goto lable;
		}
		judge = judge->Next;
	}
	gotoxy(food.x, food.y); 
	printf("口");
}


//是否吃到食物判定[如果吃到了,蛇链表长度加1]
void Eating(){
	if(head->x == food.x && head->y == food.y){
		creatfood();
		snake *_new = (snake *)malloc(sizeof(snake));
		snake *p;
		p = head;	
		while(true){
			if(p->Next->Next == NULL){
				break;
			}
			p = p->Next;
		}
		p->Next = _new; //如果吃到了食物,蛇链就长长一个,分数也加10分
		_new->Next = tail;
		score += 10;
		gotoxy(77, 15);  //分数打印
		printf("%d", score);
	}
}

//======================键盘监听======================
int ClickControl(){
	while(true){ //一直都在监听ww
		if(judge() == 0){
			return 0;
		}
		if(_kbhit()){ //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
			tmp_for_click = _getch(); //得到键盘敲的字符,并根据字符去让蛇运动
		}
		//无论敲不敲键盘,蛇都会按照当前click值去选择方向前进
		MovingBody();
		Eating();
	}
	return 1;
}

//======================蛇移动相关======================
void MovingBody(){
	int count = 0; //统计蛇一共多长
	int a = head->x, b = head->y; //记录头蛇头坐标;
	snake *p = head;
	while(true){ //清空蛇[不会清空蛇尾]
		if(p->Next == NULL){
			break;
		}
		gotodelete(p->x, p->y);
		count++; 
		p = p->Next;
	}
	
    if_change_click();
	switch(click){ //根据键盘事件, 重新画出蛇
			case up:
				head->y -= 1;
				ChangeBody(a, b);
				break;
			case down:
				head->y += 1;
				ChangeBody(a, b);
				break;
			case left:
				head->x -= 2;
				ChangeBody(a, b);
				break;
			case right:
				head->x += 2;
				ChangeBody(a, b);
				break;
			case stop:
				click = way_before_stop; //让click重新等于停止之前蛇运行方向值
				goto label2;
				_getch(); //程序会等待你按下任意键,再执行下面的语句
				break;
	}
        label2:
	p = head;
	while(true){ //输出蛇头新位置、以及整个蛇身的。
		if(p->Next == NULL){
			break;
		}
		gotoprint(p->x, p->y);
		p = p->Next;
	}
	p = head;
	gotoxy(0, 28);
	if(count <= 10){
		speed = 150;
	}
	else{ 
		if(count > 10 && count <=20){
			speed = 100;
		}else{
			if(count > 20 && count <= 40){
				speed = 50;
			}else{speed = 10;}
		}
	}
	Sleep(speed);
}


void ChangeBody(int a, int b){
	//无论蛇怎么移动, 下一段身体都会继承上一段身体的位置
	snake *p = head->Next; //记录蛇头下一段身体数据[p将继承蛇头的位置]
	int mid1, mid2, mid3, mid4;
	mid1 = p->x;
	mid2 = p->y; //记录下一段身体坐标
	//从蛇头下下一段开始,不断继承上一段坐标
	while(true){ 
		//主要是p->Next继承p的坐标[所以p->Next不能为tail, 也就是p->Next->Next!=NULL]
		if(p->Next->Next == NULL){ 
			break;
		}
		mid3 = p->Next->x; //mid3,mid4: 记录下一段身体的坐标
		mid4 = p->Next->y;
		p->Next->x = mid1; //下一段身体坐标改为前一段身体坐标[继承]
		p->Next->y = mid2;
		mid1 = mid3; //继承后,mid1,mid2改为mid3,mid4的值,用于下下一段身体的继承;
		mid2 = mid4;
		p = p->Next; 
	}

	p = head->Next; //蛇头下一段,继承蛇头
	if(p->Next != NULL)
	{
		p->x = a;
		p->y = b;
	}
}


//此函数限制: 只有tmp_for_click为[stop, right, up, left, right]时,click才会改变, 也就是click从开始运行时,就一直是这5个值中的一个
//如果蛇在向上跑,但是玩家点击向下,此时蛇不会改变方向
void if_change_click(){
	if(click == 1 && tmp_for_click == down){//开始的时候,不会给定蛇运动方向,但此时蛇不能向下运动
		return;
	}
	if(tmp_for_click == stop){
		click = tmp_for_click;
		return;
	}
	if((tmp_for_click == right || tmp_for_click == left) && (click != right && click != left)){
		click = tmp_for_click;
		way_before_stop = click; //way_before_stop总是记录stop之前,蛇运行方向的值
		return;
	}
	if((tmp_for_click == up || tmp_for_click == down) && (click != up && click != down)){
		click = tmp_for_click;
		way_before_stop = click;
		return;
	}
}


//======================判断蛇头是否和墙壁发生碰撞、蛇是否与身体碰撞======================
int judge(){
	if(head->x ==0 || head->x == 58 || head->y == 0 || head->y == 29){  //判断蛇头是否和墙壁发生碰撞
		Finish();
		return 0;
	}
	snake *p = head->Next;   //判断蛇头是否和蛇身体发生重叠
	while(true){
		if(p->Next == NULL){ //蛇身遍历完了[没有发生任何碰撞]
			break;
		}
		if(head->x == p->x && head->y == p->y){ //蛇头和身体重叠;
			Finish();
			return 0;
		}
		p = p->Next;
	}
	return 1;
}

//======================游戏失败逻辑======================
void Finish(){  
	system("cls");
	gotoxy(15, 10);
	printf("/==============================================/");
	gotoxy(15, 20);
	printf("/==============================================/");
	gotoxy(18, 14);
	printf("GAME   OVER      o(* ̄▽ ̄*)o");
	gotoxy(20, 16);
	printf("Your Score is %d    hiahiahia", score);
	gotoxy(18, 18);
	printf("还不错哦,     继续努力O(∩_∩)O");
	gotoxy(0, 27);
	system("pause");
}



猜你喜欢

转载自blog.csdn.net/qq_32446743/article/details/85125818