Description: Cross platform game, I coded for my first ICT106 (Fundamentals of Computer Systems) assignment.

The game is coded in C, and is approximatly 1000 lines of badly (some good) written code that I hacked together. It was a very good learning experience, and I learned a lot: i.e. cross platform development is annoying.

Mark: HD

Source Code (Compiles with borland & gcc – should compile in other compilers as well.)

Excutables: Windows  Linux (Ubuntu)

 

Limitations:

  • Should have done prototyping
  • Should have split up my code into seperate files

 


/*
Author: Matthew Vlietstra
Version: 0.5
Date: 28/09/2010

Discription:
This is a snake game that can (or should) work in linux & windows environments - it runs in a console 😛
I have squished a majority (*Caugh minority*) of bugs, however there are still some hiding away.... *Gets a can of Doom*

NOTE:
A problem with linux, It seems Ubuntu doesn't have extended ascii characters (*Sniff*). Therefore I have resorted to using different symbols.. Concluding that the game looks better in windows!!!! meh.. 

Windows:
Compile with borland

Linux:
Please note, tested under Ubuntu not sure if it works in other linux environments... It should though.. I recommend compiling with borland under windows for best results...
Compile with gcc in linux using the following parameters:
gcc snake.c –lm –o snake.out

Cygwin:
Although this program may compile/ run in Cygwin it DOESN'T RUN AS INTENDED!! EXTREMELY BUGGY/LAGGY DEFENTLY NOT RECOMMENED!! <- It works a bit better now, but for some reason it sometimes jurks forward.......

Known Bugs:
* If the user spams arrow keys the snake can slither onto it's self.. <- FIXED
* Sometimes a wall will block will disappear - most prob something to do with removing the snake tail?
* A slight lag issue when the snake eats food. I assume this is due to 
  the fact that the food was going to generate on top of the snake, however 
  since it collitaded it generates more random numbers and checks again... Thus 
  probability of this increases as the snake gets longer.
* Highscores, if a user exits when the program is about to enter their highscore the highscores file becomes blank.

Todo:
* Lots
* Highscores (Open, Save) <- DONE - HOWEVER, if the highscores file has been edited i.e. isn't in the correct format the program may crash..... Also im pretty sure if a person enters a string (for name) longer then 128(or maybe 126?) the program might crash......... AND (i'm pretty sure) if a person enters a long string (for the name), when the highscores are displayed it'll be messed up...
* Pause Game/Resume > Maybe a multifunction menu for the key's c & exc, which brings up a menu. <- Done, however currently it doesn't allow the user to exit the game

* HIGH PRIORITY: Fix the way the snake moves to milliseconds!!!! the way its done now is really bad........ <FIXED
* Slight bug with adding a new score to the high scores.. It add's an ascii smiley face next to EMPTY fields... Pretty kewl actually.. Currently it's a feature 🙂
* Clean up the code, put functions that relate together etc
* LINUX/ CYGWIN > Buggy, Walls in ubuntu are messed up, same in sygwin - even though windows works perfectly. (Most prob have to re-write the wall stuff)
				> Also (int cygwin) for some reason the pause menu is printed @ the top.. even though i cannot possibly get into the if state (Esc or P) needs to be pressed... well it is a possible as it's doing it 
* FIXED UP THE HIGHSCORES MESS & ENCRYPT IT SO PPL CAN'T CHEAT 🙂 <- output it as a binary file			

Assumptions:
* User doesn't enter a string >128 characters long (for the highscores name)
				
*/

#include <stdio.h>
#include <time.h>
#include <math.h>

#define SNAKE_ARRAY_SIZE 310

#ifdef _WIN32
	//Windows Libraries
	#include <conio.h>

	//Windows Constants
	//Controls
	#define UP_ARROW 72
	#define LEFT_ARROW 75
	#define RIGHT_ARROW 77
	#define DOWN_ARROW 80
	
	#define ENTER_KEY 13
	
	const char SNAKE_HEAD = (char)177;
	const char SNAKE_BODY = (char)178;
	const char WALL = (char)219;	
	const char FOOD = (char)254;
	const char BLANK = ' ';
#else
	//Linux Libraries
	#include <stdlib.h>
	#include <termios.h>
	#include <unistd.h>
	#include <fcntl.h>
	
	//Linux Constants

	//Controls (arrow keys for Ubuntu) 
	#define UP_ARROW (char)'A' //Originally I used constants but borland started giving me errors, so I changed to #define - I do realize that is not the best way.
	#define LEFT_ARROW (char)'D'
	#define RIGHT_ARROW (char)'C'
	#define DOWN_ARROW (char)'B'

	#define ENTER_KEY 10
	
	const char SNAKE_HEAD = 'X';
	const char SNAKE_BODY = '#';
	const char WALL = '#';	
	const char FOOD = '*';
	const char BLANK = ' ';

	/* 
	 * PLEASE NOTE I DID NOT WRITE THESE LINUX FUNCTIONS, 
	 * THE ONLY REASON I'M USING THEM IS TO MAKE THIS GAME 
	 * CROSS PLATFORM. THESE FUNCTIONS ARE AN ALTERNATIVE TO 
	 * THE WINDOWS HEADER FILE "conio.h".
	*/
	
	//Linux Functions - These functions emulate some functions from the windows only conio header file
	//Code: http://ubuntuforums.org/showthread.php?t=549023
	void gotoxy(int x,int y)
	{
		printf("%c[%d;%df",0x1B,y,x);
	}

	//http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html
	int kbhit(void)
	{
	  struct termios oldt, newt;
	  int ch;
	  int oldf;

	  tcgetattr(STDIN_FILENO, &oldt);
	  newt = oldt;
	  newt.c_lflag &= ~(ICANON | ECHO);
	  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
	  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
	  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

	  ch = getchar();

	  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
	  fcntl(STDIN_FILENO, F_SETFL, oldf);

	  if(ch != EOF)
	  {
		ungetc(ch, stdin);
		return 1;
	  }

	  return 0;
	}

	//http://www.experts-exchange.com/Programming/Languages/C/Q_10119844.html - posted by jos
	char getch()
	{
		char c;
		system("stty raw");
		c= getchar();
		system("stty sane");
		//printf("%c",c);
		return(c);
	}

	void clrscr()
	{
		system("clear");
		return;
	}
	//End linux Functions
#endif

//This should be the same on both operating systems
#define EXIT_BUTTON 27 //ESC
#define PAUSE_BUTTON 112 //P

char waitForAnyKey(void)
{
	int pressed;
	
	while(!kbhit());
	
	pressed = getch();
	//pressed = tolower(pressed);
	return((char)pressed);
}

int getGameSpeed(void)
{
	int speed;
	clrscr();
	
	do
	{
		gotoxy(10,5);
		printf("Select The game speed between 1 and 9.");
		speed = waitForAnyKey()-48;
	} while(speed < 1 || speed > 9);
	return(speed);
}

void pauseMenu(void)
{
	int i;
	
	gotoxy(28,23);
	printf("**Paused**");
	
	waitForAnyKey();
	gotoxy(28,23);
	printf("            ");

	return;
}

//Need to think of a better name for this function
//This function checks if a key has pressed, then checks if its any of the arrow keys/ p/esc key. It changes direction acording to the key pressed.
int checkKeysPressed(int direction)
{
	int pressed;
	
	if(kbhit()) //If a key has been pressed
	{
		pressed=getch();
		if (direction != pressed)
		{	
			/*This needs to be fixed up... This code prevents the user from directing the snake to slither over itself... 
			Although it works a majority of the time... If the user changes direction(and the snake doesn't move), 
			then quickly directs the snake back onto itself there's a collision and the game ends 
			perhaps anthor variable is required to prevent this. <- FIXED, Need to test.
			*/
			if(pressed == DOWN_ARROW && direction != UP_ARROW)
				direction = pressed;
			else if (pressed == UP_ARROW && direction != DOWN_ARROW)
				direction = pressed;
			else if (pressed == LEFT_ARROW && direction != RIGHT_ARROW)
				direction = pressed;
			else if (pressed == RIGHT_ARROW && direction != LEFT_ARROW)
				direction = pressed;
			else if (pressed == EXIT_BUTTON || pressed == PAUSE_BUTTON)
				pauseMenu();
		}
	}
	return(direction);
}

//Cycles around checking if the x y coordinates ='s the snake coordinates as one of this parts
//One thing to note, a snake of length 4 cannot collide with itself, therefore there is no need to call this function when the snakes length is <= 4
int collisionSnake (int x, int y, int snakeXY&#91;&#93;&#91;SNAKE_ARRAY_SIZE&#93;, int snakeLength, int detect)
{
	int i;
	for (i = detect; i < snakeLength; i++) //Checks if the snake collided with itself
	{
		if ( x == snakeXY&#91;0&#93;&#91;i&#93; && y == snakeXY&#91;1&#93;&#91;i&#93;)
			return(1);
	}
	return(0);
}

//Generates food & Makes sure the food doesn't appear on top of the snake <- This sometimes causes a lag issue!!! Not too much of a problem tho
int generateFood(int foodXY&#91;&#93;, int width, int height, int snakeXY&#91;&#93;&#91;SNAKE_ARRAY_SIZE&#93;, int snakeLength)
{
	int i;
	
	do
	{
		srand ( time(NULL) );
		foodXY&#91;0&#93; = rand() % (width-2) + 2;
		srand ( time(NULL) );
		foodXY&#91;1&#93; = rand() % (height-6) + 2;
	} while (collisionSnake(foodXY&#91;0&#93;, foodXY&#91;1&#93;, snakeXY, snakeLength, 0)); //This should prevent the "Food" from being created on top of the snake. - However the food has a chance to be created ontop of the snake, in which case the snake should eat it...

	gotoxy(foodXY&#91;0&#93; ,foodXY&#91;1&#93;);
	printf("%c", FOOD);
	
	return(0);
}

/*
Moves the snake array forward, i.e. 
This:
 x 1 2 3 4 5 6
 y 1 1 1 1 1 1
Becomes This:
 x 1 1 2 3 4 5
 y 1 1 1 1 1 1
 
 Then depending on the direction (in this case west - left) it becomes:
 
 x 0 1 2 3 4 5
 y 1 1 1 1 1 1
 
 snakeXY&#91;0&#93;&#91;0&#93;--; <- if direction left, take 1 away from the x coordinate
*/
void moveSnakeArray(int snakeXY&#91;&#93;&#91;SNAKE_ARRAY_SIZE&#93;, int snakeLength, int direction)
{
	int i;
	for( i = snakeLength-1; i >= 1; i-- )
	{
		snakeXY[0][i] = snakeXY[0][i-1];
		snakeXY[1][i] = snakeXY[1][i-1];
	}	
	
	/*
	because we dont actually know the new snakes head x y, 
	we have to check the direction and add or take from it depending on the direction.
	*/
	switch(direction)
	{
		case DOWN_ARROW:
			snakeXY[1][0]++;
			break;
		case RIGHT_ARROW:
			snakeXY[0][0]++;
			break;
		case UP_ARROW:
			snakeXY[1][0]--;
			break;
		case LEFT_ARROW:
			snakeXY[0][0]--;
			break;			
	}
	
	return;
}

void move(int snakeXY[][SNAKE_ARRAY_SIZE], int snakeLength, int direction)
{
	int x;
	int y;

	//Remove the tail ( HAS TO BE DONE BEFORE THE ARRAY IS MOVED!!!!! )
	x = snakeXY[0][snakeLength-1];
	y = snakeXY[1][snakeLength-1];
	
	gotoxy(x,y);
	printf("%c",BLANK);	
	
	//Changes the head of the snake to a body part
	gotoxy(snakeXY[0][0],snakeXY[1][0]);	
	printf("%c", SNAKE_BODY);
	
	moveSnakeArray(snakeXY, snakeLength, direction);
	
	gotoxy(snakeXY[0][0],snakeXY[1][0]);	
	printf("%c",SNAKE_HEAD);
	
	gotoxy(1,1); //Gets rid of the darn flashing underscore.
	
	return;
}

int eatFood(int snakeXY[][SNAKE_ARRAY_SIZE], int foodXY[]) //This function checks if the snakes head his on top of the food, if it is then it'll generate some more food...
{
	if (snakeXY[0][0] == foodXY[0] && snakeXY[1][0] == foodXY[1])
	{
		foodXY[0] = 0;
		foodXY[1] = 0; //This should prevent a nasty bug (loops) need to check if the bug still exists...
		
		printf("\7"); //Beep
		return(1);
	}		
	
	return(0);
}

int collisionDetection(int snakeXY[][SNAKE_ARRAY_SIZE], int consoleWidth, int consoleHeight, int snakeLength ) //Need to Clean this up a bit
{
	int colision = 0;
	if ((snakeXY[0][0] == 1) || (snakeXY[1][0] == 1) || (snakeXY[0][0] == consoleWidth) || (snakeXY[1][0] == consoleHeight - 4)) //Checks if the snake collided wit the wall or it's self
		colision = 1;
	else
		if (collisionSnake(snakeXY[0][0], snakeXY[1][0], snakeXY, snakeLength, 1)) //If the snake collided with the wall, theres no point in checking if it collided with itself.
			colision = 1;
			
	return(colision);
}

void refreshInfoBar(int score, int speed)
{
	gotoxy(5,23);
	printf("Score: %d", score);
	
	gotoxy(5,24);
	printf("Speed: %d", speed);

	gotoxy(52,23);
	printf("Coder: Matthew Vlietstra");

	gotoxy(52,24);
	printf("Version: 0.5");
	
	return;
}

//**************HIGHSCORE STUFF**************//

//-> The highscores system seriously needs to be clean. There are some bugs, entering a name etc

void createHighScores(void)
{
	FILE *file; 
	int i;

	file = fopen("highscores.txt","w+");
	
	if(file == NULL)
	{
		printf("FAILED TO CREATE HIGHSCORES!!! EXITING!");
		exit(0);	
	}
	
	for(i = 0; i < 5; i++)
	{
		fprintf(file,"%d",i+1);
		fprintf(file,"%s","\t0\t\t\tEMPTY\n");
	}	
	
	fclose(file);
	return;
}

int getLowestScore()
{
	FILE *fp;
	char str&#91;128&#93;;
	int lowestScore = 0;
	int i;
	int intLength;
	
	if((fp = fopen("highscores.txt", "r")) == NULL)
	{
		//Create the file, then try open it again.. if it fails this time exit.
		createHighScores(); //This should create a highscores file (If there isn't one)
		if((fp = fopen("highscores.txt", "r")) == NULL)
			exit(1);
	}

	while(!feof(fp))
	{
		fgets(str, 126, fp);  
	}
	fclose(fp);	
	
	i=0;
	
	//Gets the Int length
	while(str&#91;2+i&#93; != '\t')
	{
		i++;
	}
	
	intLength = i;
	
	//Gets converts the string to int
	for(i=0;i < intLength; i++)
	{
		lowestScore = lowestScore + ((int)str&#91;2+i&#93; - 48) * pow(10,intLength-i-1);
	}

	return(lowestScore);
}

void inputScore(int score) //This seriously needs to be cleaned up
{
	FILE *fp;
	FILE *file; 
	char str&#91;20&#93;;
	int fScore;
	int i, s, y;
	int intLength;
	int scores&#91;5&#93;;
	int x;
	char highScoreName&#91;20&#93;;
	char highScoreNames&#91;5&#93;&#91;20&#93;;
	
	char name&#91;20&#93;;
	
	int entered = 0;
	
	clrscr(); //clear the console
	
	if((fp = fopen("highscores.txt", "r")) == NULL)
	{
		//Create the file, then try open it again.. if it fails this time exit.
		createHighScores(); //This should create a highscores file (If there isn't one)
		if((fp = fopen("highscores.txt", "r")) == NULL)
			exit(1);
	}
	gotoxy(10,5);
	printf("Your Score made it into the top 5!!!");
	gotoxy(10,6);
	printf("Please enter your name: ");
	gets(name);
	
	x = 0;
	while(!feof(fp))
	{
		fgets(str, 126, fp);  //Gets a line of text
		
		i=0;
		
		//Gets the Int length
		while(str&#91;2+i&#93; != '\t')
		{
			i++;
		}
		
		s = i;
		intLength = i;
		i=0;
		while(str&#91;5+s&#93; != '\n')
		{
			//printf("%c",str&#91;5+s&#93;);
			highScoreName&#91;i&#93; = str&#91;5+s&#93;;
			s++;
			i++;
		}
		//printf("\n");
		
		fScore = 0;
		//Gets converts the string to int
		for(i=0;i < intLength; i++)
		{
			//printf("%c", str&#91;2+i&#93;);
			fScore = fScore + ((int)str&#91;2+i&#93; - 48) * pow(10,intLength-i-1);
		}
		
		if(score >= fScore && entered != 1)
		{
			scores[x] = score;
			strcpy(highScoreNames[x], name);
			
			//printf("%d",x+1);
			//printf("\t%d\t\t\t%s\n",score, name);		
			x++;
			entered = 1;
		}
		
		//printf("%d",x+1);
		//printf("\t%d\t\t\t%s\n",fScore, highScoreName);
		//strcpy(text, text+"%d\t%d\t\t\t%s\n");
		strcpy(highScoreNames[x], highScoreName);
		scores[x] = fScore;
		
		//highScoreName = "";
		for(y=0;y<20;y++)
		{
			highScoreName&#91;y&#93; = NULL;
		}
		
		x++;
		if(x >= 5)
			break;
	}
	
	fclose(fp);
	
	file = fopen("highscores.txt","w+");
	
	for(i=0;i<5;i++)
	{
		//printf("%d\t%d\t\t\t%s\n", i+1, scores&#91;i&#93;, highScoreNames&#91;i&#93;);
		fprintf(file, "%d\t%d\t\t\t%s\n", i+1, scores&#91;i&#93;, highScoreNames&#91;i&#93;);	
	}

	fclose(file);
	
	return;
}

void displayHighScores(void) //NEED TO CHECK THIS CODE!!!
{
	FILE *fp;
	char str&#91;128&#93;;
	int y = 5;
	
	clrscr(); //clear the console
	
	if((fp = fopen("highscores.txt", "r")) == NULL) {
		//Create the file, then try open it again.. if it fails this time exit.
		createHighScores(); //This should create a highscores file (If there isn't one)
		if((fp = fopen("highscores.txt", "r")) == NULL)
			exit(1);
	}
	
	gotoxy(10,y++);
	printf("High Scores");	
	gotoxy(10,y++);
	printf("Rank\tScore\t\t\tName");
	while(!feof(fp)) {
		gotoxy(10,y++);
		if(fgets(str, 126, fp)) 
			printf("%s", str);
	}

	fclose(fp);	//Close the file
	gotoxy(10,y++);
	
	printf("Press any key to continue...");
	waitForAnyKey();	
	return;
}

//**************END HIGHSCORE STUFF**************//

void youWinScreen(void)
{
	int x = 6, y = 7;
	gotoxy(x,y++);
	printf("'##:::'##::'#######::'##::::'##::::'##:::::'##:'####:'##::: ##:'####:");
	gotoxy(x,y++);
	printf(". ##:'##::'##.... ##: ##:::: ##:::: ##:'##: ##:. ##:: ###:: ##: ####:");
	gotoxy(x,y++);
	printf(":. ####::: ##:::: ##: ##:::: ##:::: ##: ##: ##:: ##:: ####: ##: ####:");
	gotoxy(x,y++);
	printf("::. ##:::: ##:::: ##: ##:::: ##:::: ##: ##: ##:: ##:: ## ## ##:: ##::");
	gotoxy(x,y++);
	printf("::: ##:::: ##:::: ##: ##:::: ##:::: ##: ##: ##:: ##:: ##. ####::..:::");
	gotoxy(x,y++);
	printf("::: ##:::: ##:::: ##: ##:::: ##:::: ##: ##: ##:: ##:: ##:. ###:'####:");
	gotoxy(x,y++);
	printf("::: ##::::. #######::. #######:::::. ###. ###::'####: ##::. ##: ####:");
	gotoxy(x,y++);
	printf(":::..::::::.......::::.......:::::::...::...:::....::..::::..::....::");
	gotoxy(x,y++);	
	
	waitForAnyKey();
	clrscr(); //clear the console
	return;
}

void gameOverScreen(void)
{
	int x = 17, y = 3;
	
	//http://www.network-science.de/ascii/ <- Ascii Art Gen
	
	gotoxy(x,y++);
	printf(":'######::::::'###::::'##::::'##:'########:\n");
	gotoxy(x,y++);
	printf("'##... ##::::'## ##::: ###::'###: ##.....::\n");
	gotoxy(x,y++);
	printf(" ##:::..::::'##:. ##:: ####'####: ##:::::::\n");
	gotoxy(x,y++);
	printf(" ##::'####:'##:::. ##: ## ### ##: ######:::\n");
	gotoxy(x,y++);
	printf(" ##::: ##:: #########: ##. #: ##: ##...::::\n");
	gotoxy(x,y++);
	printf(" ##::: ##:: ##.... ##: ##:.:: ##: ##:::::::\n");
	gotoxy(x,y++);
	printf(". ######::: ##:::: ##: ##:::: ##: ########:\n");
	gotoxy(x,y++);
	printf(":......::::..:::::..::..:::::..::........::\n");
	gotoxy(x,y++);
	printf(":'#######::'##::::'##:'########:'########::'####:\n");
	gotoxy(x,y++);
	printf("'##.... ##: ##:::: ##: ##.....:: ##.... ##: ####:\n");
	gotoxy(x,y++);
	printf(" ##:::: ##: ##:::: ##: ##::::::: ##:::: ##: ####:\n");
	gotoxy(x,y++);
	printf(" ##:::: ##: ##:::: ##: ######::: ########::: ##::\n");
	gotoxy(x,y++);
	printf(" ##:::: ##:. ##:: ##:: ##...:::: ##.. ##::::..:::\n");
	gotoxy(x,y++);
	printf(" ##:::: ##::. ## ##::: ##::::::: ##::. ##::'####:\n");
	gotoxy(x,y++);
	printf(". #######::::. ###:::: ########: ##:::. ##: ####:\n");
	gotoxy(x,y++);
	printf(":.......::::::...:::::........::..:::::..::....::\n");
	
	waitForAnyKey();
	clrscr(); //clear the console
	return;
}

//Messy, need to clean this function up
void startGame( int snakeXY&#91;&#93;&#91;SNAKE_ARRAY_SIZE&#93;, int foodXY&#91;&#93;, int consoleWidth, int consoleHeight, int snakeLength, int direction, int score, int speed)
{
	int gameOver = 0;
	clock_t endWait;
	
	//CLOCKS_PER_SEC-(n-1)*(CLOCKS_PER_SEC/10)
	int waitMili = CLOCKS_PER_SEC-(speed)*(CLOCKS_PER_SEC/10);	//Sets the correct wait time according to the selected speed
	int tempScore = 10*speed;
	int oldDirection;
	int canChangeDirection = 1;
	//int seconds = 1;

	endWait = clock() + waitMili;

	do
	{
		if(canChangeDirection)
		{
			oldDirection = direction;
			direction = checkKeysPressed(direction);
		}
		
		if(oldDirection != direction)//Temp fix to prevent the snake from colliding with itself
			canChangeDirection = 0;
			
		if(clock() >= endWait) //haha, it moves according to how fast the computer running it is...
		{
			//gotoxy(1,1);
			//printf("%d - %d",clock() , endWait);
			move(snakeXY, snakeLength, direction);
			canChangeDirection = 1;

				
			if(eatFood(snakeXY, foodXY))
			{
				generateFood( foodXY, consoleWidth, consoleHeight, snakeXY, snakeLength); //Generate More Food
				snakeLength++;
				score+=speed;
				//x++;
				//gotoxy(1,1);
				//printf("%d >= %d", 10*speed+score, tempScore);
				if( score >= 10*speed+tempScore)
				//if( 2 >= 2)
				{
					speed++;
					tempScore = score;

					if(speed <= 9)//this needs to be fixed
						waitMili = waitMili - (CLOCKS_PER_SEC/10);
					else
					{
						if(waitMili >= 40) //Maximum Speed (the game has to be beatable)
							waitMili = waitMili - (CLOCKS_PER_SEC/200);
						
					}
					//level++;
					//gotoxy(1,2);
					//printf("    ");
					//gotoxy(1,2);
					//printf("%d",waitMili);
					//x = 0;
				}
				
				refreshInfoBar(score, speed);
			}
			
			endWait = clock() + waitMili; //TEMP FIX, NEED TO FIND A WAY TO RESET CLOCK().. Na, seems to work fine this way...
		}
		
		gameOver = collisionDetection(snakeXY, consoleWidth, consoleHeight, snakeLength);

		if(snakeLength >= SNAKE_ARRAY_SIZE-5) //Just to make sure it doesn't get longer then the array size & crash
		{
			gameOver = 2;//You Win! <- doesn't seem to work - NEED TO FIX/TEST THIS
			score+=1500; //When you win you get an extra 1500 points!!!
		}
		
	} while (!gameOver);
	
	switch(gameOver)
	{
		case 1:
			printf("\7"); //Beep
			printf("\7"); //Beep

			gameOverScreen();

			break;
		case 2:
			youWinScreen();
			break;
	}
	
	if(score >= getLowestScore() && score != 0)
	{
		inputScore(score);
		displayHighScores();
	}
	
	return;
}

void loadEnviroment(int consoleWidth, int consoleHeight)//This can be done in a better way... FIX ME!!!! Also i think it doesn't work properly in ubuntu <- Fixed
{
	int i;
	int x = 1, y = 1;
	int rectangleHeight = consoleHeight - 4;
	clrscr(); //clear the console
	
	gotoxy(x,y); //Top left corner
	
	for (; y < rectangleHeight; y++)
	{
		gotoxy(x, y); //Left Wall 
		printf("%c",WALL);
		
		gotoxy(consoleWidth, y); //Right Wall
		printf("%c",WALL);
	}
	
	y = 1;
	for (; x < consoleWidth+1; x++)
	{
		gotoxy(x, y); //Left Wall 
		printf("%c",WALL);
		
		gotoxy(x, rectangleHeight); //Right Wall
		printf("%c",WALL);
	}
	
/*
	for (i = 0; i < 80; i++)
	{
		printf("%c",WALL);
	}
	
	for (i = 0; i < 17; i++)
	{
		printf("%c\n",WALL);
	}

	for (i = 0; i < 21; i++)
	{
		printf("%c\n",WALL);
		gotoxy(80,i);
	}
	
	for (i = 0; i < 81; i++)
	{
		printf("%c",WALL);
	}	
*/	
	return;
}

void loadSnake(int snakeXY&#91;&#93;&#91;SNAKE_ARRAY_SIZE&#93;, int snakeLength)
{
	int i;
	/*
	First off, The snake doesn't actually have enough XY coordinates (only 1 - the starting location), thus we use
	these XY coordinates to "create" the other coordinates. For this we can actually use the function used to move the snake.
	This helps create a "whole" snake instead of one "dot", when someone starts a game.
	*/
	//moveSnakeArray(snakeXY, snakeLength); //One thing to note ATM, the snake starts of one coordinate to whatever direction it's pointing...
	
	//This should print out a snake 😛
	for (i = 0; i < snakeLength; i++)
	{
		gotoxy(snakeXY&#91;0&#93;&#91;i&#93;, snakeXY&#91;1&#93;&#91;i&#93;);
		printf("%c", SNAKE_BODY); //Meh, at some point I should make it so the snake starts off with a head...
	}
	
	return;
}

/* NOTE, This function will only work if the snakes starting direction is left!!!! 
Well it will work, but the results wont be the ones expected.. I need to fix this at some point.. */
void prepairSnakeArray(int snakeXY&#91;&#93;&#91;SNAKE_ARRAY_SIZE&#93;, int snakeLength)
{
	int i, x;
	int snakeX = snakeXY&#91;0&#93;&#91;0&#93;;
	int snakeY = snakeXY&#91;1&#93;&#91;0&#93;;
	
	// this is used in the function move.. should maybe create a function for it...
	/*switch(direction)
	{
		case DOWN_ARROW:
			snakeXY&#91;1&#93;&#91;0&#93;++;
			break;
		case RIGHT_ARROW:
			snakeXY&#91;0&#93;&#91;0&#93;++;
			break;
		case UP_ARROW:
			snakeXY&#91;1&#93;&#91;0&#93;--;
			break;
		case LEFT_ARROW:
			snakeXY&#91;0&#93;&#91;0&#93;--;
			break;			
	}
	*/
	
	
	for(i = 1; i <= snakeLength; i++)
	{
		snakeXY&#91;0&#93;&#91;i&#93; = snakeX + i;
		snakeXY&#91;1&#93;&#91;i&#93; = snakeY;
	}
	
	return;
}

//This function loads the enviroment, snake, etc
void loadGame(void)
{
	int snakeXY&#91;2&#93;&#91;SNAKE_ARRAY_SIZE&#93;; //Two Dimentional Array, the first array is for the X coordinates and the second array for the Y coordinates
	
	int snakeLength = 4; //Starting Length
	
	int direction = LEFT_ARROW; //DO NOT CHANGE THIS TO RIGHT ARROW, THE GAME WILL INSTANTLY BE OVER IF YOU DO!!! <- Unless the prepairSnakeArray function is changed to take into account the direction....
	
	int foodXY&#91;&#93; = {5,5};// Stores the location of the food
	
	int score = 0;
	//int level = 1;
	
	//Window Width * Height - at some point find a way to get the actual dimensions of the console... <- Also somethings that display dont take this dimentions into account.. need to fix this...
	int consoleWidth = 80;
	int consoleHeight = 25;
	
	int speed = getGameSpeed();
	
	//The starting location of the snake
	snakeXY&#91;0&#93;&#91;0&#93; = 40; 
	snakeXY&#91;1&#93;&#91;0&#93; = 10;
	
	loadEnviroment(consoleWidth, consoleHeight); //borders
	prepairSnakeArray(snakeXY, snakeLength);
	loadSnake(snakeXY, snakeLength);
	generateFood( foodXY, consoleWidth, consoleHeight, snakeXY, snakeLength);
	refreshInfoBar(score, speed); //Bottom info bar. Score, Level etc
	startGame(snakeXY, foodXY, consoleWidth, consoleHeight, snakeLength, direction, score, speed);

	return;
}

//**************MENU STUFF**************//

int menuSelector(int x, int y, int yStart)
{
	char key;
	int i = 0;
	x = x - 2;
	gotoxy(x,yStart);
	
	printf(">");
	
	gotoxy(1,1);

	
	do
	{
		key = waitForAnyKey();
		//printf("%c %d", key, (int)key);
		if ( key == (char)UP_ARROW )
		{
			gotoxy(x,yStart+i);
			printf(" ");
			
			if (yStart >= yStart+i ) 
				i = y - yStart - 2;
			else
				i--;
			gotoxy(x,yStart+i);
			printf(">");
		}
		else
			if ( key == (char)DOWN_ARROW )
			{
				gotoxy(x,yStart+i);
				printf(" ");
				
				if (i+2 >= y - yStart ) 
					i = 0;
				else
					i++;
				gotoxy(x,yStart+i);
				printf(">");				
			}	
			//gotoxy(1,1);
			//printf("%d", key);
	} while(key != (char)ENTER_KEY); //While doesn't equal enter... (13 ASCII code for enter) - note ubuntu is 10
	return(i);
}

void welcomeArt(void)
{
	clrscr(); //clear the console
	//Ascii art reference: http://www.chris.com/ascii/index.php?art=animals/reptiles/snakes
	printf("\n");	
	printf("\t\t    _________         _________ 			\n");	
	printf("\t\t   /         \\       /         \\ 			\n");	
	printf("\t\t  /  /~~~~~\\  \\     /  /~~~~~\\  \\ 			\n");	
	printf("\t\t  |  |     |  |     |  |     |  | 			\n");		
	printf("\t\t  |  |     |  |     |  |     |  | 			\n");
	printf("\t\t  |  |     |  |     |  |     |  |         /	\n");
	printf("\t\t  |  |     |  |     |  |     |  |       //	\n");
	printf("\t\t (o  o)    \\  \\_____/  /     \\  \\_____/ / 	\n");
	printf("\t\t  \\__/      \\         /       \\        / 	\n");
	printf("\t\t    |        ~~~~~~~~~         ~~~~~~~~ 		\n");
	printf("\t\t    ^											\n");
	printf("\t		Welcome To The Snake Game!			\n");
	printf("\t			    Press Any Key To Continue...	\n");			
	printf("\n");
	
	waitForAnyKey();
	return;
}

void controls(void)
{
	int x = 10, y = 5;
	clrscr(); //clear the console
	gotoxy(x,y++);
	printf("Controls\n");
	gotoxy(x++,y++);
	printf("Use the following arrow keys to direct the snake to the food: ");
	gotoxy(x,y++);
	printf("Right Arrow");
	gotoxy(x,y++);
	printf("Left Arrow");
	gotoxy(x,y++);
	printf("Top Arrow");
	gotoxy(x,y++);
	printf("Bottom Arrow");
	gotoxy(x,y++);
	gotoxy(x,y++);
	printf("P & Esc pauses the game.");
	gotoxy(x,y++);
	gotoxy(x,y++);
	printf("Press any key to continue...");
	waitForAnyKey();
	return;
}

void exitYN(void)
{
	char pressed;
	gotoxy(9,8);
	printf("Are you sure you want to exit(Y/N)\n");
	
	do
	{
		pressed = waitForAnyKey();
		pressed = tolower(pressed);
	} while (!(pressed == 'y' || pressed == 'n'));
	
	if (pressed == 'y')
	{
		clrscr(); //clear the console
		exit(1);
	}
	return;
}

int mainMenu(void)
{
	int x = 10, y = 5;
	int yStart = y;
	
	int selected;
	
	clrscr(); //clear the console
	//Might be better with arrays of strings???
	gotoxy(x,y++);
	printf("New Game\n");
	gotoxy(x,y++);
	printf("High Scores\n");
	gotoxy(x,y++);
	printf("Controls\n");
	gotoxy(x,y++);
	printf("Exit\n");
	gotoxy(x,y++);

	selected = menuSelector(x, y, yStart);

	return(selected);
}

//**************END MENU STUFF**************//

int main() //Need to fix this up
{

	welcomeArt();
	
	do
	{	
		switch(mainMenu())
		{
			case 0:
				loadGame();
				break;
			case 1:
				displayHighScores();
				break;	
			case 2:
				controls();
				break;		
			case 3:
				exitYN(); 
				break;			
		}		
	} while(1);	//
	
	return(0);
}

Call Graph: