

import java.util.* ;
import java.awt.* ;
import java.awt.event.* ;
import java.applet.Applet ;
import java.applet.AudioClip ;

class Node {
  double x ;
  double y ;
  int state ;                 // Can be player 1 or Player 2 ;
  double r ;
  Node (){
  }
  Node (double x, double y, int state, double r){
    this.x = x ;
    this.y = y ;
    this.state = state ;
    this.r = r ;
  }
  Node( Node n ){
    this.x = n.x ;
    this.y = n.y ;
    this.state = n.state ;
    this.r = n.r ;
  }
}


class GamePanel extends Panel implements Runnable, MouseListener, MouseMotionListener {

       Game game ;
       Image backGroundImage ;
       Image playerCounter ;
       int soundOn ;
       Image computerCounter ;
       Image motionCounter ;
       AudioClip backAudio, pressAudio, releaseAudio , dragAudio ;
       boolean player ;
       int ito = -1 ;
       int ifrom = -1 ;
       String banner, banner1 ;
       Thread relaxer ;
       Dimension d ;
       int nnodes = 5 ;
       int winner = -1 ;
       Node nodes[] = new Node[5] ;
       Node counters[] = new Node[5] ;
       
       GamePanel(Game game) {
         this.game = game ;
         
	 addMouseListener(this);
	 addMouseMotionListener(this);
	 
	 double r = 10.0 ;
	 Dimension d = game.getSize(); 
	 double shift = d.height/12.0 ;
	 double x1 = d.width/4.0 , y1 = d.height/4.0 - shift ;
	 double x2 = d.width/2.0 , y2 = d.height/2.0 - shift ;
	 double x3 = (3.0*d.width)/4.0 , y3 = (3.0*d.height)/4.0 - shift ;
	 
	 
	 nodes[0] = new Node(x1,y1,0,r) ;     //   0       1
         nodes[1] = new Node(x3,y1,0,r);      //
	 nodes[2] = new Node(x2,y2,0,r);      //       2
	 nodes[3] = new Node(x1,y3,0,r);      //
	 nodes[4] = new Node(x3,y3,0,r);      //   3       4
       }
       
       

       public void run() {
         Thread me = Thread.currentThread();
	 while (relaxer == me) {
	   relax();
	   try {
	     Thread.sleep(100);
           } catch (InterruptedException e) {
	       break ;
           }
        }

       }      
       synchronized void relax(){
          repaint();
       }

       Image offscreen ;
       Dimension offscreensize ;
       Graphics offgraphics ;
       final Color emptyNodeColor = Color.black ;
       final Color oneNodeColor = Color.red ;
       final Color twoNodeColor = Color.blue ;
       final Color joinNodeColor = Color.black ;
       
