码迷,mamicode.com
首页 > 系统相关 > 详细

Linux Curses编程实现贪吃蛇

时间:2015-10-06 20:43:48      阅读:379      评论:0      收藏:0      [点我收藏+]

标签:

curses库 简单而言,提供UNIX中多种终端 操作光标和显示字符 的接口。我们常见的vi就是使用curses实现的。现在一般都用ncurses库。

Linux下curses函数库    Linux curses库使用      这两篇文章很详细地介绍了curses,在此就不重复了。

  1. 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);    //使用一个终端命令来清除整个屏幕
View Code

 


 

贪吃蛇,要点在于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)

Linux Curses编程实现贪吃蛇

标签:

原文地址:http://www.cnblogs.com/eledim/p/4857557.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!