TCS 贪吃蛇C源码

tech2022-09-11  107

linux版:这是我自己独立制作的自认为比较成功的小游戏,贪吃蛇主要运用了链表技术以及排序算法。

原版是2014年的windows版后期我移植到了linux:

// mtcsV1_2.cpp : Defines the entry point for the console application. // //#include "stdafx.h" #include<stdio.h> //#include<conio.h> #include<termios.h> #include<stdlib.h> //#include<windows.h> #include<time.h> #define MAX 3200 #define LEN sizeof(Snake) typedef unsigned char uchar; //------------------------ typedef struct snake{ char x; char y; struct snake *next; struct snake *last; }Snake;//双向链表 Snake *h,*tp1,*tp2; Snake *t,*hp1,*hp2;//表尾tail //----------------------(数组图片)结构体化--------------------------------- typedef struct Pic{ char x[MAX]; char y[MAX]; int pnum; }PIC; //----------------------------------------------------------------------- PIC p0,p1; //------------------------ char f;//食物标志 int m=0,n=0;//食物位置 int a=20; int BaseCount=0; //------------------------ int color=5;//设置蛇头颜色 int score=0; int _score=0;//历史分数变量 int step=0;//记录吃食物所用步数 int foodlose=0;//记录未吃到的食物数 //SYSTEMTIME SysTime; char TimeStr[30]; #define AutoStepNum 40 //自动行走的初始步数 #define AutoStepWidth 1 #define SIMPLE 300 #define DIFFICULT 100 #define ORDINARY 1 //180 //运行界面大小设置 #define XStart 10 #define XEnd 40 #define YStart 2 #define YEnd 12 #define XLen (XEnd-XStart) #define YLen (YEnd-YStart) #define ALLDISP 1 #define PARTDISP 0 // //#define AUTOGO //--------函数声明------- void save(); void load(); void creat(); void gameover(); void move(void); char autogo(); //------------------------ static struct termios initial_settings,new_settings; static int peek_character = -1; void init_keyboard(void) { tcgetattr(fileno(stdin),&initial_settings); new_settings = initial_settings; new_settings.c_lflag &= (~ICANON) ;//(ICANON|ECHO|ISIG); new_settings.c_lflag &= (~ ECHO) ;//(ICANON|ECHO|ISIG); new_settings.c_lflag |= ISIG ;//(ICANON|ECHO|ISIG); new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; tcsetattr(fileno(stdin),TCSANOW,&new_settings); } void close_keyboard(void) { tcsetattr(0,TCSANOW,&initial_settings); } /* 1. NTR:该字符使终端驱动程序向与终端相连的进程以送SIGINT信号 QUIT:该字符使终端驱动程序向与终端相连的进程发送SIGQUIT信号 EOF;该字符使终端驱动程序将输入行中的全部字符传递给正在读取输入的应用程序.如果输入行为空,read调用将返回0,就好像在文件尾调用read一样 ... 2.TIME和MIN值 这两个值只用于非标准模式,两者结合共同控制对输入的读取方式,还能控制在一个程序试图与一个终端关联的文件描述符时将发生的情况 MIN = 0, TIME = 0时:read立即返回,如果有待处理的字符,它们就会被返回,如果没有,read调用返回0,且不读取任何字符 MIN = 0, TIME > 0时:有字符处理或经过TIME个0.1秒后返回 MIN > 0, TIME = 0时:read一直等待,直到有MIN个字符可以读取,返回值是字符的数量.到达文件尾时返回0 MIN > 0, TIME > 0时:read调用时,它会等待接收一个字符.在接收到第一个字符及其后续的每个字符后,启用一个字符间隔定时器.当有MIN个字符可读或两字符间的时间间隔超进TIME个0.1秒时,read返回 通过设置MIN和TIME值,我们可以逐个字符地对输入进行处理 3.通过shell访问终端模式 stty -a:这个命令用来查看当前终端的设置情况 stty sane:如果不小心设错了终端模式,可用这个命令恢复,另一种恢复办法是在设置之前保存当前stty设置,在需要时再读 stty -g > save_stty:将当前设置保存到文件save_atty中 stty $(cat save_stty):读出save_atty文件,恢复原终端设置 第三种恢复的办法是重新打下一个终端模拟器.查看死掉的终端进程,kill掉它 4.在命令行模式下设置终端模式 比如想让shell脚本读取单个字符,就需要关闭标准模式,同时将MIN设为1,TIME设为0: stty -icanon min1 time 0 另一个例子是关闭输入密码时的回显功能: atty -echo 使用完这个命令后要执行atty echo,将回显功能再次恢复 5.其他函数 这些函数直接作用于文件描述符,不需要读写termios结构: #include <termios.h> int tcdrain(int fd);让调用程序一直等待,直到所有排队的输出都发送完毕 int tcflow(int, int flowtype);暂停或重新开始输出 int tcflush(int fd, int in_out_selector);清空输入,输出或两者都清华空 */ int kbhit(void) { unsigned char ch; int nread; if(peek_character != -1) return 1; new_settings.c_cc[VMIN] = 0; tcsetattr(fileno(stdin),TCSANOW,&new_settings); nread = read(0,&ch,1); new_settings.c_cc[VMIN] = 1; tcsetattr(fileno(stdin),TCSANOW,&new_settings); if(nread == 1) { peek_character = ch; return 1; } return 0; } int readch(void) { char ch; if(peek_character != -1) { ch = peek_character; peek_character = -1; return ch; } read(0,&ch,1); return ch; } //-------颜色设置----------------- void colorf(unsigned short t) { printf("\033[%dm",t);//value=30~40 // HANDLE color;//创建句柄 color是变量名可以自己定义 // color=GetStdHandle(STD_OUTPUT_HANDLE);//句柄实例化 // SetConsoleTextAttribute(color,t);//设置字体颜色 } //-------光标定位------------------------- void gotoxy(int x,int y) { printf("\033[%d;%dH",y+1,x+1); //COORD c; //c.X=x;c.Y=y; //SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c); } //-------------------------------------- void picture(char DispSle) {//图框重绘 int i; //销前 for(i=0;i<p1.pnum;i++) { gotoxy(p1.y[i]*2,p1.x[i]); printf(" "); } //重绘 if(DispSle) { i=0; } else { i=BaseCount; } // for(;i<p0.pnum;i++) { gotoxy(p0.y[i]*2,p0.x[i]);//用于宽字符 if(p0.x[i]==h->x&&p0.y[i]==h->y) { //蛇头 colorf(color+30); //printf("■"); printf("@"); colorf(34); } else if(p0.x[i]==YStart||p0.x[i]==YEnd||p0.y[i]==XStart||p0.y[i]==XEnd) { colorf(35); printf("□");//墙壁 colorf(34); } else if(p0.x[i]==n&&p0.y[i]==m) { printf("&");//食物 } else { //printf("●");//蛇身 printf("#"); } // if(i>=BaseCount) { p1.x[i-BaseCount]=p0.x[i]; p1.y[i-BaseCount]=p0.y[i]; } } p1.pnum=p0.pnum-BaseCount; putchar(10); } //------------------------------ void s_add_t(char a,char b)//加尾 { //---开辟新节点并链接--- tp2=(Snake*)malloc(LEN); tp2->x=a; tp2->y=b; tp1->next=tp2; tp2->last=tp1; tp2->next=NULL; //------------------- t=tp1=tp2; } void s_add_h(char a,char b)//加头 { hp2=(Snake*)malloc(LEN); hp2->x=a; hp2->y=b; hp2->last=NULL; hp2->next=hp1; hp1->last=hp2; h=hp1=hp2; } void s_sub(char a) { Snake* p; if(a>0){ p=h; hp1=h=h->next;//此处要当心hp1应始终和h保持同步,在创建一个新h时默认hp1指向当前h,此处若不更新 //hp1将为NULL,导致链接错误 h->last=NULL;} //删去蛇头 if(a<0){ p=t; tp1=t=t->last; t->next=NULL; }//删去蛇尾 free(p); } void s_play(Snake *p,char DispSle) { int i=BaseCount;//前面点做图框 if(f) { p0.y[i]=m=rand()%(XLen-1)+1+XStart; p0.x[i]=n=rand()%(YLen-1)+1+YStart; f=0;}//放置食物 else { p0.y[i]=m; p0.x[i]=n; } i++; p0.x[i]=p->x; p0.y[i]=p->y; // printf("%d\t%d\n",p->x,p->y); i++; do { if(p->next==NULL) break; p=p->next; p0.x[i]=p->x; p0.y[i]=p->y; // printf("%d\t%d\n",p->x,p->y); i++; } while(p->next!=NULL); p0.pnum=i; //getch(); colorf(31); picture(DispSle); colorf(32); gotoxy(YStart,YEnd); printf("\n\r当前得分:%d\n当前蛇头(%d,%d)\t当前食物(%d,%d)\n\r当前步数:%d(step<=50)\t未吃到食物数:%d\n\r",score,h->x,h->y,m,n,step,foodlose); printf("按z可自动行走步数:%d\t按q退出\t按e暂停\n\r",a); //时间输出 // ::GetLocalTime(&SysTime); // sprintf(TimeStr,"%d:%d:%d",SysTime.wHour,SysTime.wMinute,SysTime.wSecond);//文件名中不能含有'/'字符 // printf("%s\n",TimeStr); } void init(void) { int j=0; // system("mode con cols=180 lines=60"); system("clear"); //------食物设置--------- srand(time(NULL)); f=1; m=n=0; //---------使用说明-------- printf("\n\r\n\r\n\r\n\r\n\r\n\r\n\r\t\t按键盘w、s、a、d键控制蛇的上下左右移动\n\r"); printf("\t\t按z键可自动行走\n\r"); printf("\t\t按键盘q键选择退出 按e键暂停\n\r"); printf("\t\t注意:当蛇头触碰到墙壁时会:game over!\n\r\t\t"); colorf(34); printf("进入游戏前请先确定你处于引文输入状态,否则按Shift键切换!\n\r\t\t"); colorf(35); printf("输入你希望的蛇头颜色(0-9):\n\r\t\t"); scanf("%d",&color); getchar(); system("clear"); colorf(34); //system("pause"); //system("cls"); //getchar(); //system("color 09"); //----------------------- t=h=hp1=tp1=(Snake*)malloc(LEN); tp1->last=NULL; tp1->next=NULL;//空链表 tp1->x=(YStart+YEnd)/2; tp1->y=(XStart+XEnd)/2; //蛇头 //-----初始化---------- s_add_t((YStart+YEnd)/2+1,(XStart+XEnd)/2); //---------------- //--------------(墙壁)------------------------ //上横(X)框 BaseCount=0; for(j=0;j<=XLen;j++) { p0.x[ j+BaseCount]=YStart;p0.y[ j+BaseCount]=XStart+j;} //下横框 BaseCount=XLen+1; for(j=0;j<=XLen;j++) { p0.x[ j+BaseCount]=YEnd;p0.y[ j+BaseCount]=XStart+j;} //左竖(Y)框 BaseCount+=XLen+1; for(j=0;j<YLen-1;j++) { p0.x[ j+BaseCount]=YStart+j+1;p0.y[ j+BaseCount]=XStart; } //右竖框 BaseCount+=YLen-1; for(j=0;j<YLen-1;j++) { p0.x[ j+BaseCount]=YStart+j+1;p0.y[ j+BaseCount]=XEnd; } BaseCount+=YLen-1; //--------------------------------------------- #define RightBlank 4 gotoxy(XEnd*2+2+RightBlank,YStart);//此处乘以2主要用于横宽比调节 printf("%s\t%s",__DATE__,__TIME__); gotoxy(XEnd*2+2+RightBlank,YStart+1); //可以在不破坏原图的情况下定位光标 printf("mtcs V1.2 版权制作 xx所有 2014.5.20 "); #undef RightBlank p1.pnum=0; s_play(h,ALLDISP); } void main() { creat();//外部文件生成判断 init(); init_keyboard(); while(1) { move(); } } //-----方向控制------------------------- void move(void) { //--捕获键盘 //c_(键盘捕获值) c(当前运行值) c__(键盘锁存值) static char GetCExit=0; static char GetCha=0; static int GetTime=30; static char c='a'; static char LockFlag=1;//锁存标志 #ifdef AUTOGO static char c_='z'; static char c__='z'; #else static char c_='d'; static char c__='a'; #endif GetTime=30; while(GetTime--) {//捕获时隙 if(kbhit()) { GetCha=readch();//getchar(); if((c_=='z'||c_=='Z')&&!LockFlag&&(a>=0)) { //退出自动运行 if(GetCha=='z'||GetCha=='Z') { c_=c__; c=c_;//还原原来的方向 LockFlag=1; } //自动运行中断 if(GetCha=='e'||GetCha=='E') { getchar(); // system("pause"); //暂停 break; } c=GetCha; break; } c_=GetCha; // if((c_=='z'||c_=='Z')&&LockFlag&&(a>0)) { c__=c; LockFlag=0; c=autogo(); a--; break; } if((c_=='e'||c_=='E')) { getchar(); //system("pause"); //暂停 break; } // c=c_;break; } } if((c_=='z'||c_=='Z')&&LockFlag&&(a>0)) { c__=c; LockFlag=0; c=autogo(); a--; } usleep(ORDINARY*200000);//调节该时间可以进行时间难度控制 gotoxy(0,0); putchar(c); //-- if(h->x<=YStart||h->x>=YEnd||h->y<=XStart||h->y>=XEnd) gameover(); //撞墙判断 switch(c) { case 'W': case 'w':{ {s_sub(-1);s_add_h(h->x-1,h->y);step++; } break; } case 'S': case 's':{ {s_sub(-1);s_add_h(h->x+1,h->y);step++;} break;} case 'A': case 'a':{ {s_sub(-1);s_add_h(h->x,h->y-1);step++;} break; } case'D': case 'd':{ { s_sub(-1);s_add_h(h->x,h->y+1);step++; } break; } case'Q': case 'q': { system("clear"); colorf(34); // system("color 0a"); printf("\033[38m\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\t\t\t退出?('y' or 'n')\n\r\t\t\t\t"); GetCExit=getchar(); if(GetCExit=='y'||GetCExit=='Y') { printf("\t\t\t\n\r"); close_keyboard(); exit(0); } else { system("clear"); s_play(h,ALLDISP); colorf(34); //system("color 09"); } } } if(h->x<=YStart||h->x>=YEnd||h->y<=XStart||h->y>=XEnd) gameover(); //撞墙判断 if((h->x==n&&h->y==m)||(t->x==n&&t->y==m)) { s_add_t(t->x,t->y+1); step=0;score++;f=1; // printf("\007"); // Beep(500,200); printf("\a");//发出ALERT声音 if(a<AutoStepNum) a=AutoStepNum;else a+=AutoStepWidth; } //吃到食物,增加节点,并重新放置食物 if(step==50) { step=0;f=1;foodlose++; // printf("\007"); // Beep(1500,200); printf("\a"); }//走完u步未吃到食物则更新食物 // s_play(h,PARTDISP); if(c_=='z'||c_=='Z') { if(a>0) { c=autogo(); a--; } else { c_=c__; c=c_;//还原原来的方向 LockFlag=1; } } } //------------自动寻找食物演示---------------- char autogo() { char c=0; if(h->x>n) c='w'; if(h->x<n) c='s'; if(h->x==n) { if(h->y>m) c='a'; if(h->y<m) c='d'; } return c; } //------------------------------------------ void gameover() { load(); printf("\033[2J\033H\033[34m");// system("cls"); // system("color 0a"); printf("\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\t\t\tgame over!\n\r\t\t"); printf("\t您的得分是:%d\n\r\t\t\t",score); printf("历史最高分数:%d\n\r\t\t\t\n\r",_score); save(); getchar(); sleep(1); close_keyboard(); exit(0); } //-----------------数据保存----------------------- void save() { FILE *fp; fp=fopen("mtcs.mine","w"); if(fp==NULL) printf("\n\r\t\t\t数据保存失败!\n\r"); if(score<_score) {fclose(fp); return;} fprintf(fp,"%d",score); fclose(fp); } //------------------数据提取------------------ void load() { FILE *fp; fp=fopen("mtcs.mine","r"); if(fp==NULL) printf("\n\r\t\t\t加载数据失败!\n\r"); fscanf(fp,"%d",&_score); fclose(fp); } //------------------------------------------ void creat() //判断文件是否存在,若无则新建 { void save(); FILE *fp; fp=fopen("mtcs.mine","a"); if(fp==NULL) save(); else fclose(fp); } //-------------------------------------------------

最新回复(0)