/**
 * Game logic for Othello game
 * @author Tommi Laukkanen 
 * http://www.substanceofcode.com
 */

function OthelloGame() {
		
	this.intTurn = 0;
	this.intTurnNumber = 0;
	this.board = {};
	this.board_save = {};
	this.board_save2 = {};
	this.gameIsNotOver = true;
	this.blnPass = false;
	this.level = 2;
    this.w=10;
    this.bestX = 0;
	this.bestY = 0;   

    this.getBoard = function(x, y) {
        return this.board[x+1+(y+1)*this.w];
    };

	this.initGame = function() {
		//typedef enum {WHITE=0,BLACK=1,BLANK_NEAR=2,BLANK_FAR=3} FIGURE;
		var i,j;
		for (i = 0; i <= 9; i++) {
			for (j = 0; j <= 9; j++) {
				this.board[i + j * this.w] = 3;
			}
		}
		for (i = 3; i <= 6; i++) {
			for (j = 3; j <= 6; j++) {
				this.board[i + j * this.w] = 2;
			}
		}
		this.board[5+4*this.w]=1;
		this.board[4+5*this.w]=1;
		this.board[4+4*this.w]=0;
		this.board[5+5*this.w]=0;
		this.intTurn=1;
		this.level=2;
		this.gameIsNotOver=true;
		this.blnPass=false;
		this.intTurnNumber=0;
	};

	this.getScore = function(intPlayer) {
		var intResult=0;
		for(var i=1; i<9; i++) {
			for(var j=1; j<9; j++) {
				if (this.board[i + j * this.w] == intPlayer) {
					intResult++;
				}
			}
		}
		return intResult;
	};
	
	this.getTurnNumber = function() {
		return this.intTurnNumber;
	};

	this.getGame = function() {
		return this.gameIsNotOver;
	};

	this.setGame = function(flag) {
		this.gameIsNotOver = flag;
	};

	this.getPlayer = function() {
		return this.intTurn;
	};

	this.turn = function(x, y) {
		var intResult=0;
		var blnCompPass = true;
		if(this.intTurn==1) {
			if(x>=0 && y>=0) {
				intResult = this.humanMove(this.intTurn,Number(x)+1,Number(y)+1);
			}
		}
		else {
			intResult = 1;
			blnCompPass = this.cpuMove();
		}

		if(intResult==-1 || intResult==1) {
			this.intTurn++; 
			if (this.intTurn > 1) {
				this.intTurn = 0;
			}
			this.intTurnNumber++;
		}

		if ((!blnCompPass && this.blnPass) || (intResult == -1 && this.blnPass)) {
			this.setGame(false);
		}
		if ((intResult == -1) && !this.blnPass) {
			this.blnPass = true;
		}
		return intResult;
	};

	this.saveBoard = function() {
		//this.board_save = this.board.slice();
		
		for(var i=0; i<100; i++) {
			this.board_save[i] = this.board[i];
		}
	};
	
	this.restoreBoard = function() {
		//this.board = this.board_save.slice();
		for (var i = 0; i < 100; i++) {
			this.board[i] = this.board_save[i];
		}
	};

	this.saveBoard2 = function() {
		for (var i = 0; i < 100; i++) {
			this.board_save2[i] = this.board[i];
		}
	};

	this.restoreBoard2 = function() {
		for (var i = 0; i < 100; i++) {
			this.board[i] = this.board_save2[i];
		}
	};
	
	function cloneArray(oldArray,newArray) {
		//newArray = oldArray.slice(0);
		for(var i=0;i<100;i++) {
			newArray[i] = oldArray[i];
		}
	}

	// Performs a human move, and returns -1 if there is no moves avaliable
	// 0 if wrong move
	// 1 if all ok
	this.humanMove = function(intPlayer,x,y) {
		var i;
		var j;
		var score;
		this.saveBoard();
		for (i = 1; i <= 8; i++) {
			for (j = 1; j <= 8; j++) {
				if (this.tryMove(i, j, intPlayer) !== 0) {
					this.restoreBoard();
					do {
						score = this.tryMove(x, y, intPlayer);
						if (score === 0) {
							// Cannot move
							return 0;
						}
					}while (score === 0);
					return 1;
				}
			}
		}
		return -1;
	};

	// Tries to perform a move, and returns an advance in the material plus
	// extra bonus for corners (returns zero if the move is not possible)
	this.tryMove = function(i, j, figure) {
		var captured;
		var locX;
		var locY;
		var score = 0;
		var next;
		if(this.board[i+j*this.w]==2) {
			for (var stepX = -1; stepX <= 1; stepX++) {
				for (var stepY = -1; stepY <= 1; stepY++) {
					if (stepX !== 0 || stepY !== 0) {
						locX = i;
						locY = j;
						captured = 0;
						var k;
						do {
							// Get the next figure to direction
							locX += stepX;
							locY += stepY;
							next = this.board[locX + locY * this.w];
							
							// Check if that figure was empty or border
							if (next >= 2) {
								captured = 0; // Then zero captured
							}
							else {
								if (next != figure) { // Or if it is opponents figure
									captured++; // Add captured pieces
								}
							}
							if (figure === 0) {
								k = 1;
							}
							else {
								k = 0;
							}
						} while (next == k);
						if (captured > 0) {
							// Got some captured pieces
							locX = i;
							locY = j;
							do {
								this.board[locX + locY * this.w] = figure;
								locX += stepX;
								locY += stepY;
							}while (this.board[locX + locY * this.w] != figure && 
									( (locX > 0 && locX < 9) && 
									  (locY > 0 && locY < 9) ) );
							score += captured;
						}
					}
				}
			}
			if(score>0) {
				this.board[i+j*this.w] = figure;
				for (var p = i - 1; p <= i + 1; p++) {
					for (var q = j - 1; q <= j + 1; q++) {
						if (this.board[p + q * this.w] == 3) {
							this.board[p + q * this.w] = 2;
						}
					}
				}
				// Corners
				if ((i == 1 || i == 8) && (j == 1 || j == 8)) {
					score += 8;
				}
				// Next to corners
				else if ((i == 2 || i == 7) && (j == 1 || j == 8)) {
					score -= 6; //3
				}
				else if ((i == 1 || i == 8) && (j == 2 || j == 7)) {
					score -= 6;
				}
				else if ((i == 2 || i == 7) && (j == 2 || j == 7)) {
					score -= 8; //10
				}
				// Sides
				else if ((i == 1 || i == 8) && (j > 2 || j < 7)) {
					score += 2; //10
				}
				else if ((j == 1 || j == 8) && (i > 2 || i < 7)) {
					score += 2; //10
				}
				// Next to sides
				else if ((i == 2 || i == 7) && (j > 2 || j < 7)) {
					score -= 6; //10
				}
				else if ((j == 2 || j == 7) && (i > 2 || i < 7)) {
					score -= 6; //10
				}
				else if (score === 0 && intTurnNumber>22) {
					score++;
				}
			}
		}
		return score;
	};

	// Performs computer move, and returns FALSE if there is no avaliable moves
	this.cpuMove = function() {
		//alert('cpuMove1');
		this.bestX=0; 
		this.bestY=0;
		this.minMaxSearch(0,1,1000);
		if(this.bestX!==0) {
			this.tryMove(this.bestX,this.bestY,0);
		}
		return this.bestX!==0;
	};

	// Recursive minmax search for a best move with alpha pruning: returns best
	// minmax value, and as a side effect, returns coordinates of such best move
	this.minMaxSearch = function(figure, depth, alpha) {
		var max = -1000;
		var score = 0;
		this.bestX = 0; 
		this.bestY = 0;
		var alphaVille = 20;
		var tmpBoard = {};
		var tmpBoard2 = {};
		//this.saveBoard2();
		var currentLevel = depth;
		
		cloneArray(this.board, tmpBoard2);
		for(var i=1;i<=8;i++) {
			for (var j = 1; j <= 8; j++) {
				//this.saveBoard();
				//if (board[i + j * this.w] == 2) {
				cloneArray(this.board, tmpBoard);
				var fig = 0;
				if(figure===0) {
					fig = 1;
				}
				score = this.tryMove(i, j, 0);
				cloneArray(tmpBoard, this.board);
				//this.restoreBoard();
				if(score!==0) {
					if (depth != this.level) {
						if (figure === 0) {
							// Next human move
							score -= this.minMaxSearch(1, depth + 1, score - max);
						}
						else {
							// Next CPU move
							score += this.minMaxSearch(0, depth + 1, score - max);
						}
					}
					//cloneArray(tmpBoard, this.board);
					if (score > max) {
						max = score;
						//if (currentLevel == 1) {
							this.bestX = i;
							this.bestY = j;
						//}
					}
					else {
						if ( //currentLevel == 1 &&
							 score == max && 
							 (Math.random()%1 === 0)) {
							this.bestX = i;
							this.bestY = j;
						}
					}
					//cloneArray(tmpBoard, this.board);
					cloneArray(tmpBoard2, this.board);
					//this.restoreBoard2();
					if (score >= alphaVille) {
						return max;
					}
				} else {
					//cloneArray(tmpBoard, this.board);
					cloneArray(tmpBoard2, this.board);
					//this.restoreBoard2();
				}
			}
		}
		cloneArray(tmpBoard2, this.board);
		if (this.bestX === 0) {
			return 0;
		}
		return max;
	};
}

