• 小项目特供 贪吃蛇游戏(基于C语言)


      C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次。

      是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第二天写了第二版和第三版。

      相信C语言写个小游戏或小项目是大多数计算机相关专业的学生都做的事情,但是作为一个数学专业的学生,我们教研室的老师对C语言的要求也就比较低了,大一没有让我们做个小项目实践一次。至今为止用C/C++做过的三个小项目(大作业),一个是外校同学让我帮忙写的学生信息管理系统(天呐,这名字都被大学用烂了...),一个是本校计院的同学让我帮忙写的链表设计的大作业,实话说,真的很无聊...

      最后一个小项目就是贪吃蛇了,比前两个有意思多了,强烈建议大学老师让学生做些简单的界面程序而非黑框框下的所谓管理系统....让学生体会到乐趣才是真谛啊。


      附:源码及游戏程序下载地址:链接:贪吃蛇 密码:v55s

      


      

     第一版

      首先是第一版的源码奉上,因为没有学过MFC,这一版是借鉴了其他网站上的C语言贪吃蛇算法思路和图形写成的。

      我认为这里的贪吃蛇算法思路非常棒,虽然不知道这种算法的来由但是我对此算法创始人由衷敬佩...

        这里的算法思路其实是将图上每一个点的二维坐标转换成一维序列,然后通过一维序列映射地图上的每一个坐标

        最让我敬佩的地方就是在蛇头重绘的时候,这里的贪吃蛇移动地图上的二维坐标和原始地图的一维序列将随着蛇头的移动而不断发生交替改变,以适应贪吃蛇循环队列的算法要求。

      操作说明:WSAD控制方向。

      

      1 #include <stdio.h>
      2 #include <ctype.h>
      3 #include <conio.h>
      4 #include <time.h>
      5 #include <windows.h>
      6 
      7 //背景宽-高
      8 #define HEIGHT 20
      9 #define WIDTH 20
     10 //地图中心
     11 #define XCENTER HEIGHT/2
     12 #define YCENTER WIDTH/2
     13 //最大蛇长
     14 #define SNAKE_MAXLEN ((HEIGHT - 2) * (WIDTH - 2))
     15 //坐标->编号
     16 #define NUM(x,y) ((x-1)*(WIDTH-2)+(y-1))
     17 
     18 //模型
     19 struct Model{
     20     char *ch;
     21     int color;
     22     char flag;
     23 }
     24 border = { "", 4, 1 },
     25 background = { "", 2, 2 },
     26 snakeHead = { "", 0xE, 3 },
     27 snakeBody = { "", 0xE, 3 },
     28 food = { "", 0xC, 4 };
     29 
     30 int snakeLen = 3;    //贪吃蛇长度
     31 
     32 //地图
     33 struct Map {
     34     int flag;    //模型标识
     35     int num;    //坐标编号
     36 }map[WIDTH][HEIGHT];
     37 
     38 //坐标
     39 //贪吃蛇移动范围(循环队列),分数位置
     40 struct Coordinate{
     41     int x, y;
     42 }snake[SNAKE_MAXLEN], scorePos;
     43 
     44 
     45 int score;    //分数
     46 int header, tail;    //蛇头蛇尾下标
     47 HANDLE hConsole;
     48 
     49 void setCursor(int x, int y)
     50 {
     51     //参数x为行,y为列-与光标设置相反
     52     COORD coord;
     53     coord.X = 2*y;    //宽字符决定占两列
     54     coord.Y = x;
     55     SetConsoleCursorPosition(hConsole, coord);
     56 }
     57 
     58 void setColor(int color)
     59 {
     60     SetConsoleTextAttribute(hConsole, color);
     61 }
     62 
     63 //随机生成食物
     64 void createFood()
     65 {
     66     int num;    //新食物对应的坐标编号
     67     int range;    //新食物所在范围
     68     srand((unsigned)time(NULL));    //初始种子
     69     range = SNAKE_MAXLEN - snakeLen;    //食物生成范围
     70     if (tail < header)
     71         num = rand() % range + tail + 1;
     72     else {
     73         num = rand() % range;
     74         if (num >= header)
     75             num += snakeLen;
     76     }
     77     //绘制食物
     78     int tx = snake[num].x;
     79     int ty = snake[num].y;
     80     setCursor(tx, ty);
     81     setColor(food.color);
     82     printf("%s", food.ch);
     83     map[tx][ty].flag = food.flag;
     84 }
     85 
     86 //游戏结束
     87 void gameOver()
     88 {
     89     setCursor(XCENTER, YCENTER - 4);
     90     setColor(0xC);
     91     
     92     printf("Game Over!");
     93     getch();
     94     exit(0);    //结束程序
     95 }
     96 
     97 void move(char direction)
     98 {
     99     //新蛇头
    100     int tx = snake[header].x;
    101     int ty = snake[header].y;
    102     switch (direction) 
    103     {
    104         case 'w':
    105             tx--; break;
    106         case 's':
    107             tx++; break;
    108         case 'a':
    109             ty--; break;
    110         case 'd':
    111             ty++; break;
    112     }
    113 
    114     //判断是否会出界或撞到自己
    115     if (map[tx][ty].flag == border.flag || map[tx][ty].flag == snakeBody.flag)
    116         gameOver();
    117 
    118     //新蛇头绘制(此时未refresh模型标识)
    119     setCursor(tx, ty);
    120     setColor(snakeHead.color);
    121     printf("%s", snakeHead.ch);    
    122     //原蛇头重绘制
    123     setCursor(snake[header].x, snake[header].y);
    124     printf("%s", snakeBody.ch);    //小方块
    125 
    126     //蛇头更新-队首移动
    127     header = header == 0 ? SNAKE_MAXLEN - 1 : header - 1;
    128     //旧编号位置更正
    129     int preNum = map[tx][ty].num;    //蛇头位置旧编号
    130     snake[preNum].x = snake[header].x;
    131     snake[preNum].y = snake[header].y;
    132     map[snake[preNum].x][snake[preNum].y].num = preNum;
    133     //蛇头位置更正
    134     snake[header].x = tx;
    135     snake[header].y = ty;
    136     map[tx][ty].num = header;    //蛇头位置新编号
    137 
    138     //判断是否吃到食物
    139     if (map[tx][ty].flag == food.flag)
    140     {
    141         createFood();    //随机生成食物
    142         snakeLen++;    //蛇身加长
    143         //更新-score
    144         setCursor(scorePos.x, scorePos.y);
    145         printf("%d", ++score);
    146     }
    147     else {    
    148         //删除蛇尾
    149         setCursor(snake[tail].x, snake[tail].y);
    150         map[snake[tail].x][snake[tail].y].flag = background.flag;
    151         setColor(background.color);
    152         printf("%s", background.ch);
    153         tail = tail == 0 ? SNAKE_MAXLEN - 1 : tail - 1;
    154     }
    155 
    156     //更新-蛇头模型标识
    157     map[tx][ty].flag = snakeBody.flag;
    158 }
    159 
    160 //初始化界面
    161 void init() 
    162 {
    163     CONSOLE_CURSOR_INFO cci;    //光标信息
    164     header = 0;
    165     tail = snakeLen - 1;
    166 
    167     //设置光标不可见
    168     hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    169     GetConsoleCursorInfo(hConsole, &cci);
    170     cci.bVisible = 0;
    171     SetConsoleCursorInfo(hConsole, &cci);
    172 
    173     //dos命令设置窗口大小
    174     system("mode con:cols=100 lines=32");
    175 
    176     //绘制背景
    177     for (int x = 0; x < HEIGHT; x++)
    178     {
    179         for (int y = 0; y < WIDTH; y++)
    180         {
    181             if (x == 0 || y == 0 || x == HEIGHT - 1 || y == WIDTH - 1)
    182             {
    183                 map[x][y].flag = border.flag;
    184                 setColor(border.color);
    185                 printf("%s", border.ch);
    186             }
    187             else {
    188                 int num = NUM(x,y);
    189                 snake[num].x = x;
    190                 snake[num].y = y;
    191                 //背景设置
    192                 map[x][y].num = num;
    193                 map[x][y].flag = background.flag;
    194                 setColor(background.color);
    195                 printf("%s", background.ch);
    196             }
    197         }
    198         printf("
    ");
    199     }
    200 
    201     //绘制初始贪吃蛇
    202     setCursor(XCENTER, YCENTER - snakeLen / 2);
    203     header = NUM(XCENTER, YCENTER - 1);    //蛇头位置
    204     tail = header + snakeLen - 1;
    205     setColor(snakeBody.color);
    206     for (int i = header; i <= tail; i++)
    207     {
    208         if (i == header) printf("%s", snakeHead.ch);
    209         else printf("%s", snakeBody.ch);
    210         map[snake[i].x][snake[i].y].flag = snakeBody.flag;
    211     }
    212 
    213     //随机生成食物
    214     createFood();
    215 
    216     //其他信息
    217     setCursor(XCENTER - 1, WIDTH + 2);
    218     printf("   score: 0");
    219     setCursor(XCENTER, WIDTH + 2);
    220     printf("   Author: InkMark");
    221     setCursor(XCENTER + 1, WIDTH + 2);
    222     printf("   Blog: www.cnblogs.com/inkblots");
    223     //分数位置
    224     scorePos.x = XCENTER - 1;
    225     scorePos.y = WIDTH + 7;
    226 }
    227 
    228 
    229 int main()
    230 {
    231     //方向及其他初始化
    232     char direction = 'a';
    233     init();
    234     
    235     char ch = tolower(getch());
    236     if (ch == 'a' || ch == 'w' || ch == 's')
    237         direction = ch;
    238 
    239     while (1) {
    240         if (kbhit()) {
    241             ch = tolower(getch());
    242             if (ch == ' ')
    243                 ch = getch();    //实现空格-暂停
    244             if(ch == 'a' || ch == 'd' || ch == 'w' || ch=='s')
    245                 if (ch + direction != 234 && ch + direction != 197)
    246                     direction = ch;    //不为反向时改变方向
    247         }
    248         move(direction);
    249         Sleep(500);
    250     }
    251 
    252     return 0;
    253 }
    = 。=

      


      第二版:

      第二版在第一版的算法基础上改进了图形界面和源码管理,新增了“加速”技能(比较方便实现)。

      控制方向也改为方向键了。

      第二版源码分为三个文件,运行源码需要链接一次。

      先是一个头文件,定义了要用到的库文件,常用常量和结构体,以及声明main中要用到的函数

      

     1 #ifndef CATALOG_H
     2 #define CATALOG_H
     3 
     4 #include<stdio.h>
     5 #include<conio.h>
     6 #include<time.h>
     7 #include<windows.h>
     8 
     9 //背景上下伸展长度
    10 #define TOP_EXTEND 3
    11 #define BOTTOM_EXTEND 3
    12 //背景宽-高
    13 #define WIDTH 33
    14 #define HEIGHT 20
    15 //地图中心
    16 #define XCENTER ((HEIGHT + 1)/2 + TOP_EXTEND)
    17 #define YCENTER (WIDTH + 1)/2
    18 //最大蛇长
    19 #define SNAKE_MAXLEN ((HEIGHT - 2) * (WIDTH - 2))
    20 //坐标->序号
    21 #define ORDER(x,y) ((x - TOP_EXTEND - 1) * (WIDTH - 2) + (y - 1))
    22 //点:在移动范围内
    23 #define IN_RANGE(x,y) (x > TOP_EXTEND && x < HEIGHT + TOP_EXTEND - 1 && y > 0 && y < WIDTH - 1)
    24 //默认暂停时间
    25 #define PAUSE_TIME 500
    26 
    27 /*全局变量*/
    28 extern int snakeLen;
    29 extern int direction;
    30 extern int header, tail;
    31 extern int score;
    32 extern int skill;
    33 extern int pauseTime;
    34 extern HANDLE hConsole;
    35 
    36 //模型
    37 typedef struct Model {
    38     char *ch;
    39     int color;
    40     int flag;
    41 }Model;
    42 
    43 //地图
    44 typedef struct Map {
    45     int flag;    //模型标识
    46     int order;    //序号
    47 }Map;
    48 
    49 //坐标
    50 //贪吃蛇移动范围(循环队列),分数位置
    51 typedef struct Coordinate {
    52     int x, y;
    53 }Coordinate;
    54 
    55 
    56 /******界面设定******/
    57 
    58 void setCursor(int x, int y);
    59 
    60 void setColor(int color);
    61 
    62 void control(char ch);
    63 
    64 /******框架及功能******/
    65 
    66 void init();
    67 
    68 int move(char direction);
    69 
    70 void createFood();
    71 
    72 
    73 #endif
    catalog.h

      第二个是功能部分源码

      1 #include "catalog.h"
      2 
      3 Model    border = { NULL, 0x7, 1 },
      4 snakeHead = { "", 0xE, 2 },
      5 snakeBody = { "", 0xE, 2 },
      6 food = { "", 0xC, 3 };
      7 
      8 Map map[HEIGHT][WIDTH];
      9 
     10 Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos;
     11 
     12 /************游戏界面设定************/
     13 
     14 //隐藏光标
     15 void hideCursor()
     16 {
     17     CONSOLE_CURSOR_INFO cci;    //光标信息
     18     hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
     19     GetConsoleCursorInfo(hConsole, &cci);
     20     cci.bVisible = 0;
     21     SetConsoleCursorInfo(hConsole, &cci);
     22 }
     23 
     24 //设置光标位置
     25 void setCursor(int x, int y)
     26 {
     27     //参数x为行,y为列-与光标设置相反
     28     COORD coord;
     29     coord.X = 2 * y;    //宽字符决定占两列
     30     coord.Y = x;
     31     SetConsoleCursorPosition(hConsole, coord);
     32 }
     33 
     34 //设置文字颜色
     35 void setColor(int color)
     36 {
     37     SetConsoleTextAttribute(hConsole, color);
     38 }
     39 
     40 //绘制游戏窗口(包括dos环境初始窗口)
     41 void createWindow()
     42 {
     43     //dos命令设置窗口大小
     44     system("mode con:cols=72 lines=27");
     45 
     46     //╔ ╗ ╝ ╚ ═ ║ ╠ ╣ ╦ ╩
     47     setColor(0x7);
     48     printf("╔════════════════╦════════════════╗
    ");    //0~34
     49     for (int i = 1; i < TOP_EXTEND; i++)
     50         printf("║                                ║                                ║
    ");
     51     printf("╠════════════════╩════════════════╣
    ");
     52     for (int i = 1; i < HEIGHT - 1; i++)
     53         printf("║                                                                  ║
    ");
     54     printf("╠═════════════════════════════════╣
    ");
     55     for (int i = 1; i < BOTTOM_EXTEND; i++)
     56         printf("║                                                                  ║
    ");
     57     printf("╚═════════════════════════════════╝
    ");
     58     setCursor(HEIGHT + TOP_EXTEND, 1);
     59     printf(" 操作指南:方向键控制,空格可暂停,5分后可获得技能.");
     60     setCursor(HEIGHT + TOP_EXTEND + 1, 1);
     61     printf(" Version : 2.0");
     62 
     63     for (int x = TOP_EXTEND; x < HEIGHT + TOP_EXTEND; x++)
     64     {
     65         for (int y = 0; y < WIDTH; y++)
     66             if (IN_RANGE(x, y))
     67             {
     68                 int order = ORDER(x, y);
     69                 snake[order].x = x;
     70                 snake[order].y = y;
     71                 //背景设置
     72                 map[x][y].order = order;
     73                 map[x][y].flag = 0;
     74             }
     75             else map[x][y].flag = border.flag;
     76     }
     77     //其他信息
     78     setColor(0x7);
     79     setCursor(1, 1);
     80     printf("          你的分数:0");
     81     setCursor(2, 1);
     82     printf("          技能:无");
     83     setCursor(1, YCENTER + 1);
     84     printf("        Author: InkMark");
     85     setCursor(2, YCENTER + 1);
     86     printf(" Blog: www.cnblogs.com/inkblots");
     87 
     88     //分数位置
     89     scorePos.x = 1; scorePos.y = 11;
     90     skillPos.x = 2; skillPos.y = 9;
     91 }
     92 
     93 //控制键
     94 void control(char ch)
     95 {
     96     //本应取方向键高位再取低位,在此以简便为主
     97     if (ch != -32) return;
     98     ch = getch();
     99     //上72下80左75右77
    100     if (ch == 72 || ch == 80 || ch == 75 || ch == 77)    //低位相同
    101         if (ch + direction != 152) direction = ch;    //不为反向时改变方向
    102 }
    103 
    104 /************游戏信息输出************/
    105 
    106 void getSkill()
    107 {
    108     setCursor(skillPos.x, skillPos.y);
    109     printf("加速(S键)");
    110     skill = 1;    //加速技能开启
    111 }
    112 
    113 void alterScore(int addition)
    114 {
    115     score += addition;
    116     if (score >= 5)
    117         getSkill();
    118     setCursor(scorePos.x, scorePos.y);
    119     setColor(0x7);
    120     printf("%d", score);
    121 }
    122 
    123 void gameOver()
    124 {
    125     if (IDYES == MessageBox(NULL, "是否重新开始游戏?", "Snake_2.0", MB_YESNO))
    126     {
    127         system("CLS");
    128         return;
    129     }
    130 
    131     exit(0);
    132 }
    133 
    134 /************游戏框架及功能************/
    135 
    136 //变量初始化及界面初始化
    137 void init()
    138 {
    139     pauseTime = PAUSE_TIME;
    140     skill = 0;
    141     score = 0;
    142     snakeLen = 1;
    143     header = 0;
    144     tail = snakeLen - 1;
    145 
    146     hideCursor();//隐藏光标
    147     createWindow();    //绘制背景
    148 }
    149 
    150 //贪吃蛇移动
    151 //return:1为成功,0为失败
    152 int move(char direction)
    153 {
    154     //新蛇头
    155     int tx = snake[header].x;
    156     int ty = snake[header].y;
    157     //上72下80左75右77
    158     switch (direction)
    159     {
    160         case 72: tx--; break;
    161         case 80: tx++; break;
    162         case 75: ty--; break;
    163         case 77: ty++; break;
    164     }
    165 
    166     //判断是否撞墙或撞到自己
    167     if (map[tx][ty].flag == border.flag || map[tx][ty].flag == snakeBody.flag)
    168     {
    169         gameOver();
    170         return 0;
    171     }
    172 
    173     //新蛇头绘制(此时未refresh模型标识)
    174     setCursor(tx, ty);
    175     setColor(snakeHead.color);
    176     printf("%s", snakeHead.ch);
    177     //原蛇头重绘制
    178     setCursor(snake[header].x, snake[header].y);
    179     printf("%s", snakeBody.ch);
    180 
    181     //蛇头更新-队首移动
    182     header = header == 0 ? SNAKE_MAXLEN - 1 : header - 1;
    183     //旧编号位置更正
    184     int preNum = map[tx][ty].order;    //蛇头位置旧编号
    185     snake[preNum].x = snake[header].x;
    186     snake[preNum].y = snake[header].y;
    187     map[snake[preNum].x][snake[preNum].y].order = preNum;
    188     //蛇头位置更正
    189     snake[header].x = tx;
    190     snake[header].y = ty;
    191     map[tx][ty].order = header;    //蛇头位置新编号
    192 
    193     //判断是否吃到食物
    194     if (map[tx][ty].flag == food.flag)
    195     {
    196         snakeLen++;    //蛇身加长
    197         createFood();    //随机生成食物
    198         //更新-score
    199         alterScore(1);
    200     }
    201     else {
    202         //删除蛇尾
    203         setCursor(snake[tail].x, snake[tail].y);
    204         map[snake[tail].x][snake[tail].y].flag = 0;
    205         printf(" ");
    206         tail = tail == 0 ? SNAKE_MAXLEN - 1 : tail - 1;
    207     }
    208 
    209     //更新-蛇头模型标识
    210     map[tx][ty].flag = snakeBody.flag;
    211 
    212     return 1;
    213 }
    214 
    215 //随机生成食物
    216 void createFood()
    217 {
    218     int order;    //新食物对应的坐标编号
    219     int range;    //新食物所在范围
    220     srand((unsigned)time(NULL));    //初始种子
    221     range = SNAKE_MAXLEN - snakeLen;    //食物生成范围
    222     if (tail < header)
    223         order = rand() % range + tail + 1;
    224     else {
    225         order = rand() % range;
    226         if (order >= header)
    227             order += snakeLen;
    228     }
    229     //绘制食物
    230     int tx = snake[order].x;
    231     int ty = snake[order].y;
    232     setCursor(tx, ty);
    233     setColor(food.color);
    234     printf("%s", food.ch);
    235     map[tx][ty].flag = food.flag;
    236 }
    Util.c

      第三个是主函数及初始化部分

     1 #include "catalog.h"
     2 
     3 int snakeLen;    //贪吃蛇长度
     4 int direction;    //贪吃蛇移动方向
     5 int header, tail;    //蛇头蛇尾下标
     6 int score;    //分数
     7 int skill;    //技能开关
     8 int pauseTime;    //暂停时间(Ms)
     9 HANDLE hConsole;
    10 
    11 extern Model border, snakeHead, snakeBody, food;
    12 extern Map map[HEIGHT][WIDTH];
    13 extern Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos;
    14 
    15 //初始化方案
    16 void scheme()
    17 {
    18     //变量初始化及界面初始化
    19     init();
    20 
    21     //绘制初始贪吃蛇
    22     setCursor(XCENTER, YCENTER - snakeLen / 2);
    23     header = ORDER(XCENTER, YCENTER - snakeLen / 2);    //蛇头位置
    24     tail = header + snakeLen - 1;
    25     setColor(snakeBody.color);
    26     for (int i = header; i <= tail; i++)
    27     {
    28         if (i == header) printf("%s", snakeHead.ch);
    29         else printf("%s", snakeBody.ch);
    30         map[snake[i].x][snake[i].y].flag = snakeBody.flag;
    31     }
    32 
    33     //随机生成食物
    34     createFood();
    35 
    36     //初始方向处理
    37     direction = 75;
    38     char ch = getch();
    39     control(ch);
    40 }
    41 
    42 
    43 int main()
    44 {
    45     //初始方案
    46     scheme();
    47 
    48     while (1) {
    49         if (kbhit()) {
    50             char ch = getch();
    51             if (ch == ' ') MessageBox(NULL, "           暂停中...
      请单击确定继续游戏", "暂停游戏", MB_OK);
    52             if (skill == 1 && (ch == 's' || ch == 'S'))
    53                 pauseTime = pauseTime == PAUSE_TIME ? PAUSE_TIME / 2 : PAUSE_TIME;
    54             else control(ch);    //控制方向
    55         }
    56         if (move(direction))
    57             Sleep(pauseTime);
    58         else scheme();
    59     }
    60     
    61     return 0;
    62 }
    SnakeMain.c

      第三版:

      第三版加入了菜单,优化了部分界面,并将游戏分为两个基本模式。

      

      和第二版一样,分为三个文件

      头文件:

      

     1 #ifndef CATALOG_H
     2 #define CATALOG_H
     3 
     4 #include<stdio.h>
     5 #include<conio.h>
     6 #include<time.h>
     7 #include<windows.h>
     8 
     9 //背景上下伸展长度
    10 #define TOP_EXTEND 3
    11 #define BOTTOM_EXTEND 3
    12 //背景宽-高
    13 #define WIDTH 33
    14 #define HEIGHT 20
    15 //地图中心
    16 #define XCENTER ((HEIGHT + 1)/2 + TOP_EXTEND)
    17 #define YCENTER (WIDTH + 1)/2
    18 //最大蛇长
    19 #define SNAKE_MAXLEN ((HEIGHT - 2) * (WIDTH - 2))
    20 //坐标->序号
    21 #define ORDER(x,y) ((x - TOP_EXTEND - 1) * (WIDTH - 2) + (y - 1))
    22 //点:在移动范围内
    23 #define IN_RANGE(x,y) (x > TOP_EXTEND && x < HEIGHT + TOP_EXTEND - 1 && y > 0 && y < WIDTH - 1)
    24 //默认暂停时间
    25 #define PAUSE_TIME 500
    26 
    27 /*全局变量*/
    28 extern int snakeLen;
    29 extern int direction;
    30 extern int header, tail;
    31 extern int score;
    32 extern int skill;
    33 extern int pauseTime;
    34 extern int mode;
    35 extern HANDLE hConsole;
    36 
    37 //模型
    38 typedef struct Model {
    39     char *ch;
    40     int color;
    41     int flag;
    42 }Model;
    43 
    44 //地图
    45 typedef struct Map {
    46     int flag;    //模型标识
    47     int order;    //序号
    48 }Map;
    49 
    50 //坐标
    51 //贪吃蛇移动范围(循环队列),分数位置
    52 typedef struct Coordinate {
    53     int x, y;
    54 }Coordinate;
    55 
    56 
    57 /******界面设定******/
    58 
    59 extern void setCursor(int x, int y);
    60 
    61 extern void setColor(int color);
    62 
    63 extern void control(char ch);
    64 
    65 /******框架及功能******/
    66 
    67 extern void init();
    68 
    69 extern int move(char direction);
    70 
    71 extern void createFood();
    72 
    73 
    74 #endif
    catalog.h

      功能文件:

      1 #include "catalog.h"
      2 
      3 Model    border = { NULL, 0x7, 1 },
      4 snakeHead = { "", 0xE, 2 },
      5 snakeBody = { "", 0xE, 2 },
      6 food = { "", 0xC, 3 };
      7 
      8 Map map[HEIGHT][WIDTH];
      9 
     10 Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos, dotPos;
     11 
     12 #define UP 4    //向上偏移量
     13 #define LEFT 6    //左移偏移量
     14 
     15 /************游戏界面设定************/
     16 
     17 //隐藏光标
     18 void hideCursor()
     19 {
     20     CONSOLE_CURSOR_INFO cci;    //光标信息
     21     hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
     22     GetConsoleCursorInfo(hConsole, &cci);
     23     cci.bVisible = 0;
     24     SetConsoleCursorInfo(hConsole, &cci);
     25 }
     26 
     27 //设置光标位置
     28 void setCursor(int x, int y)
     29 {
     30     //参数x为行,y为列-与光标设置相反
     31     COORD coord;
     32     coord.X = 2 * y;    //宽字符决定占两列
     33     coord.Y = x;
     34     SetConsoleCursorPosition(hConsole, coord);
     35 }
     36 
     37 //设置文字颜色
     38 void setColor(int color)
     39 {
     40     SetConsoleTextAttribute(hConsole, color);
     41 }
     42 
     43 //绘制游戏窗口(包括dos环境初始窗口)
     44 void createWindow()
     45 {
     46     //dos命令设置窗口大小
     47     system("mode con:cols=72 lines=28");
     48 
     49     //╔ ╗ ╝ ╚ ═ ║ ╠ ╣ ╦ ╩
     50     setColor(0x7);
     51     printf("╔════════════════╦════════════════╗
    ");    //0~34
     52     for (int i = 1; i < TOP_EXTEND; i++)
     53         printf("║                                ║                                ║
    ");
     54     printf("╠════════════════╩════════════════╣
    ");
     55     for (int i = 1; i < HEIGHT - 1; i++)
     56         printf("║                                                                  ║
    ");
     57     printf("╠═════════════════════════════════╣
    ");
     58     for (int i = 1; i < BOTTOM_EXTEND; i++)
     59         printf("║                                                                  ║
    ");
     60     printf("╚═════════════════════════════════╝
    ");
     61     setCursor(HEIGHT + TOP_EXTEND, 1);
     62     printf(" 操作指南:上下键及回车键选择.");
     63     setCursor(HEIGHT + TOP_EXTEND + 1, 1);
     64     printf(" Version : 3.0");
     65 
     66     for (int x = TOP_EXTEND; x < HEIGHT + TOP_EXTEND; x++)
     67     {
     68         for (int y = 0; y < WIDTH; y++)
     69             if (IN_RANGE(x, y))
     70             {
     71                 int order = ORDER(x, y);
     72                 snake[order].x = x;
     73                 snake[order].y = y;
     74                 //背景设置
     75                 map[x][y].order = order;
     76                 map[x][y].flag = 0;
     77             }
     78             else map[x][y].flag = border.flag;
     79     }
     80     //其他信息
     81     setColor(0x7);
     82     setCursor(1, 1);
     83     printf("          你的分数:0");
     84     setCursor(2, 1);
     85     printf("          技能:无");
     86     setCursor(1, YCENTER + 1);
     87     printf("        Author: InkMark");
     88     setCursor(2, YCENTER + 1);
     89     printf(" Blog: www.cnblogs.com/inkblots");
     90 
     91     //分数位置
     92     scorePos.x = 1; scorePos.y = 11;
     93     skillPos.x = 2; skillPos.y = 9;
     94 }
     95 
     96 void menuButton(char ch)
     97 {
     98     int tx = dotPos.x;
     99     int ty = dotPos.y;
    100     if (ch == 72)    tx--;
    101     else tx++;
    102 
    103     if (tx > XCENTER - UP && tx < XCENTER - UP + 4)
    104     {
    105         setCursor(dotPos.x, dotPos.y);
    106         printf(" ");
    107         setCursor(tx, ty);
    108         printf("");
    109         dotPos.x = tx;
    110         dotPos.y = ty;
    111     }
    112 }
    113 
    114 //绘制菜单并进行选择
    115 void menu()
    116 {
    117     //╔ ╗ ╚ ╝ ═ ║ ╠ ╣ ╦ ╩
    118     setCursor(XCENTER - UP, YCENTER - LEFT);
    119     printf("╔═════════╗");    //11
    120     for (int i = 1; i <= 3; i++)
    121     {
    122         setCursor(XCENTER - UP + i, YCENTER - LEFT);
    123         printf("║                  ║");
    124     }
    125     setCursor(XCENTER - UP + 4, YCENTER - LEFT);
    126     printf("╚═════════╝");
    127 
    128     dotPos.x = XCENTER - UP + 1;
    129     dotPos.y = YCENTER - 4;
    130     setCursor(dotPos.x, dotPos.y);
    131     printf("●  1.死亡模式");
    132     setCursor(XCENTER - UP + 2, YCENTER - 2);
    133     printf("2.技能模式");
    134     setCursor(XCENTER - UP + 3, YCENTER - 2);
    135     printf("3.退出游戏");
    136 
    137     char ch;
    138     while ((ch = getch()) != 13)    //确定键:13
    139     {
    140         if (ch != -32) continue;
    141         ch = getch();
    142         if (ch == 72 || ch == 80)
    143             menuButton(ch);
    144     }
    145 
    146     for (int i = 0; i < 5; i++)
    147     {
    148         setCursor(XCENTER - UP + i, YCENTER - LEFT);
    149         printf("                      ");
    150     }
    151 
    152     mode = dotPos.x - XCENTER + UP;
    153     if (mode == 3) exit(0);
    154 }
    155 
    156 //控制键
    157 void control(char ch)
    158 {
    159     //本应取方向键高位再取低位,在此以简便为主
    160     if (ch != -32) return;
    161     ch = getch();
    162     //上72下80左75右77
    163     if (ch == 72 || ch == 80 || ch == 75 || ch == 77)    //低位相同
    164         if (ch + direction != 152) 
    165             direction = ch;    //不为反向时改变方向
    166 }
    167 
    168 /************游戏信息输出************/
    169 
    170 void getSkill()
    171 {
    172     setCursor(skillPos.x, skillPos.y);
    173     printf("加速(S键)");
    174     skill = 1;    //加速技能开启
    175 }
    176 
    177 void alterScore(int addition)
    178 {
    179     score += addition;
    180     if (mode == 2 && score >= 8)
    181         getSkill();
    182     setCursor(scorePos.x, scorePos.y);
    183     setColor(0x7);
    184     printf("%d", score);
    185 }
    186 
    187 void printInfo()
    188 {
    189     //╔ ╗ ╚ ╝ ═ ║ ╠ ╣ ╦ ╩
    190     int col = 28;
    191     setCursor(HEIGHT + TOP_EXTEND - 1, col);
    192     printf("");
    193     setCursor(HEIGHT + TOP_EXTEND, col);
    194     printf("");
    195     setCursor(HEIGHT + TOP_EXTEND + 1, col);
    196     printf("║  模 式");
    197     if (mode == 1) {
    198 
    199         setCursor(HEIGHT + TOP_EXTEND, 1);
    200         printf(" 操作指南:方向键控制,空格可暂停.");
    201         setCursor(HEIGHT + TOP_EXTEND, col + 1);
    202         printf("  无 尽");
    203     }
    204     else {
    205         setCursor(HEIGHT + TOP_EXTEND, 1);
    206         printf(" 操作指南:方向键控制,空格可暂停,8分后有惊喜.");
    207         setCursor(HEIGHT + TOP_EXTEND, col + 1);
    208         printf("  技 能");
    209     }
    210     setCursor(HEIGHT + TOP_EXTEND + 2, col);
    211     printf("");
    212 }
    213 
    214 void gameOver()
    215 {
    216     if (IDYES == MessageBox(NULL, "是否重新开始游戏?", "Snake_2.0", MB_YESNO))
    217     {
    218         system("CLS");
    219         return;
    220     }
    221 
    222     exit(0);
    223 }
    224 
    225 /************游戏框架及功能************/
    226 
    227 //变量初始化及界面初始化
    228 void init()
    229 {
    230     pauseTime = PAUSE_TIME;
    231     skill = 0;
    232     score = 0;
    233     snakeLen = 1;
    234     header = 0;
    235     tail = snakeLen - 1;
    236 
    237     hideCursor();//隐藏光标
    238     createWindow();    //绘制背景
    239     
    240     menu();    //菜单操作
    241 
    242     printInfo();
    243 }
    244 
    245 //贪吃蛇移动
    246 //return:1为成功,0为失败
    247 int move(char direction)
    248 {
    249     //新蛇头
    250     int tx = snake[header].x;
    251     int ty = snake[header].y;
    252     //上72下80左75右77
    253     switch (direction)
    254     {
    255         case 72: tx--; break;
    256         case 80: tx++; break;
    257         case 75: ty--; break;
    258         case 77: ty++; break;
    259     }
    260 
    261     //判断是否撞墙或撞到自己
    262     if (map[tx][ty].flag == border.flag || map[tx][ty].flag == snakeBody.flag)
    263     {
    264         gameOver();
    265         return 0;
    266     }
    267 
    268     //新蛇头绘制(此时未refresh模型标识)
    269     setCursor(tx, ty);
    270     setColor(snakeHead.color);
    271     printf("%s", snakeHead.ch);
    272     //原蛇头重绘制
    273     setCursor(snake[header].x, snake[header].y);
    274     printf("%s", snakeBody.ch);
    275 
    276     //蛇头更新-队首移动
    277     header = header == 0 ? SNAKE_MAXLEN - 1 : header - 1;
    278     //旧编号位置更正
    279     int preNum = map[tx][ty].order;    //蛇头位置旧编号
    280     snake[preNum].x = snake[header].x;
    281     snake[preNum].y = snake[header].y;
    282     map[snake[preNum].x][snake[preNum].y].order = preNum;
    283     //蛇头位置更正
    284     snake[header].x = tx;
    285     snake[header].y = ty;
    286     map[tx][ty].order = header;    //蛇头位置新编号
    287 
    288     //判断是否吃到食物
    289     if (map[tx][ty].flag == food.flag)
    290     {
    291         snakeLen++;    //蛇身加长
    292         createFood();    //随机生成食物
    293         //更新-score
    294         if(pauseTime == PAUSE_TIME) alterScore(1);
    295         else alterScore(2);
    296     }
    297     else {
    298         //删除蛇尾
    299         setCursor(snake[tail].x, snake[tail].y);
    300         map[snake[tail].x][snake[tail].y].flag = 0;
    301         printf(" ");
    302         tail = tail == 0 ? SNAKE_MAXLEN - 1 : tail - 1;
    303     }
    304 
    305     //更新-蛇头模型标识
    306     map[tx][ty].flag = snakeBody.flag;
    307 
    308     return 1;
    309 }
    310 
    311 //随机生成食物
    312 void createFood()
    313 {
    314     int order;    //新食物对应的坐标编号
    315     int range;    //新食物所在范围
    316     srand((unsigned)time(NULL));    //初始种子
    317     range = SNAKE_MAXLEN - snakeLen;    //食物生成范围
    318     if (tail < header)
    319         order = rand() % range + tail + 1;
    320     else {
    321         order = rand() % range;
    322         if (order >= header)
    323             order += snakeLen;
    324     }
    325     //绘制食物
    326     int tx = snake[order].x;
    327     int ty = snake[order].y;
    328     setCursor(tx, ty);
    329     setColor(food.color);
    330     printf("%s", food.ch);
    331     map[tx][ty].flag = food.flag;
    332 }
    Util.c

      主文件:

     

     1 #include "catalog.h"
     2 
     3 int snakeLen;    //贪吃蛇长度
     4 int direction;    //贪吃蛇移动方向
     5 int header, tail;    //蛇头蛇尾下标
     6 int score;    //分数
     7 int skill;    //技能开关
     8 int pauseTime;    //暂停时间(Ms)
     9 int mode;
    10 HANDLE hConsole;
    11 
    12 extern Model border, snakeHead, snakeBody, food;
    13 extern Map map[HEIGHT][WIDTH];
    14 extern Coordinate snake[SNAKE_MAXLEN], scorePos, skillPos;
    15 
    16 //初始化方案
    17 void scheme()
    18 {
    19     //变量初始化及界面初始化
    20     init();
    21 
    22     //绘制初始贪吃蛇
    23     setCursor(XCENTER, YCENTER - snakeLen / 2);
    24     header = ORDER(XCENTER, YCENTER - snakeLen / 2);    //蛇头位置
    25     tail = header + snakeLen - 1;
    26     setColor(snakeBody.color);
    27     for (int i = header; i <= tail; i++)
    28     {
    29         if (i == header) printf("%s", snakeHead.ch);
    30         else printf("%s", snakeBody.ch);
    31         map[snake[i].x][snake[i].y].flag = snakeBody.flag;
    32     }
    33 
    34     //随机生成食物
    35     createFood();
    36 
    37     //初始方向处理
    38     direction = 75;
    39     char ch = getch();
    40     control(ch);
    41 }
    42 
    43 
    44 int main()
    45 {
    46     //初始方案
    47     scheme();
    48 
    49     while (1) {
    50         if (kbhit()) {
    51             char ch = getch();
    52             if (ch == ' ') MessageBox(NULL, "           暂停中...
      请单击确定继续游戏", "暂停游戏", MB_OK);
    53             if (mode == 2 && skill == 1 && (ch == 's' || ch == 'S'))
    54                 pauseTime = pauseTime == PAUSE_TIME ? PAUSE_TIME / 2 : PAUSE_TIME;
    55             else control(ch);    //控制方向
    56         }
    57         if (move(direction))
    58             Sleep(pauseTime);
    59         else scheme();
    60     }
    61     
    62     return 0;
    63 }
    SnakeMain.c
    他坐在湖边,望向天空,她坐在对岸,盯着湖面
  • 相关阅读:
    18种典型算法
    幂法和反幂法
    关于Ubuntu下安装Win8和Win8下安装Ubuntu的注意事项
    静态链接库与动态链接库
    面向对象系列二(封装)
    基于ASP.NET WPF技术及MVP模式实战太平人寿客户管理项目开发(Repository模式)
    不要对终于用户谈云
    cocos2d-x 3.0 创建项目
    Android OpenGL ES 画球体
    设计模式 适配器模式 以手机充电器为例
  • 原文地址:https://www.cnblogs.com/Inkblots/p/5205267.html
Copyright © 2020-2023  润新知