Last active
August 29, 2015 14:10
-
-
Save soachishti/1242625c0ccc71c540f6 to your computer and use it in GitHub Desktop.
Snake Game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Name: Syed Owais Ali Chishti | |
* Task: Snake Game with ladder | |
* Major: BS(CS) | |
*/ | |
// Help from: http://gamedev.stackexchange.com/questions/33786/snake-game-logic | |
#include <iostream> | |
#include <conio.h> // getch() | |
#include <cstdlib> // System() | |
#include <windows.h> // Sleep() | |
#include <iomanip> // setw() | |
#include <thread> // thread | |
using namespace std; | |
/* | |
* Feature to add | |
* - Restart Game | |
*/ | |
/* | |
* Prototype functions | |
*/ | |
void coutRepeated(char, int); | |
void newBody(int, int); | |
void gotoXY(int, int); | |
void generateFood(); | |
void setDirection(bool, bool, bool, bool); | |
void arrowCatch(); | |
void printSnake(); | |
void coutRepeated(char, int); | |
void cls(bool fullScreen = true); // fullScreen = true to clear whole console | |
/* | |
* Structure for holding X and Y axis | |
*/ | |
struct XYAxis{ | |
short int x; | |
short int y; | |
}; | |
signed short int length = 10; // Initial length of snake | |
char headChar = '@'; // Head repesenter for snake | |
char bodyChar = 'O'; // Body repesenter for snake | |
char foodChar = '*'; // Food repesenter for snake | |
const short int snakeCoord = 512; // Max Snake Size | |
XYAxis snake[snakeCoord]; // Structre array for holding snake body | |
XYAxis food; // For holding food location, X and Y axis | |
XYAxis screenSize = {79,20}; // For holding screen size. | |
int mode = 10; // Initial mode, mode is equal milliseconds for Sleep() | |
COORD coord; // For gotoXY() | |
short int last = -1; // Holds head location in array of snake | |
bool movement[4] = {false,false,true,false}; // top, bottom, right, left | |
long int score = 0; // Score info | |
bool pause = false; | |
bool paused = false; | |
int tmp; // Dust bin :) | |
char laddarChar = '|'; | |
XYAxis ladder[2]; | |
bool ladderAI = true; // Ladder Specific / For opening ladder after eating food | |
int main(){ | |
ladder[0].x = 20; | |
ladder[0].y = 5; | |
ladder[1].x = 70; | |
ladder[1].y = 15; | |
gotoXY(0,7); | |
cout<<setw(45)<<"*******************"<<endl<<endl; | |
cout<<setw(45)<<"* SNAKE GAME *"<<endl<<endl; | |
cout<<setw(45)<<"*******************"<<endl<<endl; | |
cout<<setw(46)<<"--- SOAC PROJECT ---"<<endl; | |
gotoXY(53,20); | |
cout<<"Press any key to continue."; | |
_getch(); | |
cls(); | |
gotoXY(0,4); | |
cout<<setw(45)<<"*******************"<<endl<<endl; | |
cout<<setw(45)<<"* MODE *"<<endl<<endl; | |
cout<<setw(45)<<"*******************"<<endl<<endl; | |
cout<<setw(45)<<"1) Beginner - PRESS 1"<<endl; | |
cout<<setw(45)<<"2) Casual - PRESS 2"<<endl; | |
cout<<setw(45)<<"3) Pro - PRESS 3"<<endl; | |
do{ | |
cout<<endl<<setw(37)<<"Choice: "; | |
cin>>tmp; | |
if(tmp > 0 && tmp < 4 ) | |
break; | |
} while(true); | |
switch(tmp){ | |
case 1: | |
mode = 100; | |
break; | |
case 2: | |
mode = 50; | |
break; | |
case 3: | |
mode = 10; | |
break; | |
} | |
cls(); | |
// Start arrowCatch function to cathc keyboard signal | |
thread threadArrowCatch (arrowCatch); | |
// Generate initial body of snake. | |
for(int i = 0; i < length; i++) | |
{ | |
last++; | |
snake[last].x = 10 + i; | |
snake[last].y = 4; | |
} | |
generateFood(); // Generate food location | |
while(true) { | |
if(pause == false){ | |
gotoXY(food.x,food.y); | |
cout<<foodChar; | |
gotoXY(ladder[0].x,ladder[0].y); | |
cout<<"|"; | |
gotoXY(ladder[1].x,ladder[1].y); | |
cout<<"|"; | |
gotoXY(screenSize.x,screenSize.y); | |
cout<<" "; | |
coutRepeated('=',screenSize.x); // Print border | |
cout<<endl; | |
cout<<"Score: "<<score<<endl; | |
cout<<setw(80)<<"Eat food to open open ladder."<<endl; | |
cout<<setw(80)<<"Press Escape to Pause!"; // Print Score | |
printSnake(); // Print snake | |
last++; | |
if(last >= snakeCoord) { | |
int temp[snakeCoord][2]; | |
int firstBodyElement = last-length; | |
for(int i = 0;i < length; i++) { | |
temp[i][0] = snake[firstBodyElement+i].x; | |
temp[i][1] = snake[firstBodyElement+i].y; | |
} | |
last = -1; | |
for(int i = 0;i < length; i++) { | |
last++; | |
snake[last].x = temp[last][0]; | |
snake[last].y = temp[last][1]; | |
} | |
last++; | |
} | |
/* | |
* According to keyboard signal stored in movement new snake body is generated | |
*/ | |
if(movement[0]) // UP | |
newBody(snake[last-1].x,snake[last-1].y-1); | |
if(movement[1]) // DOWN | |
newBody(snake[last-1].x,snake[last-1].y+1); | |
if(movement[2]) //RIGHT | |
newBody(snake[last-1].x+1,snake[last-1].y); | |
if(movement[3]) // DOWN | |
newBody(snake[last-1].x-1,snake[last-1].y); | |
Sleep(mode); // Sleeping to manage speed of snake. | |
cls(false); // Clear only snake | |
} | |
else { | |
paused = true; | |
} | |
} | |
cout<<endl; | |
system("PAUSE"); | |
return 0; | |
} | |
/* | |
* Add body to snake | |
* | |
* @param x XAxis of snake body | |
* @param y YAxis of snake body | |
*/ | |
void newBody(int x,int y) { | |
snake[last].x = x; | |
snake[last].y = y; | |
} | |
/* | |
* For jumping on console | |
* | |
* @param x XAxis of console | |
* @param y YAxis of console | |
*/ | |
void gotoXY(int x,int y) | |
{ | |
coord.X=x; | |
coord.Y=y; | |
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),coord); | |
} | |
/* | |
* Add x and y axis to food structure. | |
*/ | |
void generateFood() { | |
srand ((unsigned int)time(0)); // Seeding before using rand() | |
food.x = rand() % screenSize.x + 1; | |
food.y = rand() % screenSize.y + 1; | |
} | |
/* | |
* Set current direction of snake | |
* | |
* @param a TOP | |
* @param b DOWN | |
* @param c RIGHT | |
* @param d LEFT | |
*/ | |
void setDirection(bool a,bool b, bool c, bool d){ | |
movement[0] = a; | |
movement[1] = b; | |
movement[2] = c; | |
movement[3] = d; | |
} | |
/* | |
* Catch keyboard signal and change snake direction variable | |
* This is function is run through backkground thread | |
*/ | |
void arrowCatch() { | |
int direction; | |
while(true) { | |
direction = _getch(); // Asking for signal | |
switch(direction) { | |
case 72: // If up arrow is pressed | |
if(movement[1] == false) // If snake is not moving DOWN | |
setDirection(true,false,false,false); | |
break; | |
case 80: // IF DOWN arrow is pressed | |
if(movement[0] == false) // If snake is not moving UP | |
setDirection(false,true,false,false); | |
break; | |
case 77: // IF RIGHT arrow is pressed | |
if(movement[3] == false) // If snake is not moving LEFT | |
setDirection(false,false,true,false); | |
break; | |
case 75: // If LEFT arrow is pressed | |
if(movement[2] == false) // If snake is not moving RIGHT | |
setDirection(false,false,false,true); | |
break; | |
case 97: //If press hacking cheat | |
score += 10; | |
break; | |
case 27: // If Esc key is pressed | |
pause = true; // Paused the game. | |
while(paused == false) { // Waiting for stop signal from main() | |
Sleep(200); | |
} | |
cls(); | |
gotoXY(0,5); | |
cout<<setw(45)<<"*******************"<<endl<<endl; | |
cout<<setw(45)<<"* GAME PAUSED *"<<endl<<endl; | |
cout<<setw(45)<<"*******************"<<endl<<endl; | |
cout<<setw(45)<<"1) Continue - PRESS 1"<<endl; | |
cout<<setw(45)<<"2) Exit - PRESS 2"<<endl; | |
do{ | |
cout<<endl<<setw(37)<<"Choice: "; | |
cin>>tmp; | |
if(tmp > 0 && tmp < 3 ) | |
{ | |
if(tmp == 1) { | |
pause = false; | |
paused = false; | |
} | |
else { | |
exit(0); | |
} | |
break; | |
} | |
} while(true); | |
break; | |
} | |
} | |
} | |
/* | |
* Print snake to STDOUT | |
*/ | |
void printSnake(){ | |
short int end = last-length; | |
for (int i = last; i > end;i--){ | |
// If snake is equal to food location | |
if(food.x == snake[i].x && food.y == snake[i].y) { | |
score += 10; | |
length++; | |
generateFood(); // Generate new food location | |
ladderAI = false; | |
} | |
if(ladder[0].x == snake[i].x && ladder[0].y == snake[i].y && ladderAI == false) { | |
newBody(ladder[1].x,ladder[1].y); | |
ladderAI = true; | |
} | |
if(ladder[1].x == snake[i].x && ladder[1].y == snake[i].y && ladderAI == false) { | |
newBody(ladder[0].x,ladder[0].y); | |
ladderAI = true; | |
} | |
/* | |
* Check is Snake have touched wall so it start snake from other side. | |
*/ | |
if(snake[i].x > screenSize.x) // Snake have touched wall on left | |
newBody(0,snake[last-1].y); | |
if(snake[i].x < 0) // Snake have touched wall on right | |
newBody(screenSize.x,snake[last-1].y); | |
if(snake[i].y > screenSize.y) // Snake have touched wall on down | |
newBody(snake[last-1].x,0); | |
if(snake[i].y < 0) // Snake have touched wall on up | |
newBody(snake[last-1].x,screenSize.y); | |
// Jump to x and y axis on console to draw snake. | |
gotoXY(snake[i].x,snake[i].y); | |
if(i == last) | |
cout<<headChar; // HEAD | |
else | |
cout<<bodyChar; // TAIL | |
} | |
} | |
/* | |
* Cout repeated char to STDOUT | |
* | |
*@param chr Character to repeat | |
*@param limit Times to repeat | |
*/ | |
void coutRepeated(char chr,int limit){ | |
for(int i = 0; i < limit; i++) | |
cout<<chr; | |
} | |
/* | |
* Faster appraoch to clear screen as compared to system("cls"); | |
* | |
*@link http://www.cplusplus.com/forum/articles/10515/ | |
*/ | |
void cls(bool fullScreen) | |
{ | |
HANDLE hStdOut; | |
CONSOLE_SCREEN_BUFFER_INFO csbi; | |
DWORD count; | |
DWORD cellCount; | |
COORD homeCoords = { 0, 0 }; | |
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); | |
if (hStdOut == INVALID_HANDLE_VALUE) return; | |
/* Get the number of cells in the current buffer */ | |
if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return; | |
if(fullScreen == true){ | |
cellCount = csbi.dwSize.X *csbi.dwSize.Y; | |
} | |
else{ | |
cellCount = (screenSize.x+1) * (screenSize.y+1); | |
} | |
/* Fill the entire buffer with spaces */ | |
if (!FillConsoleOutputCharacter( | |
hStdOut, | |
(TCHAR) ' ', | |
cellCount, | |
homeCoords, | |
&count | |
)) return; | |
/* Fill the entire buffer with the current colors and attributes */ | |
if (!FillConsoleOutputAttribute( | |
hStdOut, | |
csbi.wAttributes, | |
cellCount, | |
homeCoords, | |
&count | |
)) return; | |
/* Move the cursor home */ | |
SetConsoleCursorPosition( hStdOut, homeCoords ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
kbhit and getch can be used together to do remove thread or GetKeyStatus()