实现简单的贪吃蛇(C/C++)

一.编程环境:

  1. 下载并安装visual studio 2019。
  2. 下载并安装Easyx最新支持vc2019版本。

二.编程思路:

  • 打开一个图形窗口。

  • 定义一条蛇。

  • 定义(生成)一只小鸡

  • 定义蛇的初始化方向

  • do{
    获取键盘输入按键。
    把蛇显示在图形窗口中。
    把小鸡显示在图形窗口中。
    移动蛇(其中包括:1.把原来的蛇在图形窗口中隐藏 2.移动蛇3.移动后看有没有撞墙或咬到自己4.看有没有吃到鸡(若吃到鸡,把原来的鸡隐藏,生成新的鸡的坐标,把蛇的长度加长))。

    }while(输入的按键不是ESC);
    (while循环结束,游戏结束)

  • 当蛇的长度小于游戏目标时,在窗口上显示“You lost!” 否则显示“Congratulations! you won!”
    关闭程序

三.数据结构的选取{逻辑结构,存储结构,数据的运算}:

  1. 程序设中的蛇选取什么样的数据结构。对于数据结构的选取主要看对数据进行什么样的操作。
  2. 蛇:在以上的编程思路中,蛇有两个动作,一是移动,二是增长。蛇的移动即蛇各部分坐标的改变,蛇的增长即链表或者数组中元素的增多。
  3. 蛇:对于逻辑结构需选举用线性结构,对于存储结构,可选顺序存储结构和链式存储结构。具体选择哪一种先分析蛇的增长。
  4. 假设蛇增长的一段加在蛇末尾,但蛇末尾有三个方向如下图1.0,具体加在什么坐标上害的判断最后一块的移动方向比较麻烦,所以加在末尾的方法舍去。
    图1.0:

图1.0

  1. 假设加在蛇中间,中间有这么多快加在哪两块中间也不好判断,所以舍去。
  2. 假设加在头的位置,这就好实现,只需要在头后面加一块,让这块继承头的坐标,同时更根据蛇移动方向,让蛇头沿着移动方向移动一块的距离即可,即更新头的坐标,如下图2.0。
  3. 图2.0:

图2.0

  1. 既然要在顶头插入元素,为了避免大量元素移动应该选择链式存储结构。
  2. 链式存储结构有单链表,单循环链表,双链表,双循环链表等,因为蛇的移动方式:每一块移动后的坐标为其前驱的坐标,头结点则沿着移动方向移动一块的距离。根据移动方式需要找前驱结点,所以采用双向链表,且为带头结点的双向链表。又因为更改每一块的坐标时需要从蛇尾开始改,所以还需一个尾指针,故最终选取带头结点和尾指针的双链表。

四.编程中部分关键代码实现细节:

1.蛇的数据类型的设计:

 typedef struct body_ {
    int x;//蛇身体所在横坐标
    int y;//蛇身体所在纵坐标
    struct body_* prex;//前驱指针
    struct body_* next;//后继指针
 }*body, BODY;

2.小鸡的数据类型的设计:

 typedef struct Chicken_ {
   //鸡所在的横纵坐标
   int x;
   int y;
 }*chicken,CHICKEN;

3.定义蛇:

 body rear = NULL;//蛇尾指针
 //生成蛇头
 BODY Head;
 Head.next = NULL;
 Head.prex = NULL;
 Head.x = 400;//蛇头的x,y坐标
 Head.y = 400;
 //蛇的长度
 int Snake_len = 1;

4.定义小鸡:

 CHICKEN Chi;
 Chi.x = 20 + rand() % (800 - 20 + 1);//随机生成小鸡坐标 (20~800间)
 Chi.y = 20 + rand() %(800 - 20 + 1);

5.图形窗口建立:

initgraph(820, 920);//打开一个长820像素,宽920像素的窗口
//该窗口大小的设计得先经过设计如下图3.0,黑色区域为蛇和鸡出现的区域,外面为边框

