/* 
java jode 
this is an applet that demonstraits the dining philosophers problem
jdk 1.00
*/

import java.awt.*;
import java.applet.Applet;

public class DiningPhil extends java.applet.Applet 
{
  String audio;
  boolean slide1click=false, slide2click=false,   slide3click=false;
  sticks Sticks[] = new sticks[5];
  ScenePainter Scn;
  Philosopher Phil;
  Thread au=null;
  Thread bu=null;
  Thread cu=null;
  Thread du=null;
  Thread eu=null;

  public void init() 
    {
      audio="eat";
      
      Sticks[0]=new sticks();
      Sticks[1]=new sticks();
      Sticks[2]=new sticks();
      Sticks[3]=new sticks();
      Sticks[4]=new sticks();
      
      Sticks[0].returnStick(7);
      Sticks[1].returnStick(3);
      Sticks[2].returnStick(3);
      Sticks[3].returnStick(3);
      Sticks[4].returnStick(3);

      Scn=new ScenePainter(this);

      Phil=new Philosopher(Sticks,Scn);
      
      au=new Thread(Phil);
      bu=new Thread(Phil);
      cu=new Thread(Phil);
      du=new Thread(Phil);
      eu=new Thread(Phil);
      System.out.println("we think, therefore we are");
      
      au.start();
      bu.start();
      cu.start();
      du.start();
      eu.start();
    }
  
  public void start()
    {
      System.out.println("we live");
      
      au.resume();
      bu.resume();
      cu.resume();
      du.resume();
      eu.resume();
    }
  
  public void stop()
    {
      System.out.println("help, they're comming to stop us");
       
      au.suspend();
      bu.suspend();
      cu.suspend();
      du.suspend();
      eu.suspend();
    }
  public void destroy()
    {
      System.out.println("help, they're comming to killing us!");

      au.stop();
      bu.stop();
      cu.stop();
      du.stop();
      eu.stop();
    }
  
  public void paint(Graphics g)
    {
/*only draw things that might have changed */
      Scn.drawScene(g);
	  
    }
  
  public void update(Graphics g)
    {
      /* pretty cheesey, but gets rid of flash */
      paint(g);
    }
  
  public boolean mouseDown(java.awt.Event evt, int x, int y)
    {
/* grab bullet */
      int by2;
      
      if(y<20)
	{
	  System.out.println("eh?");
	  System.out.println("number of threads:"+Thread.activeCount());
	}
      
      if((y>Scn.buttony)&&(y<Scn.buttony+16))
	{
	  if((x>Scn.buttonx)&&(x<Scn.buttonx+16)) audio="eat";
	  if((x>Scn.buttonx+Scn.buttonspace)&&
	     (x<Scn.buttonx+Scn.buttonspace+16)) audio="talk";
	  if((x>Scn.buttonx+Scn.buttonspace*2)&&
	     (x<Scn.buttonx+Scn.buttonspace*2+16)) audio="laugh";
	  if((x>Scn.buttonx+Scn.buttonspace*3)&&
	     (x<Scn.buttonx+Scn.buttonspace*3+16)) audio=null;
	}
      
/*      if((y>60)&&(y<76)&&(x>(Scn.slide3-8))&&(x<(Scn.slide3+8))) 
	slide3click=true;
*/      
      y=y-Scn.slide1y;
      x=x-Scn.slidexl-8;
      
      
      if((y>0 )&&(y<16)&&(x>(Scn.slide1-8))&&(x<(Scn.slide1+8))) slide1click=true;
      if((y>30)&&(y<46)&&(x>(Scn.slide2-8))&&(x<(Scn.slide2+8))) slide2click=true;
      if((y>60)&&(y<76)&&(x>(Scn.slide3-8))&&(x<(Scn.slide3+8))) slide3click=true;
      
      
      return true;
    }
  
  public boolean mouseExit(java.awt.Event evt)
    {
/* let go bullet */
      slide1click=false;
      slide2click=false;
      slide3click=false;
      return true;
    }
  
  public boolean mouseUp(java.awt.Event evt, int x, int y)
    {
/* let go bullet */
      slide1click=false;
      slide2click=false;
      slide3click=false;
      return true;
    }
  
