//******************************************************************************
// Tic.java:	Applet
//
//******************************************************************************
import java.applet.*;
import java.awt.*;

//==============================================================================
// Main Class for applet Tic
//
//==============================================================================
public class Tic extends Applet
{
public int nextgo, winner, go;
public int grid[] = new int[9];
public int gridlines[][] = new int [8][3];
public Image resetimage;
private Font font;


	// Tic Class Constructor
	//--------------------------------------------------------------------------
	public Tic()
	{

	}

	// APPLET INFO SUPPORT:
	//		The getAppletInfo() method returns a string describing the applet's
	// author, copyright date, or miscellaneous information.
    //--------------------------------------------------------------------------
	public String getAppletInfo()
	{
		return "Name: TicTacToe\r\n" +
		       "Author: Gareth Blades\r\n" +
		       "Created with Microsoft Visual J++ Version 1.1";
	}


	// The init() method is called by the AWT when an applet is first loaded or
	// reloaded.  Override this method to perform whatever initialization your
	// applet needs, such as initializing data structures, loading images or
	// fonts, creating frame windows, setting the layout manager, or adding UI
	// components.
    //--------------------------------------------------------------------------
	public void init()
	{
        // If you use a ResourceWizard-generated "control creator" class to
        // arrange controls in your applet, you may want to call its
        // CreateControls() method from within this method. Remove the following
        // call to resize() before adding the call to CreateControls();
        // CreateControls() does its own resizing.
        //----------------------------------------------------------------------
    	resize(300, 400);
		font = new Font("Helvetica",Font.PLAIN,24);
		nextgo=2;
		winner=0;
		go=1;
		// clear grid
		for (int i=0; i<9; i++)
		{
			grid[i]=0;
		}
		// setup list of grid lines
		for (int i=0; i<3; i++)
		{
			// vertical
			gridlines[i][0]=i;
			gridlines[i][1]=i+3;
			gridlines[i][2]=i+6;
			// horizontal
			gridlines[i+3][0]=i*3;
			gridlines[i+3][1]=i*3+1;
			gridlines[i+3][2]=i*3+2;
		}
		// diagonals
		gridlines[6][0]=0;
		gridlines[6][1]=4;
		gridlines[6][2]=8;
		gridlines[7][0]=2;
		gridlines[7][1]=4;
		gridlines[7][2]=6;
		// load reset image
		resetimage = getImage(getDocumentBase(),"reset.gif");

	}

	// Place additional applet clean up code here.  destroy() is called when
	// when you applet is terminating and being unloaded.
	//-------------------------------------------------------------------------
	public void destroy()
	{
		// TODO: Place applet cleanup code here
	}

	// Tic Paint Handler
	//--------------------------------------------------------------------------
	public void paint(Graphics g)
	{
		g.setColor(Color.black);
		for (int i=100;i<300;i+=100)
		{
			g.drawLine(0,i,300,i);
			g.drawLine(i,0,i,300);
		}
		g.setColor(Color.red);
		g.fillOval(20,305,20,20);
		g.setColor(Color.blue);
		g.fillOval(20,330,20,20);
		g.setColor(Color.black);
		g.setFont(font);
		g.drawString("Me",50,325);
		g.drawString("You",50,350);
		for (int i=0; i<9; i++)
		{
			if (grid[i]!=0)
				drawpos(i);
		}
		g.drawImage(resetimage,250,350,this);

	}

	//		The start() method is called when the page containing the applet
	// first appears on the screen. The AppletWizard's initial implementation
	// of this method starts execution of the applet's thread.
	//--------------------------------------------------------------------------
	public void start()
	{
		// TODO: Place additional applet start code here
	}
	
	//		The stop() method is called when the page containing the applet is
	// no longer on the screen. The AppletWizard's initial implementation of
	// this method stops execution of the applet's thread.
	//--------------------------------------------------------------------------
	public void stop()
	{
	}


	// MOUSE SUPPORT:
	//		The mouseUp() method is called if the mouse button is released
	// while the mouse cursor is over the applet's portion of the screen.
	//--------------------------------------------------------------------------
	public boolean mouseUp(Event evt, int x, int y)
	{
		int mousepos;

		mousepos = getmousepos(x,y);
		if ( (mousepos<10) && (nextgo==2) )
			if (grid[mousepos]==0)
			{
				grid[mousepos]=nextgo;
				drawpos(mousepos);
				nextgo=1;
				go++;
			}

		if (mousepos==50)
			reset();

		if (gameover())
		{
			Graphics g = getGraphics();
			g.setColor(Color.black);
			g.setFont(font);
			nextgo=0;
			if (winner==0)
				g.drawString("It is a draw!",50,380);
			if (winner==1)
				g.drawString("I win!",50,380);
			if (winner==2)
				g.drawString("You win!",50,380);
		}

		if (nextgo==1)
		{
			int move;
			move = decidemove();
			grid[move]=1;
			drawpos(move);
			nextgo=2;
			go++;
		}

		if (gameover())
		{
			Graphics g = getGraphics();
			g.setColor(Color.black);
			g.setFont(font);
			nextgo=0;
			if (winner==0)
				g.drawString("It is a draw!",50,380);
			if (winner==1)
				g.drawString("I win!",50,380);
			if (winner==2)
				g.drawString("You win!",50,380);
		}
				

		return true;
	}

	// returns true if the game is over.
	// Winner contains the winning player number or 0 for a draw
	public boolean gameover()
	{
		boolean over = false;
		boolean nomoves = true;

		for (int i=0;i<3;i++)
		{
			if ((grid[i+0]==grid[i+3]) && (grid[i+3]==grid[i+6]) && (grid[i]!=0))
			{
				over = true;
				winner = grid[i];
			}
			if ((grid[i*3]==grid[i*3+1]) && (grid[i*3+1]==grid[i*3+2]) && (grid[i*3]!=0))
			{
				over = true;
				winner = grid[i*3];
			}
		}
		if ( (((grid[0]==grid[4]) && (grid[4]==grid[8])) ||
			 ((grid[2]==grid[4]) && (grid[4]==grid[6])))
			&& (grid[4]!=0) )
		{
			over = true;
			winner = grid[4];
		}
		for (int i=0;i<9;i++)
		{
			if (grid[i]==0)
				nomoves = false;
		}
		if (nomoves==true)
			over = true;
		return over;
	}
	
	// converts a mouse display position into a grid position
	public int getmousepos(int x, int y)
	{
		int xpos, ypos;

		if ((x>250) && (x<280) && (y>350) && (y<380))
			return 50;

		xpos=x/100;
		ypos=y/100;

		if ((x>=300) || (y>=300))
		{
			ypos=0;
			xpos=255;
		}

		return (ypos*3+xpos);
	}

	// draws a marker on the screen
	public void drawpos(int ref)
	{
		int x,y;
		Graphics g = getGraphics();

		// x and y pos in grid
		y=ref/3;
		x=ref-(y*3);
		// change x and y to top left corner of grid pos
		y*=100;
		x*=100;
		// move to top left of circle
		y+=20;
		x+=20;
		g.setColor(Color.red);								// assume computer move
		if (grid[ref]==2)										// if player move
			g.setColor(Color.blue);
		g.fillOval(x,y,60,60);								// display circle
	}

	// computer decides the next move to make
	public int decidemove()
	{
		int move=0;

//		for (int i=0; i<9; i++)
//		{
//			if (grid[i]==0)
//				move=i;
//		}
//		return move;

		// first priority is to win game if possible
		// RULE 1
		move = detectwinpos(1);
		if (move<10)
			return move;
		// next priority is to stop player from winning
		// RULE 2
		move = detectwinpos(2);
		if (move<10)
			return move;
		// O.i rule 5 would make a move in a or b in this specific case.
		// .Xb This would leave to a loose. Either 'i' must therefore be payed
		// iaX
		// RULE 3
		if (go==4)
		{
			if ((grid[4]==2) && (grid[8]==2))
				return 2;
		}
		// a.X rule 3 would make a move in either a's which is not the
		// .O. correct move is a dual coincident point situation. Instead the
		// X.a player must be forced to make a move. Therefore make a move in '.'
		// RULE 4
		if (go==4)
		{
			if ((grid[2]==2) && (grid[6]==2) && (grid[4]==1))
				return 1;
			if ((grid[0]==2) && (grid[8]==2) && (grid[4]==1))
				return 1;
		}
		// next priority is to block trap move
		// RULE 5
		move = detecttrap(2);
		if (move<10)
			return move;
		// next priority is to set trap if poss
		// RULE 6
		move = detecttrap(1);
		if (move<10)
			return move;
		// next priority is to make center move if possible
		// RULE 7
		if (grid[4]==0)
			return 4;
		// next priority is to try and make a winable line
		// RULE 8
		move = detectwinline(1);
		if (move<10)
			return move;
		// next priority is to just make a move anywhere
		// RULE 9
		for (int i=0; i<9; i++)
		{
			if (grid[i]==0)
				return i;
		}
		return 255;
	}