图3.0:
tu3.0

6.画地图蓝色边框(该边框算法见杭电oj 2052题:Picture)

void Creat_map()
{
 //画边框
 setfillcolor(BLUE); //选择填充颜色
 for (int i = 0; i <= 820; i = i + size)
 {
     for (int j = 0; j <= 820; j = j + size)
     {
         if (i == 0 || i == 820)
         {  //bar3d(int left, int top, int right, int bottom, int depth, int topflag, PIMAGE pimg = NULL);   // 画有边框三维填充矩形
             fillrectangle(i, j, i + size, j + size);
         }
         else
         {
             if (j == 0 || j == 820)
             {
                 fillrectangle(i, j, i + size, j + size);
             }
         }
     }
 }
}

7.显示蛇:

 void Show_Snake(body Head)
 {
   body p = Head;
   setfillcolor(RED); //选择蛇的填充颜色
   while (p)
   {
       fillrectangle(p->x, p->y, p->x + size, p->y + size);
       p = p->next;
   }
 }
 //把蛇的每一个节点显示出来,显示为一个正方形,蛇的节点坐标为正方形的左上角坐标,正方形左下角坐标等于左上角分别加上宽度

8.显示小鸡:

void Show_Chicken(chicken Chi)
{
 setfillcolor(YELLOW); //选择鸡的填充颜色
 fillcircle(Chi->x+size/2, Chi->y+size/2 ,size/2);
}
//和显示蛇的方式差不多,这里显示的是以鸡的坐标为正方形的左上角坐标且宽度为size的正方形的内切圆

9.蛇的移动:

void Snake_move(int *button, int *len,body Head, body* rear,chicken Chi)
{
 //把原来的蛇隐藏
 Hide_Snake(Head);
 //移动蛇身
 body p= (*rear);
 while (p&&p!=Head)
 {
     p->x = p->prex->x;
     p->y = p->prex->y;
     p = p->prex;
 }
 //移动蛇头
 switch (*button)
     {
     case 72:Head->y = Head->y - 20; break;  //向上走一步
     case 80:Head->y = Head->y + 20; break;  //向下走一步
     case 75:Head->x = Head->x - 20; break;  //向左走一步
     case 77:Head->x = Head->x + 20; break;  //向右走一步
     default:break;
     }
     //判断是不是撞到墙或者咬到自己
        if (Ifdead(Head))
         {
             (*button) = 27;//button ==27时结束do while循环 游戏结束
         }
     //看有没有吃到鸡
     if (Head->x+size/2>= Chi->x&&Head->x+size/2<Chi->x+size && Head->y+size/2 >= Chi->y&&Head->y+size/2<Chi->y+size)
     {
         //把原先的鸡隐藏
         Hide_Chicjen(Chi);
         //更新鸡的位置
         Rnew_Chi(Head, Chi);
         //蛇的身子变长
         Snake_Grow(Head, len, rear, button);
     }
}
/*判断有没有吃到鸡的方法:
获得蛇头所在矩形(正方形)区域的中心点坐标:A:(Head->x+size/2,Head->y+size/2)
获得鸡所在圆形区域的外切正方形区域的范围:B:{横坐标范围:[Chi->x,Chi->x+size];纵坐标范围[Chi->y,Chi->y+size]}
当点A在区域B内则吃到鸡*/

五.参考代码:

