4
\$\begingroup\$

I made a simple C++ snake game, and I want some advice from you: what is good, what I have to improve. One main problem is that flipping (which you'll notice if you play it).

#include <conio.h>
#include <ctime>
#include <iostream>
#include "windows.h"

const int width = 60;
const int height = 15;
double diff=0.5;

class Snake
{
    public:
        Snake();
        void color(int choosecolor);
        void create_map(char map[][height]);
        void credits();
        void difficulty();
        void game_over(int score);
        void generate_food(char map[][height],int & food_x,int & food_y);
        bool invalid_snake(char map[][height],int snake_position[][height]);
        bool is_food(char map[][height],int food_x,int food_y);
        bool is_food_pgame(char map[][height], char mapfood[][height]);
        void movement(char map[][height],int snake_position[][height],int eating);
        void options();
        void pause();
        bool poison_snake(char map[][height],int snake_parts[][height], int x1, int x2, int y1, int y2);
        void poison_game();
        void rules();
        void show_map(char map[][height],int score);
        void start_game();

    private:
        int check[width][height];
        char keyboard = 'n';
        int lenght;
};

int main()
{
    char selection;

    start:
    Snake object;
    srand(time(0));

    std::cin >> selection;

    if(selection=='1')
    {
        system("cls");
        object.start_game();
    }
    else if(selection=='2')
        {
            system("cls");
            object.poison_game();
        }
        else if(selection=='3')
            {
                object.options();
                system("cls");
                goto start;
            }
            else if(selection=='4') system("exit");
                else
                    {
                        system("cls");
                        goto start;
                    }
    return 0;
}

void Snake::color(int choosecolor)
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), choosecolor);
}

void Snake::create_map(char map[][height])
{
    for(int i=0;i<height;i++)
    {
        for(int j=0;j<width;j++)
        {
            if(i==0 || i==height-1) map[j][i]=char(178);
            if(i>0 && i<height-1) map[j][i]=' ';
            if(j==0 || j==width-1) map[j][i]=char(178);
        }
    }
}

void Snake::credits()
{
    char selection;
    system("cls");
    while(true)
    {
        std::cout << std::endl;
        std::cout << " Lead programmer: contact.istrate " << std::endl;
        std::cout << " Designer: contact.istrate " << std::endl;
        std::cout << " Testers: contact.istrate " << std::endl << std::endl;
        std::cout << " Go back <1> ";
        std::cin >> selection;
        if(selection=='1') return;
            else system("cls");
    }
}

void Snake::difficulty()
{
    char selection;
    system("cls");
    while(true)
    {
        std::cout << std::endl;
        std::cout << " CURRENT DIFFICULTY" << std::endl;
        if(diff==1) std::cout << " Easy" << std::endl << std::endl;
            else if(diff==0.5) std::cout << " Normal" << std::endl << std::endl;
                else if(diff==0.2) std::cout << " Hard" << std::endl << std::endl;
                    else if(diff==0.1) std::cout <<" Super hard" << std::endl << std::endl;
                        else if(diff==0.03) std::cout <<" Impossible" << std::endl << std::endl;

    std::cout << " CHOOSE DIFFICULTY" << std::endl;
    std::cout << " Easy <1>" << std::endl;
    std::cout << " Normal <2>" << std::endl;
    std::cout << " Hard <3>" << std::endl;
    std::cout << " Super hard <4>" << std::endl;
    std::cout << " Impossible <5>" << std::endl << std::endl;
    std::cout << " Go back <6>" << std::endl;

    std::cin >> selection;
    if(selection=='1')  //there are 5 ready difficulties
    {
        diff=1;
        system("cls");
    }
    else if(selection=='2')
        {
            diff=0.5;
            system("cls");
        }
        else if(selection=='3')
            {
                diff=0.2;
                system("cls");
            }
            else if(selection=='4')
                {
                    diff=0.1;
                    system("cls");
                }
                else if(selection=='5')
                    {
                        diff=0.03;
                        system("cls");
                    }
                    else if(selection=='6') return;
                            else system("cls");
    }
}

