• 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()函数的实现)

  • 相关阅读:
    关于程序收到消息的顺序
    窗口过程
    消息循环
    解剖窗口程序
    开始了解窗口程序
    编码的规范
    汇编的除法和乘法
    Win32汇编--Win32汇编的高级语法
    H5-音频列表音乐切换
    移动端-ibokan
  • 原文地址:https://www.cnblogs.com/ytytzzz/p/6958497.html
Copyright © 2020-2023  润新知