### Skilled eye needed for Snake game!

Hello. I'm in the process of writing a console version of the classic Snake game and I need a little help.

The scenario:
Currently, I have a board (represented as a 2-D array), a snake head (represented by a '^', 'v' '<' or '>' depending on the direction the snake is traveling), and food (represented by 'X'). The user inputs 'f' to move the snake forward, 'l' to rotate the head of the snake to the left, and 'r' to rotate the head to the right. Each movement costs a unit of energy. Eating the food restores energy by 20 units. Once the energy runs out, the program ends.

The problem:
When the head of the snake is pointing to the right ('>') or pointing down ('v') and the user tries to move forward ('f'), the program bugs out in a very strange way. However, when the head is pointing to the left ('<') and up ('^'), the snake moves perfectly.

Where is the bug?!!

 ``123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229`` ``````#include #include using namespace std; //Game boundaries const int ROWS = 21; const int COLS = 61; class Snake{ public: void make_board(); void play(); private: char board[ROWS][COLS]; //The game board int energy; //Number of movements remaining void print(); void start_snake(); void set_food(); void set_energy(); bool current_energy(); void command(); void rotate_left(); void rotate_right(); void forward(); void hungry(); }; int main() { Snake game; srand(time(NULL)); //Used in set_food() game.make_board(); game.play(); return 0; } void Snake::make_board() { //Creates empty board for(int i = 0; i < ROWS; i++){ for(int j = 0; j < COLS; j++){ board[i][j] = ' '; } } //Top boundary for(int i = 0; i < COLS; i++){ board[0][i] = '-'; } board[0][0] = '+'; board[0][60] = '+'; //Bottom boundary for(int i = 0; i < COLS; i++){ board[20][i] = '-'; } board[20][0] = '+'; board[20][60] = '+'; //Left boundary for(int i = 1; i < ROWS - 1; i++){ board[i][0] = '|'; } //Right boundary for(int i = 1; i < ROWS - 1; i++){ board[i][60] = '|'; } } void Snake::set_energy() { energy = 50; //Initilizes movements } void Snake::print() { for(int i = 0; i < ROWS; i++){ for(int j = 0; j < COLS; j++){ cout << board[i][j]; } cout << endl; } cout << "Current energy: " << energy << " units" << endl; } void Snake::play() { start_snake(); set_food(); //Initilizes food location set_energy(); print(); do{ command(); //Takes input from user, requires 'enter' key print(); //Prints every move }while(current_energy()); //Loop ends when energy = 0 } void Snake::start_snake() { //Starts snake in center of board board[10][30] = '^'; } void Snake::set_food() { int pos1 = 0; int pos2 = 0; //Randomly generates location of food pos1 = rand() % 21; pos2 = rand() % 61; //Sets food only if index is vacant if(board[pos1][pos2] == ' ') board[pos1][pos2] = 'X'; else{ set_food(); //Relocates food if index is occupied } } bool Snake::current_energy() { energy--; //Decrements after each movement if(energy != 0) return true; else{ return false; } } void Snake::command() { char input = ' '; cin >> input; if(input == 'l') rotate_left(); else if(input == 'r') rotate_right(); else if(input == 'f') forward(); else{ cout << "Invalid command!" << endl; command(); } } void Snake::rotate_left() { for(int i = 0; i < ROWS; i++){ for(int j = 0; j < COLS; j++){ if((board[i][j] == '^') || (board[i][j] == '<') || (board[i][j] == '>') || (board[i][j] == 'v')){ if(board[i][j] == '^') board[i][j] = '<'; else if(board[i][j] == '<') board[i][j] = 'v'; else if(board[i][j] == 'v') board[i][j] = '<'; else if(board[i][j] == '>') board[i][j] = 'v'; } } } } void Snake::rotate_right() { for(int i = 0; i < ROWS; i++){ for(int j = 0; j < COLS; j++){ if((board[i][j] == '^') || (board[i][j] == '<') || (board[i][j] == '>') || (board[i][j] == 'v')){ if(board[i][j] == '^') board[i][j] = '>'; else if(board[i][j] == '<') board[i][j] = '^'; else if(board[i][j] == 'v') board[i][j] = '>'; else if(board[i][j] == '>') board[i][j] = 'v'; } } } } void Snake::forward() { for(int i = 0; i < ROWS; i++){ for(int j = 0; j < COLS; j++){ if((board[i][j] == '^') || (board[i][j] == '<') || (board[i][j] == '>') || (board[i][j] == 'v')){ if(board[i][j] == '^'){ if(board[i - 1][j] == 'X') hungry(); board[i - 1][j] = '^'; board[i][j] = ' '; } else if(board[i][j] == '<'){ if(board[i][j - 1] == 'X') hungry(); board[i][j - 1] = '<'; board[i][j] = ' '; } else if(board[i][j] == '>'){ if(board[i][j + 1] == 'X') hungry(); board[i][j + 1] = '>'; board[i][j] = ' '; } else if(board[i][j] == 'v'){ if(board[i + 1][j] == 'X') hungry(); board[i + 1][j] = 'v'; board[i][j] = ' '; } } } } } void Snake::hungry() { energy = energy + 20; //Restores energy set_food(); //Relocates food }``````