  public boolean mouseDrag(java.awt.Event evt, int x, int y)
    {
/* handles sliders, and sets parameters */
      y=y-Scn.slide1y;
      x=x-Scn.slidexl-8;
      int len=Scn.slidexr-Scn.slidexl-16;
      
      if(slide1click)
	{
	  if(x<=0) x=0;
	  if(x>=len) x=len;
	  Phil.speed=len-x;
	  Phil.speed=(Phil.speed*Phil.speed)/len;
	  Scn.slide1=x;
	  Scn.slideDraw(Scn.slide1y);
	}
      
      
      if(slide2click)
	{
	  if(x<=0) x=0;
	  if(x>=len) x=len;
	  Phil.ThinkChance=(float)x/len;
	  Phil.ThinkChance*=Phil.ThinkChance;
	  Phil.ThinkChance=1-Phil.ThinkChance;
	  Scn.slide2=x;
	  Scn.slideDraw(Scn.slide2y);
	}
      
      if(slide3click)
	{	
	  if(x<=0) x=0;
	  if(x>=len) x=len;
	  Phil.EatChance=1-(float)x/len;
	  Phil.EatChance=Phil.EatChance*Phil.EatChance;
	  Scn.slide3=x;
	  Scn.slideDraw(Scn.slide3y);
	}
      return true;
    }
}

class ScenePainter extends Object
{
  boolean dobg;
  boolean philChanged[] = new boolean[5];
  Image bullet;
  Image phil[] = new Image[5];
  Image oldphil[] = new Image[5];
  Image thinkingphil[][] = new Image[5][4];
  Image hungryphil[][] = new Image[5][4];
  Image eatingphil[][] = new Image[5][4];
  Image background;
  int philX[] = new int[5];
  int philY[] = new int[5];
  int philW[] = new int[5];
  int philH[] = new int[5];
  DiningPhil C;
  int slide1=185,slide2=185,slide3=185,oldslide1,oldslide2,oldslide3;

  final int xoffset =-80 ,yoffset=-100;
  final int buttonx=xoffset+80+73, buttony=yoffset+138+398, buttonspace=90;
  final int slide1y=yoffset+138+433, slide2y=yoffset+138+463, slide3y=yoffset+138+493;
  final int slidexl=xoffset+80+60,slidexr=xoffset+80+430;
  final int AsizeX=440, AsizeY=550;

  public ScenePainter(DiningPhil c)
    {
      C=c;
      oldslide1=slide1;
      oldslide2=slide2;
      oldslide3=slide3;
      initializeImages();
      
    }

  void initializeImages()
    {
/* load in lots of pretty pictures */
      int i,j;
      MediaTracker MT=new MediaTracker(C);

      background=C.getImage(C.getDocumentBase(), "images/background.gif");
      MT.addImage(background,0);
      if(background==null) System.out.println("no picture ");
      
      bullet=C.getImage(C.getDocumentBase(), "images/b2.gif");
      MT.addImage(bullet,5);
      

      for(i=0;i<5;i++)
	{
	  
	  for(j=0;j<4;j++)
	    {
	      thinkingphil[i][j]=C.getImage(C.getDocumentBase(), "images/thinking"+j+"."+i+".gif");
	      MT.addImage(thinkingphil[i][j],5);
	    }
	  
	  for(j=0;j<4;j++)
	    {
	      hungryphil[i][j]=C.getImage(C.getDocumentBase(), "images/hungry"+j+"."+i+".gif");
	      MT.addImage(hungryphil[i][j],5);
	    }
	  
	  for(j=0;j<4;j++)
	    {
	      eatingphil[i][j]=C.getImage(C.getDocumentBase(), "images/eating"+j+"."+i+".gif");
	      MT.addImage(eatingphil[i][j],5);
	    }
	}

      if((MT.COMPLETE&MT.statusAll(true))==0)
	{ /* added stuff */

/*
 System.out.println("waiting for images ");
	  try {MT.waitForAll();}
	  catch (InterruptedException e) {}
 System.out.println("done waiting");
*/
	  if(MT.isErrorAny())
	    {
	      Object O[];
	      System.out.println("Error loading images");
	      O = MT.getErrorsAny();
	      for(i=0;;i++)
		{
		  System.out.println(O[i]);
		}
	    }
/*
	  C.getPeer().getGraphics().drawImage(bullet, 20, 20, null);

	  try {Thread.sleep(100000);} catch (InterruptedException e){}
*/
	}

      phil[0]=thinkingphil[0][0];
      phil[1]=thinkingphil[1][0];
      phil[2]=thinkingphil[2][0];
      phil[3]=thinkingphil[3][0];
      phil[4]=thinkingphil[4][0];

      philX[0]=xoffset+216;     philY[0]=yoffset+181;
      philX[1]=xoffset+335;     philY[1]=yoffset+187;
      philX[2]=xoffset+379;     philY[2]=yoffset+261;
      philX[3]=xoffset+245;     philY[3]=yoffset+318;
      philX[4]=xoffset+138;     philY[4]=yoffset+247;

      for(i=0;i<5;i++)
	{
	  philH[i]=phil[i].getHeight(null);
	  philW[i]=phil[i].getWidth(null);
	}

    }

