//------------------ Main Class -------------------//

import java.awt.*;
import java.applet.*;
import java.io.*;
import java.lang.String;
import java.util.Random;
import java.util.Date;

public class myPoint 
{
 int h,v;
}

public class myPieceType
 {
       myPoint   loc[];
       int       halfindex[][];
       boolean   up[],full[];
       
     public myPieceType()
     {
      int i;
         loc=new myPoint[12]; for(i=0;i<12;i++) loc[i]=new myPoint();
         halfindex=new int[12][2];
         up=new boolean[12];
         full=new boolean[12];
      }
       
} 

public class myPieceTypeClass
{
     myPieceType   mytype[];
     int           totalnum,num0,num1; 
     
     public myPieceTypeClass()
     {
      int i;
           mytype=new myPieceType[6]; for(i=0;i<6;i++) mytype[i]=new myPieceType();
     }
     
}  

public class  myHistory
{
  int     ID;
  boolean  up;
} 


public class Eternity extends Applet  implements Runnable 
{
	 int       WIDTH,HEIGHT,CellSize,Width,Height,offW,offH,SS[],SS2[],PP[],badP[],gNum;
     Graphics  g2,off_g;
     Random    nrand;
     Image     offscreen;
     myCell    gCell[];
     myPiece   gPiece[];
     Color     gColor[];
     myPieceTypeClass gPieceType[];
     myCellUtility     cellU;
     myTypeUtility     typeU;
     myHistory         history[];
    
     boolean    MOVE=false;
     Thread    runner;
     
      int      oldID,badcounter=0,badNum=0,badcounter2=0,badhisID2,count=0,count2=0;
      boolean   existbad=false,existbad2=false;

     
       public void start()
	{
		if (runner == null)
		{
			runner= new Thread(this);
			runner.start();
		}
	}
	
	public void stop()
	{
		if (runner!=null)
		{
			runner.stop();
			runner=null;
		}
	}
	
   
	public void run() //----------------------------------------//
	{
	  // init(); 
		
		while (true) {
			try { Thread.sleep(10); } catch (InterruptedException e){};
			if ( isVisible()  ) {
		 	
	            DomyRun();
	  	     //	paint(g2);
			}
		}
		  
	}
    
	 public int randomise(Random nrand,int range)
	 {
	    if(range==0) return 0;
		return( java.lang.Math.abs(nrand.nextInt()) % range);
	 }
	
	public void init() 
	{
	 int i;
	 mySetUpPiece0 piece0;
	 mySetUpPiece1 piece1;
	 mySetUpPiece2 piece2;
	 mySetUpPiece3 piece3;
	 mySetUpPiece4 piece4;
	 mySetUpPiece5 piece5;
	 mySetUpPiece6 piece6;
	 mySetUpPiece7 piece7;
	 mySetUpPiece8 piece8;
	 mySetUpPiece9 piece9;
	 mySetUpPiece10 piece10;
	 mySetUpPiece11 piece11;
	 mySetUpPiece12 piece12;
	 mySetUpPiece13 piece13;
	 mySetUpPiece14 piece14;
	 mySetUpPiece15 piece15;
	 mySetUpPiece16 piece16;
	 mySetUpPiece17 piece17;
	 mySetUpPiece18 piece18;
	 mySetUpPiece19 piece19;
	 mySetUpPiece20 piece20;
	
	  
	 	Date d=new Date();
		nrand=new Random(d.getTime());
	 	 
		WIDTH=this.size().width;
		HEIGHT=this.size().height;
		
		offW=WIDTH;
		offH=HEIGHT-30;
		
	    offscreen = this.createImage(offW, offH);
        off_g = offscreen.getGraphics();
        off_g.setColor(Color.black);
        off_g.fillRect(0,0,offW,offH);
	   
		g2=this.getGraphics();
		g2.setColor(Color.black);
        g2.fillRect(0,0,WIDTH,HEIGHT);
       
		//--------------------------------//
		
	   this.setLayout(new FlowLayout(FlowLayout.LEFT));
       
       add(new Button("Run"));
       add(new Button("Stop"));
       
         cellU=new myCellUtility();
         typeU=new myTypeUtility();
        
       gPiece=new myPiece[209];
      
       for(i=0;i<209;i++){
         gPiece[i]=new myPiece();
         gPiece[i].DoInit(i);
       }
    
       DoInitSet();
        
        SS=new int[209]; 
        SS2=new int[209]; 
        PP=new int[209];  
        badP=new int[209]; 
        history=new myHistory[100]; for(i=0;i<100;i++) history[i]=new myHistory();
    
     //---------------------------//
      gColor=new Color[209];
      
       for(i=0;i<209;i++){
         float f0,f1,f2;
         int   n0;
         
          f0=(float)i*0.0047f;
          n0=(i*5) % 209;  f1=(float)n0*0.0047f;
          n0=(i*9) % 209;  f2=(float)n0*0.0035f + 0.25f;
         
         gColor[i]=new Color(Color.HSBtoRGB(f0,f1,f2));
      }
     //----------------------------// 
    
     piece0 =new  mySetUpPiece0();
     piece1 =new  mySetUpPiece1();
     piece2 =new  mySetUpPiece2();
     piece3 =new  mySetUpPiece3();
     piece4 =new  mySetUpPiece4();
     piece5 =new  mySetUpPiece5();
     piece6 =new  mySetUpPiece6();
     piece7 =new  mySetUpPiece7();
     piece8 =new  mySetUpPiece8();
     piece9 =new  mySetUpPiece9();
     piece10=new  mySetUpPiece10();
     piece11=new  mySetUpPiece11();
     piece12=new  mySetUpPiece12();
     piece13=new  mySetUpPiece13();
     piece14=new  mySetUpPiece14();
     piece15=new  mySetUpPiece15();
     piece16=new  mySetUpPiece16();
     piece17=new  mySetUpPiece17();
     piece18=new  mySetUpPiece18();
     piece19=new  mySetUpPiece19();
     piece20=new  mySetUpPiece20();
     
     gPieceType=new myPieceTypeClass[209];
     for(i=0;i<209;i++) gPieceType[i]=new myPieceTypeClass();
     
     piece0.TP_DoSetUpEternityPiece0(gPieceType);
     piece1.TP_DoSetUpEternityPiece1(gPieceType);
     piece2.TP_DoSetUpEternityPiece2(gPieceType);
     piece3.TP_DoSetUpEternityPiece3(gPieceType);
     piece4.TP_DoSetUpEternityPiece4(gPieceType);
     piece5.TP_DoSetUpEternityPiece5(gPieceType);
     piece6.TP_DoSetUpEternityPiece6(gPieceType);
     piece7.TP_DoSetUpEternityPiece7(gPieceType);
     piece8.TP_DoSetUpEternityPiece8(gPieceType);
     piece9.TP_DoSetUpEternityPiece9(gPieceType);
     piece10.TP_DoSetUpEternityPiece10(gPieceType);
     piece11.TP_DoSetUpEternityPiece11(gPieceType);
     piece12.TP_DoSetUpEternityPiece12(gPieceType);
     piece13.TP_DoSetUpEternityPiece13(gPieceType);
     piece14.TP_DoSetUpEternityPiece14(gPieceType);
     piece15.TP_DoSetUpEternityPiece15(gPieceType);
     piece16.TP_DoSetUpEternityPiece16(gPieceType);
     piece17.TP_DoSetUpEternityPiece17(gPieceType);
     piece18.TP_DoSetUpEternityPiece18(gPieceType);
     piece19.TP_DoSetUpEternityPiece19(gPieceType);
     piece20.TP_DoSetUpEternityPiece20(gPieceType);
   
     
    { myPieceType type=new myPieceType();
        for(i=0;i<209;i++){
          typeU.TP_DomyCopyType(gPieceType[i].mytype[0],type);
          typeU.doAddThisType(type,i,gPieceType);
         }
     }
     
	}
	
