这两天刚考完试,有两天的休息时间。本打算出去逛逛,可是又不知道去哪好......所以就待在学校写代码吧!:laughing:(其实是因为没有女票qwq

    贪吃蛇大家都玩过吧,实现起来也并不难,主要思路就是用数组来模拟画布(即蛇活动的那个地方)。关于蛇的移动与吃食物后变长,用的是移动前后数组中各个元素间的关系的方法。详见代码。
    另外,为了熟悉C++中类的使用,这次用的是OOP的思想(而不再是“用C++的语法写C”),写了两个类,即Snake类和Canvas类,分别表示蛇和画布。

代码如下

#include <windows.h>
#include <conio.h>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <cstdlib>
int new_tail_i,new_tail_j;
void gotoxy(int x,int y)        //将光标的坐标设为(x,y) 
{
    HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);        //取得一个句柄 
    COORD pos;
    pos.X=x;
    pos.Y=y;
    SetConsoleCursorPosition(handle,pos);        //设置光标的坐标 
}
class Canvas{        //定义一个Canvas类来表示画布 
    public:
        Canvas();        //构造函数 
        void show();    //显示画布 
        void generate_food();        //生成画布上的食物 
        int get_hei();        //得到画布高度 
        int get_wid();        //得到画布宽度 
        void change_canvas(int i,int j,int value_to_write);        //改变画布(即修改私有成员canvas数组中的某个元素值) 
        int show_ele(int i,int j);        //得到画布上某个位置的状态(即canvas中某个元素的元素值) 
    private:
        int canvas[100][100];        //用一个数组来表示画布各处的状态 
        int hei;        //画布高度 
        int wid;        //画布宽度 
};
void Canvas::generate_food()
{
    int success=1;
    int flag=1;
    for(int i=1;i<hei-1;i++)
    {
        for(int j=1;j<wid-1;j++)
        {
            if(canvas[i][j]==-2)
            {
                flag=0;
                goto out_loop;
            }
        }
    }
    //上面一段代码用于判断画布中是否有食物。若有,则不需要生成新食物;否则生成新食物 
    out_loop:;
    if(flag)
    {
        while(success)        //加个while循环是为了在生成错误(指食物生成到了蛇的身上或墙壁上)的情况下重新再生成 
        {
            int x=rand()%(hei-2)+1;
            int y=rand()%(wid-2)+1;
            if(canvas[x][y]==0)
            {
                canvas[x][y]=-2;
                success=0;
            }
        }
        return ;
    }
    else
        return ;
}
Canvas::Canvas()
{    
    hei=20;wid=40;
    system("cls");        //清屏 
    memset(canvas,0,sizeof(canvas));
    for(int i=0;i<hei;i++)
    {
        canvas[i][0]=-1;
        canvas[i][wid-1]=-1;
    }
    for(int j=1;j<wid-1;j++)
    {
        canvas[0][j]=-1;
        canvas[hei-1][j]=-1;
    }
}
void Canvas::show()
{    
//    system("cls");
    gotoxy(0,0);
    for(int i=0;i<hei;i++)
    {
        for(int j=0;j<wid;j++)
        {
            if(canvas[i][j]==-1)
                printf("#");
            if(canvas[i][j]==0)
                printf(" ");
            if(canvas[i][j]==1)
                printf("@");
            if(canvas[i][j]>1)
                printf("*");
            if(canvas[i][j]==-2)
                printf("F");
        }
        printf("\n");
    }
}
int Canvas::show_ele(int i,int j)
{
    return canvas[i][j];
}
void Canvas::change_canvas(int i,int j,int value_to_write)
{
    canvas[i][j]=value_to_write;
}
int Canvas::get_hei()
{
    return hei;
}
int Canvas::get_wid()
{
    return wid;
}

class Snake{
    public:
        void move(int dir,Canvas& can);
        void grow(int x,int y,Canvas& can);
        void put_snake(Canvas& sele_can,int x,int y,int len);
        bool check(Canvas& can,int x,int y);
        int get_score();
        int get_head_i();
        int get_head_j();
        void quit();
        Snake();
    private:
        int head_i,head_j;
        int old_tail_i,old_tail_j;
        int score;
        int live;
};
Snake::Snake()
{
    live=3;
    score=0;
}
int Snake::get_score()
{
    return score;
}
int Snake::get_head_i()
{
    return head_i;
}
int Snake::get_head_j()
{
    return head_j;
}
bool Snake::check(Canvas& can,int x,int y)        //用于判断蛇是否撞墙或咬到自己 
{    
    if(can.show_ele(x,y)>0||can.show_ele(x,y)==-1)         
        return false;
    return true;
}
void Snake::put_snake(Canvas& sele_can,int x,int y,int len)            //把蛇放到一个画布上。这样做的目的是为了蛇和画布能更彻底地分离 
{
    int hei=sele_can.get_hei();
    int wid=sele_can.get_wid();
    int body=1;
    for(int j=y;j>=y-len;j--,body++)
        sele_can.change_canvas(x,j,body);
}
void Snake::move(int dir,Canvas& can)        //蛇的移动 
{    
    int hei=can.get_hei();
    int wid=can.get_wid();
    for(int i=1;i<hei-1;i++)
    {
        for(int j=1;j<wid-1;j++)
        {
            if(can.show_ele(i,j)>=1)
            {    
                int tmp=can.show_ele(i,j);
                can.change_canvas(i,j,tmp+1);
            }
        }
    }
    //将蛇的身体"+1" 
    int maxi=-10;
    for(int i=1;i<hei-1;i++)
    {
        for(int j=1;j<wid-1;j++)
        {
            if(can.show_ele(i,j)==2)
            {
                head_i=i;
                head_j=j;
            }
            if(can.show_ele(i,j)>maxi)
            {
                maxi=can.show_ele(i,j);
                old_tail_i=i;
                old_tail_j=j;
            }
        }
    }
    //找到头部坐标 
    //下面则是根据移动方向放置蛇头 
    if(dir==1)        //左移 
    {
        if(can.show_ele(head_i,head_j-1)==-2)
            grow(head_i,head_j-1,can);
        else
        {
            if(check(can,head_i,head_j-1))
            {
                can.change_canvas(head_i,head_j-1,1);
                can.change_canvas(old_tail_i,old_tail_j,0);
                can.show();
            }
            else
            {
                quit();        //这个还没写 
                return; 
            }
        }
    }
    else if(dir==2)        //上移 
    {
        if(can.show_ele(head_i-1,head_j)==-2)
            grow(head_i-1,head_j,can);
        else
        {
            if(check(can,head_i-1,head_j))
            {
                can.change_canvas(head_i-1,head_j,1);
                can.change_canvas(old_tail_i,old_tail_j,0);
                can.show();
            }
            else
            {
                quit();
                return ;
            }
        }
    }
    else if(dir==3)        //右移 
    {
        if(can.show_ele(head_i,head_j+1)==-2)
            grow(head_i,head_j+1,can);
        else
        {
            if(check(can,head_i,head_j+1))
            {
                can.change_canvas(head_i,head_j+1,1);
                can.change_canvas(old_tail_i,old_tail_j,0);
                can.show();
            }
            else
            {
                quit();
                return ;
            }
        }
    }
    else if(dir==4)        //下移 
    {
        if(can.show_ele(head_i+1,head_j)==-2)
            grow(head_i+1,head_j,can);
        else
        {
            if(check(can,head_i+1,head_j))
            {
                can.change_canvas(head_i+1,head_j,1);
                can.change_canvas(old_tail_i,old_tail_j,0);
                can.show();
            }
            else
            {
                quit();
                return ;
            }
        }
    }
}
//蛇的变长函数,其原理与蛇的移动函数相似,只是不需要擦除蛇尾 
void Snake::grow(int x,int y,Canvas& can)
{
    for(int i=1;i<can.get_hei()-1;i++)
    {
        for(int j=1;j<can.get_wid()-1;j++)
        {
            if(can.show_ele(i,j)>=1)
            {
                int tmp=can.show_ele(i,j);
                can.change_canvas(i,j,tmp+1);
            }
        }
    }
    can.change_canvas(x,y,1);
    score++;
    can.generate_food();
    can.show();
    printf("\t     Your score is %d.\n",score);
}
void Snake::quit()
{
    printf("\nI(the snake) lose!\n");
    printf("Press any key to quit.\n"); 
    getchar();
    exit(0);
}
//应实现,同一条蛇可以在两个或多个不同的画布上移动、吃食物
//主体应该是蛇本身,而非画布 
int main()
{    
    srand(time(NULL));            //用时间作为随机数种子,这样每一局的食物分布都会与上一局不同 
    Snake sna;
    Canvas canv;
    sna.put_snake(canv,canv.get_hei()-3,(canv.get_wid())/2+2,0);
    canv.generate_food();
    canv.show();
    printf("\t     Your score is %d.\n",sna.get_score());
    while(1)
    {    
        char op;
        op=getch();
        switch(op)
        {
            case 'a':sna.move(1,canv);break;
            case 'w':sna.move(2,canv);break;
            case 'd':sna.move(3,canv);break;
            case 's':sna.move(4,canv);break;
        }
    }
    return 0;
}

简单说一下蛇移动的思路:
    首先,蛇的表示方法是这样的:若数组中某个元素为1,则它表示蛇头所在处;若数组中某个元素大于1,则它表示蛇身。从蛇头到蛇尾,是一个从1开始的连续自然数序列(即1,2,3,4,5,···)
    通过观察下面的图片,我们可以得出这样的方法:若要移动蛇,只需要先将蛇头到蛇身的每一个数都++,然后将移动方向所对应的元素的值改为1,并将移动前的蛇尾的元素值改为0(0表示空白处,即蛇可以到达的地方)。

微信截图_20190113215711.png
                        移动前

微信截图_20190113215745.png
                    将蛇头和蛇身++

微信截图_20190113215800.png

                       完成移动

大致就是如此,接下来应该还会有所补充,现在就先这样了吧(其实是懒)

Last modification:January 13th, 2019 at 10:22 pm
If you think my article is useful to you, please feel free to appreciate