//---------------------------------------------------------------------------------------------
#include <stdio.h>
#include <system.h>
#include <io.h>
#include <sys/time.h>
#include "begin.h"
#include "intermission.h"
#include "death.h"
//---------------------------------------------------------------------------------------------
#define IOWR_VGA(base,offset,data) IOWR_16DIRECT(base,(offset)*2,data)
#define IORD_VGA(base,offset) IORD_16DIRECT(base,(offset)*2)
#define PAC_V 1
#define GHOST_V 1
#define PAC_R 9
#define GHOST_R 9

#define INITIAL_GAME_SPEED 5000
#define TOTAL_PACDOT_NUM 240
#define INITIAL_PACX 14
#define INITIAL_PACY 23
#define INITIAL_GHOST_X 13
#define INITIAL_GHOST_Y 14
#define INITIAL_PAC_LIFE 3
#define PAC_MOUTH_SPEED 15
#define PAC_DIE_TIME 120
#define GHOST_FLOAT_SPEED 20
#define GHOST_WEAK_TIME 600
#define GHOST_BLINK_TIME 30
#define AUDIO_TIME 1000
//---------------------------------------------------------------------------------------------
int gameSpeed = INITIAL_GAME_SPEED;
int pacX[2] = {INITIAL_PACX, INITIAL_PACX};
int pacY[2] = {INITIAL_PACY, INITIAL_PACY};
int pacImgX[2] = {203 + 9 * INITIAL_PACX, 203 + 9 * INITIAL_PACX};
int pacImgY[2] = {105 + 9 * (INITIAL_PACY - 6), 105 + 9 * INITIAL_PACY};
int pacLife[2] = {INITIAL_PAC_LIFE, INITIAL_PAC_LIFE};
int pacMove[2] = {1, 3};
int pacMouth[2] = {0, 0};
int pacDie[2] = {0, 0};
int ghostX[3];
int ghostY[3];
int ghostV[3] = {GHOST_V, GHOST_V, GHOST_V};
int ghostImgX[3];
int ghostImgY[3];
int ghostOption[3][4];
int ghostMove[3];
int ghostOperation[3];
int ghostFloat[] = {0, 0, 0};
int ghostWeakMode[] = {0, 0, 0};
int ghostEaten[] = {0, 0, 0};
int ghostModeSwitchTime[] = {800, 1000, 1200};
int ghostMode[] = {0, 0, 0};
int pacDot = TOTAL_PACDOT_NUM;
int score = 0;
int level = 1;
unsigned keyBoardCode[2] = {0, 0};
int gameMode = 0;	//1P or 2P
//---------------------------------------------------------------------------------------------
//struct timeval tv;
//struct timeval curtime;
int audioFlag = 0;
//---------------------------------------------------------------------------------------------
void createPac(int pacNum) {
	keyBoardCode[pacNum] = 0;
	if(pacNum <= gameMode && pacLife[pacNum] > 0) {
		pacX[pacNum] = INITIAL_PACX;
		pacY[pacNum] = INITIAL_PACY - 6 * pacNum;
		pacImgX[pacNum] = 203 + 9 * INITIAL_PACX;
		pacImgY[pacNum] = 105 + 9 * pacY[pacNum];
		pacDie[pacNum] = 0;
		pacMove[pacNum] = 1;
	}
	else {
		pacX[pacNum] = 0;
		pacY[pacNum] = 0;
		pacImgX[pacNum] = 0;
		pacImgY[pacNum] = 0;
		pacDie[pacNum] = 0;
		pacMove[pacNum] = 1;
		IOWR_VGA(VGA_BASE, 0 + 16 * pacNum, 0);
		IOWR_VGA(VGA_BASE, 1 + 16 * pacNum, 0);
		IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 28);
	}
}
//---------------------------------------------------------------------------------------------
void createGhost() {
	int i;
	for(i = 0; i < 3; i++) {
		ghostX[i] = INITIAL_GHOST_X;
		ghostY[i] = INITIAL_GHOST_Y;
		ghostImgX[i] = 203 + 9 * ghostX[i];
		ghostImgY[i] = 105 + 9 * ghostY[i];
		ghostMove[i] = 1;
		ghostV[i] = GHOST_V;
		ghostWeakMode[i] = 0;
		ghostEaten[i] = 0;
		ghostMode[i] = 0;
	}
}
//---------------------------------------------------------------------------------------------
void initializeStage() {
	if(level < 10) {
		IOWR_VGA(VGA_BASE, 12, 882);	//write level to VGA
		IOWR_VGA(VGA_BASE, 13, 38 + level++);
		if(level > 1)
			gameSpeed -= 250;
	}
	IOWR_VGA(VGA_BASE, 14, 3);	//display level
	audioPlaySDRAM(1);
	IOWR_VGA(VGA_BASE, 14, 0);	//create maze
	pacDot = TOTAL_PACDOT_NUM;	//initialize game parameters
	createPac(0);
	createPac(1);
	createGhost();
}
//---------------------------------------------------------------------------------------------
void paintScore() {
	int scoreDigi[4];
	scoreDigi[0] = score / 1000;
	scoreDigi[1] = (score - scoreDigi[0] * 1000) / 100;
	scoreDigi[2] = (score - scoreDigi[0] *  1000 - scoreDigi[1] * 100) / 10;
	scoreDigi[3] = score - scoreDigi[0] *  1000 - scoreDigi[1] * 100 - scoreDigi[2] *  10;
	int i;
	for(i = 0; i < 4; i++) {
		IOWR_VGA(VGA_BASE, 12, 873 + i);
		IOWR_VGA(VGA_BASE, 13, 38 + scoreDigi[i]);
	}
}
//---------------------------------------------------------------------------------------------
void paintLife(int pacNum) {
	switch(pacLife[pacNum]) {
	case 3:
		IOWR_VGA(VGA_BASE, 12, 883 + 2 * pacNum);
		IOWR_VGA(VGA_BASE, 13, 50);
		IOWR_VGA(VGA_BASE, 12, 884 + 2 * pacNum);
		IOWR_VGA(VGA_BASE, 13, 50);
		break;
	case 2:
		IOWR_VGA(VGA_BASE, 12, 883 + 2 * pacNum);
		IOWR_VGA(VGA_BASE, 13, 28);
		IOWR_VGA(VGA_BASE, 12, 884 + 2 * pacNum);
		IOWR_VGA(VGA_BASE, 13, 50);
		break;
	case 1:
	case 0:
		IOWR_VGA(VGA_BASE, 12, 883 + 2 * pacNum);
		IOWR_VGA(VGA_BASE, 13, 28);
		IOWR_VGA(VGA_BASE, 12, 884 + 2 * pacNum);
		IOWR_VGA(VGA_BASE, 13, 28);
		break;
	default:
		break;
	}
}
//---------------------------------------------------------------------------------------------
void paintPac(int pacNum) {
	IOWR_VGA(VGA_BASE, 0 + 16 * pacNum, pacImgX[pacNum]);
	IOWR_VGA(VGA_BASE, 1 + 16 * pacNum, pacImgY[pacNum]);
	if(pacDie[pacNum] == 0) {
		if(pacMouth[pacNum] < PAC_MOUTH_SPEED / 3) {
			switch(pacMove[pacNum]) {
			case 1:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 1);
				break;
			case 2:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 3);
				break;
			case 3:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 5);
				break;
			case 4:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 7);
				break;
			default:
				break;
			}
		}
		if(pacMouth[pacNum] < 2 * PAC_MOUTH_SPEED / 3) {
			switch(pacMove[pacNum]) {
			case 1:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 2);
				break;
			case 2:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 4);
				break;
			case 3:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 6);
				break;
			case 4:
				IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 8);
				break;
			default:
				break;
			}
		}
		else
			IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 0);
		pacMouth[pacNum] = (pacMouth[pacNum] + 1) % PAC_MOUTH_SPEED;
	}
	else {
		if(pacDie[pacNum] < PAC_DIE_TIME / 3)
			IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 17);
		else if(pacDie[pacNum] < 2 * PAC_DIE_TIME / 3)
			IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 18);
		else
			IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 19);
		pacDie[pacNum] = (pacDie[pacNum] + 1) % PAC_DIE_TIME;
		if(pacDie[pacNum] == 0) {
			if(pacLife[0] + pacLife[1] > 0) {
				createPac(pacNum);
				createGhost();
			}
			else  {
				printf("[Game Over]\n");
				printf("[Your Score] %d Press any key to continue...\n", score);
			}
		}
	}

	if(pacLife[pacNum] == 0) {
		pacX[pacNum] = 0;
		pacY[pacNum] = 0;
		pacImgX[pacNum] = 0;
		pacImgY[pacNum] = 0;
		IOWR_VGA(VGA_BASE, 0 + 16 * pacNum, pacImgX[pacNum]);
		IOWR_VGA(VGA_BASE, 1 + 16 * pacNum, pacImgY[pacNum]);
		IOWR_VGA(VGA_BASE, 2 + 17 * pacNum, 28);
	}

	paintScore();
	paintLife(pacNum);
	if(audioFlag > 0)
		audioFlag = (audioFlag + 1) % AUDIO_TIME;
}
//---------------------------------------------------------------------------------------------
void paintGhost(int ghostNum) {
	IOWR_VGA(VGA_BASE, 3 + 3 * ghostNum, ghostImgX[ghostNum]);
	IOWR_VGA(VGA_BASE, 4 + 3 * ghostNum, ghostImgY[ghostNum]);
	//ghost is in house
	if(ghostImgX[ghostNum] >= 302 && ghostImgX[ghostNum] <= 306
			&& ghostY[ghostNum] >= 13 && ghostY[ghostNum] <= 15) {
		IOWR_VGA(VGA_BASE, 3 + 3 * ghostNum, 306);
		if(ghostMove[ghostNum] == 3)
			ghostImgX[ghostNum] = 302;
	}
	if(ghostImgX[ghostNum] >= 343 && ghostImgX[ghostNum] <= 347
			&& ghostY[ghostNum] >= 13 && ghostY[ghostNum] <= 15) {
		IOWR_VGA(VGA_BASE, 3 + 3 * ghostNum, 343);
		if(ghostMove[ghostNum] == 1)
			ghostImgX[ghostNum] = 347;
	}
	if(ghostImgY[ghostNum] >= 236 && ghostImgY[ghostNum] <= 240
			&& ghostX[ghostNum] >= 11 && ghostX[ghostNum] <= 16) {
		IOWR_VGA(VGA_BASE, 4 + 3 * ghostNum, 236);
		if(ghostMove[ghostNum] == 2)
			ghostImgY[ghostNum] = 240;
	}
	if(ghostImgY[ghostNum] >= 222 && ghostImgY[ghostNum] <= 227
			&& (ghostX[ghostNum] == 11 || ghostX[ghostNum] == 12
					||	ghostX[ghostNum] == 15 || ghostX[ghostNum] == 16)) {
		IOWR_VGA(VGA_BASE, 4 + 3 * ghostNum, 227);
		if(ghostMove[ghostNum] == 4)
			ghostImgY[ghostNum] = 222;
	}

	if(ghostWeakMode[ghostNum] == 0) {
		if(ghostFloat[ghostNum] < GHOST_FLOAT_SPEED / 2)
			IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 7 + ghostMove[ghostNum] * 2);
		else
			IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 8 + ghostMove[ghostNum] * 2);
	}
	else {
		if(ghostWeakMode[ghostNum] < 3 * GHOST_WEAK_TIME / 4) {
			if(ghostFloat[ghostNum] < GHOST_FLOAT_SPEED / 2)
				IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 24);
			else
				IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 25);
		}
		else
		{
			if(ghostWeakMode[ghostNum] % GHOST_BLINK_TIME < GHOST_BLINK_TIME / 2) {
				if(ghostFloat[ghostNum] < GHOST_FLOAT_SPEED / 2)
					IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 26);
				else
					IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 27);
			}
			else {
				if(ghostFloat[ghostNum] < GHOST_FLOAT_SPEED / 2)
					IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 24);
				else
					IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 25);
			}
		}
		ghostWeakMode[ghostNum] = (ghostWeakMode[ghostNum] + 1) % GHOST_WEAK_TIME;
	}

	if(ghostEaten[ghostNum] == 1) {
		IOWR_VGA(VGA_BASE, 5 + 3 * ghostNum, 19 + ghostMove[ghostNum]);
	}
	ghostFloat[ghostNum] = (ghostFloat[ghostNum] + 1) % GHOST_FLOAT_SPEED;
}
//---------------------------------------------------------------------------------------------
void movePac(int pacNum) {
	if(pacLife[pacNum] > 0 && pacDie[pacNum] == 0) {
		int nextPacX;
		int nextPacY;
		unsigned nextContent;
		if(pacNum == 0) {
			switch(keyBoardCode[pacNum])
			{
			case 116:	//right
				if(pacImgX[pacNum] < 446) {
					nextPacX = (pacImgX[pacNum] + PAC_R - 203) / 9;
					IOWR_VGA(VGA_BASE, 15, pacY[pacNum] * 28 + nextPacX);
					nextContent = IORD_VGA(VGA_BASE, 0);
					if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
						pacImgX[pacNum] += PAC_V;
						pacImgY[pacNum] = pacImgY[pacNum] - (pacImgY[pacNum] - 105) % 9;
						pacMove[pacNum] = 1;
					}
					else if(pacMove[pacNum] != 1) {
						switch(pacMove[pacNum]) {
						case 2:
							keyBoardCode[pacNum] = 114;
							break;
						case 3:
							keyBoardCode[pacNum] = 107;
							break;
						case 4:
							keyBoardCode[pacNum] = 117;
							break;
						}
					}
				}
				else {
					pacImgX[pacNum] = 203;
				}
				break;
			case 114:	//down
				nextPacY = (pacImgY[pacNum] + PAC_R - 105) / 9;
				IOWR_VGA(VGA_BASE, 15, nextPacY * 28 + pacX[pacNum]);
				nextContent = IORD_VGA(VGA_BASE, 0);
				if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
					pacImgY[pacNum] += PAC_V;
					pacImgX[pacNum] = pacImgX[pacNum] - (pacImgX[pacNum] - 203) % 9;
					pacMove[pacNum] = 2;
				}
				else if(pacMove[pacNum] != 2) {
					switch(pacMove[pacNum]) {
					case 1:
						keyBoardCode[pacNum] = 116;
						break;
					case 3:
						keyBoardCode[pacNum] = 107;
						break;
					case 4:
						keyBoardCode[pacNum] = 117;
						break;
					}
				}
				break;
			case 107:	//left
				if(pacImgX[pacNum] >= 203) {
					nextPacX = (pacImgX[pacNum] - PAC_V - 203) / 9;
					IOWR_VGA(VGA_BASE, 15, pacY[pacNum] * 28 + nextPacX);
					nextContent = IORD_VGA(VGA_BASE, 0);
					if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
						pacImgX[pacNum] -= PAC_V;
						pacImgY[pacNum] = pacImgY[pacNum] - (pacImgY[pacNum] - 105) % 9;
						pacMove[pacNum] = 3;
					}
					else if(pacMove[pacNum] != 3) {
						switch(pacMove[pacNum]) {
						case 1:
							keyBoardCode[pacNum] = 116;
							break;
						case 2:
							keyBoardCode[pacNum] = 114;
							break;
						case 4:
							keyBoardCode[pacNum] = 117;
							break;
						}
					}
				}
				else {
					pacImgX[pacNum] = 446;
				}
				break;
			case 117:	//up
				nextPacY = (pacImgY[pacNum] - PAC_V - 105) / 9;
				IOWR_VGA(VGA_BASE, 15, nextPacY * 28 + pacX[pacNum]);
				nextContent = IORD_VGA(VGA_BASE, 0);
				if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
					pacImgY[pacNum] -= PAC_V;
					pacImgX[pacNum] = pacImgX[pacNum] - (pacImgX[pacNum] - 203) % 9;
					pacMove[pacNum] = 4;
				}
				else if(pacMove[pacNum] != 4) {
					switch(pacMove[pacNum]) {
					case 1:
						keyBoardCode[pacNum] = 116;
						break;
					case 2:
						keyBoardCode[pacNum] = 114;
						break;
					case 3:
						keyBoardCode[pacNum] = 107;
						break;
					}
				}
				break;
			case 118:	//pause
				printf("[Game Paused] Press any key to continue...\n");
				while(keyBoardCode[pacNum] == 118) {
					while(!IORD_8DIRECT(KEYBOARD_BASE,0));
					keyBoardCode[pacNum] = IORD_8DIRECT(KEYBOARD_BASE,1);
				}
				if(keyBoardCode[pacNum] != 0) {
					switch(pacMove[pacNum]) {
					case 1:
						keyBoardCode[pacNum] = 116;
						break;
					case 2:
						keyBoardCode[pacNum] = 114;
						break;
					case 3:
						keyBoardCode[pacNum] = 107;
						break;
					case 4:
						keyBoardCode[pacNum] = 117;
						break;
					default:
						break;
					}
				}
				break;
			default:
				break;
			}
		}
		else
		{
			switch(keyBoardCode[pacNum])
			{
			case 35:	//right
				if(pacImgX[pacNum] < 446) {
					nextPacX = (pacImgX[pacNum] + PAC_R - 203) / 9;
					IOWR_VGA(VGA_BASE, 15, pacY[pacNum] * 28 + nextPacX);
					nextContent = IORD_VGA(VGA_BASE, 0);
					if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
						pacImgX[pacNum] += PAC_V;
						pacImgY[pacNum] = pacImgY[pacNum] - (pacImgY[pacNum] - 105) % 9;
						pacMove[pacNum] = 1;
					}
					else if(pacMove[pacNum] != 1) {
						switch(pacMove[pacNum]) {
						case 2:
							keyBoardCode[pacNum] = 27;
							break;
						case 3:
							keyBoardCode[pacNum] = 28;
							break;
						case 4:
							keyBoardCode[pacNum] = 29;
							break;
						}
					}
				}
				else {
					pacImgX[pacNum] = 203;
				}
				break;
			case 27:	//down
				nextPacY = (pacImgY[pacNum] + PAC_R - 105) / 9;
				IOWR_VGA(VGA_BASE, 15, nextPacY * 28 + pacX[pacNum]);
				nextContent = IORD_VGA(VGA_BASE, 0);
				if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
					pacImgY[pacNum] += PAC_V;
					pacImgX[pacNum] = pacImgX[pacNum] - (pacImgX[pacNum] - 203) % 9;
					pacMove[pacNum] = 2;
				}
				else if(pacMove[pacNum] != 2) {
					switch(pacMove[pacNum]) {
					case 1:
						keyBoardCode[pacNum] = 35;
						break;
					case 3:
						keyBoardCode[pacNum] = 28;
						break;
					case 4:
						keyBoardCode[pacNum] = 29;
						break;
					}
				}
				break;
			case 28:	//left
				if(pacImgX[pacNum] >= 203) {
					nextPacX = (pacImgX[pacNum] - PAC_V - 203) / 9;
					IOWR_VGA(VGA_BASE, 15, pacY[pacNum] * 28 + nextPacX);
					nextContent = IORD_VGA(VGA_BASE, 0);
					if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
						pacImgX[pacNum] -= PAC_V;
						pacImgY[pacNum] = pacImgY[pacNum] - (pacImgY[pacNum] - 105) % 9;
						pacMove[pacNum] = 3;
					}
					else if(pacMove[pacNum] != 3) {
						switch(pacMove[pacNum]) {
						case 1:
							keyBoardCode[pacNum] = 35;
							break;
						case 2:
							keyBoardCode[pacNum] = 27;
							break;
						case 4:
							keyBoardCode[pacNum] = 29;
							break;
						}
					}
				}
				else {
					pacImgX[pacNum] = 446;
				}
				break;
			case 29:	//up
				nextPacY = (pacImgY[pacNum] - PAC_V - 105) / 9;
				IOWR_VGA(VGA_BASE, 15, nextPacY * 28 + pacX[pacNum]);
				nextContent = IORD_VGA(VGA_BASE, 0);
				if(nextContent == 26 || nextContent == 27 || nextContent == 28) {
					pacImgY[pacNum] -= PAC_V;
					pacImgX[pacNum] = pacImgX[pacNum] - (pacImgX[pacNum] - 203) % 9;
					pacMove[pacNum] = 4;
				}
				else if(pacMove[pacNum] != 4) {
					switch(pacMove[pacNum]) {
					case 1:
						keyBoardCode[pacNum] = 35;
						break;
					case 2:
						keyBoardCode[pacNum] = 27;
						break;
					case 3:
						keyBoardCode[pacNum] = 28;
						break;
					}
				}
				break;
			case 118:	//pause
				printf("[Game Paused] Press any key to continue...\n");
				while(keyBoardCode[pacNum] == 118) {
					while(!IORD_8DIRECT(KEYBOARD_BASE,0));
					keyBoardCode[pacNum] = IORD_8DIRECT(KEYBOARD_BASE,1);
				}
				if(keyBoardCode[pacNum] != 0) {
					switch(pacMove[pacNum]) {
					case 1:
						keyBoardCode[pacNum] = 35;
						break;
					case 2:
						keyBoardCode[pacNum] = 27;
						break;
					case 3:
						keyBoardCode[pacNum] = 28;
						break;
					case 4:
						keyBoardCode[pacNum] = 29;
						break;
					default:
						break;
					}
				}
				break;
			default:
				break;
			}
		}

		pacX[pacNum] = (pacImgX[pacNum] - 203) / 9;
		pacY[pacNum] = (pacImgY[pacNum] - 105) / 9;

		unsigned mazeContent;
		IOWR_VGA(VGA_BASE, 15, pacY[pacNum] * 28 + pacX[pacNum]);
		mazeContent = IORD_VGA(VGA_BASE, 0);

		switch(mazeContent) {
		case 26:	// pacDot
			IOWR_VGA(VGA_BASE, 12, pacY[pacNum] * 28 + pacX[pacNum]);
			IOWR_VGA(VGA_BASE, 13, 28);
			pacDot--;
			score += 1;
			audioPlayROM(0);
			break;
		case 27:	// energizer
			IOWR_VGA(VGA_BASE, 12, pacY[pacNum] * 28 + pacX[pacNum]);
			IOWR_VGA(VGA_BASE, 13, 28);
			int i;
			for(i = 0; i < 3; i++) {
				if(ghostEaten[i] == 0)
				ghostWeakMode[i] = 1;
			}
			score += 10;
			audioPlayROM(1);
			break;
		default:
			break;
		}
	}

	paintPac(pacNum);
}
//---------------------------------------------------------------------------------------------
int isAvailable(int ghostNum, int direction) {
	int nextX;
	int nextY;
	unsigned nextContent;
	switch(direction) {
	case 1:	//right
		if(ghostImgX[ghostNum] < 446) {
			nextX = (ghostImgX[ghostNum] + GHOST_R - 203) / 9;
			if((ghostImgY[ghostNum] - 105) % 9 == 0) {
				IOWR_VGA(VGA_BASE, 15, ghostY[ghostNum] * 28 + nextX);
				nextContent = IORD_VGA(VGA_BASE, 0);
				if(nextContent == 26 || nextContent == 27 || nextContent == 28
						|| nextContent == 17 || nextContent == 18)
					return 1;
			}
		}
		else
			return 1;
		break;
	case 2:	//down
		nextY = (ghostImgY[ghostNum] + GHOST_R - 105) / 9;
		if((ghostImgX[ghostNum] - 203) % 9 == 0) {
			IOWR_VGA(VGA_BASE, 15, nextY * 28 + ghostX[ghostNum]);
			nextContent = IORD_VGA(VGA_BASE, 0);
			if(nextContent == 26 || nextContent == 27 || nextContent == 28
					|| nextContent == 17 || nextContent == 18)
				return 1;
		}
		break;
	case 3:	//left
		if(ghostImgX[ghostNum] >= 203) {
			nextX = (ghostImgX[ghostNum] - GHOST_V - 203) / 9;
			if((ghostImgY[ghostNum] - 105) % 9 == 0) {
				IOWR_VGA(VGA_BASE, 15, ghostY[ghostNum] * 28 + nextX);
				nextContent = IORD_VGA(VGA_BASE, 0);
				if(nextContent == 26 || nextContent == 27 || nextContent == 28
						|| nextContent == 17 || nextContent == 18)
					return 1;
			}
		}
		else
			return 1;
		break;
	case 4:	//up
		nextY = (ghostImgY[ghostNum] - GHOST_V - 105) / 9;
		if((ghostImgX[ghostNum] - 203) % 9 == 0) {
			IOWR_VGA(VGA_BASE, 15, nextY * 28 + ghostX[ghostNum]);
			nextContent = IORD_VGA(VGA_BASE, 0);
			if(nextContent == 26 || nextContent == 27 || nextContent == 28
					|| nextContent == 17 || nextContent == 18)
				return 1;
		}
		break;
	default:
		break;
	}
	return 0;
}
//---------------------------------------------------------------------------------------------
int randomMode(int ghostNum) {
	int optionNum = 0;
	//compute all the possible directions at time
	int i;
	for(i = 0; i < 4; i++) {
		ghostOption[ghostNum][i] = isAvailable(ghostNum, i + 1);
		optionNum += ghostOption[ghostNum][i];
	}

	int choice = 0;
	switch(optionNum) {
		case 0:	//no available direction
			return 0;
		case 1:	//choose the only available direction
			for(i = 0; i < 4; i++) {
				if(ghostOption[ghostNum][i] == 1)
					return i + 1;
			}
			break;
			//choose direction according to current moving direction & random choice
		case 2:
		switch(ghostMove[ghostNum]) {
			case 1:
				if(ghostOption[ghostNum][0] == 1)
					return 1;
				else if(ghostOption[ghostNum][1] == 1)
					return 2;
				else
					return 4;
			case 2:
				if(ghostOption[ghostNum][0] == 1)
					return 1;
				else if(ghostOption[ghostNum][1] == 1)
					return 2;
				else
					return 3;
			case 3:
				if(ghostOption[ghostNum][1] == 1)
					return 2;
				else if(ghostOption[ghostNum][2] == 1)
					return 3;
				else
					return 4;
			case 4:
				if(ghostOption[ghostNum][0] == 1)
					return 1;
				else if(ghostOption[ghostNum][2] == 1)
					return 3;
				else
					return 4;
			default:
				break;
			}
		break;
		case 3:
			choice = rand() % 2;
			int badDirection = 0;
			for(i = 0; i < 4; i++) {
				if(ghostOption[ghostNum][i] == 0)
					badDirection = i + 1;
			}
			switch(ghostMove[ghostNum]) {
			case 1:
				if(badDirection == 1) {
					if(choice == 0)
						return 2;
					else
						return 4;
				}
				else if(badDirection == 2) {
					if(choice == 0)
						return 1;
					else
						return 4;
				}
				else {
					if(choice == 0)
						return 1;
					else
						return 2;
				}
			case 2:
				if(badDirection == 1) {
					if(choice == 0)
						return 2;
					else
						return 3;
				}
				else if(badDirection == 2) {
					if(choice == 0)
						return 1;
					else
						return 3;
				}
				else {
					if(choice == 0)
						return 1;
					else
						return 2;
				}
			case 3:
				if(badDirection == 2) {
					if(choice == 0)
						return 3;
					else
						return 4;
				}
				else if(badDirection == 3) {
					if(choice == 0)
						return 2;
					else
						return 4;
				}
				else {
					if(choice == 0)
						return 2;
					else
						return 3;
				}
			case 4:
				if(badDirection == 1) {
					if(choice == 0)
						return 3;
					else
						return 4;
				}
				else if(badDirection == 3) {
					if(choice == 0)
						return 1;
					else
						return 4;
				}
				else {
					if(choice == 0)
						return 1;
					else
						return 3;
				}
			default: break;
			}
			break;
		case 4:
			choice = rand() % 3;
			switch(ghostMove[ghostNum]) {
			case 1:
				if(choice == 0)
					return 1;
				else if(choice == 1)
					return 2;
				else
					return 4;
			case 2:
				if(choice == 0)
					return 1;
				else if(choice == 1)
					return 2;
				else
					return 3;
			case 3:
				if(choice == 0)
					return 2;
				else if(choice == 1)
					return 3;
				else
					return 4;
			case 4:
				if(choice == 0)
					return 1;
				else if(choice == 1)
					return 3;
				else
					return 4;
			default: break;
			}
			break;
			default: break;
	}
	return 0;
}
//---------------------------------------------------------------------------------------------
int chaseMode(int ghostNum, int x, int y) {
	int i;
	for(i = 0; i < 4; i++) {
		ghostOption[ghostNum][i] = isAvailable(ghostNum, i + 1);
	}
	//make choice depending on target position
	if(ghostImgX[ghostNum] > x) {
		if(ghostImgY[ghostNum] > y) {	//ghost is at right-bottom of the target
			if(ghostOption[ghostNum][2] == 1 && ghostOption[ghostNum][3] == 1) {
				if(ghostMove[ghostNum] == 1)
					return 4;
				else if(ghostMove[ghostNum] == 2)
					return 3;
				else {
					int choice = rand() % 2;
					if(choice == 0 && ghostMove[ghostNum] != 1)
						return 3;
					else if(choice == 1 && ghostMove[ghostNum] != 2)
						return 4;
				}
			}
			if(ghostOption[ghostNum][2] == 1 && ghostOption[ghostNum][3] == 0
					&& ghostMove[ghostNum] != 1)
				return 3;
			if(ghostOption[ghostNum][2] == 0 && ghostOption[ghostNum][3] == 1
					&& ghostMove[ghostNum] != 2)
				return 4;
		}
		else if(ghostImgY[ghostNum] < y) {	//ghost is at right-top of the target
			if(ghostOption[ghostNum][1] == 1 && ghostOption[ghostNum][2] == 1) {
				if(ghostMove[ghostNum] == 1)
					return 2;
				else if(ghostMove[ghostNum] == 4)
					return 3;
				else {
					int choice = rand() % 2;
					if(choice == 0 && ghostMove[ghostNum] != 4)
						return 2;
					else if(choice == 1 && ghostMove[ghostNum] != 1)
						return 3;
				}
			}
			if(ghostOption[ghostNum][1] == 1 && ghostOption[ghostNum][2] == 0
					&& ghostMove[ghostNum] != 4)
				return 2;
			if(ghostOption[ghostNum][1] == 0 && ghostOption[ghostNum][2] == 1
					&& ghostMove[ghostNum] != 1)
				return 3;
		}
		else {	//ghost is at right of the target
			if(ghostOption[ghostNum][2] == 1 && ghostMove[ghostNum] != 1)
				return 3;
		}
	}
	else if(ghostImgX[ghostNum] < x) {
		if(ghostImgY[ghostNum] > y) {	//ghost is at left-bottom of the target
			if(ghostOption[ghostNum][0] == 1 && ghostOption[ghostNum][3] == 1) {
				if(ghostMove[ghostNum] == 3)
					return 4;
				else if(ghostMove[ghostNum] == 2)
					return 1;
				else {
					int choice = rand() % 2;
					if(choice == 0 && ghostMove[ghostNum] != 3)
						return 1;
					else if(choice ==1 && ghostMove[ghostNum] != 2)
						return 4;
				}
			}
			if(ghostOption[ghostNum][0] == 1 && ghostOption[ghostNum][3] == 0
					&& ghostMove[ghostNum] != 3)
				return 1;
			if(ghostOption[ghostNum][0] == 0 && ghostOption[ghostNum][3] == 1
					&& ghostMove[ghostNum] != 2)
				return 4;
		}
		else if(ghostImgY[ghostNum] < y) {	//ghost is at left-top of the target
			if(ghostOption[ghostNum][0] == 1 && ghostOption[ghostNum][1] == 1) {
				if(ghostMove[ghostNum] == 4)
					return 1;
				else if(ghostMove[ghostNum] == 3)
					return 2;
				else {
					int choice = rand() % 2;
					if(choice == 0 && ghostMove[ghostNum] != 3)
						return 1;
					else if(choice == 1 && ghostMove[ghostNum] != 4)
						return 2;
				}
			}
			if(ghostOption[ghostNum][0] == 1 && ghostOption[ghostNum][1] == 0
					&& ghostMove[ghostNum] != 3)
				return 1;
			if(ghostOption[ghostNum][0] == 0 && ghostOption[ghostNum][1] == 1
					&& ghostMove[ghostNum] != 4)
				return 2;
		}
		else {	//ghost is at left of the target
			if(ghostOption[ghostNum][0] == 1 && ghostMove[ghostNum] != 3)
				return 1;
		}
	}
	else
	{
		if(ghostImgY[ghostNum] > y) {	//ghost is at bottom of the target
			if(ghostOption[ghostNum][3] == 1 && ghostMove[ghostNum] != 2)
				return 4;
		}
		else if(ghostImgY[ghostNum] < y) {	//ghost is at top of the target
			if(ghostOption[ghostNum][1] == 1 && ghostMove[ghostNum] != 4)
				return 2;
		}
		else {	//ghost reaches target
			return 0;
		}
	}

	if(ghostOption[ghostNum][ghostMove[ghostNum] - 1] == 1)
		return ghostMove[ghostNum];

	return randomMode(ghostNum);
}
//---------------------------------------------------------------------------------------------
void moveGhost(int ghostNum) {
	if(pacDie[0] == 0 && pacDie[1] == 0) {
		if(ghostEaten[ghostNum] == 0) {
		//random or chasing?
			if(ghostWeakMode[ghostNum] == 0) {
				if(gameMode == 0) {
					if(ghostMode[ghostNum] < ghostModeSwitchTime[ghostNum] / 2)
						ghostOperation[ghostNum] = randomMode(ghostNum);
					else
						ghostOperation[ghostNum] = chaseMode(ghostNum, pacImgX[0], pacImgY[0]);
				}
				else
				{
					if(ghostMode[ghostNum] < ghostModeSwitchTime[ghostNum] / 3)
						ghostOperation[ghostNum] = randomMode(ghostNum);
					else if(ghostMode[ghostNum] < 2 * ghostModeSwitchTime[ghostNum] / 3)
						ghostOperation[ghostNum] = chaseMode(ghostNum, pacImgX[0], pacImgY[0]);
					else
						ghostOperation[ghostNum] = chaseMode(ghostNum, pacImgX[1], pacImgY[1]);
				}
				ghostMode[ghostNum] = (ghostMode[ghostNum] + 1) % ghostModeSwitchTime[ghostNum];
			}
			else
				ghostOperation[ghostNum] = randomMode(ghostNum);
		}
		else {
			ghostOperation[ghostNum] = chaseMode(ghostNum, 203 + INITIAL_GHOST_X * 9, 105 + INITIAL_GHOST_Y * 9);
		}

		switch(ghostOperation[ghostNum]) {
		case 1:	//right
			if(ghostImgX[ghostNum] < 446) {
				if(ghostWeakMode[ghostNum] % 2 == 0) {
					ghostImgX[ghostNum] += ghostV[ghostNum];
					ghostMove[ghostNum] = 1;
				}
			}
			else {
				ghostImgX[ghostNum] = 203;
				ghostMove[ghostNum] = 1;
			}
			break;
		case 2:	//down
			if(ghostWeakMode[ghostNum] % 2 == 0) {
				ghostImgY[ghostNum] += ghostV[ghostNum];
				ghostMove[ghostNum] = 2;
			}
			break;
		case 3: //left
			if(ghostImgX[ghostNum] >= 203) {
				if(ghostWeakMode[ghostNum] % 2 == 0) {
					ghostImgX[ghostNum] -= ghostV[ghostNum];
					ghostMove[ghostNum] = 3;
				}
			}
			else {
				ghostImgX[ghostNum] = 446;
				ghostMove[ghostNum] = 3;
			}
			break;
		case 4: //up
			if(ghostWeakMode[ghostNum] % 2 == 0) {
				ghostImgY[ghostNum] -= ghostV[ghostNum];
				ghostMove[ghostNum] = 4;
			}
			break;
		default:
			break;
		}

		ghostX[ghostNum] = (ghostImgX[ghostNum] - 203) / 9;
		ghostY[ghostNum] = (ghostImgY[ghostNum] - 105) / 9;
		int i;
		for(i = 0; i <= gameMode; i++) {
			if(ghostX[ghostNum] == pacX[i] && ghostY[ghostNum] == pacY[i]) {
				if(ghostWeakMode[ghostNum] == 0 && ghostEaten[ghostNum] == 0) {
					pacDie[i] = 1;
					pacLife[i]--;
					audioPlayROM(3);
				}
				else {
					ghostEaten[ghostNum] = 1;
					ghostWeakMode[ghostNum] = 0;
					audioPlayROM(1);
				}
			}
			if(ghostEaten[ghostNum] == 1) {
				if(ghostX[ghostNum] == INITIAL_GHOST_X && ghostY[ghostNum] == INITIAL_GHOST_Y)
					ghostEaten[ghostNum] = 0;
			}
		}
	}

	paintGhost(ghostNum);
}
//---------------------------------------------------------------------------------------------
void startPac() {
	srand(time(NULL));
	printf("[New Game]\n");
	score = 0;
	pacLife[0] = INITIAL_PAC_LIFE;
	if(gameMode == 0) {
		pacLife[1] = 0;
		IOWR_VGA(VGA_BASE, 16, 0);
		IOWR_VGA(VGA_BASE, 17, 0);
		IOWR_VGA(VGA_BASE, 19, 28);
		paintLife(1);
	}
	else
		pacLife[1] = INITIAL_PAC_LIFE;
	level = 1;
	gameSpeed = INITIAL_GAME_SPEED;
	initializeStage();
	unsigned keyC = 0;
	while(pacLife[0] + pacLife[1] > 0) {
		while(!IORD_8DIRECT(KEYBOARD_BASE,0)){
			int i;
			for(i = 0; i < gameSpeed; i++);
			movePac(0);
			if(gameMode == 1)
				movePac(1);
			for(i = 0; i < 3; i++)
				moveGhost(i);
			if(pacDot == 0)
				initializeStage();
			if(pacLife[0] + pacLife[1] == 0)
				break;
		}
		keyC = IORD_8DIRECT(KEYBOARD_BASE,1);
		if(keyC == 116 || keyC == 114 || keyC == 107 || keyC == 117 || keyC == 118)
			keyBoardCode[0] = keyC;
		//printf("%u ", keyBoardCode);
		movePac(0);
		if(gameMode == 1) {
			if(keyC == 35 || keyC == 27 || keyC == 28 || keyC == 29 || keyC == 118)
				keyBoardCode[1] = keyC;
			movePac(1);
		}
		int i;
		for(i = 0; i < 3; i++)
			moveGhost(i);
		if(pacDot == 0)
			initializeStage();
	}
	printf("[Game Over]\n");
	printf("[Your Score] %d Press any key to continue...\n", score);
}
//---------------------------------------------------------------------------------------------
int modeChoose() {
	IOWR_VGA(VGA_BASE, 14, 1);
	IOWR_VGA(VGA_BASE, 12, 0);
	IOWR_VGA(VGA_BASE, 18, 51);
	IOWR_VGA(VGA_BASE, 12, 6);
	IOWR_VGA(VGA_BASE, 18, 28);
	audioPlaySDRAM(0);
	int modeC = 0;
	unsigned modeSelection;
	for(;;) {
		while(!IORD_8DIRECT(KEYBOARD_BASE,0));
		modeSelection = IORD_8DIRECT(KEYBOARD_BASE,1);
		switch(modeSelection) {
		case 117:
			modeC = 0;	//1P
			IOWR_VGA(VGA_BASE, 12, 0);
			IOWR_VGA(VGA_BASE, 18, 51);
			IOWR_VGA(VGA_BASE, 12, 6);
			IOWR_VGA(VGA_BASE, 18, 28);
			break;
		case 114:
			modeC = 1;	//2P
			IOWR_VGA(VGA_BASE, 12, 0);
			IOWR_VGA(VGA_BASE, 18, 28);
			IOWR_VGA(VGA_BASE, 12, 6);
			IOWR_VGA(VGA_BASE, 18, 51);
			break;
		case 90:
			return modeC;
			break;
		default:
			break;
		}
	}
	return 0;
}
//---------------------------------------------------------------------------------------------
void audioPlaySDRAM(int audioNum) {
	long unsigned length;
	int i;
	unsigned indicator;
	switch(audioNum) {
	case 0:
		length = sizeof(begin);
		i = 0;
		indicator = IORD_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 0);
		while (i < length - 1) {
			indicator = IORD_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 0);
			if (indicator == 0x00000000){
				IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 0, begin[i]);
				i++;
			}
		}
		break;
	case 1:
		length = sizeof(intermission);
		i = 0;
		indicator = IORD_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 0);
		while(i < length - 1) {
			indicator = IORD_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 0);
			if (indicator == 0x00000000){
				IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 0, intermission[i]);
				i++;
			}
		}
		break;
	default:
		break;
	}
}
//---------------------------------------------------------------------------------------------
void audioPlayROM(int audioNum) {
	switch(audioNum) {
	case 0:
		if (audioFlag == 0) {
			IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x01);
			IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x02);
		}
		break;
	case 1:
	    audioFlag = 1;
	    //gettimeofday(&tv,NULL);
		IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x03);
		IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x04);
		break;
	case 2:
		audioFlag = 1;
		//gettimeofday(&tv,NULL);
		IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x05);
		IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x06);
		break;
	case 3:
		IOWR_8DIRECT(AUDIO_PLAYER_BUFFER_BASE, 1, 0x07);
		break;
	default:
		break;
	}
}
//---------------------------------------------------------------------------------------------
int main() {
	printf("[Staring PacMan] ...\n");

	for(;;)
	{
		gameMode = modeChoose();
		startPac();
	}
	return 0;
}


