Linux Curses编程实现贪吃蛇

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



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

时间: 2024-10-03 22:40:45

Linux Curses编程实现贪吃蛇的相关文章

[C入门 - 游戏编程系列] 贪吃蛇篇(六) - 蛇实现

这一篇是关于设置蛇的属性的,接上一篇(五). 设置蛇的速度,很简单,只要不是负数就行了. void SNK_SetSnakeSpeed(Snake *snake, int speed) { if (snake != 0) snake->speed = SDL_abs(speed); } 设置蛇的方向有些复杂,玩过贪吃蛇的都知道,蛇向前移动时,它无法向后转弯:向左移动时,它无法向右转弯.所以,我也要做些这样的判断. void SNK_SetSnakeDirection(Snake *snake,

《结队-结队编程项目贪吃蛇--需求分析》

项目功能设计概述: 1.需求分析: 本系统主要完成贪吃蛇的基本操作,满足余下几点要求: 1)利用方向键(或A,B,C,D)改变蛇的运行方向 2)空格键控制游戏的开始和暂停,并在随机的地方生成食物 3)蛇吃到的食物变成新的蛇体,碰到墙壁或自身,游戏结束,否则正常运行. 2.项目的可行性分析: 贪吃蛇是一种简单大众的游戏,其核心算法是如何移动和吃掉食物,没有碰到食物时,把当前运动方向的上个节点入队,并以蛇身的颜色绘制,然后把蛇头指针指向的出对,并以屏幕颜色绘制,这样以达到运动效果.而吃到食物时,只需

结队-结队编程项目贪吃蛇-需求分析

1.目标 贪吃蛇游戏能够稳定运行,有基本的功能. 2.特点 简单易操作,交互性好 3.功能模块的需求 包括游戏图形区域界面(比如经典的无米宫地图,方格).游戏开始按钮.暂停按钮.退出按钮. 主要完成游戏的开始.暂停.退出等功能. 玩家用键盘方向键控住蛇的方向,当蛇撞到墙壁 或者自己的身体游戏结束 屏幕显示gameover 并显示得分情况. 成员:张洪剑,张明成

[C入门 - 游戏编程系列] 贪吃蛇篇(三) - 蛇定义

蛇是这个游戏的主角,要实现的功能也是最复杂的一个.因为蛇不止有属性,还有行为.它会动,还会吃东西,还会长大!而且还会死!这是很要命的.我一向看不懂复杂的代码,也写不出复杂的代码.所以对于蛇,我很纠结,如何才能简单的实现它. 毫无质疑的一点是,食物具有的属性,蛇也具有.蛇必须存在于世界中,有大小和位置以及颜色.这样最起码可以推测出一个蛇的简单定义.但是这还远远不够,蛇是活的,它能动,就必须有速度和方向,它能吃,就必须能长大.所以,抛开其它的一切,它最简单的形式起码也得这样: typedef str

[C入门 - 游戏编程系列] 贪吃蛇篇(四) - 食物实现

由于食物是贪吃蛇游戏中最简单的一部分,而且和其他部分关联性不强,基本上是一个独立的部分,所以我打算先实现它. 我的想法是食物必须在世界中才能被创造出来,也就是说,先有世界再有食物,所以我得先判断世界是否存在,存在的话才可以创建食物. Food * SNK_CreateFood(World *world, int size) { Food *food; if (world == 0) return 0; if ((food = (Food *)SDL_malloc(sizeof(Food))) =

[C入门 - 游戏编程系列] 贪吃蛇篇(二) - 食物定义

游戏中的食物没有那么多复杂属性,特别是贪吃蛇游戏中,我把食物看待的很简单: 1. 它必须属于世界,才能出现在世界.不可能一个不属于世界的食物,出现在世界中:但是可能存在着一个食物,它属于世界,但是却没有出现在世界中(即食物的颜色和世界的颜色相同,因此看不见食物).这就像鬼一样,它可能存在于这个世界上,但我们看不到它. 2. 一个属于世界的食物,具有在这个世界中的位置. 3. 它有颜色和大小. 因此,食物的结构体定义就显而易见了! typedef struct Food { World *worl

[C入门 - 游戏编程系列] 贪吃蛇篇(五) - 蛇实现

因为已经写了食物的实现,所以我不知道到底是该先写世界的实现还是蛇的实现.因为世界就是一个窗口,可以立刻在世界中看到食物的样子,对于大多数人来说,如果写完代码立刻就能看到效果,那就再好不过了.可是,我最后还是选择了先写蛇的实现这篇笔记.如果先写世界的实现,我就无法按照现在的思路完完整整的写下去,因为没有蛇,世界部分的代码就不完整,看完食物的效果后,我还是得写蛇的实现,然后又得修改世界部分的代码,来查看蛇的效果.反反复复,实在折腾不起.所以我打算把食物和蛇的实现都写完,最后统一看运行效果. 蛇和食物

C语言之贪吃蛇(conio.h)

写完基于Linux中curses库的贪吃蛇,发现只需要用到一个WIN的API(移动光标那个)改写move(int y, int x)函数,然后加上conio.h(一般编译器都会包含这个头文件)的getch(),就可以在Windows系统运行! Linux版链接:http://blog.csdn.net/u013351484/article/details/43940803 游戏规则: 初始时两条蛇会在中间,一条蛇头是'@',食物是'$':另一条蛇头是'+',食物是'*',蛇身都是'#'.WASD

结对-张宸&amp;张文然-贪吃蛇

结对编程:贪吃蛇 准备阶段:安装Python.pygame 编写阶段:设置游戏窗口 2. 设置游戏必要功能:    开始 结束 暂停等按钮 蛇身体 食物表示 操作按钮 3. 完善游戏:分数,奖励等 完成阶段:相互检查代码,查看是否有错误,准确无误后发布代码