《贪吃蛇》局部刷新法(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
原文地址:https://www.cnblogs.com/ozel/p/7173345.html