0

I've been following a few tutorials for making a somewhat Snake Game , just not a full-tailed-one , It's going alright and i think i got it almost right but I've got two problems

#include <bits/stdc++.h>
#include <conio.h>
#include <unistd.h>
using namespace std;

int side,x,y;
int fruitx,fruity,score,flag;
bool isOver;

void randomize();
void frame(int);
void userinp();
void game();

int main(){
    do{
        cout << "Enter the side of your frame!(not less than 20)\n";
        cin >> side;
    }while(side < 20);
    randomize();
    while(!isOver){
        frame(side);
        userinp();
        game();
    }
    return 0;
}

void randomize(){
    x = side/2;
    y = side/2;
    label1: fruitx = rand()%side;
    if (fruitx == 0 || fruitx == side)
        goto label1;
    label2:
    fruity = rand()%side;
    if (fruity == 0 || fruity == side)
        goto label2;

    score = 0;
}
void frame(int s){
    system("cls");
    for(int i=1;i<=s;i++){
        for(int j=1;j<=s;j++){
            if(i == 1 || i == s || j == 1 || j == s)
                cout << "*";
            else{
                if(i == x && j == y){
                    cout << "-";
                }
                else if(i == fruitx && j == fruity){
                    cout << "x";
                }
                else
                    cout << " ";
            }
        }
        cout << "\n";
    }
    cout << "Your score: " << score << endl;
}
void userinp(){
    if(kbhit()){
        switch (getch()){
        case 'a':
            flag = 1;
            break;
        case 's':
            flag = 2;
            break;
        case 'd':
            flag = 3;
            break;
        case 'w':
            flag = 4;
            break;
        default:
            isOver = true;
        }
    }
}
void game(){
    sleep(0.899);
    switch (flag){
    case 1:
        y--;
        break;
    case 2:
        x++;
        break;
    case 3:
        y++;
        break;
    case 4:
        x--;
        break;
    }
     if (x < 0 || x > side || y < 0 || y > side){
        x = side/2;
        y = side/2;
     }
     if (x == fruitx && y == fruity) {
        label3: fruitx = rand()%(side);
        if (fruitx == 0)
            goto label3;

        label4: fruity = rand()%(side);
        if (fruity == 0)
            goto label4;

        score++;
    }
}

the first problem:

using system"(cls") helped with the screen not going down way many times to avoid the screen scrolling down i tried it to make the screen clear every time the sleep() is called but i cannot get how to slow down the process a bit using the sleep() function , It's either too slow or too fast The screen , Is there a different way to approach the whole thing or is the function itself not proper to use here ?

second problem: whenever i input the side with 20 , i cannot find the fruit itself at all but when it is any number more than this it does show me the random fruits just fine so is there a way to fix why at just side = 20 it would not work ?

PS: If there is anything that could be improved aside from these 2 problems i'd appreciate it .. Thanks in advance!

  • 2
    `sleep(0.899);` looks like sleep is in seconds and is an integer value: [https://man7.org/linux/man-pages/man3/sleep.3.html](https://man7.org/linux/man-pages/man3/sleep.3.html) I think you want: [https://man7.org/linux/man-pages/man3/usleep.3.html](https://man7.org/linux/man-pages/man3/usleep.3.html) but then there is this question about using that on MS Windows: [https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw/17283549](https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw/17283549) – drescherjm Jan 01 '22 at 15:34
  • @drescherjm alright that makes sense because the fractions all seemed to have the same speed as the 0 one – Oliver_Queen Jan 01 '22 at 15:44
  • You probably want to look at this answer for modern c++ code: [https://stackoverflow.com/a/32319170/487892](https://stackoverflow.com/a/32319170/487892) – drescherjm Jan 01 '22 at 15:49
  • @drescherjm Indeed , im scrolling through all of the answers for the new functions/understanding sleep() itself .. Thanks – Oliver_Queen Jan 01 '22 at 15:51

1 Answers1

0

First sleep is a bit rough. See answers here for better solutions: Sleep for milliseconds

Second you don't account for your own overhead. Your program might run faster/slower on different setups. For games in general you want to use a somewhat precise but fast time function to get an idea of how much to move everything (granted not really needed for snake). For more suggestions on this topic see this quesiton: How to Make a Basic FPS Counter?

Last the console is not really good for this sort of application. I would suggest switching to a simple GUI, just so you don't have to deal with the console craziness. If your goal is to have some sort of demo to learn something and move on it might be ok, and live with the strange behavior.

For the issue with the fruit:

  • NEVER use goto. It's very hard to reason about it and for the standard tools to deal with it. Use functions/loops that are easier reason about. In your case a do{ friutx = ...; } while (friuitx==0) would do.
  • The two places where you setup the fruit use different code. Likely even if you fix one, the other might still be broken. Create a function and call it from both places.
  • % will do a reminder (0 to side-1, inclusive). Looks like your target is 2 to side-1, so maybe do rand()%(side-1) + 1 or better yet, use std::uniform_int_distribution.
  • It's not clear to me why it's not seen correctly. Use a debugger to check when you are rendering a frame what are the coordinates of the fruit. They might overlap the edges and be replaced with a *.
Sorin
  • 11,863
  • 22
  • 26
  • 1
    Never use goto is too strong. There are valid use cases for goto, such as breaking out of nested loops. – infinitezero Jan 01 '22 at 16:54
  • Thanks , Yes i checked the coordinates and it was overwritten by ```*``` so i had to check that it was not either 0 , 1 or side-1. But can i ask why do many references/people always say that we should not use **goto** ? I think it is just the same as a loop so what is the difference ? – Oliver_Queen Jan 01 '22 at 17:01
  • Edgar Dijkstra: Contra Goto: https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf (1968); Donald E. Knuth: Pro Goto: https://pic.plover.com/knuth-GOTO.pdf (1974); Peter Naur: Contra Goto: https://sci-hub.se/https://doi.org/10.1007/BF01939987 (1963). Dijkstra and Knuth have not a strong opinion, but a more balanced view. – Sebastian Jan 01 '22 at 17:15
  • @Oliver_Queen The main reasons, for me at least, are that you can make extra mistakes compared to a loop (say have a typo that says label1 instead of label2) and that in more complex cases it's harder to read and understand the code. Just add a few more conditions inside the loop, maybe a break equivalent and it will be easy to miss that you're using a uninitialized variable, or double delete something or other similar trivial mistakes that you wouldn't otherwise do. When I read a traditional loop I can focus on the conditions and logic, and not have to worry that there's a bug in the jump. – Sorin Jan 01 '22 at 19:12