play snake on linux

在写完超Low的windows上的贪吃蛇

被人吐槽了几个方面:

1.界面真的Low,开始,结束,游戏中,都太简陋了...

2.每次都清屏在输出字符矩阵的解决方案...太晃眼了

3.一个BUG,为了解决贪吃蛇隔固定时间time移动一个单位的问题

  我们写的是while(距上次移动时间 < time && 没有键盘方向键的读入);

  于是我们惊喜的发现,只要一直摁方向键,就不必等待固定时间time

  而是会直接下一步移动...手动加快贪吃蛇移动速度...

但是我们暂时并不想改进这个程序...毕竟怎么说还是能玩一玩的

于是ytz决定在自己的deepin系统上写一个能运行的贪吃蛇...

先想到的方法当然是直接把windows上的代码拿来改一改啊...

但很快我们就遇到了重重障碍!

1.我们使用了conio.h中的_kbhit函数来判断是否有键盘读入

然而linux系统下是没有conio.h这个库的...

百度了一下linux下也没有自带库函数有相同功能

于是我们就百度了一个手动实现_kbhit函数加进去

(这个实现原理不是很懂我是照抄的...)

2.conio.h中的getch函数同样需要替代品

这时候就有人指出明路,curses.h库里有啊

然后我们需要先安装这个库,在终端输入

sudo apt-get install libncurses5-dev

回车即可开始安装

然后编译时需要加入 -lncurses 命令

比如 g++ -o Snake -lncurses Snake.cpp

否则编译无法通过

3.啊,编译通过了!

我们愉快的运行一下吧!

运行出了一坨屎!

我们百度一下curses.h 这个库

发现是一个图形库,类似于大一学习C和C++的时候

老师提供的windows上的的第三方库ege.h

只不过curses的评价似乎比ege好一点2333

然后ege那个你懂的吧,开始运行进入图形界面后

各种函数失效,printf,scanf...

以及输出基本靠定位定点输出, , 什么的都会gg你懂的吧

4.于是我们把pritf全都换成printw

抛弃 改用move来移动光标位置

然后只在游戏开始时使用一次清屏system("clear")

其它都使用 curses.h 里的 refresh 函数即可

(我们的 printw 是输出在逻辑屏幕上的,

refresh 函数用来同步逻辑屏幕和物理屏幕

物理屏幕即我们看到的屏幕

然后refresh函数表现的十分流畅的

最重要的是,它不晃眼!!!)

然后终于能正确打印出字符矩阵了!

5.汉字在终端显示成乱码

百度了很久没有找到解决方案

我们就退让一步,把汉字都改成了英文

然后调用noecho函数,让我们的按键不显示在屏幕上

尝试运行......发现我们造出来了一条瘫痪蛇...

不贪吃了瘫痪了...我们不摁下方向键它自己不动...

