import java.awt.*;
import java.util.*;

public class ReversiAI implements Runnable {
	private int m_nMe,m_nPlayers;
	private int m_nBoardW,m_nBoardH;
	private int[][] m_pBoard;
	private double[] m_pWegingen;
	private double[] m_pAfwijkingen;
	private double m_dZetWaarde;
	private Reversi m_pParent;
	private int m_nMaxRecurse;
	
	ReversiAI(int nPlayers, int nMyPlayer, Reversi pParent) {
		m_nMe=nMyPlayer;
		m_nPlayers=nPlayers;
		m_pParent=pParent;
		m_nMaxRecurse=Integer.parseInt(pParent.getParameter("ai_maxrecurse","3"));
		m_pWegingen=new double[12];
		m_pAfwijkingen=new double[12];
		this.FillWegingen(pParent.getParameter("ai_params"+Integer.toString(nMyPlayer),""));
	}
	
	private void FillWegingen(String data) {
		String szParams[]=data.split(" ");
		if (szParams.length<24)
			return;
		for (int i=0; i<12; i++) {
			m_pWegingen[i]=Double.valueOf(szParams[i*2]);
			m_pAfwijkingen[i]=Double.valueOf(szParams[(i*2)+1]);
		}
	}
	
	public void UpdateBoard(int[][] pBoard, int nBoardW, int nBoardH) {
		m_pBoard=pBoard;
		m_nBoardW=nBoardW;
		m_nBoardH=nBoardH;
	}
	
	private int[][] copyBoard(int[][] pBoard) {
		int[][] pNewBoard=new int[m_nBoardW][m_nBoardH];
		int x,y;
		for (x=0; x<m_nBoardW; x++)
			pNewBoard[x]=(int[])pBoard[x].clone();
			//for (y=0; y<m_nBoardH; y++)
			//	pNewBoard[x][y]=pBoard[x][y];
		return pNewBoard;
	}
	
	//Returned 0 op geen blok, 1 op eigen blok, of -1 op andere blok
	private int getStand(int[][] pBoard, int x, int y) {
		if (pBoard[x][y]==0)
			return 0;
		if (pBoard[x][y]==m_nMe+1)
			return 1;
		return -1;
	}
	
	private int getWaarde(int[][] pBoard, int nTurn) {
		if (m_nBoardW != 8 || m_nBoardH!=8)
			return 0;
		double waarde=0.0D;
        waarde += getStand(pBoard,0,0) * 100;
        waarde += getStand(pBoard,0,7) * 100;
        waarde += getStand(pBoard,7,0) * 100;
        waarde += getStand(pBoard,7,7) * 100;
        if(getStand(pBoard,0,0) != 0)
        {
            waarde += (double)getStand(pBoard,1,0) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,0,1) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,1,1) * m_pWegingen[3];
        } else
        {
            waarde += (double)getStand(pBoard,1,0) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,0,1) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,1,1) * m_pWegingen[2];
        }
        if(getStand(pBoard,0,7) != 0)
        {
            waarde += (double)getStand(pBoard,0,6) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,1,7) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,1,6) * m_pWegingen[3];
        } else
        {
            waarde += (double)getStand(pBoard,0,6) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,1,7) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,1,6) * m_pWegingen[2];
        }
        if(getStand(pBoard,7,0) != 0)
        {
            waarde += (double)getStand(pBoard,6,0) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,7,1) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,6,1) * m_pWegingen[3];
        } else
        {
            waarde += (double)getStand(pBoard,6,0) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,7,1) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,6,1) * m_pWegingen[2];
        }
        if(getStand(pBoard,7,7) != 0)
        {
            waarde += (double)getStand(pBoard,6,7) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,7,6) * m_pWegingen[1];
            waarde += (double)getStand(pBoard,6,6) * m_pWegingen[3];
        } else
        {
            waarde += (double)getStand(pBoard,6,7) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,7,6) * m_pWegingen[0];
            waarde += (double)getStand(pBoard,6,6) * m_pWegingen[2];
        }
        waarde += (double)getStand(pBoard,0,2) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,0,5) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,2,0) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,2,7) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,5,0) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,5,7) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,7,2) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,7,5) * m_pWegingen[4];
        waarde += (double)getStand(pBoard,0,3) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,0,4) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,3,0) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,3,7) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,4,0) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,4,7) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,7,3) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,7,4) * m_pWegingen[5];
        waarde += (double)getStand(pBoard,1,2) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,1,5) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,2,1) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,2,6) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,5,1) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,5,6) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,6,2) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,6,5) * m_pWegingen[6];
        waarde += (double)getStand(pBoard,1,3) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,1,4) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,3,1) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,3,6) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,4,1) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,4,6) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,6,3) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,6,4) * m_pWegingen[7];
        waarde += (double)getStand(pBoard,2,3) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,2,4) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,3,2) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,3,5) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,4,2) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,4,5) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,5,3) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,5,4) * m_pWegingen[8];
        waarde += (double)getStand(pBoard,2,2) * m_pWegingen[9];
        waarde += (double)getStand(pBoard,2,5) * m_pWegingen[9];
        waarde += (double)getStand(pBoard,5,2) * m_pWegingen[9];
        waarde += (double)getStand(pBoard,5,5) * m_pWegingen[9];
        waarde += (double)getStand(pBoard,3,3) * m_pWegingen[10];
        waarde += (double)getStand(pBoard,3,4) * m_pWegingen[10];
        waarde += (double)getStand(pBoard,4,3) * m_pWegingen[10];
        waarde += (double)getStand(pBoard,4,4) * m_pWegingen[10];
        //Wordt al rekening mee gehouden, alle andere spelers worden negatief, eigen blokjes worden positief :)
        //waarde = (double)currentField.aanZet * waarde;
        //waarde += (double)(currentField.mogelijk - currentField.mogelijkNiet) * m_pWegingen[11];
        return (int)waarde;
	}
	
	private boolean doMove(int[][] pBoard, int x, int y, int player) {
		//Als er al een steen staat mag deze zet niet
		if (pBoard[x][y]!=0)
			return false;
		//Houd bij of er stenen veroverd zijn
		boolean bRetval=false;
		//Zelfde riedeltje als validMove, maar dan niet stoppen na een mogelijke move, en de stenen meteen overnemen :)
		int dx,dy,n;
		for (dx=-1; dx<=1; dx++) {
			for (dy=-1; dy<=1; dy++) {
				if (dx==0 && dy==0)
					continue;
				if (x+dx<0 || y+dy<0 || x+dx>=m_nBoardW || y+dy>=m_nBoardH)
					continue;
				if (pBoard[x+dx][y+dy]==player+1)
					continue;
				for (n=1; x+n*dx>=0 && y+n*dy>=0 && x+n*dx<m_nBoardW && y+n*dy<m_nBoardH; n++) {
					if (pBoard[x+n*dx][y+n*dy]==0)
						break;
					if (pBoard[x+n*dx][y+n*dy]==player+1) {
						//Even onthouden dat er stenen zijn veroverd
						bRetval=true;
						//Als er stenen veroverd kunnen worden, weer "teruggaan" en al deze stenen omzetten
						for (; n>0; n--)
							pBoard[x+n*dx][y+n*dy]=player+1;
						break;
					}
				}
			}
		}
		//Als er stenen zijn veroverd, was de zet geldig, en kunnen we de steen in kwestie ook nerzetten :)
		if (bRetval)
			pBoard[x][y]=player+1;
		return bRetval;
	}
	
	private int estimateCount(int[][] pBoard, int nTurn, int nMaxTurns) {
		if (nMaxTurns==0) {
			/*int mycount=0;
			int othercount=0;
			int x,y;
			for (x=0; x<m_nBoardW; x++)
				for (y=0; y<m_nBoardH; y++)
					if (pBoard[x][y]==m_nMe+1)
						mycount++;
					else if (pBoard[x][y]!=0)
						othercount++;
			return mycount;*/
			return getWaarde(pBoard,nTurn);
		}
		int nMin=-1;
		int nNextTurn=(nTurn+1)%m_nPlayers;
		int[][] pCurBoard=copyBoard(pBoard);
		int x,y;
		for (x=0; x<m_nBoardW; x++) {
			for (y=0; y<m_nBoardH; y++) {
				if (doMove(pCurBoard,x,y,nTurn)) {
					int est=estimateCount(pCurBoard,nNextTurn,nMaxTurns-1);
					if (nTurn==m_nMe) {
						if (nMin==-1 || est>nMin)
							nMin=est;
					} else {
						if (nMin==-1 || est<nMin)
							nMin=est;
					}
					pCurBoard=copyBoard(pBoard);
				}
			}
		}
		if (nMin==-1) {
			if (nTurn==m_nMe)
				return 0;
			else
				return estimateCount(pBoard,nNextTurn,nMaxTurns-1);
		}
		return nMin;
	}
	
	private Point getBestMove(int[][] pBoard, int nMaxRecurse) {
		int x,y;
		int mx,my;
		int mv;
		mx=my=-1;
		mv=0;
		int nNextTurn=(m_nMe+1)%m_nPlayers;
		int[][] pCurBoard=copyBoard(pBoard);
		for (x=0; x<m_nBoardW; x++) {
			for (y=0; y<m_nBoardH; y++) {
				if (doMove(pCurBoard,x,y,m_nMe)) {
					int est=estimateCount(pCurBoard,nNextTurn,nMaxRecurse);
					if (est>mv || mx==-1) {
						mv=est;
						mx=x;
						my=y;
					}
					pCurBoard=copyBoard(pBoard);
				}
			}
		}
		return new Point(mx,my);
	}
	
	public void run() {
		Calendar starttime=Calendar.getInstance();
		//Gewoon alle zetten proberen en kijken welke de meeste moves heeft :)
		Point bestmove=getBestMove(m_pBoard,m_nMaxRecurse);
		Calendar endtime=Calendar.getInstance();
		long dif=endtime.getTimeInMillis()-starttime.getTimeInMillis();
		try {
			int minwait=500;
			if (dif<minwait) {
				Thread.sleep(minwait-dif);
			} else
				Thread.sleep(10); //zoieso even wachten, om niet vast te lopen ;)
		} catch (Exception e) {}
		m_pParent.AICallback(bestmove.x,bestmove.y);
	}
}