       double lineWidth = 10 ;
       int istep = 0 ;
       int nosteps = 20 ;
       Node animNode = new Node()  ;
       public synchronized void update(Graphics g) {
         if ( (soundOn >= 0) && (soundOn <= 10)  ) 
	    soundOn++ ;
         else
	    backAudio.stop() ;
	 d = getSize() ;
         if ((offscreen == null) || ( d.width != offscreensize.width ) 
	                          || (d.height != offscreensize.height)) {
	    offscreen = createImage(d.width,d.height);
	    offscreensize = d ;
	    offgraphics = offscreen.getGraphics();
	 }
	 offgraphics.setColor(getBackground());
	 
	 offgraphics.fillRect(0,0,d.width,d.height);
	 
	 offgraphics.drawImage(backGroundImage,0,0,d.width,d.height,this);
         //System.out.println("img width= "+d.width+", img height= "+d.height);
         joinNodes(offgraphics, nodes[0], nodes[3], lineWidth );
         joinNodes(offgraphics, nodes[3], nodes[4], lineWidth );
	 joinNodes(offgraphics, nodes[4], nodes[1], lineWidth );
	 joinNodes(offgraphics, nodes[2], nodes[0], lineWidth );
	 joinNodes(offgraphics, nodes[2], nodes[1], lineWidth );
	 joinNodes(offgraphics, nodes[2], nodes[3], lineWidth );
	 joinNodes(offgraphics, nodes[2], nodes[4], lineWidth );
	 for ( int i = 0 ; i <= nnodes - 1; i++ )
	    paintNode(offgraphics,nodes[i]);
         for ( int i = 0  ; i <= nnodes - 1; i++ ){
	   if ( i != ifrom ){
	       paintNode(offgraphics,counters[i]);
           }
           if ( i == ifrom ){
	       if ( istep < nosteps ){
		 if ( istep == 0 )
		   backAudio.loop() ;
		 soundOn = 0 ;
		 player = false ;
		 counters[ifrom].state = -1;
		 counters[ito].state = -1 ;
	         animNode = vectorNode(counters[ifrom],counters[ito],istep);
		 paintNode(offgraphics,animNode) ;
		 istep++ ;
		 //System.out.println("istep: "+istep);
                 //System.out.println("animNode.state: "+animNode.state);
               }
	       if ( istep == nosteps) {
		  soundOn  = -1 ;
		  counters[ito].state = 2 ;
	          istep = 0 ;
		  ifrom = -1 ;
		  ito = -1 ;
		  player = true ;
                  if ( aWinner(false) == true ){ // If computer is a winner 
	            winner = 2 ;               
	            repaint();
                  }
               }
            }
         }
	 offgraphics.setColor(joinNodeColor);
	 
	 if ( winner == -1 ){
           offgraphics.setFont(new Font("TimesRoman", Font.BOLD, 15));
	   if ( player == true )
             offgraphics.drawString("Your Turn",20,15);
           if ( player == false )
	     offgraphics.drawString("Computers Turn",20,15);
         }
	 if ( (winner == 1) || (winner == 2) ){
           if ( winner == 1 ){
	        banner = new String("Well Done");
		banner1 = new String("You Won");
           }
           if ( winner == 2 ){
	        banner = new String("I Win");
                banner1 = new String("This Time");
           }
	   int bannerLength = banner.length();
	   int bannerLength1 = banner1.length();
           char bannerChars[] = new char[bannerLength] ;
	   banner.getChars(0, banner.length(), bannerChars, 0);
           char bannerChars1[] = new char[bannerLength1] ;
	   banner1.getChars(0, banner1.length(), bannerChars1, 0);
	   offgraphics.setColor(Color.black);
	   offgraphics.setFont(new Font("TimesRoman", Font.BOLD, 36));
           for(int i = 0 ; i < banner.length() ; i++){
	     int x = (int) (5*Math.random() + 25*i + 35);
	     int y = (int) (5*Math.random() + 100);
	     offgraphics.drawChars(bannerChars, i, 1, x, y);
           }
           for(int i = 0 ; i < banner1.length() ; i++){
	     int x = (int) (5*Math.random() + 25*i + 40);
	     int y = (int) (5*Math.random() + 150);
	     offgraphics.drawChars(bannerChars1, i, 1, x, y);
           }
         }
         
	 g.drawImage(offscreen, 0 , 0 , null );
         
	 
       }
       public Node  vectorNode(Node from,Node to ,int step){
            Node aNode = new Node(from);
	    
            double dx,dy ;
	    double  phase = 0.0 ;
	    double  theta = 0.0 ;
	    double  amp   = 15.0 ;
	    double  loops = 5.0 ;
            aNode.state = 3 ;
	    phase =  ((2.0 * Math.PI)/(double)nosteps) * loops  ; 
            dx = (from.x - to.x)/(double)nosteps ;
            dy = (from.y - to.y)/(double)nosteps ;
	    theta = (double)step * phase ;
	    aNode.x = from.x - ((double)step * dx) - ( amp * Math.cos(theta) );  
	    aNode.y = from.y - ((double)step * dy) - ( amp * Math.sin(theta) );

	    return(aNode) ;
       }
       public void paintNode(Graphics g, Node n){
         int x = (int)(n.x + 1.0) ;
	 int y = (int)(n.y + 1.0) ;
	 int state = n.state ;
	 int r = (int)(n.r + 1.0);

	 if ( n.state >= 0 ){                        // <0: Should we draw it at all
	   if ( n.state == 0 ){                       // 0 : Is an empty Node
	      g.setColor(emptyNodeColor);
              g.fillOval(x-r,y-r,2*r,2*r);
           }
	   if ( n.state == 1 ){                        // 1 : Is the Players Piece
	      g.setColor(Color.red);
              g.fillOval(x-r,y-r,2*r,2*r);
	      g.drawImage(playerCounter,x-r,y-r,2*r,2*r,this);
           }
           if ( n.state == 2 ){                       // 2 : Is the Computers Piece
	      g.setColor(Color.blue);
              g.fillOval(x-r,y-r,2*r,2*r);
	      g.drawImage(computerCounter,x-r,y-r,2*r,2*r,this);              
           }
           if ( n.state == 3 ){                        // 3 : Has been selected 
	      g.setColor(Color.yellow);
              g.fillOval(x-r,y-r,2*r,2*r);
	      g.drawImage(motionCounter,x-r,y-r,2*r,2*r,this);
           }
         }
       }

       public void joinNodes(Graphics g, Node n, Node m, double w ){
	     int x1 = (int)(n.x + 1.0) ; int  y1 = (int)(n.y + 1.0 );
	     int x2 = (int)(m.x + 1.0) ; int  y2 = (int)(m.y + 1.0 );
             g.setColor(joinNodeColor) ;
             g.drawLine(x1,y1,x2,y2);
       }