先把瘫痪蛇代码放上来吧

  1 #include <ctime>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <stdio.h>
  5 #include <termios.h>
  6 #include <unistd.h>
  7 #include <fcntl.h>
  8 #include <curses.h>
  9 
 10 #define map(pos) map[pos.x][pos.y]
 11 
 12 char map[30][30];
 13 
 14 struct point {
 15     int x, y;
 16     
 17     void _rand() {
 18         x = rand() % 20 + 1;
 19         y = rand() % 20 + 1;
 20     }
 21     
 22     bool operator == (const point &a) const {
 23         return x == a.x && y == a.y;
 24     } 
 25     
 26 };
 27 
 28 int head, tail;
 29 point snake[500], food, next;
 30 int dir, grade, length, uptime;
 31 
 32 inline int _kbhit() {
 33     termios oldt, newt;
 34     int ch, oldf;
 35     tcgetattr(STDIN_FILENO, &oldt);
 36     newt = oldt;
 37     newt.c_lflag &= -(ICANON | ECHO);
 38     tcsetattr(STDIN_FILENO, TCSANOW, &newt);
 39     oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
 40     fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
 41     ch = getchar();
 42     tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
 43     fcntl(STDIN_FILENO, F_SETFL, oldf);
 44     if(ch != EOF) {
 45         ungetc(ch, stdin);
 46         return 1;
 47     }
 48     return 0;
 49 }
 50 
 51 inline void find_food() {
 52     do {
 53         food._rand();
 54     }while(map(food) != ' ');
 55     map(food) = '*';
 56 }
 57 
 58 inline void update() {
 59     //system("clear");
 60     for(int i = 0;i < 22;i ++) {
 61         for(int j = 0;j < 22;j ++)
 62              move(i + 3, 3 + j * 2), printw("%c ", map[i][j]);
 63         if(i == 0) move(i + 3, 60), printw("Level:%d", grade);
 64         if(i == 2) move(i + 3, 60), printw("length:%d", length);
 65         if(i == 6) move(i + 3, 60), printw("Time_interval:");
 66         if(i == 8) move(i + 3, 60), printw("%d ms", uptime);
 67     }
 68     refresh();
 69 }
 70 
 71 inline bool GO() {
 72     bool timeover = 1;
 73     double start = (double) clock() / CLOCKS_PER_SEC;
 74     while((timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0) && !(_kbhit()));
 75     if(timeover) dir = getch();
 76     next = snake[head];
 77     switch (dir) {
 78         case 'w':next.x -= 1;break;
 79         case 's':next.x += 1;break;
 80         case 'a':next.y -= 1;break;
 81         case 'd':next.y += 1;break;
 82         default:
 83             move(30, 10);
 84             printw("Game Over!");
 85             return 0;
 86     }
 87     if(!next.x || next.x == 21 || !next.y || next.y == 21) {
 88         move(30, 10);
 89         printw("Game Over!");
 90         return 0;
 91     }
 92     if(map(next) != ' ' && !(next == food)) {
 93         move(30, 10);
 94         printw("Game Over!");
 95         return 0;
 96     }
 97     if(length == 400) {
 98         move(30, 10);
 99         printw("Game Over!");
100         return 0;
101     }
102     return 1;
103 }
104 
105 int main() {
106     initscr();
107     clear();
108     curs_set(0);
109     srand(19980320);
110     for(int i = 1;i <= 20;i ++)
111         for(int j = 1;j <= 20;j ++)
112             map[i][j] = ' ';
113     for(int i = 0;i < 22;i ++)
114         map[i][0] = map[21][i] = map[0][i] = map[i][21] = '!';
115     map[1][1] = map[1][2] = 'o', map[1][3] = '@';
116     snake[0] = (point){1, 1};
117     snake[1] = (point){1, 2};
118     snake[2] = (point){1, 3};
119     head = 2, tail = 0, grade = 1, length = 3, uptime = 500;
120     find_food(), dir = 'd';
121     
122     move(3, 10);
123     printw("Let's play snake!");
124     refresh();
125     double start;
126     for(int i = 3;i >= 0;i --) {
127         start = (double)clock() / CLOCKS_PER_SEC;
128         while((double)clock() / CLOCKS_PER_SEC <= start + 1);
129         if(i > 0) {
130             //system("clear");
131             move(3, 10);
132             printw("Enter the countdown:%d", i);
133             refresh();
134         }
135         else {
136             update();
137         }
138     }
139  
140     while(1) {
141         int tmp = GO();
142         refresh();
143         if(tmp) {
144             if(next == food) {
145                 length ++;
146                 if(length % 10 == 0) {
147                     grade ++;
148                     if(uptime >= 100) uptime -= 50; 
149                 }
150                 map(next) = '@';
151                 map(snake[head]) = 'o';
152                 head = (head + 1) % 500;
153                 snake[head] = next;
154                 find_food(), update();
155             }
156             else {
157                 map(snake[tail]) = ' ';
158                 tail = (tail + 1) % 500;
159                 map(next)  = '@';
160                 map(snake[head]) = 'o';
161                 head = (head + 1) % 500;
162                 snake[head] = next;
163                 update();
164             }
165         }
166         else break;
167     }
168     getch();
169     endwin();
170     return 0;
171 }
View Code

6.我们经过实验发现

问题出在getch和kbhit函数的配合上面

于是我们修改一下kbhit函数

在获得键盘读入之后不再把字符存入缓冲区

而是直接return回去就好了

然后为了解决开始提到的第三个问题

我们在获得键盘读入之后

依旧等待固定时间间隔结束再让蛇移动即可!

然后为了解决第一个问题...

我加了个最终分数提示...

重新开始游戏我懒就没加...

至此我们的windows贪吃蛇魔改之linux版基本就完成了!