void Snake::game_over(int score)
{
    std::cout << std::endl;
    char selection; int i;

    std::cout << " \nGAME OVER!";

    std::cout << std::endl << std::endl;
    std::cout << "Do you want to play again ? [y/n] ";

    std::cin >> selection;

    if(selection=='y')
    {
        system("cls");
        start_game();
    }
    else
        {
            system("cls");
            main();
        }
}

void Snake::generate_food(char map[][height],int & food_x,int & food_y)
{
    int i,j;

    food_x=(rand()%(width-2))+1;
    food_y=(rand()%(height-2))+1;

    for(j=1;j<width-1;j++)
    {
        for(i=1;i<height-1;i++)
        {
            if(map[j][i]!=char(233) && map[j][i]!=char(254))
            {
                map[food_x][food_y]='*';
                return;
            }
            else
            {
                food_x=(rand()%(width-2))+1;
                food_y=(rand()%(height-2))+1;
                i=1;
                j=1;
            }
        }
    }
}

bool Snake::invalid_snake(char map[][height],int snake_parts[][height])
{
    int x,y,c=0;

    for(x=0;x<width;x++)
        for(y=0;y<height;y++)
        {
            if((x==0 || x==width-1 || y==0 || y==height-1) && map[x][y]==char(254))
                c=1;
            if(check[x][y]==1 && map[x][y]==char(254))
                c=1;
        }

    if(c==1) return true;
        else return false;

}

bool Snake::is_food(char map[][height],int food_x,int food_y)
{
    int x,y,c=0;

    for(x=1;x<width-1;x++)
        for(y=1;y<height-1;y++)
            if(map[x][y]==char(254) && x==food_x && y==food_y)
                c=1;

    if(c==1) return true;
        else return false;
}

void Snake::movement(char map[][height],int snake_position[][height],int eating)
{
    int i,x,y;
    for(i=lenght;i>0;i--)
    {
        if(i==lenght)
        {
            for(x=1;x<width-1;x++)
            {
                for(y=1;y<height-1;y++)
                {
                    if(snake_position[x][y]==i && map[x][y]== char(233))
                    {
                        if(eating==0)
                        {
                            map[x][y]=' '; snake_position[x][y]=0;
                            x=width-2;
                            y=height-2;
                        }
                        else if(eating==1)
                        {
                            map[x][y]= char(233); snake_position[x][y]=lenght+1;
                            lenght++;
                            x=width-2;
                            y=height-2;
                        }
                    }
                }
            }
        }
        else if(i>1 && i<lenght)
        {
            for(x=1;x<width-1;x++)
            {
                for(y=1;y<height-1;y++)
                {
                    if(snake_position[x][y]==i && map[x][y]== char(233))
                    {
                        snake_position[x][y]=i+1;
                        x=width-2;
                        y=height-2;
                    }
                }
            }
        }
        else if(i==1)
        {
            for(x=1;x<width-1;x++)
            {
                for(y=1;y<height-1;y++)
                {
                    if(snake_position[x][y]==i && map[x][y]==char(254))
                    {
                        movecheck:

                        if(kbhit())
                        {
                            if(GetAsyncKeyState(VK_UP)) keyboard = 'u';
                                else if(GetAsyncKeyState(VK_DOWN)) keyboard = 'd';
                                    else if(GetAsyncKeyState(VK_LEFT)) keyboard = 'l';
                                        else if(GetAsyncKeyState(VK_RIGHT)) keyboard = 'r';
                        }

                        if(keyboard!='u' && keyboard!='d' && keyboard!='l' && keyboard!='r')
                        {
                            map[x][y]=char(233); snake_position[x][y]=i+1;
                            if(snake_position[x-1][y]==i+2)
                            {
                                map[x+1][y]=char(254); snake_position[x+1][y]=1;
                                return;
                            }
                            else if(snake_position[x+1][y]==i+2)
                                {
                                    map[x-1][y]=char(254); snake_position[x-1][y]=1;
                                    return;
                                }
                                else if(snake_position[x][y+1]==i+2)
                                    {
                                        Sleep(50);
                                        map[x][y-1]=char(254); snake_position[x][y-1]=1;
                                        return;
                                    }
                                    else if(snake_position[x][y-1]==i+2)
                                        {
                                            Sleep(50);
                                            map[x][y+1]=char(254); snake_position[x][y+1]=1;
                                            return;
                                        }
                        }
                        else if (keyboard=='u')
                            {
                                if(snake_position[x][y-1]==0)
                                {
                                    map[x][y]= char(233); snake_position[x][y]=i+1;
                                    map[x][y-1]=char(254); snake_position[x][y-1]=1;
                                    return;
                                }
                                else
                                    {
                                        keyboard='n';
                                        goto movecheck;
                                    }
                            }
                            else if(keyboard=='d')
                                {
                                    if(snake_position[x][y+1]==0)
                                    {
                                        map[x][y]= char(233); snake_position[x][y]=i+1;
                                        map[x][y+1]=char(254); snake_position[x][y+1]=1;
                                        return;
                                    }
                                    else
                                        {
                                            keyboard='n';
                                            goto movecheck;
                                        }
                                }
                                else if(keyboard=='l')
                                    {
                                        if(snake_position[x-1][y]==0)
                                        {
                                            map[x][y]= char(233); snake_position[x][y]=i+1;
                                            map[x-1][y]=char(254); snake_position[x-1][y]=1;
                                            return;
                                        }
                                        else
                                            {
                                                keyboard='n';
                                                goto movecheck;
                                            }
                                    }
                                    else if(keyboard=='r')
                                        {
                                            if(snake_position[x+1][y]==0)
                                            {
                                                map[x][y]= char(233); snake_position[x][y]=i+1;
                                                map[x+1][y]=char(254); snake_position[x+1][y]=1;
                                                return;
                                            }
                                            else
                                                {
                                                    keyboard='n';
                                                    goto movecheck;
                                                }
                                        }
                    }
                }
            }
        }
    }
}