	public void paint(Graphics G)
	{
		 G.drawImage(offscreen, 0, 30,Color.black,this);
	}
	
	
	private void DoReDrawOff()
	{
	 int i;
	      
        off_g.setColor(Color.black);
        off_g.fillRect(0,0,offW,offH);
        
        off_g.setColor(Color.white);
	    for(i=0;i<1278;i++) gCell[i].DoDrawFrame(off_g,0);
	 	  
		g2.drawImage(offscreen, 0, 30,Color.black,this);
	 
	}
	
   public boolean action(Event evt,Object arg)
   {
     if(evt.target instanceof Button) {
     
         if(((String)arg).equals("Run"))  DoMakeNewSet();
         else if(((String)arg).equals("Stop")) MOVE=false;
      
     }
     
     return true;
   }
   

   //---------------------- Init --------------------------//
   
private void DoInitSet()
{
 int     i,j;
 myPoint  p0=new myPoint(),p1=new myPoint();
 boolean  up0,up1;
 
    gCell=new  myCell[1278];
    for(i=0;i<1278;i++) {
       gCell[i]=new myCell();
       gCell[i].DoInit();
    }
  
    for(i=0;i<19;i++) gCell[i].DoSetLoc(i,0);
    for(i=0;i<25;i++) gCell[i+19].DoSetLoc(i,1);
    for(i=0;i<31;i++) gCell[i+44].DoSetLoc(i,2);
    for(i=0;i<37;i++) gCell[i+75].DoSetLoc(i,3);
    for(i=0;i<39;i++) gCell[i+112].DoSetLoc(i,4);
    for(i=0;i<41;i++) gCell[i+151].DoSetLoc(i,5);
    for(i=0;i<43;i++) gCell[i+192].DoSetLoc(i,6);
    for(i=0;i<45;i++) gCell[i+235].DoSetLoc(i,7);
    for(i=0;i<47;i++) gCell[i+280].DoSetLoc(i,8);
    for(i=0;i<49;i++) gCell[i+327].DoSetLoc(i,9);
    for(i=0;i<51;i++) gCell[i+376].DoSetLoc(i,10);
    for(i=0;i<53;i++) gCell[i+427].DoSetLoc(i,11);
    for(i=0;i<53;i++) gCell[i+480].DoSetLoc(i,12);
    for(i=0;i<53;i++) gCell[i+533].DoSetLoc(i,13);
    for(i=0;i<53;i++) gCell[i+586].DoSetLoc(i,14);
    for(i=0;i<53;i++) gCell[i+639].DoSetLoc(i,15);
    for(i=0;i<53;i++) gCell[i+692].DoSetLoc(i,16);
    for(i=0;i<53;i++) gCell[i+745].DoSetLoc(i,17);
    for(i=0;i<53;i++) gCell[i+798].DoSetLoc(i,18);
    for(i=0;i<51;i++) gCell[i+851].DoSetLoc(i,19);
    for(i=0;i<49;i++) gCell[i+902].DoSetLoc(i,20);
    for(i=0;i<47;i++) gCell[i+951].DoSetLoc(i,21);
    for(i=0;i<45;i++) gCell[i+998].DoSetLoc(i,22);
    for(i=0;i<43;i++) gCell[i+1043].DoSetLoc(i,23);
    for(i=0;i<41;i++) gCell[i+1086].DoSetLoc(i,24);
    for(i=0;i<39;i++) gCell[i+1127].DoSetLoc(i,25);
    for(i=0;i<37;i++) gCell[i+1166].DoSetLoc(i,26);
    for(i=0;i<31;i++) gCell[i+1203].DoSetLoc(i,27);
    for(i=0;i<25;i++) gCell[i+1234].DoSetLoc(i,28);
    for(i=0;i<19;i++) gCell[i+1259].DoSetLoc(i,29);
    
    for(i=0;i<1278;i++) gCell[i].DoSetupUpDown();
   
      
    for(i=0;i<1277;i++){
       for(j=i+1;j<1278;j++){
       
           gCell[i].DoGetLoc(p0);
           gCell[j].DoGetLoc(p1);
           
           up0=gCell[i].DoGetUP();
           up1=gCell[j].DoGetUP();
       
          if(DoCheckNearCell(p0,p1,up0,up1)) doAddEach(i,j);
       }
     } 
 
    for(i=0;i<1278;i++) gCell[i].DoSetUpmyLoc(gCell);
    for(i=0;i<1278;i++) gCell[i].DoSetEdge();  
}

private boolean  DoCheckNearCell(myPoint P0,myPoint P1,boolean up0,boolean up1)
{
 int      s0,s1;
 
   s0=myabs(P0.v - P1.v); if(s0>1) return false;
   
   if(s0==0) {
     s1=myabs(P0.h - P1.h); 
     if(s1==1) return true;
     else      return false;
    }
   
    s0= cellU.DoGetCellDistW(P0);
    s1= cellU.DoGetCellDistW(P1);
    if(s0!=s1) return false;
    
    if(P0.v < P1.v) { if(up0 && !up1) return true;}
    else { if(!up0 && up1) return true;}
   
   return false;

}

private void     doAddEach(int ID0,int ID1)
{
 int i,id0=-1,id1=-1;
 
     for(i=0;i<3;i++){
      if(!gCell[ID0].DoGetOpen(i)) { id0=i; break;}
     }
     if(id0==-1) return;
     
     for(i=0;i<3;i++){
      if(!gCell[ID1].DoGetOpen(i)) { id1=i; break;}
     }
     if(id1==-1) return;

     gCell[ID0].DoSetDoorNBID(id0,0,ID1); //doorNB[id0][0]=ID1;
     gCell[ID0].DoSetDoorNBID(id0,1,id1); // doorNB[id0][1]=id1;
     gCell[ID0].DoSetOpen(id0,true); // open[id0]=true;
  
     gCell[ID1].DoSetDoorNBID(id1,0,ID0); // doorNB[id1][0]=ID0;
     gCell[ID1].DoSetDoorNBID(id1,1,id0); // doorNB[id1][1]=id0;
     gCell[ID1].DoSetOpen(id1,true); // open[id1]=true;
}

private int myabs(int s0)
{
  if(s0<0) return -s0;
  else return s0;
}
//--------------------------------------------------//

   
private void  DoMakeNewSet()
{
 int i;
 
     for(i=0;i<1278;i++) gCell[i].DoResetPieceID();
     for(i=0;i<209;i++)  gPiece[i].DoSetExist(false);
     DoReDrawOff();
     MOVE=true;
     gNum=0;
}

private void DomyRun() 
{
 int i,j,k;
 boolean ok=false;

 
     if(!MOVE) return;
     
   
      if(gNum==0){//-------------------//
         int id,s0=0;
         
          for(i=0;i<1278;i++) gCell[i].DoResetPieceID();
          for(i=0;i<209;i++)  gPiece[i].DoSetExist(false);
     
          ok=false;
          while(true){
           id=randomise(nrand,209);
           if(gPiece[id].DoContactEdge(nrand,gPieceType,gCell,cellU,typeU)) { ok=true; break;}
           s0++; if(s0>200) break;
          }
          if(!ok) return;
          
          PP[0]=oldID=id;
          gNum ++;
          DoReDrawOff();
          
           g2.setColor(gColor[id]);
           gPiece[id].DoReDrawFrame(g2,gCell,30);
          
          existbad=false;   badNum=0;     badcounter=0;
          existbad2=false;  badcounter2=0;  for(k=0;k<100;k++) history[k].ID=-1;
          
          count=0;
          count2=0;
       }//----------------------------//
       
    
         domySort(SS2,SS);
         ok=false;
         for(i=0;i<209;i++){
           int mynextcellid;
          
             if(gPiece[SS[i]].DoGetExist())  continue;
             
             mynextcellid=cellU.PC_DoGetNextEdgeCell(oldID,gCell,gPiece);
             
            if(gPiece[SS[i]].DoContactNext(mynextcellid,nrand, gPieceType, gCell,cellU,typeU))  {
            
                if(doCheckRepeat(SS[i])){ 
                   gPiece[SS[i]].DoResetExist(gCell);
                   continue;
                }
            
                ok=true;
                PP[gNum]=SS[i]; gNum ++;  oldID=SS[i]; 
                
                if(existbad){//----------------//
                   badcounter ++;
                   if(badcounter>=2) existbad=false;
                }//----------------------------//
                
                if(existbad2){//----------------//
                  badcounter2 ++;
                  if(badcounter2>=3) existbad2=false; // 3
                }//-----------------------------//
                
                 doaddHistory(SS[i],true);
                
                // off_g.setColor(gColor[SS[i]]);
                // gPiece[SS[i]].DoReDrawFrame(off_g,gCell,0);
                 g2.setColor(gColor[SS[i]]);
                 gPiece[SS[i]].DoReDrawFrame(g2,gCell,30);
               // gPiece[ss[i]].DoReDrawFrame();
                
              if(doCheckIfEndEdge()) {//-------------------//
                    MOVE=false;
                    return;
              }//------------------------------------------//
                 
             }
             
             if(gNum==0) break;
         }
         
          
        //---------------- not exist -----------------------//
       if(!ok){
          boolean doback=false;
         
          gPiece[PP[gNum-1]].DoResetExist(gCell);
          doEraseDraw(PP[gNum-1]); //
          doaddHistory(PP[gNum-1],false);//
          
          if(!existbad){//-----------------------//
              existbad=true;
              badcounter=0;
              badNum=1;
              badP[0]=PP[gNum-1];
          }else {
            
              for(k=0;k=2) gPiece[PP[gNum-2]].DoResetExist(gCell);
                 existbad=false;
              }else {
                 badP[badNum]=PP[gNum-1];
                 badNum++;
              }
              
              badcounter --;
          
          }//-----------------------------------//
          
          
          if(!doback){//-------------------------//
             
               if(gNum>=2) oldID=PP[gNum-2];
               gNum --;
              
          }else {
               doaddHistory(PP[gNum-2],false);//////
               doEraseDraw(PP[gNum-2]);
          
               if(gNum>=3) oldID=PP[gNum-3];
               gNum -=2;
               
              if(existbad2)  badcounter2 --; ///////////
           }//----------------------------------//
          
          //------------------------------------------------//
        
           
         if(!existbad2){
             if(history[0].ID!=-1 && history[1].ID!=-1 && history[2].ID!=-1){
             
               if(!history[0].up && !history[1].up){
                  existbad2=true;
                  badcounter2=0;
                  badhisID2=history[0].ID;
                }
            }
          }else {
             badcounter2--;
             if(badcounter2<0) existbad2=false;
             else if(badcounter2==0){
             
                    if(badhisID2==history[0].ID){
                        existbad2=false;
                        
                        gPiece[PP[gNum-1]].DoResetExist(gCell);//
                        doaddHistory(PP[gNum-1],false);//////
                        doEraseDraw(PP[gNum-1]);
                        
                        if(gNum>=2) oldID=PP[gNum-2];
                        gNum --;
                    }
                    
               }
           } //---------------------------------------//
          
         
          
        }//------------------------------- not exist ----------------------------//
       
       
       { int s0;//------------------------//
           s0=doGetRemainEdgeNum();
           if(s0>=80) count ++;
          
          if(count>100){  count=0;  gNum=0; }
             
        }//--------------------------------//
      
   
}
   

private  void   domySort(int ss[],int ss2[])
{
 int i,j,num=209,s0;
 
   for(i=0;i<209;i++) ss[i]=i;
  
    for(i=0;i<209;i++){
        s0=randomise(nrand,num);
        ss2[i]=ss[s0];
        for(j=s0;j=1;i--) {
      history[i].ID=history[i-1].ID;
      history[i].up=history[i-1].up;
    }
      
    history[0].ID=id;
    history[0].up=up;

}

private boolean  doCheckRepeat(int id)
{
int i,num=0;

    if(history[0].up) return false;
 
   for(i=0;i<100;i++){
     if(history[i].ID==-1) continue;
     if(history[i].ID==id){  num ++; if(num>=5) return true;}
   }
   
    return false;
}

private int    doGetRemainEdgeNum()
{
 int i,num=0;
 
    for(i=0;i<1278;i++){
       if(!gCell[i].DoGetIfEdge()) continue;
       if(!gCell[i].DoCheckIfExistEmpty()) num++;
    }

    return num;
}

  
}