  void drawScene(Graphics g)
    {
      Rectangle R;
      R=g.getClipRect();
      dobg=(R.x!=1)||(R.y!=1)||(R.width!=AsizeX-2)||(R.height!=AsizeY-2);
/* redraw backgound if repaint wasnt called by me.
  (yea, yea, its a hack . . . )
  if the system calls repaint in the center of the applet, 
   the backgroung may not be redrawn
*/
/*      System.out.println("painting background" + R.x + " " +R.y +","+
			 R.width+","+R.height+",' "+dobg);
*/

      if(dobg) 
	{
	  
	  if(background==null) return;
	  g.drawImage(background,xoffset+80,yoffset+138, null);
	  g.drawImage(bullet, buttonx, buttony, null);
	  g.drawImage(bullet, buttonx+buttonspace, buttony, null);
	  g.drawImage(bullet, buttonx+buttonspace*2, buttony, null);
	  g.drawImage(bullet, buttonx+buttonspace*3, buttony, null);
	  g.drawString("Speed",xoffset+80,slide1y+12);
	  g.drawString("Think",xoffset+80,slide2y+12);
	  g.drawString("Eat",xoffset+80,slide3y+12);
	  g.drawLine(slidexl,slide1y+8,slidexr,slide1y+8);
	  g.drawLine(slidexl,slide2y+8,slidexr,slide2y+8);
	  g.drawLine(slidexl,slide3y+8,slidexr,slide3y+8);
	}
      
      if(philChanged[0]) g.drawImage(phil[0],philX[0],philY[0], null);
      if(philChanged[1]) g.drawImage(phil[1],philX[1],philY[1], null);
      if(philChanged[2]) g.drawImage(phil[2],philX[2],philY[2], null);
      if(philChanged[3]) g.drawImage(phil[3],philX[3],philY[3], null);
      if(philChanged[4]) g.drawImage(phil[4],philX[4],philY[4], null);
      
      if((oldslide1!=slide1)||dobg)
	{
	  g.clearRect(slidexl+oldslide1, slide1y,16,16);
	  g.drawLine(slidexl+oldslide1,slide1y+8,slidexl+oldslide1+16,slide1y+8);
	  g.drawImage(bullet, slidexl+slide1, slide1y, null);
	  oldslide1=slide1;
	}
      
      if((oldslide2!=slide2)||dobg)
	{
	  g.clearRect(slidexl+oldslide2, slide2y,16,16);
	  g.drawLine(slidexl+oldslide2,slide2y+8,slidexl+oldslide2+16,slide2y+8);
	  g.drawImage(bullet, slidexl+slide2, slide2y, null);
	  oldslide2=slide2;
	}
      
      if((oldslide3!=slide3)||dobg)
	{
	  g.clearRect(slidexl+oldslide3, slide3y,16,16);
	  g.drawLine(slidexl+oldslide3,slide3y+8,slidexl+oldslide3+16,slide3y+8);
	  g.drawImage(bullet, slidexl+slide3, slide3y, null);
	  oldslide3=slide3;
	}
      synchronized (this)
	{
	  notifyAll();
	}
    }


  void slideDraw(int s)
    {
      C.repaint(0,s,slidexr,16);
    }

  void thinkingphil(int mynumber, int n)
    {
      phil[mynumber]=thinkingphil[mynumber][n];      
      redraw(mynumber);
    }
  void hungryphil(int mynumber, int n)
    {
      phil[mynumber]=hungryphil[mynumber][n];
      redraw(mynumber);
    }
  void eatingphil(int mynumber, int n)
    {
      phil[mynumber]=eatingphil[mynumber][n];
      redraw(mynumber);
    }

  synchronized void redraw(int mynum)
    {
      philChanged[mynum]=true;
      C.repaint(1,1,AsizeX-2,AsizeY-2);
/*      C.repaint(philX[mynum],philY[mynum],
		philW[mynum],philH[mynum]);
*/	{ /* so the phils dont get ahead of the painting */
	  try {	  wait();} 
	  catch (java.lang.InterruptedException e) {}
	}
      philChanged[mynum]=false;
    }
  
  
  void playSound(int mynumber)
    {
      if(C.audio!=null) C.play(C.getDocumentBase(),
			     "audio/"+C.audio+mynumber+".au");
    }

}

class Philosopher extends Thread
{
  sticks Sticks[];
  ScenePainter Scene;
  int ticketnumber=0;
  float EatChance=.25f;
  float ThinkChance=.75f;
  int speed=80;
  int thishand[] = new int[5];
  int thathand[] = new int[5];
  
  
/* the phil process */	

  Philosopher(sticks[] St, ScenePainter Sc)
    {
      Sticks=St; Scene=Sc;
    }
  public void run()
    {
      int mynumber=getTicket();
      for(;;)
	{
	  think(mynumber);
	  PickUpSticks(mynumber);
	  eat(mynumber);
	  DropSticks(mynumber);
	}
    }
    
  void think(int mynumber)
    {
      int i=0;
      
      while(i<4)
	{
	  Scene.thinkingphil(mynumber,i);
	  i+= (Math.random()<ThinkChance) ? 1 : -1;
	  try {Thread.sleep(speed*8);} catch (InterruptedException e){}
	  if(i<0) i=0;
	}
      Scene.thinkingphil(mynumber,0);
      
    }
  
  void eat(int mynumber)
    {
      do
	{
	  Scene.eatingphil(mynumber,0);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.eatingphil(mynumber,1);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.eatingphil(mynumber,2);

	  Scene.playSound(mynumber);

	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.eatingphil(mynumber,3);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.eatingphil(mynumber,2);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.eatingphil(mynumber,1);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.eatingphil(mynumber,0);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Scene.thinkingphil(mynumber,0);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	} while(Math.random()>EatChance);
    }

  void PickUpSticks(int n)
    {
/*  picks up stickes
a phil must have both sticks before it can eat*/

      int thisside,thatside;
      if(.5f>Math.random())
	{
	  thisside= (n<4) ? n+1 : 0;
	  thatside= n;
	}
      else
	{
	  thatside= (n<4) ? n+1 : 0;
	  thisside= n;
	}
/* pick up a stick */
      Scene.hungryphil(n,1);
      try {Thread.sleep(speed);} catch (InterruptedException e){}
      thishand[n]=Sticks[thisside].waitForStick();
      if(thishand[n]==7)
	{
/* if its the magic stick, drop, and pick up other sticks */
	  Scene.hungryphil(n,2);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  Sticks[thisside].returnStick(thishand[n]);
/* a deadlock is possible if the magic stick goes all the way around the 
   table to "thathand",  dont think it can happen though */

	  
	  thathand[n]=Sticks[thatside].waitForStick();
	  
	  Scene.hungryphil(n,3);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  thishand[n]=Sticks[thisside].waitForStick();
	  
	  Scene.hungryphil(n,0);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  
	}
      else
	{ 
/* get other stick */
	  Scene.hungryphil(n,0);
	  try {Thread.sleep(speed);} catch (InterruptedException e){}
	  thathand[n]=Sticks[thatside].waitForStick();
	}
    }

  void DropSticks(int n)
    {
/* drop sticks */
      int thisside,thatside;
      if(.5f>Math.random())
	{
	  thisside= (n<4) ? n+1 : 0;
	  thatside= n;
	}
      else
	{
	  thatside= (n<4) ? n+1 : 0;
	  thisside= n;
	}
      
      Sticks[thisside].returnStick(thathand[n]);
      Sticks[thatside].returnStick(thishand[n]);
    }
  
  synchronized int getTicket()
    {
/* so each philnumber will be unique */	
      return ticketnumber++;
    }
}
  

/* an instance of the class is created for each stick */
class sticks
{
  int Stick=0;

  int waitForStick()
    {
      return stickstuff(0);
    }
  void returnStick(int s)
    {
      stickstuff(s);
    }

  synchronized int stickstuff( int s)
/* make sure phils dont read and write at the same time */
    {
      if(s!=0)
	{
	  Stick=s;
	  notify();
	  return 0;
	}
      else
	{
	  if(Stick<1)
	    {
	      Stick=-4;
	      try {wait();} catch (InterruptedException e) {}
	    }
	  s=Stick;
	  Stick=0;
	  return s;
	}
    }
}
