Skip to content

Instantly share code, notes, and snippets.

@soachishti
Last active August 29, 2015 14:10
Show Gist options
  • Save soachishti/1242625c0ccc71c540f6 to your computer and use it in GitHub Desktop.
Save soachishti/1242625c0ccc71c540f6 to your computer and use it in GitHub Desktop.
Snake Game
/*
* 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 );
}
@soachishti
Copy link
Author

kbhit and getch can be used together to do remove thread or GetKeyStatus()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment