I managed to finish my approach on the legendary Snake game. You move around with the WASD keys. I would like to hear your opinion and maybe ideas on how I can improve frame rate because now it sort of flickers (but it is playable).
#include <conio.h>
#include <stdio.h>
#include <ctime>
#include <vector>
#include <windows.h>
#define PLANEHEIGHT 20
#define PLANEWIDTH 50
#define INITIALSNAKELENGTH 3
#define FPS 5
struct SnakeSegment
{
SnakeSegment() {};
SnakeSegment( unsigned short x1,unsigned short y1 )
{
x = x1;
y = y1;
}
unsigned short x;
unsigned short y;
};
unsigned short snakeLength = INITIALSNAKELENGTH;
char lastDirection = 'd';
std::vector< SnakeSegment > m_Snake;
unsigned short foodX;
unsigned short foodY;
char plane[ PLANEHEIGHT * PLANEWIDTH ];
void DisplayPlane();
void NormalisePlane();
void SnakeInit();
void FoodInit();
void Move();
void DeleteLastSnakeSegment();
bool HasEatenFood();
void WipeConsole();
bool Won();
bool Lost();
int main()
{
srand( (unsigned int)time( NULL ) );
NormalisePlane();
SnakeInit();
FoodInit();
for ( ;; )
{
clock_t start;
double time = 0.0;
start = clock();
while( time < 1.0 / FPS )
{
time = ( clock() - start ) / (double)CLOCKS_PER_SEC;
}
if( _kbhit() ) //can get keyboard input without pressing Enter key
{
lastDirection = _getch();
}
if( HasEatenFood() )
{
snakeLength++;
SnakeSegment segment( foodX,foodY );
m_Snake.push_back( segment ); //we add eaten food position as a new snake segment
FoodInit(); //and spawn new food
}
WipeConsole();
DisplayPlane();
Move();
if( Won() )
{
printf( "Congratulation, you have won!\n" );
break;
}
if( Lost() )
{
printf( "You have lost!\n" );
break;
}
}
system( "PAUSE" );
}
void DisplayPlane()
{
for( unsigned short i = 0; i < PLANEWIDTH + 2; i++ ) printf( "%c", '/' ); printf( "\n" ); //upper bound
for( unsigned short y = 0; y < PLANEHEIGHT; y++ )
{
printf( "%c", '/' ); //left bound
for( unsigned short x = 0; x < PLANEWIDTH; x++ )
{
printf( "%c",plane[ y * PLANEWIDTH + x ] ); //stuff: X = food, O = snake segment, ' ' = blank space
}
printf( "%c\n",'/' ); //right bound
}
for( unsigned short i = 0; i < PLANEWIDTH + 2; i++ ) printf( "%c",'/' ); printf( "\n" ); //lower bound
printf( "Snake length: %d\n",snakeLength );
}
void NormalisePlane()
{
for( unsigned short x = 0; x < PLANEWIDTH; x++ )
{
for( unsigned short y = 0; y < PLANEHEIGHT; y++ )
{
plane[ y * PLANEWIDTH + x ] = ' ' ; //sets all plane cells to ' '
}
}
}
void SnakeInit() //spawns snake in a line of set length in first row of plane
{
for( unsigned short i = 0; i < snakeLength; i++ )
{
SnakeSegment segment;
segment.x = i;
segment.y = 0;
m_Snake.push_back( segment );
plane[ i ] = 'O';
}
}
void FoodInit()
{
foodX = rand() % PLANEWIDTH;
foodY = rand() % PLANEHEIGHT;
while ( plane[ foodY * PLANEWIDTH + foodX ] != ' ' ) //new food has to be spawned at empty cell
{
foodX = rand() % PLANEWIDTH;
foodY = rand() % PLANEHEIGHT;
}
plane[ foodY * PLANEWIDTH + foodX ] = 'X';
}
void Move()
{
if( lastDirection == 'd' )
{
SnakeSegment segment = m_Snake.at( m_Snake.size() - 1 );
segment.x++;
m_Snake.push_back( segment );
plane[ segment.y * PLANEWIDTH + segment.x ] = 'O';
DeleteLastSnakeSegment();
}
else if( lastDirection == 's' )
{
SnakeSegment segment = m_Snake.at( m_Snake.size() - 1 );
segment.y++;
m_Snake.push_back( segment );
plane[ segment.y * PLANEWIDTH + segment.x ] = 'O';
DeleteLastSnakeSegment();
}
else if(lastDirection == 'a')
{
SnakeSegment segment = m_Snake.at( m_Snake.size() - 1 );
segment.x--;
m_Snake.push_back( segment );
plane[ segment.y * PLANEWIDTH + segment.x ] = 'O';
DeleteLastSnakeSegment();
}
else if( lastDirection == 'w' )
{
SnakeSegment segment = m_Snake.at( m_Snake.size() - 1 );
segment.y--;
m_Snake.push_back( segment );
plane[ segment.y * PLANEWIDTH + segment.x ] = 'O';
DeleteLastSnakeSegment();
}
}
void DeleteLastSnakeSegment()
{
SnakeSegment temp = m_Snake[ 0 ];
plane[ temp.y * PLANEWIDTH + temp.x ] = ' ';
m_Snake.erase( m_Snake.begin() ); //first is last
}
bool HasEatenFood()
{
SnakeSegment segment = m_Snake.at( m_Snake.size() - 1 ); //first element of snake
SnakeSegment food;
food.x = foodX;
food.y = foodY;
return segment.x == food.x && segment.y == food.y; //if his "head" collides with food cell
}
void WipeConsole()
{
HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
COORD coord = { 0,0 };
DWORD count;
CONSOLE_SCREEN_BUFFER_INFO csbi;
if( GetConsoleScreenBufferInfo( hStdOut,&csbi ) )
{
FillConsoleOutputCharacter( hStdOut,(TCHAR)32,csbi.dwSize.X * csbi.dwSize.Y,coord,&count );
FillConsoleOutputAttribute( hStdOut,csbi.wAttributes,csbi.dwSize.X - 1 * csbi.dwSize.Y - 1,coord,&count );
SetConsoleCursorPosition( hStdOut,coord );
}
return;
}
bool Won()
{
bool space = false;
for( unsigned short x = 0; x < PLANEWIDTH; x++ )
{
for( unsigned short y = 0; y < PLANEHEIGHT; y++ )
{
if( plane[ y * PLANEWIDTH + x ] == ' ' ) //if none of cells is empty we won
{
space = true;
break;
}
}
}
return !space;
}
bool Lost()
{
SnakeSegment segment = m_Snake.at( m_Snake.size() - 1 ); //first element of snake
if( segment.x < 0 || segment.x > PLANEWIDTH - 1 ||
segment.y < 0 || segment.y > PLANEHEIGHT - 1 ) return true; //if colides with boundries
for( unsigned short i = 0; i < m_Snake.size() - 1; i++ ) //or with itself we return true
{
SnakeSegment temp = m_Snake[ i ];
if( temp.x == segment.x && temp.y == segment.y ) return true;
}
return false;
}