zoukankan      html  css  js  c++  java
  • 《贪吃蛇》局部刷新法(C语言,字符界面)

    一, 游戏规则:

    1. 蛇的初始长度为 SNAKE_LEN , 蛇按照原来的方向移动, 如果有键盘输入按照键盘改变方向。(反向无效, 例如: 方向为向上时按向下无效)
    2. 如果吃到食物则变长一个结点,得一分。 如果没有则继续移动
    3. 移动范围为 COLUMNS*ROWS 个格子
    4. 结束游戏条件:  (1)自食 (2)蛇头碰到墙

    二, 数据结构和宏

    #define ROWS 15                //行 对应 Y 轴 
    #define COLUMNS 30            //列 对应 X 轴 
    #define SNAKE_LEN 4      //蛇的初始长度
    #define    LEFT    75    
    #define    UP        72    
    #define    RIGHT    77    
    #define    DOWN    80

      贪吃蛇用链表来表示:

    typedef struct snake{ 
        COORD cor;          //坐标
        struct snake *next;
    }Snake;
    Snake *head = NULL;
    COORD Food;    //食物的坐标

       

      用到了一个windows API定义的结构 COORD ,正如其名字coordinate(坐标)一样,这是一个存储二维坐标的结构体。其实也就是坐标:

    typedef struct _COORD {
    
      SHORT X; SHORT Y;
    
    } COORD, *PCOORD;

    三, 用函数实现功能模块

      0. 主函数中给出了游戏流程:

     1 int main(void){    
     2     int score = 0, direction = UP;    //初始方向向上 
     3     COORD edge;
     4     edge.X = 0;
     5     edge.Y = ROWS+1;
     6     init_game();            //初始化游戏界面 
     7     for(; ;){
     8         direction = get_dir(direction);
     9         score += move_snake(direction);        //贪吃蛇做下一步的动作  
    10         Sleep(500);            //延迟 x ms 
    11         if(!is_alive())
    12             break;
    13     }
    14     gotoxy(edge); 
    15     freeSnake();    
    16     printf("	Game Over ! Your score is : %d
    ", score);
    17     return 0;
    18 }

      1. 首先初始化游戏界面:

    /* 初始化游戏界面 */ 
    int init_game(void){    
        int i,j;
        COORD snake_body;
        head = (Snake *)malloc(sizeof(Snake));
        head->cor.Y = ROWS/2;                        //初始化蛇头位置为地图中心(附近)
        head->cor.X = COLUMNS/2;
        head->next = NULL;
        Snake *p = head;
        for(i = 1; i<SNAKE_LEN; i++){            //初始化蛇身长度为4 
            p->next = (Snake *)malloc(sizeof(Snake));
            p = p->next;
            p->cor.X = head->cor.X;
            p->cor.Y = head->cor.Y + i;
            p->next = NULL;
        }
        for(i=0; i<ROWS; i++){
            for(j=0; j<COLUMNS; j++){
                if(i==0 || i==ROWS-1 || j==0 || j==COLUMNS-1)
                    printf("#");  
                else
                    printf(" ");
            }
            putchar('
    ');
        }    
        p = head;
        while(p){
            gotoxy(p->cor);
            printf("*");
            p = p->next;
        }
        creatFood();
        gotoxy(Food);
        printf("@");
        return 1;
    }

       

      用到了gotoxy()函数,TC自带(不知道TC是啥),所以自己写一个,顾名思义就是一个转到x,y坐标的一个函数。

    void gotoxy(COORD pt)
    
    {
    
        HANDLE hout;
    
        hout = GetStdHandle(STD_OUTPUT_HANDLE);
    
        SetConsoleCursorPosition(hout, pt);
    
    }

    HANDLE(句柄)在windows编程中是一个十分重要的概念。在window编程中,对于一个Object(对象)我们只能通过Handle来访问它。觉得不好理解的同学把句柄当作指针来看待就好了。

    GetStdHandle函数获取一个指向特定标准设备的句柄,包括标准输入,标准输出和标准错误。STD_OUTPUT_HANDLE正是代表标准输出(也就是显示屏)的宏。

    SetConsoleCursorPosition函数用于设置控制台光标的位置。

      

      2. 获取方向函数 get_dir ( ) :

    /* 考虑蛇原来的方向的反方向无效 */ 
    int get_dir(int old_dir){
        int new_dir = old_dir;    //如果没有键盘被按下则按照 old_dir 返回
        if(kbhit()){    //kbhit() 函数功能: 判断有无键盘被按下,如果有返回1,没有返回0
            getch();
            new_dir = getch();    //getch() 要使用两次 
            if(abs(new_dir - old_dir) == 2 || abs(new_dir - old_dir) == 8)    //反方向无效, 参照键盘方向键的宏的值 
                new_dir = old_dir;            
        }
        return new_dir; 
    } 

      abs()是绝对值函数, 方向的判断可看上下左右的宏值。 左(75)和右(77)差2, 上(72)和下(80)差8.

      3. 获取了方向,就要按照方向做下一步的移动。 

      按照获得的 direction 改变 head->cor 的坐标, 用gotoxy 在新的 head 位置打印蛇符号。 

      cor_cmp(COORD pt1, COORD pt2) 函数判断蛇头是否碰到食物或墙。

      creatFood() 函数随机生成新的食物坐标, 再用 gotoxy 打印食物符号。

    int move_snake(int direction){
        Snake *p = head;            //保存原来的 蛇头 
        COORD tail = head->cor, temp;
        //根据键盘改变蛇头的坐标
        switch(direction){         
            case UP:
                head->cor.Y--;
                break;
            
            case DOWN:
                head->cor.Y++;
                break;
            
            case RIGHT:
                head->cor.X++;
                break;
            
            case LEFT:
                head->cor.X--;
                break;
        }
        gotoxy(head->cor);            //打印新的蛇头 
        printf("*");
        for(p = head->next; p != NULL; p = p->next){    //更新蛇身坐标 
            temp = p->cor;
            p->cor = tail;                // 前面的和后面的坐标互换, p再移到后面的 
            tail = temp;
        }    
        /* 如果吃到了食物 */ 
        if(cor_cmp(Food, head->cor)){  //比较坐标函数
            //添加头部, 保留尾部 
            for(p = head->next; p->next != NULL; p = p->next)        //找到尾部     
                ;        
            p->next = (Snake *)malloc(sizeof(Snake));     
            p->next->cor = tail;
            p->next->next = NULL;    
            
            creatFood();            //生成新的食物
            gotoxy(Food);
            printf("@");
            return 1; 
        }
        else{
            //head在前已更改, 后面的链表改成前一链表的坐标 
            gotoxy(tail);
            printf(" ");
            return 0;
        }
    }

    因为需要比较很多次坐标, 所以写成比较函数:

    int cor_cmp(COORD pt1, COORD pt2)           //可见COORD的方便
    
    {
    
      return (pt1.X == pt2.X&&pt1.Y == pt2.Y);
    
    }

    随机生成食物函数 creatFood ( ) :

    COORD creatFood(){
        Snake *p = NULL;
        int in_snake = 0;                    //判断食物是否落在了蛇身上, 默认为否 0 
        srand((unsigned int)time(NULL));
        do{
            in_snake = 0;
            Food.X = rand() % COLUMNS-1;
            Food.Y = rand() % ROWS-1;
            for(p = head; p!= NULL; p = p->next){ //判断食物是否落在了蛇
                if(cor_cmp(p->cor, Food))
                    in_snake = 1;
            }            
        }while(Food.X == 0 || Food.Y ==0 || in_snake);
        return Food;
    }

    如果坐标落到墙或蛇身上,则重新生成新的Food。 不过这个程序有 Bug , 吃了几次食物后,食物就从图中消失了, 找不到是什么原因。

    最后加上判断蛇的死活函数 is_alive() :

    int is_alive(){
        int eat_self = 0;
        Snake *p = head->next->next;    //从第三结点与头结点比较坐标 
        while(p){
            if(cor_cmp(head->cor, p->cor)){
                eat_self = 1;
                break;
            }
            p = p->next;
        }
        if(head->cor.X == 0 || head->cor.X == COLUMNS-1 || head->cor.Y == 0 || head->cor.Y == ROWS-1 || eat_self)
            return 0;
        return 1;
    }

       游戏结束, 销毁链表 : 

    void freeSnake(){
        Snake *p = NULL, *q;
        for(p = head; p!=NULL; p = q){
            q = p->next;
            free(p);
        }    
    }

      最后给上代码 : 

      1 #include<stdio.h>
      2 #include<windows.h>
      3 #include<time.h>
      4 
      5 #define ROWS 10                //行 对应 Y 轴 
      6 #define COLUMNS 10            //列 对应 X 轴 
      7 #define SNAKE_LEN 4
      8 #define    LEFT    75    
      9 #define    UP        72    
     10 #define    RIGHT    77    
     11 #define    DOWN    80
     12 
     13 typedef struct snake{ 
     14     COORD cor;          //坐标
     15      struct snake *next;
     16 }Snake;
     17 
     18 Snake *head;
     19 COORD Food;
     20 
     21 //函数声明 
     22 void gotoxy(COORD pt);
     23 COORD creatFood(void);
     24 int move_snake(int);
     25 void init_game(void);
     26 int cor_cmp(COORD pt1, COORD pt2);
     27 int is_alive(void);
     28 int get_dir(int );
     29 void freeSnake(void);
     30 
     31 int main(void){    
     32     int score = 0, direction = UP;    //初始方向向上 
     33     COORD edge;
     34     edge.X = 0;
     35     edge.Y = ROWS+1;
     36     init_game();            //初始化游戏界面 
     37     for(; ;){
     38         direction = get_dir(direction);
     39         score += move_snake(direction);        //贪吃蛇做下一步的动作  
     40         Sleep(500);            //延迟 x ms 
     41         if(!is_alive())
     42             break;
     43     }
     44     gotoxy(edge); 
     45     freeSnake();    
     46     printf("	Game Over ! Your score is : %d
    ", score);
     47     return 0;
     48 }
     49 
     50 void freeSnake(){
     51     Snake *p = NULL, *q;
     52     for(p = head; p!=NULL; p = q){
     53         q = p->next;
     54         free(p);
     55     }    
     56 }
     57 
     58 int is_alive(){
     59     int eat_self = 0;
     60     Snake *p = head->next->next;    //从第三结点与头结点比较坐标 
     61     while(p){
     62         if(cor_cmp(head->cor, p->cor)){
     63             eat_self = 1;
     64             break;
     65         }
     66         p = p->next;
     67     }
     68     if(head->cor.X == 0 || head->cor.X == COLUMNS-1 || head->cor.Y == 0 || head->cor.Y == ROWS-1 || eat_self)
     69         return 0;
     70     return 1;
     71 }
     72 
     73 /* 考虑蛇原来的方向的反方向无效 */ 
     74 int get_dir(int old_dir){
     75     int new_dir = old_dir;    //如果没有键盘被按下则按照 old_dir 返回
     76     if(kbhit()){    //kbhit() 函数功能: 判断有无键盘被按下,如果有返回1,没有返回0
     77         getch();
     78         new_dir = getch();    //getch() 要使用两次 
     79         if(abs(new_dir - old_dir) == 2 || abs(new_dir - old_dir) == 8)    //反方向无效, 参照键盘方向键的宏的值 
     80             new_dir = old_dir;            
     81     }
     82     return new_dir; 
     83 } 
     84 //map[ROWS][COLUMNS] = {0};    //地图 
     85 /* 初始化游戏界面 */ 
     86 void init_game(void){    
     87     int i,j;
     88     COORD snake_body;
     89     head = (Snake *)malloc(sizeof(Snake));
     90     head->cor.Y = ROWS/2;                        //初始化蛇头位置 
     91     head->cor.X = COLUMNS/2;
     92     head->next = NULL;
     93     Snake *p = head;
     94     for(i = 1; i<SNAKE_LEN; i++){            //蛇身长度为4 
     95         p->next = (Snake *)malloc(sizeof(Snake));
     96         p = p->next;
     97         p->cor.X = head->cor.X;
     98         p->cor.Y = head->cor.Y + i;
     99         p->next = NULL;
    100     }
    101     for(i=0; i<ROWS; i++){
    102         for(j=0; j<COLUMNS; j++){
    103             if(i==0 || i==ROWS-1 || j==0 || j==COLUMNS-1)
    104                 printf("#");
    105             else
    106                 printf(" ");
    107         }
    108         putchar('
    ');
    109     }    
    110     p = head;
    111     while(p){
    112         gotoxy(p->cor);
    113         printf("*");
    114         p = p->next;
    115     }
    116     creatFood();
    117     gotoxy(Food);
    118     printf("@");
    119 }
    120 
    121 int cor_cmp(COORD pt1, COORD pt2)        //坐标比较函数 相等返回1,否则返回0 
    122 {
    123     return (pt1.X == pt2.X && pt1.Y == pt2.Y);
    124 }
    125 
    126 void gotoxy(COORD pt)            //坐标移动到新的X,Y 
    127 {
    128     HANDLE hout;
    129     hout = GetStdHandle(STD_OUTPUT_HANDLE);
    130     SetConsoleCursorPosition(hout, pt);
    131 }
    132 
    133 COORD creatFood(){
    134     COORD food; 
    135     Snake *p = NULL;
    136     int in_snake = 0;                    //判断食物是否落在了蛇身上, 默认为否 0 
    137     srand((unsigned int)time(NULL));
    138     do{
    139         in_snake = 0;
    140         food.X = rand() % COLUMNS-1;
    141         food.Y = rand() % ROWS-1;
    142         for(p = head; p!= NULL; p = p->next){ //判断食物是否落在了蛇
    143             if(cor_cmp(p->cor, food))
    144                 in_snake = 1;
    145         }            
    146     }while(food.X == 0 || food.Y ==0 || in_snake);
    147     Food = food;
    148 }
    149 
    150 int move_snake(int direction){
    View Code
  • 相关阅读:
    HelpersSimpleCurl
    HelpersSessions
    HelpersReservedWords
    关于Java加载属性文件放在web容器不好使的解决办法
    (更新)Java + 腾讯企业邮箱 + javamail + SSL 发送邮件
    Java + 腾讯企业邮箱 + javamail + SSL 发送邮件
    struts2实现改变超链接的显示方式
    struts2 的正则表达式验证不起作用解决办法
    Hibernate5.1.0的hello word
    hibernateTools插件安装
  • 原文地址:https://www.cnblogs.com/ozel/p/7173345.html
Copyright © 2011-2022 走看看