	public int detectwinpos(int ply)
	{
		// check horizontal and vertical
		for (int i=0; i<3; i++)
		{
			if ((grid[i]==ply) && (grid[i+3]==ply) && (grid[i+6]==0))
				return (i+6);
			if ((grid[i]==ply) && (grid[i+3]==0) && (grid[i+6]==ply))
				return (i+3);
			if ((grid[i]==0) && (grid[i+3]==ply) && (grid[i+6]==ply))
				return (i);
			if ((grid[i*3]==ply) && (grid[i*3+1]==ply) && (grid[i*3+2]==0))
				return (i*3+2);
			if ((grid[i*3]==ply) && (grid[i*3+1]==0) && (grid[i*3+2]==ply))
				return (i*3+1);
			if ((grid[i*3]==0) && (grid[i*3+1]==ply) && (grid[i*3+2]==ply))
				return (i*3);
		}
		// check diagonals
		for (int i=-1; i<2; i+=2)
		{
			if ((grid[1+i]==ply) && (grid[4]==ply) && (grid[7-i]==0))
				return (7-i);
			if ((grid[1+i]==ply) && (grid[4]==0) && (grid[7-i]==ply))
				return (4);
			if ((grid[1+i]==0) && (grid[4]==ply) && (grid[7-i]==ply))
				return (1+i);
		}
		// return 255 if no match
		return 255;
	}

	public int detecttrap(int ply)
	{
		int empty1, empty2, empty3, empty4;

		// scan through all but last line
		for (int i=0; i<7; i++)
		{
			empty1=empty2=255;
			// check for lines with 1 players marker and rest of line blank
			if ((grid[gridlines[i][0]]==ply) && (grid[gridlines[i][1]]==0) &&
				(grid[gridlines[i][2]]==0))
			{
				empty1=gridlines[i][1];
				empty2=gridlines[i][2];
			}
			if ((grid[gridlines[i][0]]==0) && (grid[gridlines[i][1]]==ply) &&
				(grid[gridlines[i][2]]==0))
			{
				empty1=gridlines[i][0];
				empty2=gridlines[i][2];
			}
			if ((grid[gridlines[i][0]]==0) && (grid[gridlines[i][1]]==0) &&
				(grid[gridlines[i][2]]==ply))
			{
				empty1=gridlines[i][0];
				empty2=gridlines[i][1];
			}
			// if first line is winable
			if (empty1<10)
			{
				// scan through remaining lines
				for (int j=i+1; j<8; j++)
				{
					empty3=empty4=255;
					// check for lines with 1 players marker and rest of line blank
					if ((grid[gridlines[j][0]]==ply) && (grid[gridlines[j][1]]==0) &&
						(grid[gridlines[j][2]]==0))
					{
						empty3=gridlines[j][1];
						empty4=gridlines[j][2];
					}
					if ((grid[gridlines[j][0]]==0) && (grid[gridlines[j][1]]==ply) &&
						(grid[gridlines[j][2]]==0))
					{
						empty3=gridlines[j][0];
						empty4=gridlines[j][2];
					}
					if ((grid[gridlines[j][0]]==0) && (grid[gridlines[j][1]]==0) &&
						(grid[gridlines[j][2]]==ply))
					{
						empty3=gridlines[j][0];
						empty4=gridlines[j][1];
					}
					// if both lines are winable.
					if (empty3<10)
					{
						// two winable lines.
						// if empty1-2 = empty3-4 then this is a coincident point
						// and placing marker here produces two winning posibilities
						// only one of which can be blocked next go.
						if ((empty1==empty3) || (empty1==empty4))
							return empty1;
						if ((empty2==empty3) || (empty2==empty4))
							return empty2;
					}
				}
			}
		}
		return 255;
	}


	public int detectwinline(int ply)
	{
		int empty1, empty2;

		// scan through all lines
		for (int i=0; i<8; i++)
		{
			empty1=empty2=255;
			// check for lines with 1 players marker and rest of line blank
			if ((grid[gridlines[i][0]]==ply) && (grid[gridlines[i][1]]==0) &&
				(grid[gridlines[i][2]]==0))
			{
				empty1=gridlines[i][1];
				empty2=gridlines[i][2];
			}
			if ((grid[gridlines[i][0]]==0) && (grid[gridlines[i][1]]==ply) &&
				(grid[gridlines[i][2]]==0))
			{
				empty1=gridlines[i][0];
				empty2=gridlines[i][2];
			}
			if ((grid[gridlines[i][0]]==0) && (grid[gridlines[i][1]]==0) &&
				(grid[gridlines[i][2]]==ply))
			{
				empty1=gridlines[i][0];
				empty2=gridlines[i][1];
			}
			if (empty1<10)
				return empty1;
		}
		return 255;
	}

	public void reset()
	{
		Graphics g = getGraphics();
		Rectangle r = bounds();
		g.setColor(getBackground());
		g.fillRect(r.x, r.y, r.width, r.height);
		g.setColor(getForeground());
		init();
		paint(g);
	}


}