#include<graphics.h>
#include <iostream>
#include<conio.h>
#include<Windows.h>
#include<stdio.h>
#include <cstdlib>
#include<malloc.h>
int speed = 150;//蛇的速度 数值越大越慢
int goal = 50;//达到goal的长度即获胜
#define size 20  //边框填充正方形的宽度
typedef struct body_ {
    int x;//蛇身体所在横坐标
    int y;//蛇身体所在纵坐标
    struct body_* prex;//前驱指针
    struct body_* next;//后继指针
}*body, BODY;
typedef struct Chicken_ {
    //鸡所在的横纵坐标
    int x;
    int y;
}*chicken,CHICKEN;
void Creat_map();//创建地图边框
void Snake_move(int *button,int *len,body Head, body *rear,chicken Chi);//移动蛇
void Snake_Grow(body Head,int *len,body *rear,int *button);//蛇变长
void Show_Snake(body Head);//展示蛇
void Hide_Snake(body Head);//隐藏蛇
void Show_Chicken(chicken Chi);//显示小鸡
void Hide_Chicjen(chicken Chi);//隐藏小鸡
int Ifdead(body Head);//判断蛇是否活
void Rnew_Chi(body Head,chicken Chi);//更新鸡的位置
int main()
{
    initgraph(840, 920);//打开一个长820像素,宽920像素的窗口
    Creat_map();//生成棋盘
    body rear = NULL;//蛇尾指针
    //生成蛇头
    BODY Head;
    Head.next = NULL;
    Head.prex = NULL;
    Head.x = 400;//蛇头的x,y坐标
    Head.y = 400;
    //蛇的长度
    int Snake_len = 1;
    //生成小鸡
    CHICKEN Chi;
    Chi.x = 20 + rand() % (800 - 20 + 1);//随机生成小鸡坐标 (20~800间)
    Chi.y = 20 + rand() %(800 - 20 + 1);
    int button=72;//开始时默认蛇向上走
    do {
        //cleardevice();
        while(_kbhit())//检查当前是否有键盘输入,若有则返回一个非0值,否则返回0。
        {
            button = _getch();
        }
        //展示蛇
        Show_Snake(&Head);
        //展示小鸡
        Show_Chicken(&Chi);
        //移动蛇
        Sleep(speed);
        Snake_move(&button,&Snake_len, &Head,&rear,&Chi);
    } while (button != 27);//esc 的ascii值时27
    if (Snake_len < goal)
    {
        TCHAR Str[] = _T("You lost!");  //在窗口500 600 的地方显示you lost
        outtextxy(500, 600, Str);
    }
    else
    {
        TCHAR Str[] = _T("Congratulations! you won!");  //在窗口500 600 的地方显示you lost
        outtextxy(500, 600, Str);
    }
    Sleep(2000);
}
/***************************************************************/
void Creat_map()
{
    //画边框
    setfillcolor(BLUE); //选择填充颜色
    for (int i = 0; i <= 820; i = i + size)
    {
        for (int j = 0; j <= 820; j = j + size)
        {
            if (i == 0 || i == 820)
            {  //bar3d(int left, int top, int right, int bottom, int depth, int topflag, PIMAGE pimg = NULL);   // 画有边框三维填充矩形
                fillrectangle(i, j, i + size, j + size);
            }
            else
            {
                if (j == 0 || j == 820)
                {
                    fillrectangle(i, j, i + size, j + size);
                }
            }
        }
    }
}
/***************************************************************/
void Show_Snake(body Head)
{
    body p = Head;
    setfillcolor(RED); //选择蛇的填充颜色
    while (p)
    {
        fillrectangle(p->x, p->y, p->x + size, p->y + size);
        p = p->next;
    }
}
/***************************************************************/
void Hide_Snake(body Head)
{
    body p = Head;
    setfillcolor(BLACK); //选择蛇的填充颜色
    while (p)
    {
        bar(p->x, p->y, p->x + size, p->y + size); //无边框矩形
        p = p->next;
    }
}
/***************************************************************/
void Show_Chicken(chicken Chi)
{
    setfillcolor(YELLOW); //选择鸡的填充颜色
    fillcircle(Chi->x+size/2, Chi->y+size/2 ,size/2);
}
/***************************************************************/
void Hide_Chicjen(chicken Chi)
{
    setfillcolor(BLACK); //选择鸡的填充颜色
    fillcircle(Chi->x + size / 2, Chi->y + size / 2, size / 2);
}
/***************************************************************/
void Snake_move(int *button, int *len,body Head, body* rear,chicken Chi)
{
    //把原来的蛇隐藏
    Hide_Snake(Head);
    //移动蛇身
    body p= (*rear);
    while (p&&p!=Head)
    {
        p->x = p->prex->x;
        p->y = p->prex->y;
        p = p->prex;
    }
    //移动蛇头
    switch (*button)
        {
        case 72:Head->y = Head->y - 20; break;  //向上走一步
        case 80:Head->y = Head->y + 20; break;  //向下走一步
        case 75:Head->x = Head->x - 20; break;  //向左走一步
        case 77:Head->x = Head->x + 20; break;  //向右走一步
        default:break;
        }
        //判断是不是撞到墙或者咬到自己
           if (Ifdead(Head))
            {
                (*button) = 27;//button ==27时结束do while循环 游戏结束
            }
        //看有没有吃到鸡
        if (Head->x+size/2>= Chi->x&&Head->x+size/2<Chi->x+size && Head->y+size/2 >= Chi->y&&Head->y+size/2<Chi->y+size)
        {
            //把原先的鸡隐藏
            Hide_Chicjen(Chi);
            //更新鸡的位置
            Rnew_Chi(Head, Chi);
            //蛇的身子变长
            Snake_Grow(Head, len, rear, button);
        }
}
/***************************************************************/
int Ifdead(body Head)
{
    int term_h = 0;//表示蛇没撞到墙
    int term_b = 0;//表示蛇没咬到自己
    //判断是否撞到墙
    if (Head->x < 20 || Head->x>800 || Head->y < 20 || Head->y>800)
        term_h = 1;//表示撞到墙
    body p = Head->next;
    while (p)
    {
        if (Head->x == p->x && Head->y == p->y)
        {
            term_b = 1;//表示咬到自己
            break;
        }
        p = p->next;
    }
    return term_h + term_b;
}
/***************************************************************/
void Rnew_Chi(body Head, chicken Chi)//更新鸡的位置
{
    //看鸡出现的位置是不是在蛇上
    int temp = 0;//表示鸡出现的位置在蛇上
    do {
        //生成鸡的位置
        Chi->x = 20 + rand() % (800 - 20 + 1);
        Chi->y = 20 + rand() % (800 - 20 + 1);
        body p = Head;
        while (p)
        {
            if (p->x == Chi->x && p->y == Chi->y)  
            {
                temp = 1; //在蛇的上则重新生成鸡
                break;
            }
            p = p->next;
        }
    } while (temp == 1);
}
/***************************************************************/
void Snake_Grow(body Head, int* len, body* rear,int *button)
{
    (*len)++;
    body q = (body)malloc(sizeof(BODY));
    //前插法
    q->next = Head->next;
    if(q->next!=NULL)
    {
        q->next->prex = q;
    }
    Head->next = q;
    q->prex = Head;
    //更新插入节点坐标
    q->x = Head->x;
    q->y = Head->y;
    switch (*button)
    {
    case 72:Head->y = Head->y - 20; break;  //向上走一步
    case 80:Head->y = Head->y + 20; break;  //向下走一步
    case 75:Head->x = Head->x - 20; break;  //向左走一步
    case 77:Head->x = Head->x + 20; break;  //向右走一步
    default:break;
    }
    if ((*rear) == NULL)//的到尾指针的值
        *rear = q;
    if ((*len) > goal) //长度达到goal时 游戏结束取得胜利
        (*button) = 27;
}

六.程序运行截图:

在这里插入图片描述

七.注意事项:

  1. 在建立工程时应建立c++工程而非c,否则无法使用graphics。但程序大部分是c语言写法,这个不要紧,c++继承了c

八.欢迎大家关注我的博客

  1. 喜欢c语言的同学可以关注 Manchester(https://www.dotcpp.com/home/wenyajie)
发布了1 篇原创文章 · 获赞 1 · 访问量 47

猜你喜欢

转载自blog.csdn.net/yajiewen/article/details/105781759