I've begun into C++ from a heavy C background. This is my first program, which I'm using as a learning experience. A simple Snake Game. Everything runs smoothly, the only issue is that no matter what I do, after the players score reaches 4, the next bit of food is ALWAYS spawned inside of the wall, in the bottom left corner (1 row up from the bottom.) Running into it = Game Over.
This is running on a Linux server. I've tried messing with the border parameters, but I'm having a hard time figuring out what to change, because everything looks fine to my eyes. (Clearly something is not.) I'm pasting the entire program below. It's quite short. I just need one of you guru's to read through/run the program and take me to school on something that's probably a simple fix. Pardon the excessive comments. I use these to teach (I have a following on pastebin as odd as that is to say)
/*
Snake Game - Tragedy
My First Program In C++
I'm Using This Much As A Learning Experience For Myself
And Would Like To Help Those Reading The Code For This Goofy Game
Understand C++ A Bit Better Too
Therefore I'm Trying To Explain As Much As Possible In Real Time
*/
#include <iostream> //Standard
#include <stdlib.h> //Standard
#include <unistd.h> //For POSIX Access
#include <sys/ioctl.h> //For Display Window, TTY Window (Console Window)
#include <termios.h> //For Line Buffering - See Below
#include <stdio.h> //Old Friend
#define CLRSCR "\e[1;1H\e[2J" //Command To Clear Terminal Screen - Change Accordingly
using namespace std; /*
A NameSpace Is Used As Additional Information
To Differentiate Between Similar Functions/Variables
That Have The Same Name In Different Libraries
Using 'namespace' You Can Define The Context
In Which Names Are Defined
Withoug Using The STD NameSpace, The Computer Will Try
To Call cout Or cin As If It Weren't Defined In A NameSpace
Trying To Call Something That Doesn't Exist = Error
So, Without Using namespace std; When You Write For Example:
'cout << value;' You'd Have To Write 'std::cout << value;''
*/
//Create Boundaries
const int width = 50;
const int height = 25;
const char block = 'o';
void ClearScreen(void)
{
cout << CLRSCR;
}
//Global Arrays For Data Records
int background[height][width]; // Background
int snake[50][2]; // Max Snake Length
int food[2] = {0,0}; // Snake Food
int score = 0; // Score
int snakelen = 3; // Snake Starting Length
int snakespeedx = 1; // Horizontal Speed
int snakespeedy = 1; // Vertical Speed
int lap = 200; // Waiting Time Betweeen Frames
//Declaring Global Temporary Variables To Save Memory
int px, py, nx, ny; //Postions
char k;
int h, w;
int x, y;
int movementx = snakespeedx; //Snake Movement
int movementy = 0; //Snake Movement
//Check For Keyboard Press
/*
Reference Link:
https://www.quora.com/With-which-function-can-I-replace-kbhit-in-C++-because-the-header-conio-h-doesnt-exist-in-linux
Ubuntu Users:
sudo apt-get install libncurses5-dev libncursesw5-dev
Life Saver:
http://www.flipcode.com/archives/_kbhit_for_Linux.shtml
*/
int bytesWaiting, i;
int _kbhit()
{
static const int STDIN = 0;
static bool initialized = false; //The Boolean Data Type Is Used To Declare A Variable Whose Value Will Be Set As True (1) Or False (0)
if (! initialized)
{
//Use Termios To Turn Off Line Buffering
termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
//Initialise background borders Onto Array
void initialise_background(void)
{
//int i;
// Insert Top Border
for(i=0; i<width; i++)
{
background[0][i]=1;
}
//Insert Left Border
for(i=0; i<height; i++)
{
background[i][0]=1;
}
//Insert Right Border
for(i=0; i<height; i++)
{
background[i][width-1]=1;
}
//Insert Bottom Border
for(i=0; i<width; i++)
{
background[height-1][i]=1;
}
}
//Initialise Snake Coordinates
void initialise_snake(void)
{
snake[0][0]=3; //Coordinates X
snake[0][1]=3; //Coordinates Y
snake[1][0]=3+1; //Coordinates X
snake[1][1]=3; //Coordinates Y
snake[2][0]=3+2; //Coordinates X
snake[2][1]=3; //Coordinates Y
snake[3][0]=3+3; //Coordinates X
snake[3][1]=3; //Coordinates Y
snake[4][0]=3+4; //Coordinates X
snake[4][1]=3; //Coordinates Y
}
//Update Snake
void update_snake_coordination(void)
{
//int px,py,nx, ny;
px = snake[0][0];
py = snake[0][1];
snake[0][0] = px + movementx;
snake[0][1] = py + movementy;
nx = snake[0][0];
ny = snake[0][1];
for(i=1; i<snakelen; i++)
{
nx = snake[i][0];
ny = snake[i][1];
snake[i][0] = px;
snake[i][1] = py;
px = nx;
py = ny;
}
}
//Install Snake Coordinates Into Background Array = ( 1 To Draw And 0 To Erase)
void draw_snake_in_background(const int rev)
{
//int x, y;
for(i = 0; i<snakelen; i++)
{
x = snake[i][0];
y = snake[i][1];
if((x!=0)&&(y!=0))
{
background[y][x] = rev;
}
}
}
//Print Array Frame
void print_array_frame(void)
{
for(h=0; h<height; h++)
{
for(w=0; w<width; w++)
{
i=background[h][w];
if(i==1)
{
cout << block;
}
else if (i == 2)
{
cout << "+";
}
else
{
cout << " ";
}
}
cout << endl;
}
}
//Update Loop
void mainloop(void)
{
ClearScreen();
draw_snake_in_background(1); // Install Snake
print_array_frame(); // Print Frame
draw_snake_in_background(0); // Uninstall Snake
}
//Waiting Function
void sleepcp(int milliseconds) // Cross-Platform Sleep Function
{
clock_t time_end;
time_end = clock() + milliseconds * CLOCKS_PER_SEC/1000;
while (clock() < time_end)
{
//
}
}
//Reaction To Keyboard Press
void reaction_on_keyboard(const char k)
{
if(k=='d'||k=='6')
{
//Right Turn
movementx = snakespeedx;
movementy = 0;
}
else if(k=='a'||k=='4')
{
//Left Turn
movementx = -snakespeedx;
movementy = 0;
}
else if(k=='w'||k=='8')
{
//Turn Up
movementx = 0;
movementy = -snakespeedy;
}
else if(k=='s'||k=='2')
{
//Turn Down
movementx = 0;
movementy = snakespeedy;
}
else if(k=='q'||k=='z'||k=='c')
{
cout << "[+] Exit Safely [+]"<<endl;
exit(0);
}
}
//Create Snake Food
void cook_food(void)
{
if (food[0]==0)
{
x = rand() % width + 1;
y = rand() % height + 1;
food[0] = x;
food[1] = y;
background[y][x] = 2;
}
}
//Check Snake & Food Status
void capture_food(void)
{
x = food[0];
y = food[1];
if ((x==snake[0][0])&&(y==snake[0][1]))
{
background[y][x] = 0;
food[0] = 0;
score ++;
snakelen ++;
cook_food();
}
}
//Check Snake is Not Touching Boundary
void check_over_lapping(void)
{
//int px,py;
px = snake[0][0];
py = snake[0][1];
if((px==0)||(px==(width-1))||(py==0)||(py==(height-1)))
{
cout << "[+] Game Over [+]" << endl;
exit(0);
}
}
//Loop
void loop(void)
{
int frame = 0;
x = 0;
y = 0;
while(x<500)
{
sleepcp(lap);
if(_kbhit()) //If Keyboard Pressed
{
cin >> k; //Character
reaction_on_keyboard(k);
}
mainloop(); //RUn Main Loop FUnction
update_snake_coordination();//Update Snake Coordinates
check_over_lapping(); //Check Snake Status
cook_food(); //Make Sure Food is Available
capture_food(); //Snake Eaten Food?
cout << "[ Frame : " << frame << " | Score : " << score << " ] "<< endl; //Print Status
frame ++;
}
}
//Main Trigger Function
main()
{
initialise_background(); //Install All Variables
initialise_snake(); //Install Snake data
loop(); //Run Update Loop
}
This is what happens when running the game:
oooooooooooooooooooooooooooooooooooooooooooooooooo
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o o
o ooooooo o
o o
o o
o o
+<---Places Here Every Time o
oooooooooooooooooooooooooooooooooooooooooooooooooo
[ Frame : 169 | Score : 4 ]
Any Help or Input is greatly appreciated!