#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <stdlib.h>
#include <conio.h>
#define U 1
#define D 2
#define L 3
#define R 4
void file_out();
void tips();
void printscore();
void createfood();
/***********************
@func: 蛇的每个节点
***********************/
typedef struct snake{
int x;
int y;
struct snake * next;
}snake;
/***********************
@func: 食物的位置信息
***********************/
typedef struct foodtype{
int x;
int y;
}foodtype;
int score = 0; // 总得分
int add = 10; // 每次增加的分数
int HighScore = 0; // 最高分
int status=R; // 蛇前进的状态
int sleeptime = 150;// 每次运行的时间间隔,控制速度
snake * head; // 蛇头
foodtype * food; // 食物
snake * q; // 遍历蛇时使用的指针
int gameover=0; // 游戏结束的状态, 撞墙, 咬到自己,主动退出游戏
HANDLE hOut; // 控制台句柄
/* 黑色 0 深蓝 1 深绿 2 深蓝绿 3 深红 4 紫 5 暗绿 6
白 7 灰 8 亮蓝 9 亮绿 10 亮蓝绿 11 红12 粉 13
黄 14 亮白 15
*/
/*******************
@func:设置控制台的颜色
@param bg: 背景色
@param fg: 前景色
*******************/
void color(unsigned char bg, unsigned char fg){
WORD wColor = ((bg & 0x0F)<<4 + (fg & 0x0F));
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),wColor);
}
/************************
@func: 设置文字颜色
************************/
void textcolor(char fg){
// 恢复原有颜色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED |
FOREGROUND_GREEN |
FOREGROUND_BLUE);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),fg);
}
/*******************
@func:设置光标的位置
@param x
@param y
*******************/
void gotoxy(int x,int y){
COORD c;
c.X = x;
c.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c);
}
/*******************
@func:不显示控制台光标
*******************/
void close_cur(){
hOut=GetStdHandle(STD_OUTPUT_HANDLE); //获得输出句柄
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOut,&CursorInfo);//获取控制台光标信息
CursorInfo.bVisible=0; //隐藏控制台光标
SetConsoleCursorInfo(hOut,&CursorInfo);//设置控制台光标状态
}
/*******************
@func: 创建游戏地图
0 2 56 (58 59) // 每个块的w: 2个字符,h: 一个字符
1
25
(26)
*******************/
void createmap(){
int i,j;
color(5,0);// 深紫色边框
gotoxy(0,0);
for(i =0;i<60;i++) printf(" "); //绘制上边框
gotoxy(0,26);
for(i =0;i<60;i++) printf(" "); //绘制下边框
for(i = 1;i<26;i++) { //绘制左右边框
gotoxy(0,i);
printf(" ");
gotoxy(58,i);
printf(" ");
}
color(3,0);
for(i=2;i<57;i+=2){ //x : 2 4 6 ... 56
for(j=1;j<26;j++){ //y : 1 2 3 ... 25
gotoxy(i,j);
printf(" ");
}
}
tips();
}
/**************************
@func: 游戏界面右侧的得分和小提示
**************************/
void tips(){
gotoxy(70,4);
textcolor(11);
printf("最高记录: %d", HighScore);
textcolor(13);
gotoxy(75,11);
printf("小提示");
gotoxy(62,13);
textcolor(6);
printf("++------------------------------++");
gotoxy(62,25);
printf("++------------------------------++");
textcolor(3);
gotoxy(66,14);
printf("每个食物的得分: %d分",add);
gotoxy(66,16);
printf("不能撞墙,不能咬到自己");
gotoxy(66,18);
printf("用 ↑↓← →分别控制移动");
gotoxy(66,20);
printf("F1: 加速 F2: 减速");
gotoxy(66,22);
printf("space: 暂停游戏");
gotoxy(66,24);
printf("esc: 退出游戏");
printscore();
}
/*******************
@func: 绘制得分
*******************/
void printscore(){
gotoxy(72,6);
textcolor(14);
printf("得分: %d",score);
}
/********************
@func: 从save.txt中读取最高分
********************/
void file_out(){
FILE*fp;
fp = fopen("save.txt","a+");
fscanf(fp,"%d",&HighScore);
fclose(fp);
}
/****************************
@func: 初始化蛇, 蛇的尾部坐标为: 24,5
****************************/
void initsnake(){
snake * tail;
int i;
tail = (snake*)malloc(sizeof(snake)); // 动态内存分配
tail->x = 24;
tail->y = 5;
tail->next = NULL;
for(i = 1;i<=4;i++){
head = (snake*)malloc(sizeof(snake));
head->next = tail;
head->x = 24+2*i;
head->y = 5;
tail = head;
}
while(tail!=NULL){
gotoxy(tail->x, tail->y);
color(14,0);
printf(" ");
tail=tail->next;
}
createfood();
}
/***************
@func: 随机出现食物
1. 食物不能和蛇身重合
2. 食物的x值坐标要为 偶数
3. 食物要在边界内
***************/
void createfood(){
int ifok=0,x=0,y=0;
while(!ifok){
x= (rand()%28+1)*2; // 为偶数
y= rand()%25 + 1 ;
q= head;
while(q->next!=NULL){
if(q->x==x && q->y ==y) break;// 判断蛇身是否和食物重合
q = q->next;
}
if(q->next==NULL) ifok=1;
}
gotoxy(x,y);
food->x = x;
food->y = y;
color(12,0);
printf(" ");
}
/*****************
@func: 判断是否咬到自己
蛇头的坐标值和蛇身上的任意坐标值重合
*****************/
int biteself(){
q = head->next;
while(q!=NULL){
if(q->x==head->x && q->y==head->y){
return 1;
}
q= q->next;
}
return 0;
}
/**********************
@func: 撞墙的情况
**********************/
int cantcrosswall(){
if(head->x <2||head->x>56|| head->y<1||head->y>25){
return 1;
}
return 0;
}
/**************
@func: 蛇加速前进
两种情况蛇会加速前进: 蛇吃到食物 和按<F1>键
加速时时 间间隔减少10 得分加2
***************/
void speedup(){
if(sleeptime>=50){
sleeptime= sleeptime-10;
add = add+2;
}
}
/*****************
@func: <F2> 蛇减速前进
时间间隔 加 10 , 得分值 比之间减少 2分
得分值 最少为 1
*****************/
void speeddown(){
if(sleeptime<200){
sleeptime += 10;
add -= 2;
if(add<1) add = 1;
}
}
/***********************
@func: 吃到了食物,移动后最后一块不擦除
***********************/
void eatfood(snake*head){
while(head!=NULL){
gotoxy(head->x, head->y);
color(14,0);
printf(" ");
head= head->next;
}
score += add;
speedup();
createfood();
}
/**************************
@func: 没有吃到食物,移动后最后一块要擦除
**************************/
void donteatfood(snake*head){
while(head->next->next!=NULL){
gotoxy(head->x,head->y);
color(14,0);
printf(" ");
head= head->next;
}
//擦除最后一块
gotoxy(head->next->x, head->next->y);
color(3,0);
printf(" ");
free(head->next); // 释放原本最后一位的指针
head->next = NULL;
}
/*********************
@func: 控制移动
**********************/
void snakemove(){
snake* nexthead;
nexthead = (snake*)malloc(sizeof(snake));
nexthead->next = head;
if(status==U){
nexthead->x = head->x;
nexthead->y = head->y-1;
}else if(status==D){
nexthead->x = head->x;
nexthead->y = head->y+1;
}else if(status==R){
nexthead->x = head->x+2;
nexthead->y = head->y;
}else if(status==L){
nexthead->x = head->x-2;
nexthead->y = head->y;
}
if(nexthead->x == food->x && nexthead->y == food->y){// 下一个位置上有食物
eatfood(nexthead);
printscore();
}
else{
donteatfood(nexthead);
}
head = nexthead;
if(cantcrosswall()){
gameover = 1;
}
if(biteself()==1){ // 判断是否咬到自己
gameover = 2;
}
}
/*********************
@func:键盘按键控制,蛇头不能转向与前进方向相反的方向
*********************/
void keyboardControl(){
while(!gameover){
if(GetAsyncKeyState(VK_UP)&0x8000 &&status!=D){
status = U;
}
if(GetAsyncKeyState(VK_DOWN)&0x8000&&status!=U){
status = D;
}
if(GetAsyncKeyState(VK_LEFT)&0x8000&&status!=R){
status = L;
}
if(GetAsyncKeyState(VK_RIGHT)&0x8000&&status!=L){
status = R;
}
if(GetAsyncKeyState(VK_SPACE)&0x8000){
while(1){
Sleep(300);
if(GetAsyncKeyState(VK_SPACE)&0x8000){
break;
}
}
}
if(GetAsyncKeyState(VK_ESCAPE)&0x8000){
gameover = 3;
}
if(GetAsyncKeyState(VK_F1)&0x8000){
speedup();
}
if(GetAsyncKeyState(VK_F2)&0x8000){
speeddown();
}
Sleep(sleeptime);
snakemove();
}
}
/******************
@func: 存储最高分
*******************/
void file_in(){
FILE*fp;
fp = fopen("save.txt","w+");
fprintf(fp,"%d",score);
fclose(fp);
}
/**********************
@func: 游戏结束
**********************/
void endgame(){
int i,j;
textcolor(4);
system("cls");
gotoxy(30,5);
for(i=0;i<40;i++) printf("+");
gotoxy(30,20);
for(i=0;i<40;i++) printf("+");
for(i=6;i<20;i++){
gotoxy(30,i);
printf("+");
gotoxy(69,i);
printf("+");
}
textcolor(15);
gotoxy(45,8);
printf("游戏结束!");
if(gameover==1){
gotoxy(43,10);
printf("您因撞墙而死!!");
}else{
gotoxy(41,10);
printf("您因咬到自己而死!!");
}
gotoxy(45,12);
printf("得分: %d",score);
if(score>HighScore){
gotoxy(42,14);
printf("您已创新的记录!");
file_in();
}else{
gotoxy(40,14);
printf("您距离最高记录还差:%d分",HighScore-score);
}
}
int main(){
close_cur();
srand((unsigned int)time(NULL));
food = (foodtype*)malloc(sizeof(foodtype));
system("mode con cols=98 lines=27");
file_out();
createmap();
initsnake();
keyboardControl();
endgame();
system("pause >nul");
return 0;
}