       Node original ;
       int ioriginal = -1  ;
       public void mouseClicked(MouseEvent e) {
       }
       public void mousePressed(MouseEvent e) {
           int x = e.getX() ;
	   int y = e.getY() ;
	   	   //dragAudio.loop() ;
	   if ( player == true ){  // It is the players turn
              backAudio.stop();
	      pressAudio.play();
	     for( int i = 0 ; i <= nnodes - 1 ; i++ ){
	       if ( (insideNode(counters[i],x,y) == true) && (counters[i].state == 1) ){
		   ioriginal = i ;
		   original = new Node(counters[i]);
                   counters[i].state = 3 ;
	       }
             }
           }
	   repaint() ;
	   e.consume() ;
       }
       public void mouseReleased(MouseEvent e) {
           int x = e.getX() ;
	   int y = e.getY() ;
	   int ito = -1 ;
	   int sum ;
	   //ragAudio.stop();
	   releaseAudio.play();
	   backAudio.loop();
           
	   if ( ioriginal != -1 && player == true ){     // Players turn and ioriginal is selected
              releaseAudio.play();
	      backAudio.loop();

	      for ( int i = 0 ; i <= nnodes - 1 ; i++ ){
	        sum = ioriginal + i ;                                     // Is this is a legal drop
	        if( (counters[i].state < 0 ) && ( insideNode(counters[i],x,y) == true) 
		                                         && (sum != 1) && ( sum != 4)){
		      counters[i].state = 1 ;                             // New Position is players                       
		      counters[ioriginal].state = -1 ;                    // Old Position is Empty
		      player = false ;                                    // Now its the computers Turn
                }
              }
	      // Return the counter whatever to its original posistion
              counters[ioriginal].x = original.x ;
              counters[ioriginal].y = original.y ;
	      if ( player == true )       // It is still players turn due to illegal move
	        counters[ioriginal].state = 1 ;
	      // THe piece has been deselected so 
              ioriginal = -1 ;
              repaint() ;
           
	      if ( player == false ) { // So it is now the computer turn 
	         if ( aWinner(true) == true ){  // If player has won
	            winner = 1 ;                  //Then winner is player or 1
		    repaint();
                 }
                 else
	            computersTurn();
              }
           }
           e.consume();
       }
       public boolean aWinner(boolean player){
	  int looser = 0 ;
	  int empty = -1 ;
	  int sum  = 0 ;
	  int canGo = 0 ;
          if ( player == false ) 
	    looser = 1 ;                        // is state = 1 the player the looser
          if ( player == true )
	    looser = 2 ;                        // is state = 0 the computer the looser
          for ( int i = 0 ; i <= nnodes - 1; i++)
	    if ( counters[i].state == -1 )
	      empty = i ;                     //Empty is the empty node
          for ( int i = 0 ; i <= nnodes - 1; i++){
	     sum = i + empty ;
	     if ( counters[i].state == looser &&  (sum != 4)  && (sum != 1) )  //All the m
	       canGo++ ;
          }
	  if ( canGo == 0 ){
            return true ;
          }
          else{
            return false ;
          }
       }
       public void mouseEntered(MouseEvent e) {
       }
       public void mouseExited(MouseEvent e) {
       }
       public void mouseDragged(MouseEvent e) {
           int x = e.getX() ;
	   int y = e.getY() ;
	   if ( ioriginal != -1 ){
	       counters[ioriginal].x = (double)x ;
	       counters[ioriginal].y = (double)y ;
           }
	   repaint() ;
	   e.consume() ;
       }
       public void mouseMoved(MouseEvent e) {
       }

       public void computersTurn() {
                  //This is the blank node
	  int diff ;
	  int j = 0 ;
	  int[] poss = new int[2] ;    //These are the computer's two bits.
	  for ( int i = 0 ; i <= nnodes - 1; i++ ){ 
	    if ( counters[i].state == -1 )
	      ito = i ;
            if ( counters[i].state == 2 )
	       poss[j++] = i ;
          }
          // See which moves are legal;
	  j = 0 ;
          diff = poss[0] + ito ;
          if ( (diff != 4 ) && (diff) != 1 ){
             ifrom = poss[0] ;
	     j++;
          }
	  diff = poss[1] + ito ;
	  if ( (diff != 4 ) && (diff ) != 1 ){
	     ifrom = poss[1] ;
	     j++;
          }
          if ( j == 2 ){        //So there are two possible moves 
	     ifrom = (int)(2.0 * Math.random()) ;
	     ifrom = poss[ifrom] ;
          }
	  repaint() ;
	  

       }
     
       public boolean insideNode(Node n, int a, int b ){
          double x = (double)a;
	  double y = (double)b;
	  double r = n.r ;
          double distance, dx, dy ;
	  double maximum ;
          dx = x - n.x ;
	  dy = y - n.y ;
	  distance = (dx * dx ) + (dy * dy) ;
	  maximum = ( r * r ) ;
	  if ( distance <= maximum )
	     return (true) ;
          else 
	     return (false ) ;
       }

       public void start(boolean player, boolean top) {
         this.player = player ;
	 backAudio.loop() ;
	 double R = 30.0 ;
	 winner = -1 ;  //There is no winner yet 
         for ( int i = 0 ; i <= nnodes - 1 ; i++ )
	    counters[i] = new Node(nodes[i]) ;
	 if ( top == true ){
           counters[0].state = counters[1].state = 1 ;
	   counters[3].state = counters[4].state = 2 ;
         }
	 else {
	   counters[0].state = counters[1].state = 2 ;
	   counters[3].state = counters[4].state = 1 ;
         }

	 counters[2].state = -1 ;
	 for(int i = 0 ; i <= nnodes - 1; i++)
	    counters[i].r = R ;

          relaxer = new Thread(this);
	  relaxer.start() ;
	  if ( player == false )
	     computersTurn();
       }
       public void stop() {
	  backAudio.stop() ;
          relaxer = null ;
       }
}

public class Game extends Applet implements ActionListener, ItemListener {


      GamePanel game ;
      AudioClip backAudio, pressAudio, releaseAudio, dragAudio ;
      Panel statusPanel, choicePanel ;
      Button startAgain ;
      Choice whoFirst ;
      Choice topBottom ;
      boolean player = true ;  // if Player goes first 
      boolean top = true ;     // if Player is at the top to start
      Image backGroundImage ;  // 
      Image playerCounter ;
      Image computerCounter ;
      Image motionCounter ;
      public void init() {

          startAgain = new Button("Start Game");

          whoFirst = new Choice();
          whoFirst.addItem("You Play First  ");             //0
          whoFirst.addItem("Computer Plays First");       //1

          topBottom = new Choice();
	  topBottom.addItem("Start at the Top");
	  topBottom.addItem("Start at the Bottom");

          System.out.println("getCodeBase: "+getCodeBase() );
          System.out.println("getDocumentBase: "+getDocumentBase() );
          backGroundImage = getImage(getCodeBase(),"background.gif") ;
          computerCounter = getImage(getCodeBase(),"computer.gif");
	  playerCounter = getImage(getCodeBase(),"player.gif");
	  motionCounter = getImage(getCodeBase(),"motion.gif");

          backAudio = getAudioClip(getCodeBase(),"background.au");
	  pressAudio = getAudioClip(getCodeBase(),"press.au" );
	  releaseAudio = getAudioClip(getCodeBase(), "release.au");
	  dragAudio = getAudioClip(getCodeBase(), "drag.au" );


          setLayout(new BorderLayout());
	  game = new GamePanel(this);
	  game.backGroundImage = backGroundImage ;
	  game.computerCounter = computerCounter ;
	  game.playerCounter = playerCounter ;
	  game.motionCounter = motionCounter ;
	  game.soundOn = 0 ;

          game.backAudio = backAudio ;
	  game.pressAudio = pressAudio ;
	  game.releaseAudio = releaseAudio ;
          game.dragAudio = dragAudio ;

	  game.setBackground(Color.white);
	  add("Center", game);

	  statusPanel = new Panel();
          statusPanel.setBackground(Color.white);
	  choicePanel = new Panel();
	  choicePanel.setLayout(new BorderLayout());
	  choicePanel.add("South",whoFirst) ; whoFirst.addItemListener(this);
	  choicePanel.add("North",topBottom) ; topBottom.addItemListener(this);
	  statusPanel.add(choicePanel);
	  statusPanel.add(startAgain) ; startAgain.addActionListener(this);
	  add("South", statusPanel);
	  
      }
      public void start() {
	game.soundOn = 0 ;
        game.start(player,top) ;
	
      }
      public void stop() {
	game.soundOn = -1 ;
        game.stop() ;

      }
      public void destroy() {
        remove(game);
	remove(statusPanel);
      }
      public void actionPerformed(ActionEvent e) {
	Object src = e.getSource();
        if ( src == startAgain ){
	  game.stop();
	  game.start(player,top) ;
	  return;
        }
	return;
      }
      public void itemStateChanged(ItemEvent e){
        Object src = e.getSource();
	int iselected ;
        if ( src == whoFirst){
	  iselected = whoFirst.getSelectedIndex();
	  if ( iselected == 0 )
	    player = true ;
	  else 
	    player = false ;
	   return ; 
	}
	if ( src == topBottom ){
	  iselected = topBottom.getSelectedIndex();
	  if ( iselected == 0 )
	    top = true ;
          else
	    top = false ;
          return ;
        }
	return ;
      }
}