void Snake::options()
{
    char selection;
    system("cls");

    while(true)
    {
        system("cls");
        std::cout << std::endl;
        std::cout << " Options" << std::endl << std::endl;
        std::cout << " Choose dificulty <1>" << std::endl;
        std::cout << " Rules <2>" << std::endl;
        std::cout << " Credits <3>" << std::endl << std::endl;
        std::cout << " Go back <4>" << std::endl << std::endl;

        std::cin >> selection;
        if(selection=='1') difficulty();
            else if(selection=='2') rules();
                else if(selection=='3') credits();
                    else if(selection=='4') return;
                        else system("cls");
    }
}

void Snake::pause()
{
    char c;
    std::cout << std::endl;
    std::cout << "Game paused. Press 'p' to return. ";
    std::cin >> c;
    if(c=='p') return;
}

bool Snake::poison_snake(char map[][height],int snake_parts[][height], int x1, int x2, int y1, int y2)
{
    int c = 0;;
    int x,y;

    for(x=0;x<width;x++)
        for(y=0;y<height;y++)
            if(((x==x1 && y==y1) || (x==x2 && y==y2)) && map[x][y]==char(254)) c=1;

    if(c==1) return true;
        else return false;
}

void Snake::rules()
{
    system("cls");
    std::cout << " \nSNAKE GAME \nThe goal is to eat as much food as you can without hit the walls or snake tail. \nYou can move your snake using arrows keys. HAVE FUN !\n\n\n";
    std::cout << " \nPOISON CHALLENGE \nThere are 10 food items on the table. 2 of them are poisoned!!\nTo win you have to eat all 8 healty foods. GOOD LUCK !\n\n";
    std::cout << "\npress 1 to return\n";
    char selection;
    std::cin >> selection;

    if(selection==1) return;
        else system("cls");
}

void Snake::show_map(char map[][height],int score)
{
    int x=width;
    int y=height;
    int i,j;

    color(155);
    for(i=0;i<y;i++)
        for(j=0;j<x;j++)
        {
            if(j<(x-1)) std::cout << map[j][i];
            if(j==(x-1)) std::cout << map[j][i] << std::endl;
        }
    color(7);

    std::cout << std::endl;
    std::cout << "Your score: " << score;

}

