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?!!

Thank you in advance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#include <iostream>
#include <cstdlib>
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
}
closed account (DEUX92yv)
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

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.

1
2
3
4
5
6
7
8
9
10
11
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
closed account (DEUX92yv)
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.