What exactly "bugs out", and how does it do so? Describe your problem so we know what to look for.
My apologies.

Here is the output moving forward from the '^' position:
 ```+-----------------------------------------------------------+ | | | | | | | | | | | | | | | ^ | | | | | | | | | | X | | | | | | | | | | | | | +-----------------------------------------------------------+ Current energy: 49 units ```

Here is the output moving forward from the '>' position:
 ```+-----------------------------------------------------------+ | | | | | | | | | | | | | | | | | | | X Current energy: 69 units ```

Same bug occurs when moving from 'v'.

Also, the food relocates after the bug.
Last edited on
Is it too vague a hint to say that the fact that it works with up or left means something?

When you move the snake right or down, you find it again later in the loop ( and again and again and again ).

I put `break;` at line 221 and this solved it. The rotation functions are buggy, maybe has to do with the same thing.

I would say your snake class is too bulky. Think about having a board class and a food class. Then the you can have something like this:
 ``1234567891011121314`` ``````// code is missing, I think the names of the functions are clear enough though Board::Draw_Board(const Snake& snake) { int snake_x = snake.get_x(); int snake_y = snake.get_y(); for ( /* nested loop to draw the board */ ) { if ( i == snake_x && j == snake_y ) snake.draw(); // writes a character depending upon it's own direction else cout << ' '; } }``````

As a side. Whenever I see myself writing a whole bunch of if statements, I think something is wrong with my approach.

For example, a rotate could be something like this:
 ``123456789101112131415161718192021222324252627282930313233343536`` ``````#include const char rotations[] = { '^' , '>', 'v', '<' }; int current_rotation = 0; void rotate_left(void) { if ( current_rotation == 0 ) current_rotation = 3; else --current_rotation; } void rotate_right(void) { if ( current_rotation == 3 ) current_rotation = 0; else ++current_rotation; } int main(void) { for ( int i = 0; i < 10; ++i ) { std::cout << rotations[current_rotation] << std::endl; rotate_right(); } for ( int i = 0; i < 10; ++i ) { std::cout << rotations[current_rotation] << std::endl; rotate_left(); } return 0; }``````
Last edited on
I tried another approach, to keep track of the position, with two variables x and y, and get rid of the for loops.

 ``1234567891011`` ``````void Snake::rotate_right() { if(board[y][x] == '^') board[y][x] = '>'; else if(board[y][x] == '<') board[y][j] = '^'; else if(board[y][x] == 'v') board[y][j] = '>'; else if(board[y][x] == '>') board[y][x] = 'v'; }``````

a similar function works for rotate_left. You can also include LowestOne's code. To move:

 ``123456789101112131415161718`` ``````void Snake::forward() { if(board[y][x] == '^'){ if(board[y- 1][x] == 'X') hungry(); board[y- 1][x] = '^'; board[y][x] = ' '; y--; } else if(board[y][x] == '<'){ if(board[y][x - 1] == 'X') hungry(); board[y][x - 1] = '<'; board[y][x] = ' '; x--; } AND SO ON }``````

Note that you want to put conditions at the edges, so you don't go out of boundaries
By rotating left and right, you mean from the snake's perspective, correct? So "right" is clockwise, and "left" counter?
The bug from moving right or down comes from how you look for the head of the snake, you go across and down the array, when you update it's movement you move it (for this example to the right), and the next place you look after updating it's position because you don't stop looking for the snake is to the right which it finds again and will perform the move right command again all the way till it's out of the screen and cause your issue. You can fix it by returning from the function after you find and update the head.
I understand now. I was able to fix the bug after reading through everyone's suggestions. I understand how the loop was catching the head over and over if it moved down or to the right.

Thanks everyone.
Topic archived. No new replies allowed.