Snake::Snake()
{
    std::cout << std::endl << std::endl;
    std::cout << "\tMENU" << std::endl << std::endl;
    std::cout << " Snake game <1>" << std::endl;
    std::cout << " Poison challenge <2>" << std::endl;
    std::cout << " Options <3>" << std::endl;
    std::cout << " Exit <4>" << std::endl << std::endl;

}

bool Snake::is_food_pgame(char map[][height], char mapfood[][height])
{
    int c =0;

    for(int x=0;x<width;x++)
        for(int y=0;y<height;y++)
            if(mapfood[x][y]=='*' && map[x][y]==char(254)) c=1;

    if(c==1) return true;
        else return false;
}

void Snake::start_game()
{
    char map[width][height];
    int snake_position[width][height];
    int i, j, food_x, food_y, x, y, score=0;
    int eating;

    create_map(map);
    generate_food(map,food_x,food_y);

    for(y=1;y<height;y++)
    {
        for(x=1;x<width;x++)
        {
            snake_position[x][y]=0;
        }
    }

    map[27][10]= char(254);
    map[26][10]= char(233);
    map[25][10]= char(233);
    map[24][10]= char(233);

    snake_position[24][10]=4;
    snake_position[25][10]=3;
    snake_position[26][10]=2;
    snake_position[27][10]=1;


    system("cls");
    show_map(map,score);
    lenght=4;

    while(true)
    {
        eating=0;
        Sleep(diff*400);

        if(kbhit())
        {
            keyboard=getch();
            if(keyboard=='p') pause();
            goto movemake;
        }

        movemake:
        {
            for(x=1;x<width-1;x++)
                for(y=1;y<height-1;y++)
                    if(map[x][y]== char(233))   check[x][y]=1;
                        else check[x][y]=0;

            if(is_food(map,food_x,food_y)==true)
            {
                eating=1;
                score=score+1;
                movement(map,snake_position,eating);
                generate_food(map,food_x,food_y);
            }
            else    movement(map,snake_position,eating);

            if(invalid_snake(map,snake_position)==true) goto gameEnd;
                else    goto LoopEnd;
        }

        LoopEnd:
        {
            system("cls");
            show_map(map,score);
        }

    }

    gameEnd:
    {
        game_over(score);
        return;
    }
}

void Snake::poison_game()
{
    char map[width][height]; char mapfood[width][height];
    int snake_position[width][height];
    int i, j, food_x, food_y, poison_x1=0, poison_y1, poison_x2, poison_y2, x, y, score=0;
    int eating, win = 8;
    bool p;

    create_map(map);


    int c=0;
    while(c<8)
    {
            food_x=(rand()%(width-2))+1;
            food_y=(rand()%(height-2))+1;
            if(map[food_x][food_y]==' ')
            {
                mapfood[food_x][food_y] = '*';
                map[food_x][food_y] = '*';
                c+=1;
            }
    }

    c=0;
    while(c<2)
    {
            food_x=(rand()%(width-2))+1;
            food_y=(rand()%(height-2))+1;

            if(map[food_x][food_y]==' ')
            {
                map[food_x][food_y] = '*';
                if(poison_x1 == 0)
                {
                    poison_x1 = food_x;
                    poison_y1 = food_y;
                }
                else
                    {
                        poison_x2 = food_x;
                        poison_y2 = food_y;
                    }
                c+=1;
                srand(time(0));
            }
    }

    for(y=1;y<height;y++)
        for(x=1;x<width;x++)
            snake_position[x][y]=0;

    map[27][10]= char(254);
    map[26][10]= char(233);
    map[25][10]= char(233);
    map[24][10]= char(233);

    snake_position[24][10]=4;
    snake_position[25][10]=3;
    snake_position[26][10]=2;
    snake_position[27][10]=1;


    system("cls");
    show_map(map,99);
    lenght=4;

    while(true)
    {
        eating=0;
        Sleep(diff*400);

        if(kbhit())
        {
            keyboard=getch();
            if(keyboard=='p') pause();
            goto movemake;
        }

        movemake:
        {
            for(x=1;x<width-1;x++)
                for(y=1;y<height-1;y++)
                    if(map[x][y]== char(233))   check[x][y]=1;
                        else check[x][y]=0;

            movement(map,snake_position,eating);

            if(invalid_snake(map,snake_position)==true)
            {
                std::cout << "\n\nGAME OVER ! You lose.\n";
                std::cout << "\n Play again ? [y/n]\n";
                char selection;
                std::cin >> selection;
                if(selection=='y') poison_game();
                    else
                    {
                        system("cls");
                        main();
                    }
                return;
            }
            else if(poison_snake(map,snake_position,poison_x1,poison_x2,poison_y1,poison_y2)==true)
                {
                    color(42);
                    std::cout << "\n\nYou got poisoned!\n";
                    color(7);
                    std::cout << "\n Play again ? [y/n]\n";
                    char selection;
                    std::cin >> selection;
                    if(selection=='y') poison_game();
                        else
                        {
                            system("cls");
                            main();
                        }
                    return;
                }
                else if(is_food_pgame(map,mapfood)==true) win--;
                    else if(win==0)
                    {
                        std::cout << std::endl << std::endl;
                        color(156);
                        std::cout << " Congrats !! YOU WON.\n";
                        color(7);
                        Sleep(4000);
                        system("cls");
                        main();
                    }
                    else
                        {
                            system("cls");
                            show_map(map,win);
                        }
        }
    }
}
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Globals with weak names:

