Linux控制台版本2048

在Github上看到一个荷兰人写的linux控制台版的2048,用的C语言,感觉很有意思。

原网址在这里。

读了一下他的源码,感觉写的不错,就厚着脸皮加了一些中文注释,源码如下:

[cpp]view plaincopy

    /*============================================================================Name:2048.cAuthor:MauritsvanderScheeDescription:Consoleversionofthegame"2048"forGNU/Linux============================================================================*NotebyZhengmingpei,ChinaTime:2014.10.13Contact:http://Zhengmingpei.github.comEmail:yueyawanbian@gmail.com*/#define_XOPEN_SOURCE500#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<termios.h>#include<stdbool.h>#include<stdint.h>#include<time.h>#include<signal.h>#defineSIZE4uint32_tscore=0;uint8_tscheme=0;//根据value获取相应的颜色,将包含设置终端颜色的字符串复制给colorvoidgetColor(uint16_tvalue,char*color,size_tlength){//声明三个颜色数组,用一维数组,但每个奇数位和偶数位组成一个前后景色//后两个数组分别对应程序的启动选项"blackwhite","bluered"uint8_toriginal[]={8,255,1,255,2,255,3,255,4,255,5,255,6,255,7,255,9,0,10,0,11,0,12,0,13,0,14,0,255,0,255,0};uint8_tblackwhite[]={232,255,234,255,236,255,238,255,240,255,242,255,244,255,246,0,248,0,249,0,250,0,251,0,252,0,253,0,254,0,255,0};uint8_tbluered[]={235,255,63,255,57,255,93,255,129,255,165,255,201,255,200,255,199,255,198,255,197,255,196,255,196,255,196,255,196,255,196,255};uint8_t*schemes[]={original,blackwhite,bluered};uint8_t*background=schemes[scheme]+0;uint8_t*foreground=schemes[scheme]+1;if(value>0)while(value>>=1)//value不断右移一位,直到值变为0,实现每个二进制一个不同的颜色{if(background+2<schemes[scheme]+sizeof(original)){background+=2;foreground+=2;}}//linux下终端及字体颜色设置语句的字符串snprintf(color,length,"\033[38;5;%d;48;5;%dm",*foreground,*background);}//绘制数据板,数据板共3×4行,7×4列voiddrawBoard(uint16_tboard[SIZE][SIZE]){int8_tx,y;//\033[m:关闭所有属性charcolor[40],reset[]="\033[m";//\033[H:调整光标位置printf("\033[H");printf("2048.c%17dpts\n\n",score);//数据板共3×4行,7×4列for(y=0;y<SIZE;y++){//首行打印空白for(x=0;x<SIZE;x++){getColor(board[x][y],color,40);printf("%s",color);printf("");//reset重置,避免对非数据板部分造成影响printf("%s",reset);}printf("\n");//次行打印数字,数字居中for(x=0;x<SIZE;x++){getColor(board[x][y],color,40);printf("%s",color);if(board[x][y]!=0){chars[8];//此处注意,是board[x][y]而不是yxsnprintf(s,8,"%u",board[x][y]);int8_tt=7-strlen(s);printf("%*s%s%*s",t-t/2,"",s,t/2,"");}else{printf("·");}printf("%s",reset);}printf("\n");//末行打印空白for(x=0;x<SIZE;x++){getColor(board[x][y],color,40);printf("%s",color);printf("");printf("%s",reset);}printf("\n");}printf("\n");printf("←,↑,→,↓orq\n");//疑似回车printf("\033[A");}//查找一维数组中x左侧待合并数的坐标,stop为检查点int8_tfindTarget(uint16_tarray[SIZE],int8_tx,int8_tstop){int8_tt;//若x为第一个数,左边无数,直接返回xif(x==0){returnx;}//遍历x左边的坐标for(t=x-1;t>=0;t–){//合并算法://1.t处的数不为0且与x处的数不相等,返回t+1//2.t处的数不为0且与x处的数相等,返回t//3.t处的数为0,根据stop判断是否向前查找,防止多次合并if(array[t]!=0){if(array[t]!=array[x]){//mergeisnotpossible,takenextpositionreturnt+1;}returnt;}else{//weshouldnotslidefurther,returnthisoneif(t==stop){returnt;}}}//wedidnotfindareturnx;}//对一维数组进行移动boolslideArray(uint16_tarray[SIZE]){boolsuccess=false;//声明当前位置,待合并的位置,检查点int8_tx,t,stop=0;for(x=0;x<SIZE;x++){if(array[x]!=0){t=findTarget(array,x,stop);//如果待合并的位置与当前位置不相等,进行移动或者合并//iftargetisnotoriginalposition,thenmoveormergeif(t!=x){//如果待合并的位置不是0,右移检查点stop//iftargetisnotzero,setstoptoavoiddoublemergeif(array[t]!=0){score+=array[t]+array[x];stop=t+1;}array[t]+=array[x];array[x]=0;success=true;}}}returnsuccess;}//旋转数据板,向右旋转90度,这样可以用一个方向的数组移动间接控制四个方向的移动voidrotateBoard(uint16_tboard[SIZE][SIZE]){int8_ti,j,n=SIZE;uint16_ttmp;//环形旋转,先外而内,先左后右for(i=0;i<n/2;i++){for(j=i;j<n-i-1;j++){tmp=board[i][j];board[i][j]=board[j][n-i-1];board[j][n-i-1]=board[n-i-1][n-j-1];board[n-i-1][n-j-1]=board[n-j-1][i];board[n-j-1][i]=tmp;}}}//向上移动数据板boolmoveUp(uint16_tboard[SIZE][SIZE]){boolsuccess=false;int8_tx;for(x=0;x<SIZE;x++){//对每一列做移动或者合并处理,//这里是列而不是行,与前面的输出顺序有关success|=slideArray(board[x]);//只要有一列成功,就成功}returnsuccess;}//左移:向右旋转90度,向上合并,再旋转3个90度boolmoveLeft(uint16_tboard[SIZE][SIZE]){boolsuccess;rotateBoard(board);success=moveUp(board);rotateBoard(board);rotateBoard(board);rotateBoard(board);returnsuccess;}//下移:向右旋转2个90度,向上合并,再旋转2个90度boolmoveDown(uint16_tboard[SIZE][SIZE]){boolsuccess;rotateBoard(board);rotateBoard(board);success=moveUp(board);rotateBoard(board);rotateBoard(board);returnsuccess;}//右移:向右旋转3个90度,向上合并,再旋转1个90度boolmoveRight(uint16_tboard[SIZE][SIZE]){boolsuccess;rotateBoard(board);rotateBoard(board);rotateBoard(board);success=moveUp(board);rotateBoard(board);returnsuccess;}boolfindPairDown(uint16_tboard[SIZE][SIZE]){boolsuccess=false;int8_tx,y;for(x=0;x<SIZE;x++){for(y=0;y<SIZE-1;y++){if(board[x][y]==board[x][y+1])returntrue;}}returnsuccess;}//计算数据板是否已满int16_tcountEmpty(uint16_tboard[SIZE][SIZE]){int8_tx,y;int16_tcount=0;for(x=0;x<SIZE;x++){for(y=0;y<SIZE;y++){if(board[x][y]==0){count++;}}}returncount;}//检查游戏是否结束boolgameEnded(uint16_tboard[SIZE][SIZE]){boolended=true;//如果有空位,未结束if(countEmpty(board)>0)returnfalse;//横向检查,有相等相邻数,未结束if(findPairDown(board))returnfalse;rotateBoard(board);//旋转一次,纵向检查,有相等相邻数,未结束if(findPairDown(board))ended=false;rotateBoard(board);rotateBoard(board);rotateBoard(board);returnended;}//随机重置数据板voidaddRandom(uint16_tboard[SIZE][SIZE]){//全局变量,是否已初始化staticboolinitialized=false;//x,y坐标int8_tx,y;//r随机位置,len所有为空的数据板数据长度int16_tr,len=0;//n随机数据,list所有为空的数据板位置uint16_tn,list[SIZE*SIZE][2];if(!initialized){srand(time(NULL));initialized=true;}//找出数据板上所有为空的坐标for(x=0;x<SIZE;x++){for(y=0;y<SIZE;y++){if(board[x][y]==0){list[len][0]=x;list[len][1]=y;len++;}}}//如果有为空的情况,才填充数据if(len>0){r=rand()%len;x=list[r][0];y=list[r][1];n=((rand()%10)/9+1)*2;board[x][y]=n;}}//设置输入模式,在行缓冲和无缓冲中切换voidsetBufferedInput(boolenable){staticboolenabled=true;staticstructtermiosold;structtermiosnew;if(enable&&!enabled){//restoretheformersettingstcsetattr(STDIN_FILENO,TCSANOW,&old);//setthenewstateenabled=true;}elseif(!enable&&enabled){//gettheterminalsettingsforstandardinputtcgetattr(STDIN_FILENO,&new);//wewanttokeeptheoldsettingtorestorethemattheendold=new;//disablecanonicalmode(bufferedi/o)andlocalechonew.c_lflag&=(~ICANON&~ECHO);//setthenewsettingsimmediatelytcsetattr(STDIN_FILENO,TCSANOW,&new);//setthenewstateenabled=false;}}inttest(){uint16_tarray[SIZE];uint16_tdata[]={0,0,0,2,2,0,0,0,0,0,2,2,4,0,0,0,0,2,0,2,4,0,0,0,2,0,0,2,4,0,0,0,2,0,2,0,4,0,0,0,2,2,2,0,4,2,0,0,2,0,2,2,4,2,0,0,2,2,0,2,4,2,0,0,2,2,2,2,4,4,0,0,4,4,2,2,8,4,0,0,2,2,4,4,4,8,0,0,8,0,2,2,8,4,0,0,4,0,2,2,4,4,0,0};uint16_t*in,*out;uint16_tt,tests;uint8_ti;boolsuccess=true;tests=(sizeof(data)/sizeof(data[0]))/(2*SIZE);for(t=0;t<tests;t++){in=data+t*2*SIZE;out=in+SIZE;for(i=0;i<SIZE;i++){array[i]=in[i];}slideArray(array);for(i=0;i<SIZE;i++){if(array[i]!=out[i]){success=false;}}if(success==false){for(i=0;i<SIZE;i++){printf("%d",in[i]);}printf("=>");for(i=0;i<SIZE;i++){printf("%d",array[i]);}printf("expected");for(i=0;i<SIZE;i++){printf("%d",in[i]);}printf("=>");for(i=0;i<SIZE;i++){printf("%d",out[i]);}printf("\n");break;}}if(success){printf("All%utestsexecutedsuccessfully\n",tests);}return!success;}voidsignal_callback_handler(intsignum){printf("TERMINATED\n");setBufferedInput(true);printf("\033[?25h");exit(signum);}intmain(intargc,char*argv[]){uint16_tboard[SIZE][SIZE];charc;boolsuccess;if(argc==2&&strcmp(argv[1],"test")==0){returntest();}if(argc==2&&strcmp(argv[1],"blackwhite")==0){scheme=1;}if(argc==2&&strcmp(argv[1],"bluered")==0){scheme=2;}//33[?25l隐藏光标//33[2J清屏//33[H设置光标位置printf("\033[?25l\033[2J\033[H");//registersignalhandlerforwhenctrl-cispressedsignal(SIGINT,signal_callback_handler);//将数据清为0memset(board,0,sizeof(board));//添加两次随机数,因为初始化时产生2个随机数addRandom(board);addRandom(board);//绘制数据板drawBoard(board);//禁用缓存输入,终端支持按字符读取且不回显setBufferedInput(false);//游戏主循环while(true){c=getchar();switch(c){case97://’a’keycase104://’h’keycase68://leftarrowsuccess=moveLeft(board);break;case100://’d’keycase108://’l’keycase67://rightarrowsuccess=moveRight(board);break;case119://’w’keycase107://’k’keycase65://uparrowsuccess=moveUp(board);break;case115://’s’keycase106://’j’keycase66://downarrowsuccess=moveDown(board);break;default:success=false;}//合并成功,则重新绘制if(success){drawBoard(board);usleep(150000);addRandom(board);drawBoard(board);if(gameEnded(board)){printf("GAMEOVER\n");break;}}//如果输入是q的话,打开行缓冲,显示光标if(c==’q’){printf("QUIT?(y/n)\n");while(true){c=getchar();if(c==’y’){setBufferedInput(true);printf("\033[?25h");exit(0);}else{drawBoard(board);break;}}}if(c==’r’){printf("RESTART?(y/n)\n");while(true){c=getchar();if(c==’y’){memset(board,0,sizeof(board));addRandom(board);addRandom(board);drawBoard(board);break;}else{drawBoard(board);break;}}}}setBufferedInput(true);printf("\033[?25h");returnEXIT_SUCCESS;}

悠然享受和大自然融合之乐。

Linux控制台版本2048

相关文章:

你感兴趣的文章:

标签云: