首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用诅咒的蛇游戏

使用诅咒的蛇游戏
EN

Code Review用户
提问于 2016-02-26 13:47:53
回答 3查看 8.1K关注 0票数 17

这是我使用ncurses库在C++中使用的蛇版本。我想听听您如何改进这段代码,以及对未来项目的编码和效率的一般建议。我尝试过用纯OOP编写这段代码,也让我知道这一点。

代码语言:javascript
复制
#include<ncurses.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>

class snake                         //to store the x and y coordinates of each snake part
{
    int x, y;
    char ch;
    public:
    snake(){ x= y= 0; ch='O';}
    snake(int a, int b)
    {
        x= a; y= b; ch= 'O';
    }
    snake(const snake &ekans)
    {
        ch= ekans.ch;
        x= ekans.x;
        y= ekans.y;
    }
    void setCh(char x)
    {
        ch= x;
    }
    char getCh()
    {
        return ch;
    }
    int getX()
    {
        return x;
    }
    void setX(int no)
    {
        x= no;
    }
    int getY()
    {
        return y;
    }
    void setY(int no)
    {
         y= no;
    }
};

class list                          //to store each snake body          
{
    node* head;
    int length;
    public:
    list()
    {
        head= NULL;
        length= 0;
    }
    void add(snake s)                   //adds at the end
    {
        node *n= new node;
        n->setSnake(s);
        n->setNext(head);
        head= n;
        length++;
    }
    int listLength()
    {
        return length;
    }
    snake get(int n)
    {
        node *temp =head;
        int count= 1;
        while(count!= n && temp!= NULL)
        { 
            count++;
            temp= temp->getNext();
        } 
        return temp->getSnake();
   }
   void remove()                            //removes the first element
   {  
        node *temp= head->getNext();
        node *t2= head;
        while(temp->getNext()!=NULL)
        {
            temp=temp->getNext();
            t2= t2->getNext();
        }
        t2->setNext(NULL);
        delete temp;
        length--;
   }
   void display()
   {
        int i= 0;
        node *temp= head;
        while(temp!= NULL)
        {
            mvaddch(10,10+i,temp->getSnake().getCh());
            mvprintw(20,5,"THIS LOVE");
            refresh();
            temp= temp->getNext();
        }
   }
   ~list()
   {
        while(head!= NULL)
        {
            node* n= head;
            head= head->getNext();
            delete n;
        }
        length= 0;
    }
};

class game
{   
    int score, max_y, max_x, food_x, food_y, direction;
    list l;
    public:
    game()
    {
        score= max_y= max_x= food_x= food_y= 0;
        direction= 2;
    }
    void launch();
    void play();
    void map();
    void genFood();
    void setSnake();
    void moveSnake(int dir);
    bool check();
};

void game:: launch()
{
    initscr();                      //initialize ncurses
    cbreak();                       //no line buffering
    curs_set(FALSE);
    keypad(stdscr, TRUE);
    noecho();
    refresh();
    attron(A_BOLD);
    mvprintw(LINES/2-2, COLS/2-10, "SNAKES WELCOMES YOU");
    mvprintw(LINES/2, COLS/2-12, "PRESS ENTER TO CONTINUE");
    int ch;
    if((ch= getch())==10)
    {
        getmaxyx(stdscr, max_y, max_x);
        play();
    }
    else 
    {
        clear();
        mvprintw(LINES/2, COLS/2- 16, "YOU DID NOT EVEN GIVE ME A CHANCE......GOODBYE");
        refresh();
        sleep(2);
    }
    attroff(A_BOLD);
    endwin();
}

void game:: genFood()
{
    srand(time(NULL));
    food_y= random()%(max_y-6)+4;
    food_x= random()%(max_x-4)+2;
}

void game:: map()
{
    box(stdscr, 0, 0);
    mvprintw(1,1, "SCORE: ");
    mvprintw(1,8, "%d",score);      
    mvprintw(2,1, "Press q to quit");
    refresh();
}

void game:: setSnake()
{
    clear();
    map();
    for(int i= 0; i< 8; i++)
    {
        snake s((COLS/2)-8+i, LINES/2);
        l.add(s);
        mvprintw(s.getY(),s.getX(),"%c",s.getCh());
    }
    mvprintw(food_y, food_x, "F");
    refresh();
}

void game::play()
{
    bool b;
    genFood();
    setSnake();
    mvprintw(8, COLS/2-20, "??????????READY??????????????");
    refresh();
    sleep(1);
    int ch= 0;
    timeout(50);                            //adjust speed of the game
    while((ch=getch())!= 'q')
    {
        switch(ch)
        {
            case KEY_UP: direction= 1; 
                        break;
            case KEY_DOWN: direction= 3;
                        break;
            case KEY_RIGHT: direction= 2;
                        break;
            case KEY_LEFT: direction= 4;
                        break;
        }
       clear();
       map();
       mvprintw(food_y, food_x,"F");
       moveSnake(direction);
       refresh();
       b= check();
       if(!b)
       {
            clear();
            mvprintw(max_y/2-2, max_x/2-8, "GAME OVER");
            mvprintw(max_y/2, max_x/2-10, "YOUR SCORE %d",score);
            refresh();
            sleep(2);
            break;
        }
    }
}

bool game:: check()
{
    bool b= false;
    snake s= l.get(1);
    int head_x= s.getX();
    int head_y= s.getY();
    if(head_x== max_x-1 || head_y== 1 || head_x== 1 || head_y== max_y-1)
        return false;
    else 
    {
        int len= l.listLength();
        for(int i= 4; i< len; i++)
        {
            s= l.get(i);
            if(head_x== s.getX() && head_y== s.getY())
            {
                b= true;
                break;
            }
        }
        if(b)
            return false;
        else
            return true;
    }  
}

void game:: moveSnake(int dir)
{
    snake k;
    k= l.get(1);
    int x= k.getX(), y= k.getY();
    //mvprintw(6,1, "BEFORE x= %d, y= %d, dir=%d",x,y,dir);
    if(dir== 1) y--;
    else if(dir== 2)    x++;
    else if(dir== 3)    y++;
    else x--;
    //mvprintw(7,1, "AFTER x=%d, y= %d", x, y);
    snake s(x,y);
    l.add(s);
    if(x==food_x && y==food_y)
    {
        score++;
        genFood();
        mvprintw(food_y, food_x, "F");
    }
    else
        l.remove();
    int len= l.listLength();
    for(int i= 1; i<= len; i++)
    {
        k= l.get(i);
        mvaddch(k.getY(), k.getX(), k.getCh());
        refresh();
    }
}

int main()
{
    game g;
    g.launch();
    return 0;
}    
EN

回答 3

Code Review用户

回答已采纳

发布于 2016-02-26 13:52:44

代码语言:javascript
复制
    if(b)
        return false;
    else
        return true;

使用!运算符,您可以否定一个布尔值。

因此,通过说!b,您可以交换周围的情况:

代码语言:javascript
复制
    if(!b)
        return true;
    else
        return false;

但是,如果b不为真,则返回true,否则返回false。此时,您最好返回“b是否为真”。

就像这样:

代码语言:javascript
复制
    return !b;

除此之外,也许b并不是真正合适的名字。collided怎么样?也可能是collisionFound

代码语言:javascript
复制
bool game:: check()
{
    bool collisionFound= false;
    snake s= l.get(1);
    int head_x= s.getX();
    int head_y= s.getY();
    if(head_x== max_x-1 || head_y== 1 || head_x== 1 || head_y== max_y-1)
        return false;
    else 
    {
        int len= l.listLength();
        for(int i= 4; i< len; i++)
        {
            s= l.get(i);
            if(head_x== s.getX() && head_y== s.getY())
            {
                collisionFound = true;
                break;
            }
        }
        return !collisionFound;
    }  
}

也许你应该给函数checkCollision贴上标签。但很奇怪你会把底片还给我。让我们看看你在用这个..。

代码语言:javascript
复制
b= check();
if(!b)

嗯..。

这里没有必要存储到b,您只需说if(!checkCollision())。但这看起来很奇怪,游戏结束了,如果不是碰撞。这是因为您使check更像一个check if snake is okay函数,但是它确实检查了冲突,然后否定了结果。所以你最好去掉否定:

代码语言:javascript
复制
bool game:: checkForCollision()
{
    bool collisionFound= false;
    snake s= l.get(1);
    int head_x= s.getX();
    int head_y= s.getY();
    if(head_x== max_x-1 || head_y== 1 || head_x== 1 || head_y== max_y-1)
        return true;
    else 
    {
        int len= l.listLength();
        for(int i= 4; i< len; i++)
        {
            s= l.get(i);
            if(head_x== s.getX() && head_y== s.getY())
            {
                collisionFound = true;
                break;
            }
        }
        return collisionFound;
    }  
}

然后像这样使用:

代码语言:javascript
复制
   if(checkForCollision())
   {
        clear();
        mvprintw(max_y/2-2, max_x/2-8, "GAME OVER");
        mvprintw(max_y/2, max_x/2-10, "YOUR SCORE %d",score);
        refresh();
        sleep(2);
        break;
    }

如果您充分使用描述性函数名和变量名,代码最终会读起来像一种奇怪的英语形式。

在这里,您开始检查从第4段开始的蛇段。

代码语言:javascript
复制
    int len= l.listLength();
    for(int i= 4; i< len; i++)

我明白它背后的逻辑:蛇的形状是这样的:

代码语言:javascript
复制
. = not snake
o = snake head
^<>v = snake body

....
.v<.
.o^.
....

不可能与索引1、2和3发生冲突,对吗?只有一条头和四节或更长的蛇才能碰撞,那么为什么不只检查4段及以后的碰撞,来节省一些性能呢?

除了..。如果我们把蛇倒过来怎么办?

你会得到一条皱巴巴的蛇。

代码语言:javascript
复制
....
.ô<.
.^..
....

可怜的凯康人。

撇开笑话不说,你应该为这个案子想点办法。我玩的一些蛇游戏会给我一个即时的游戏,而我玩的一些蛇游戏不允许我向后移动(因为蛇不能向后移动)。不允许玩家向后移动可能会对玩家更友好,因为如果玩家按错了按钮,他们可能会意外中断。

代码语言:javascript
复制
        case KEY_UP: direction= 1; 
                    break;
        case KEY_DOWN: direction= 3;
                    break;
        case KEY_RIGHT: direction= 2;
                    break;
        case KEY_LEFT: direction= 4;
                    break;

想象一下你没有KEY_UPKEY_DOWNKEY_RIGHTKEY_LEFT。您的代码将如下所示:

代码语言:javascript
复制
        case 38: direction= 1; 
                    break;
        case 40: direction= 3;
                    break;
        case 39: direction= 2;
                    break;
        case 37: direction= 4;
                    break;

很难理解,不是吗!

除了..。你对direction也做过同样的事。您应该尝试为方向定义常量。让我们使用一个枚举来完成这个任务。

因此,在代码的顶部定义您的方向:

代码语言:javascript
复制
enum Direction { UP, DOWN, LEFT, RIGHT };

声明游戏的direction变量为Direction

代码语言:javascript
复制
Direction direction;

并使用如下方式:

代码语言:javascript
复制
        case KEY_UP: direction= UP; 
                    break;
        case KEY_DOWN: direction= DOWN;
                    break;
        case KEY_RIGHT: direction= RIGHT;
                    break;
        case KEY_LEFT: direction= LEFT;
                    break;

(如果您将类和枚举保存在不同的文件中,则可能需要使用::访问它,如Direction::LEFT)。

代码语言:javascript
复制
if(dir== UP) y--;
else if(dir== RIGHT)    x++;
else if(dir== DOWN)    y++;
else x--;
票数 19
EN

Code Review用户

发布于 2016-02-28 02:10:06

效率

在" list“类中,您实现了链接列表行为,它为get和add函数提供了O(N)时间复杂度。您可以使用C++容器,例如向量,它为添加/删除/获取函数提供了固定的时间,请参阅push_back、擦除、pop_back...functions。

命名

  1. snake类实际上表示了蛇的一部分;最好将它命名为snake_fragment。
  2. map也是C++中的一个容器。当您尝试使用std::map而不指定名称解析时,这可能会导致问题。避免使用C++关键字和容器作为函数名。无效游戏::map()

设计限制

  1. 游戏中一次只能提供一种食物。可以使用阵列或矢量一次提供多组食物。

food_x,food_y

  1. 许多硬编码项使得更改或扩展变得不那么灵活。如果你想要更长或更短的蛇呢?它总是必须从某一点开始吗?>对于(int i= 0;i< 8;i++) (蛇s((COLS/2)-8+i,LINES/2);l.add(s);mvprintw(s.getY(),s.getX(),"%c",s.getCh());}

Implementation

似乎只有蛇的第一部分被移动了。

k= l.get(1);

编码风格

  1. 神奇的数字。"10“在这里是什么意思?为什么-6 +4?

如果(ch= getch())==10) food_y=随机()%(max_y-6)+4;food_x=随机()%(max_x-4)+2;mvprintw(1,8,"%d",分数);

  1. 名称解析运算符后没有空格。无效游戏::map()
  2. 毫无意义的变数。B是什么意思?bool b=假;
  3. 为什么不把x,y合并成“点”数据类型,这样比较就更容易理解了?此外,您还可以实现函数,如moveRight、moveLeft.然后把"x++“、"y++"...implementation的细节留给"snake”或"point“类?在游戏类中放置"x++"...details不利于数据封装;游戏类不会向右移动或向左移动。

int head_x= s.getX();int head_y= s.getY();if(head_x== max_x-1 head_y== _x_y-1)

票数 4
EN

Code Review用户

发布于 2016-02-28 16:31:23

这是我的代码的更新版本。使用了@Pimgd给我的很多建议。更改/改进了一些函数和变量名。简化了一些函数。这场比赛有点友好。当你想倒退的时候不会把游戏打印出来。

代码语言:javascript
复制
#include<ncurses.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>

enum Direction {Up=1 , Right, Down, Left};      //added to improve understanding of the code

class snake                         //to store the x and y coordinates of each snake part
{
    int x, y;
    char ch;
    public:
    snake(){ x= y= 0; ch='O';}
    snake(int a, int b)
    {
        x= a; y= b; ch= 'O';
    }
    snake(const snake &ekans)
    {
        ch= ekans.ch;
        x= ekans.x;
        y= ekans.y;
    }
    void setCh(char x)
    {
        ch= x;
    }
    char getCh()
    {
        return ch;
    }
    int getX()
    {
        return x;
    }
    void setX(int no)
    {
        x= no;
    }
    int getY()
    {
        return y;
    }
    void setY(int no)
    {
        y= no;
    }
};

class node                          //to make a linked list
{   
    node* next;
    snake s;
    public:
    snake getSnake()
    {
        return s;
    }  
    void setSnake(snake ekans)
    {
        s= ekans;
    }
    void setNext(node *n)
    {
        next= n;
    }
    node* getNext()
    {
        return next;
    }
};

class list                          //to store each snake body          
{
    node* head;
    int length;
    public:
    list()
    {
        head= NULL;
        length= 0;
    }
    void add(snake s)                   //adds at the end
    {
        node *n= new node;
        n->setSnake(s);
        n->setNext(head);
        head= n;
        length++;
    }
    int listLength()
    {
        return length;
    }
    snake get(int n)
    {
        node *temp =head;
        int count= 1;
        while(count!= n && temp!= NULL)
        {
            count++;
            temp= temp->getNext();
        } 
        return temp->getSnake();
    }
    void remove()                           //removes the first element
    {
        node *temp= head->getNext();
        node *t2= head;
        while(temp->getNext()!=NULL)
        {
            temp=temp->getNext();
            t2= t2->getNext();
        }
        t2->setNext(NULL);
        delete temp;
        length--;
    }
    void display()
    {
        int i= 0;
        node *temp= head;
        while(temp!= NULL)
        {
            mvaddch(10,10+i,temp->getSnake().getCh());
            refresh();
            temp= temp->getNext();
        }
    }
    ~list()
    {
        while(head!= NULL)
        {
            node* n= head;
            head= head->getNext();
            delete n;
        }
        length= 0;
    }
};

class game
{   
    int score, max_y, max_x, food_x, food_y;// direction, prev_dir;
    Direction direction, prev_dir;
    list l;
    public:
    game()
    {
        score= max_y= max_x= food_x= food_y= 0;
        prev_dir= direction= Right;
    }
    void launch();
    void play();
    void map();
    void genFood();
    void setSnake();
    void moveSnake(Direction dir);
    bool checkForCollision();       //changed
};

void game:: launch()
{
    initscr();                      //initialize ncurses
    cbreak();                       //no line buffering
    curs_set(FALSE);
    keypad(stdscr, TRUE);
    noecho();
    refresh();
    attron(A_BOLD);
    mvprintw(LINES/2-2, COLS/2-10, "SNAKES WELCOMES YOU");
    mvprintw(LINES/2, COLS/2-12, "PRESS ENTER TO CONTINUE");
    int ch;
    if((ch= getch())==10)
    {
        getmaxyx(stdscr, max_y, max_x);
        play();
    }
    else 
    {
        clear();
        mvprintw(LINES/2, COLS/2- 16, "YOU DID NOT EVEN GIVE ME A CHANCE......GOODBYE");
        refresh();
        sleep(2);
    }
    attroff(A_BOLD);
    endwin();
}  

void game:: genFood()
{
    srand(time(NULL));
    food_y= random()%(max_y-6)+4;
    food_x= random()%(max_x-4)+2;
}

void game:: map()
{
    box(stdscr, 0, 0);
    mvprintw(1,1, "SCORE: ");
    mvprintw(1,8, "%d",score);      
    mvprintw(2,1, "Press q to quit");
    refresh();
}

void game:: setSnake()
{
    clear();
    map();
    for(int i= 0; i< 8; i++)
    {
        snake s((COLS/2)-8+i, LINES/2);
        l.add(s);
        mvprintw(s.getY(),s.getX(),"%c",s.getCh());
    }
    mvprintw(food_y, food_x, "F");
    refresh();
}

void game::play()
{
    genFood();
    setSnake();
    mvprintw(8, COLS/2-20, "??????????READY??????????????");
    refresh();
    sleep(1);
    int ch= 0;
    timeout(50);                            //adjust speed of the game
    while((ch=getch())!= 'q')
    {
        switch(ch)
        {
            case KEY_UP: direction= Up; 
                    break;
            case KEY_DOWN: direction= Down;
                    break;
            case KEY_RIGHT: direction= Right;
                    break;
            case KEY_LEFT: direction= Left;
                    break;
        }
        clear();
        map();
        mvprintw(food_y, food_x,"F");
        if(direction+2== prev_dir || prev_dir+2== direction)            //added does not allow snake to go backwards
            direction= prev_dir;
        moveSnake(direction);
        refresh();
        if(checkForCollision())
        {
            clear();
            mvprintw(max_y/2-2, max_x/2-8, "GAME OVER");
            mvprintw(max_y/2, max_x/2-10, "YOUR SCORE %d",score);
            refresh();
            sleep(2);
            break;
        }
        prev_dir= direction;
    }
}

bool game:: checkForCollision()
{
    bool collided= false;
    snake s= l.get(1);
    int head_x= s.getX();
    int head_y= s.getY();
    if(head_x== max_x-1 || head_y== 1 || head_x== 1 || head_y== max_y-1)
        return true;
    else 
    {
        int len= l.listLength();
        for(int i= 4; i< len; i++)
        {
            s= l.get(i);
            if(head_x== s.getX() && head_y== s.getY())
            {
                collided= true;
                break;
            }
        }
            return collided;
    }   
}   

void game:: moveSnake(Direction dir)
{
    snake k;
    k= l.get(1);
    int x= k.getX(), y= k.getY();
    //mvprintw(6,1, "BEFORE x= %d, y= %d, dir=%d",x,y,dir);
    //getch();
    if(dir== Up)    y--;
    else if(dir== Left) x--;
    else if(dir== Down) y++;
    else x++;
    //mvprintw(7,1, "AFTER x=%d, y= %d", x, y);
    snake s(x,y);
    l.add(s);
    if(x==food_x && y==food_y)
    {
        score++; 
        genFood();
        mvprintw(food_y, food_x, "F");
    }
    else
        l.remove();
    int len= l.listLength();
    for(int i= 1; i<= len; i++)
    {
        k= l.get(i);
        mvaddch(k.getY(), k.getX(), k.getCh());
        refresh();
    }
}       

int main()
{
    game g;
    g.launch();
    return 0;
}   
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/121192

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档