These global constants & variable have far too common names to be declared at this scope:

const int width = 60;
const int height = 15;
double diff=0.5;

There is a real name collision chance with width/height if you want to declared local variables with the same names. Those can and should be static members of the Snake class. E.g.:

// Class Snake
private:
    static const int width  = 60;
    static const int height = 15;
    int check[width][height];

diff has absolutely no reason being a global. Why didn't you declare it as a member variable in the first place?

main() looks bad:

I'm going to be honest with you, that main() function looks pretty bad. That goto is jumping to a point before the initialization of the Snake instance (not sure if intentional). That's a complete mess. Replace it with some kind of for or while loop ASAP.

Also, be sure to improve that unusual indentation. if/else chains should be indented normally, like this:

if (something)
{
}
else if (some_other_thing)
{
}
else if (yet_another)
{
}

Your program invokes Undefined Behavior (UB):

You are calling main() from Snake::game_over(). This is an ill formed program which your compiler is probably allowing. You might be getting a warning out of that though. Always check warnings. Compiling with Warnings as Errors might also be a sensible approach in some cases, in special to help disciplining yourself into paying attention to them.

system("xyz") and friends are exploitable:

By calling system() you ask for the Operating System to run an external program, which might not be a trusted one. Imagine a hacker replaces the cls utility with some malware. Your program would be firing this malware program in the host machine. So be aware of this problem and never use it in a scenario where you would actually care about security. system("cls"), system("pause") are also not portable (pause and cls are Windows utilities), though that doesn't seem to be a problem since you are already using other Windows specific things in your program.

Magic numbers:

You have a lot of magic numbers and hardcoded constants laying around, such as menu indexes and quite a few char(233) and the like. Those should be turned into named constants. If I don't remember my ASCII table by memory, it is pretty hard to visualize what a 233 is going to print.

Naming and access levels:

Snake is doing everything, so it is a SnakeGame class in reality.

There are several methods in the class that are only called internally. This means that they should be private methods instead.

More spacing can help readability:

Crammed lines like this are harder to read:

for(x=1;x<width-1;x++)

Always putting a space after each arithmetical operator produces easier to read code, for the same reasons we put spaces between words.

for(x = 1; x < width-1; x++)

That's better.

I'm a "space man", so would add a couple more:

for (x = 1; x < width - 1; x++)
\$\endgroup\$
1
  • \$\begingroup\$ thank you man for your review. the reason I have diff variable global is to have the difficulty option working. if i put the diff variable inside class, when i change difficulty and then try to play, it automatically reset to previous difficulty. \$\endgroup\$
    – ghita
    Commented Jan 31, 2015 at 11:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.