curses库 简单而言,提供UNIX中多种终端 操作光标和显示字符 的接口。我们常见的vi就是使用curses实现的。现在一般都用ncurses库。
Linux下curses函数库 Linux curses库使用 这两篇文章很详细地介绍了curses,在此就不重复了。
- ubuntu安装curses函数库
>sudo apt-get install ncurses-dev
用curses库,编译程序:
gcc program.c -o program -lcurses
2.工作原理
curses工作在屏幕,窗口和子窗口之上。屏幕是设备全部可用显示面积(对终端是该窗口内所有可用字符位置),窗口与具体例程有关。如基本的stdscr窗口等。
curses使用两个数据结构映射终端屏幕,stdscr和curscr。stdscr是“标准屏幕”(逻辑屏幕),在curses函数库产生输出时就刷新,是默认输出窗口(用户不会看到该内容)。curscr是“当前屏幕”(物理屏幕),在调用refresh函数是,函数库会将curscr刷新为stdscr的样子。
3.常用函数
1 3.常用函数 2 初始化相关: 3 initscr(); //curses程序初始化必须 4 cbreak(); //关闭字符流的缓冲 5 nonl(); //输入资料时, 按下 RETURN 键是否被对应为 NEWLINE 字元 ( 如 \n ). 6 noecho(); //关闭字符回显 7 keypad(stdscr,TRUE); //可以使用键盘上的一些特殊字元, 如上下左右 8 refresh(); //刷新物理屏幕 9 int endwin(void); 关闭所有窗口 10 11 移动光标、输出相关: 12 int move(int new_y, int new_x); //移动stdcsr的光标位置 13 addch(ch): 显示某个字元. 14 mvaddch(y,x,ch): 在(x,y) 上显示某个字元. 15 addstr(str): 显示一串字串. 16 mvaddstr(y,x,str): 在(x,y) 上显示一串字串. 17 printw(format,str): 类似 printf() , 以一定的格式输出至萤幕. 18 mvprintw(y,x,format,str): 在(x,y) 位置上做 printw 的工作 19 20 前缀w用于窗口(添加一个WINDOWS指针参数),mv用于光标移动(在该位置执行操作addch或printw)(添加两个坐标值参数),mvw用于在窗口(如stdscr)中移动光标。组成如下函数: 21 addch, waddch, mvaddch, mvwaddch 22 printw, wprintw, mvprintw, mvwprintw 23 24 屏幕读取:没用到就不写了 25 键盘输入: 26 //与标准io库的getchar, gets, scanf类似 27 int getch(); 28 int getstr(char *string); 29 int getnstr(char *string, int number); //建议使用 30 scanw(format,&arg1,&arg2...): 如同 scanf, 从键盘读取一串字元. 31 清除屏幕; 32 int erase(void); //在屏幕的每个位置写上空白字符 33 int clear(void); //使用一个终端命令来清除整个屏幕
贪吃蛇,要点在于1.蛇的实现;2.移动和按键侦听的同步进行;3.碰撞的检测。
1.蛇的实现,联系到curses的特点,想到两种办法,一种是队列存储每个节点,还有一种是用头-尾-拐弯节点坐标实现。
想到蛇的长度不可能太长,所以用队列(循环数组)实现,添加头结点,删除尾节点。为了方便就没有实现循环数组,采用了一个大数组。
2.两种动作同时进行,两种方案:1可以用异步编程,涉及到信号,阻塞,可重入问题。2用多线程,或多进程,涉及到变量的共享,互斥锁等。
3.碰撞检测。为了效率可以用一个线程把它抽取出来,如果代码时间复杂度不高,可以直接写。
用信号实现按键和同时移动。又有两种方案,一种把移动放入定时信号处理函数,主程序用输入阻塞。一种是把移动放入主程序循环,把输入放入信号处理函数。
tanchishe.c
1 #include "tanchishe.h" 2 //2015 10 05 3 //#define DEBUG__ 4 void gogo(int ); 5 void move_here_show(int ); 6 int collision_detec(void); 7 8 sigjmp_buf jmpbuffer; 9 10 int main() 11 { 12 13 if(sigsetjmp(jmpbuffer,1) != 0) 14 { 15 mvprintw(LINES/2-2,COLS/2 -15, " Game Over! "); 16 char * S= " Your Score is %d!"; 17 if(score>max_score) 18 mvprintw(LINES/2+1,COLS/2 -15, " You Cut the Record!!!"); 19 else S= " Your Score is %d! Too Young!!!"; 20 if(score>75)S = " Your Score is %d! Nice!!!"; 21 mvprintw(LINES/2,COLS/2 -15, S,score); 22 mvprintw(LINES/2+2,COLS/2 -15, "Press <%c> Play Again! <%c> to Quit!",AGAIN_KEY,QUIT_KEY); 23 refresh(); 24 free(snake_s1.snake_body); 25 int geta; 26 while(1){ 27 if((geta =getch()) == AGAIN_KEY){ 28 erase();refresh(); 29 goto again; 30 } 31 else if(geta == QUIT_KEY){ 32 goto over;//_exit(0); 33 } 34 } 35 36 } 37 again: 38 init_tcs(); 39 40 #ifndef DEBUG__ 41 signal(SIGALRM,gogo); 42 set_ticker(ticker); 43 #endif 44 while(1){ 45 int i =getch(); 46 switch (i) { 47 case _KEY_UP : 48 if(real_g_forward!=_KEY_DOWN &&real_g_forward!=_KEY_UP) 49 {g_key_forward=_KEY_UP;}break; 50 case _KEY_DOWN : 51 if(real_g_forward!=_KEY_UP&&real_g_forward!=_KEY_DOWN ) 52 {g_key_forward=_KEY_DOWN ;}break; 53 case _KEY_LEFT : 54 if(real_g_forward!=_KEY_RIGHT&&real_g_forward!=_KEY_LEFT) 55 {g_key_forward=_KEY_LEFT;}break;//delay_real=&delay; 56 case _KEY_RIGHT: 57 if(real_g_forward!=_KEY_RIGHT&&real_g_forward!=_KEY_LEFT) 58 {g_key_forward=_KEY_RIGHT;}break; 59 #ifdef DEBUG__ 60 case ‘g‘:gogo(0);//raise(SIGALRM); 61 #endif 62 } 63 64 } 65 over: 66 endwin(); 67 return 0; 68 } 69 70 void gogo(int signum)//zouba 71 { 72 //signal(SIGALRM,zouba_s41); 73 int temp_g_forward=g_key_forward; 74 move_here_show(temp_g_forward); 75 76 collision_detec(); 77 //real_g_forward=temp_g_forward; 78 79 } 80 81 void move_here_show(int forward) 82 { 83 switch (forward) { 84 case _KEY_UP: --row; break; 85 case _KEY_DOWN : ++row; break; 86 case _KEY_LEFT: --col; break; 87 case _KEY_RIGHT: ++col; break; 88 default:return; 89 } 90 real_g_forward=forward; 91 92 //mvaddchstr(snake_s1.snake_body[last_s_node].row,snake_s1.snake_body[last_s_node].col,LSBLANK); 93 mvaddstr(snake_s1.snake_body[last_s_node].row,snake_s1.snake_body[last_s_node].col,BLANK); 94 snake_s1.snake_body[++first_s_node].row= row; 95 snake_s1.snake_body[first_s_node].col = col; 96 //mvaddchstr(row,col,NOB); 97 mvaddstr(row,col,NODE); 98 move(LINES-1,0); 99 #ifdef DEBUG__ 100 printw("%d",first_s_node+1-INIT_NODE_SIZE); 101 #endif 102 refresh(); 103 last_s_node++; 104 105 106 } 107 int collision_detec() 108 { 109 int i; 110 if(col== food_col&& row ==food_row){ 111 srand((unsigned)time(NULL)); 112 food_col = 2+rand()%(COLS-5); 113 food_row = 1+rand()%(LINES-3); 114 test_dupe: 115 for(i=last_s_node;i<first_s_node;++i){ 116 if(food_col == snake_s1.snake_body[i].col) 117 if(food_row == snake_s1.snake_body[i].row ){ 118 food_col = 2+rand()%(COLS-5); 119 food_row = 1+rand()%(LINES-3); 120 goto test_dupe; 121 } 122 } 123 //mvaddchstr(food_row,food_col,FOOD);//这里不改,centos也正常显示,为了保险,也改 124 mvaddstr(food_row,food_col,FOOD);// 125 last_s_node--;//muhaha 126 if(++eat_num >=times_to_upgrade_each_level[level]){ 127 ticker -=delay_sub_each_level[level++];//注意此处是累减 128 set_ticker(ticker); 129 } 130 score++; 131 max_score =max_score>score?max_score:score; 132 attrset(A_REVERSE); 133 mvprintw(0,COLS/2 -16,"Score:%d Max Score:%d Level:%d",score,max_score,level); 134 attrset(A_NORMAL); 135 move(LINES-1,0);refresh();//没有也可以,但是分数那会闪 136 137 } 138 else{ 139 if(col == LEFT_WALL||col == RIGHT_WALL||row == TOP_WALL||row == BUTT_WALL){ 140 //signal(SIGALRM,SIG_IGN); 141 set_ticker(0); 142 siglongjmp(jmpbuffer, 1); 143 } 144 145 for(i=last_s_node;i<first_s_node-2;++i){ 146 if(col == snake_s1.snake_body[i].col) 147 if(row == snake_s1.snake_body[i].row ){ 148 set_ticker(0); 149 siglongjmp(jmpbuffer, 2); 150 } 151 } 152 } 153 return 0; 154 } 155 156 157 int set_ticker(int n_msecs) 158 { 159 struct itimerval new_timeset; 160 long n_sec,n_usecs; 161 162 n_sec = n_msecs/1000; 163 n_usecs=(n_msecs%1000)*1000L; 164 165 new_timeset.it_interval.tv_sec=n_sec; 166 new_timeset.it_interval.tv_usec=n_usecs; 167 168 new_timeset.it_value.tv_sec = n_sec; 169 new_timeset.it_value.tv_usec= n_usecs; 170 171 return setitimer(ITIMER_REAL,&new_timeset,NULL); 172 }
init_tcs.c
1 #include "tanchishe.h" 2 int row=0; //the head 3 int col=0; 4 5 int g_key_forward=‘_KEY_RIGHT‘; 6 int real_g_forward=‘_KEY_RIGHT‘; 7 int food_row =0; 8 int food_col =0; 9 int eat_num=0; 10 int ticker = 200;//ms 11 12 13 struct snake_s snake_s1; 14 int last_s_node=0; 15 int first_s_node; 16 17 int score =0; 18 int max_score=0;//do not init in retry! 19 int level=0; 20 int delay_sub_each_level[MAX_LEVEL]={0}; 21 int times_to_upgrade_each_level[MAX_LEVEL]={0}; 22 23 void init_tcs(void) 24 { 25 26 initscr(); 27 cbreak(); 28 nonl(); 29 noecho(); 30 intrflush(stdscr,FALSE); 31 keypad(stdscr,TRUE); 32 33 row =LINES/2-1; 34 col =COLS/2-1; 35 36 g_key_forward=‘_KEY_RIGHT‘; 37 food_row =0; 38 food_col =0; 39 eat_num=0; 40 ticker = 310;//ms 41 score =0; 42 43 real_g_forward=‘_KEY_RIGHT‘; 44 last_s_node=0; 45 //first_s_node; 46 level=0; 47 48 int in; 49 int sum=delay_sub_each_level[0]=8;//EVERY_LEVEL_SUB_TIME; 50 times_to_upgrade_each_level[0]=TIMES_TO_UPGRADE_EACH_LEVEL; 51 for(in=1;in<MAX_LEVEL;++in){ 52 times_to_upgrade_each_level[in]=times_to_upgrade_each_level[in-1]+(TIMES_TO_UPGRADE_EACH_LEVEL); 53 54 if(sum<ticker-50){ 55 if(in<6) 56 delay_sub_each_level[in] =8; 57 else if(in<12) 58 delay_sub_each_level[in] =7; 59 else if(in <18) 60 delay_sub_each_level[in] =6; 61 else 62 delay_sub_each_level[in] =5; 63 64 } 65 else delay_sub_each_level[in]=delay_sub_each_level[in-1]; 66 sum =sum+delay_sub_each_level[in]; 67 68 } 69 attrset(A_REVERSE); 70 for(in=0;in<LINES;++in){ 71 mvaddch(in,0,‘ ‘); 72 mvaddch(in,LEFT_WALL,‘ ‘); 73 mvaddch(in,RIGHT_WALL,‘ ‘); 74 mvaddch(in,COLS-1,‘ ‘); 75 } 76 for(in=0;in<COLS;++in){ 77 mvaddch(0,in,‘ ‘); 78 mvaddch(LINES-1,in,‘ ‘); 79 } 80 81 mvprintw(0,COLS/2 -16,"Score:%d Max Score:%d Level:%d",score,max_score,level); 82 mvprintw(LINES-1,COLS/2 -18,"Eledim Walks the Earth,%c%c%c%c to Move",_KEY_UP, _KEY_DOWN, _KEY_LEFT, _KEY_RIGHT); 83 refresh(); 84 attrset(A_NORMAL); 85 86 87 snake_s1.snake_body = malloc( SNAKE_MAX_SIZE *sizeof(struct post)) ; 88 if(snake_s1.snake_body == NULL) 89 mvprintw(0,0,"malloc error"); 90 memset(snake_s1.snake_body,0,SNAKE_MAX_SIZE*sizeof(struct post)); 91 srand((unsigned)time(NULL)); //no this ,rand every time return same num 92 #ifdef DEBUG__ 93 food_row = LINES/2 -1; 94 food_col =COLS/2 +1; 95 #else 96 food_row = 1+rand()%(LINES-3); 97 food_col =2+rand()%(COLS-5); 98 #endif 99 //snake_s1.head.row=row; 100 //snake_s1.head.col=col; 101 snake_s1.node_num =INIT_NODE_SIZE; 102 first_s_node=snake_s1.node_num-1; 103 int i; 104 for(i=0;i<snake_s1.node_num;++i){ 105 snake_s1.snake_body[i].row=row; 106 snake_s1.snake_body[i].col=col-snake_s1.node_num+1+i; 107 //mvaddchstr(row,col-i,NOB); 108 mvaddstr(row,col-i,NODE); 109 } 110 //show food 111 //mvaddchstr(food_row,food_col,FOOD); 112 mvaddstr(food_row,food_col,FOOD); 113 move(LINES-1,0);refresh(); 114 115 116 }
tanchishe.h
1 #ifndef TANCHISHE_H 2 #define TANCHISHE_H 3 4 #include <stdio.h> 5 #include <curses.h> 6 #include <time.h> 7 #include <sys/time.h> //timeval 8 #include <unistd.h> //usleep 9 #include <sys/select.h> 10 #include <signal.h> 11 #include <stdlib.h> 12 #include <string.h> //memset 13 #include <setjmp.h> 14 15 #define LSBLANK L" " 16 #define BLANK " " 17 18 //for all 19 extern int row; 20 extern int col; 21 extern struct timeval delay; 22 23 24 #define INIT_NODE_SIZE 5 25 #define SNAKE_MAX_SIZE 8192 26 #define LEFT_WALL 1 27 #define RIGHT_WALL (COLS-2) 28 #define TOP_WALL 0 29 #define BUTT_WALL (LINES-1) 30 #define NOB L"#"// 31 #define LSFOOD L"O" 32 #define NODE "#"//instead of L"#" 33 #define FOOD "O" 34 #define MAX_LEVEL 100 35 #define SUB_TIME_EACH_LEVEL 6 36 #define TIMES_TO_UPGRADE_EACH_LEVEL 6 37 38 #define _KEY_UP ‘w‘ 39 #define _KEY_DOWN ‘s‘ 40 #define _KEY_LEFT ‘a‘ 41 #define _KEY_RIGHT ‘d‘ 42 #define AGAIN_KEY ‘A‘ 43 #define QUIT_KEY ‘Q‘ 44 struct post{ 45 int row; 46 int col; 47 }; 48 struct snake_s{ 49 int node_num; 50 struct post head; 51 struct post* snake_body; 52 53 }; 54 //for all 55 extern int real_g_forward; 56 extern int g_key_forward; 57 extern int food_row; 58 extern int food_col ; 59 extern int eat_num; 60 extern int score ; 61 extern int ticker ;//ms 62 63 extern struct snake_s snake_s1; 64 65 extern int last_s_node; 66 extern int first_s_node; 67 68 extern int max_score;//do not init in retry! 69 extern int level; 70 extern int delay_sub_each_level[MAX_LEVEL]; 71 extern int times_to_upgrade_each_level[MAX_LEVEL]; 72 73 74 extern int ticker_for_col; 75 extern int ticker_for_row; 76 77 int set_ticker(int n_msecs); 78 void init_tcs(void); 79 80 #endif
最后编译:
cc tanchishe.c init_tcs.c -lcurses
运行:
由于curses是面向终端的接口(虚拟终端也包含在内),与图形库无关,所以程序在远程ssh工具上也可以完美运行,就像vim一样。
可以考虑继续添加的特性:多人游戏 路障 吃字母 网络化 存档文件 用户记录 暂停(ctrl+z)