(我太懒了所以依然没有加注释

  1 #include <ctime>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <stdio.h>
  5 #include <termios.h>
  6 #include <unistd.h>
  7 #include <fcntl.h>
  8 #include <curses.h>
  9 
 10 #define map(pos) map[pos.x][pos.y]
 11 
 12 char map[30][30];
 13 
 14 struct point {
 15     int x, y;
 16     
 17     void _rand() {
 18         x = rand() % 20 + 1;
 19         y = rand() % 20 + 1;
 20     }
 21     
 22     bool operator == (const point &a) const {
 23         return x == a.x && y == a.y;
 24     } 
 25     
 26 };
 27 
 28 int head, tail;
 29 point snake[500], food, next;
 30 int dir, grade, length, uptime;
 31 
 32 inline int _kbhit() {
 33     termios oldt, newt;
 34     int ch, oldf;
 35     tcgetattr(STDIN_FILENO, &oldt);
 36     newt = oldt;
 37     newt.c_lflag &= -(ICANON | ECHO);
 38     tcsetattr(STDIN_FILENO, TCSANOW, &newt);
 39     oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
 40     fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
 41     ch = getchar();
 42     tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
 43     fcntl(STDIN_FILENO, F_SETFL, oldf);
 44     if(ch != EOF) return ch;
 45     return 0;
 46 }
 47 
 48 inline void find_food() {
 49     do {
 50         food._rand();
 51     }while(map(food) != ' ');
 52     map(food) = '*';
 53 }
 54 
 55 inline void update() {
 56     //system("clear");
 57     for(int i = 0;i < 22;i ++) {
 58         for(int j = 0;j < 22;j ++)
 59              move(i + 3, 3 + j * 2), printw("%c ", map[i][j]);
 60         if(i == 0) move(i + 3, 60), printw("Level:%d", grade);
 61         if(i == 2) move(i + 3, 60), printw("length:%d", length);
 62         if(i == 6) move(i + 3, 60), printw("Time_interval:");
 63         if(i == 8) move(i + 3, 60), printw("%d ms", uptime);
 64     }
 65     refresh();
 66 }
 67 
 68 inline bool GO() {
 69     int ch;
 70     bool timeover = 1;
 71     double start = (double) clock() / CLOCKS_PER_SEC;
 72     while((timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0) && !(ch = _kbhit()));
 73     if(timeover) {
 74         while(timeover = (double) clock() / CLOCKS_PER_SEC <= start + uptime / 1000.0);
 75         dir = ch;
 76     }
 77     next = snake[head];
 78     switch (dir) {
 79         case 'w':next.x -= 1;break;
 80         case 's':next.x += 1;break;
 81         case 'a':next.y -= 1;break;
 82         case 'd':next.y += 1;break;
 83         default:return 0;
 84     }
 85     if(!next.x || next.x == 21 || !next.y || next.y == 21) return 0;
 86     if(map(next) != ' ' && !(next == food)) return 0;
 87     if(length == 400) return 0;
 88     return 1;
 89 }
 90 
 91 int main() {
 92     initscr();
 93     noecho();
 94     clear();
 95     curs_set(0);
 96     srand(19980320);
 97     for(int i = 1;i <= 20;i ++)
 98         for(int j = 1;j <= 20;j ++)
 99             map[i][j] = ' ';
100     for(int i = 0;i < 22;i ++)
101         map[i][0] = map[21][i] = map[0][i] = map[i][21] = '!';
102     map[1][1] = map[1][2] = 'o', map[1][3] = '@';
103     snake[0] = (point){1, 1};
104     snake[1] = (point){1, 2};
105     snake[2] = (point){1, 3};
106     head = 2, tail = 0, grade = 1, length = 3, uptime = 500;
107     find_food(), dir = 'd';
108     
109     move(3, 10);
110     printw("Let's play snake!");
111     refresh();
112     double start;
113     for(int i = 3;i >= 0;i --) {
114         start = (double)clock() / CLOCKS_PER_SEC;
115         while((double)clock() / CLOCKS_PER_SEC <= start + 1);
116         if(i > 0) {
117             //system("clear");
118             move(3, 10);
119             printw("Enter the countdown:%d", i);
120             refresh();
121         }
122         else {
123             update();
124         }
125     }
126  
127     while(1) {
128         int tmp = GO();
129         refresh();
130         if(tmp) {
131             if(next == food) {
132                 length ++;
133                 if(length % 10 == 0) {
134                     grade ++;
135                     if(uptime >= 100) uptime -= 50; 
136                 }
137                 map(next) = '@';
138                 map(snake[head]) = 'o';
139                 head = (head + 1) % 500;
140                 snake[head] = next;
141                 find_food(), update();
142             }
143             else {
144                 map(snake[tail]) = ' ';
145                 tail = (tail + 1) % 500;
146                 map(next)  = '@';
147                 map(snake[head]) = 'o';
148                 head = (head + 1) % 500;
149                 snake[head] = next;
150                 update();
151             }
152         }
153         else {
154             move(30, 20);
155             printw("Game Over!");
156             move(32, 14);
157             printw("Your Final Score : %d", length);
158             break;
159         }
160     }
161     getch();
162     endwin();
163     return 0;
164 }

感谢参考资料

1.Linux struct itimerval用法

2.【Linux函数】Signal ()函数详细介绍

3. linux下c语言写的简单的贪吃蛇

4.关于curses的简单知识

5.在linux下面实现检测按键(Linux中kbhit()函数的实现)

原文地址:https://www.cnblogs.com/ytytzzz/p/6958497.html