/*simplechess.java: a very simple chess playing applet.

made by Sieuwert van Otterloo, 1999.

smotterl@cs.uu.nl

*/

import java.awt.*;



class chessboard

{

    final static int OUT=8888,VOID=-1,PAWN=2,

    ROOK=6,KNIGHT=8,BISSHOP=10,

    QUEEN=12,KING=14,FRESHKING=114,FRESHROOK=106;

    int A[];

    /*The core of a chessboard is the array A. A consists of 144 integers.

    64 of them are a field of a chessboard, the rest have the value OUT. For the

    numbering: see the image elsewhere.

    A field can be empty: It then has the value VOID.

    If a field contains a white pawn, it has the value PAWN+WHITE.

    If it has a black knight, it's value is KNIGHT+BLACK. A FRESHKING

    is displayed as a king, moves like a king, but has as special property

    that it never moved, and that means it can castle. A KING cannot castle,

    because it has moved. The same counts for the ROOK.*/

    final static int ENPASSANT=3,CASTLE=5,CAASTLE=7;

    /*Most moves are defined by their first and last field. However,

    when a pawn reaches the last line, it can change into a rook, queen,

    knight or bisshop. These are  different moves, with the same from and

    to field. Therefor a move as an extra attribute, a modifyer. It can be

    zero by default, or KNIGHT, BISSHOP, ROOK, QUEEN by promotion.

    Other values are ENPASSANT, CASTLE by castling with the A rook, or

    CASTLE when castling with the H rook (long castling).*/



    final static int WHITE=0,BLACK=1;

    int oldmove[];

    int undoinf[];

    static int oldmovesize=64,maxlistsize=80;

    int moves;

    int status;

    final static int CHECKMATE=0,CHECK=1,DRAW=2,OPEN=3;

    chessview V;

    final static int[] knightjump={-10,10,-14,14,-23,23,-25,25};

    final static int[] kingjump={-1,1,-13,-12,-11,13,12,11};

    final static int[] bisshopdir={13,-13,11,-11};

    final static int[] rookdir={1,-1,12,-12};

    final static int NULLMOVE=0;

/*

NOTES: a rook/king is fresh if it didn't move yet. Only

fresh pieces may castle.

ENPASSANT=3,CASTLE=5,CAASTLE=7 are different from any piece,

and can be used as newpieces in a move to indicate a special move.

(-1%2)=-1. [used for VOID in getside().]

*/





    chessboard()

    {

        A=new int[144];

        for(int i=0;i<144;i++)

            A[i]=OUT;

        setup();

        oldmove= new int[oldmovesize];

        undoinf= new int[oldmovesize];

    }



    void setview(chessview aview) {V=aview;}



    chessboard(chessboard c)

    //clones a chessboard. only the view is not cloned, because

    //two chessboards cannot have the same view.

    {

        A=new int[144];

        for(int i=0;i<144;i++)

            A[i]=c.A[i];

        oldmove= new int[oldmovesize];

        undoinf= new int[oldmovesize];

        for(int i=0;i<oldmovesize;i++)

        {   oldmove[i]=c.oldmove[i];

            undoinf[i]=c.undoinf[i];

        }

        moves=c.moves;

        status=c.status;

    }



    void setup()

    //brings the board in opening position

    {

        for(int x=2;x<10;x++)

        {

         for(int j=4;j<8;j++)

            A[x+12*j]=VOID;

         int[] line={FRESHROOK,KNIGHT,BISSHOP,QUEEN,

                    FRESHKING,BISSHOP,KNIGHT,FRESHROOK};

         A[x+24]=line[x-2]+WHITE;

         A[x+12*9]=line[x-2]+BLACK;

         A[x+36]=PAWN+WHITE;

         A[x+8*12]=PAWN+BLACK;

        }

        moves=0;

        status=OPEN;

        if(V!=null)

            V.repaint();

    }



    void reset()

    {

        moves=0;

        if(check(getside())) status=CHECK;

        else status=OPEN;

        if(V!=null)

            V.repaint();

    }



    int getside() {return moves%2;}

    /*The side that can move*/



    int getking(int color)

    /*the position of the king of that side. returns a field [0,143] or -1.*/

    {

     int value1=KING+color;

     int value2=FRESHKING+color;

     for(int i=0;i<144;i++)

        if(A[i]==value1||A[i]==value2)

            return i;

     return -1;//should never happen.

    }



    boolean attack(int f,int side)

    /*is field f attacked by side?*/

    {

     int x,i;

     for(i=0;i<8;i++)

     {x=f+knightjump[i];

      if(inboard(x)&&getpiece(x)==KNIGHT&&getside(x)==side)

        return true;

      x=f+kingjump[i];

      if(inboard(x)&&getpiece(x)==KING&&getside(x)==side)

        return true;

     }



      for(int d=0;d<4;d++)

      {

       x=f;

       for(i=0;i<7;i++)

       {

        x+=rookdir[d];

        if(!inboard(x))

            break;

        if(getside(x)!=VOID)

        {

         int p=getpiece(x);

         if(getside(x)==side&&(p==ROOK||p==QUEEN))

                return true;

         else               break;

        }

       }

       x=f;

       for(i=0;i<7;i++)

       {

        x+=bisshopdir[d];

        if(!inboard(x))

            break;

        if(getside(x)!=VOID)

        {

         int p=getpiece(x);

         if(getside(x)==side&&(p==BISSHOP||p==QUEEN))

            return true;

         else               break;

        }

       }

      }

    int sign=(side==WHITE)? -1:1;

    x=f+sign*11;

    for(i=0;i<2;i++)

    {if(inboard(x)&&getpiece(x)==PAWN&&getside(x)==side)

        return true;

     x=f+sign*13;

    }

     /*enpassant is ignored.*/

     return false;

    }



    /*A move could be seen as an object:

    class move

    {int from;

     int to;

     int newpiece;

    }

    However, to keep speed we pack these three int in

    one with the following methods:

    */

    //packing:

    static int newmove(int from,int to){return from+144*to;}

    static int newmove(int from,int to, int newpiece)

                {return from+144*(to+144*newpiece);}

    //unpacking:

    static int movefrom(int move)   {return move%144;}

    static int moveto(int move)     {return (move/144)%144;}

    static int movenewpiece(int move){return move/(144*144);}



    boolean inboard(int f)

    //is a field inside the board?

    {

        if(f<0||f>=144)

            return false;

        return A[f]!=OUT;



    }

    int getside(int field)

    {

     if(!inboard(field))

        return WHITE;

     return A[field]%2;

    }

    /*getside returns BLACK,WHITE or VOID.*/



    int getpiece(int field) {return A[field]%100-A[field]%2;}

    boolean getfresh(int field) {return A[field]>100;}



    /*methods to do and undo moves.*/

    void visualdomove(int move)

    /*visual domove does the move, repaints the chessview, and sets the status

    correctly. domove just does the move. During calculations, domove is used

    because it is fast. Same for visualundomove.*/

    {domove(move);

     int[] movelist=listofmoves();

     if(getlength(movelist)>0)

     {

        if(check(getside())) status=CHECK;

        else status=OPEN;

     }

     else

     {  if(check(getside())) status=CHECKMATE;

        else status=DRAW;

     }

     if(V!=null)

        V.repaint();

    }



    void visualundomove()

    {undomove();

     if(check(getside())) status=CHECK;

        else status=OPEN;

     if(V!=null)

        V.repaint();

    }



    /*store and retrieve undo information.*/

    /*Again we  store both the captured piece v and the boolean

    f in one integer.*/

    void storemove(int m,int v,boolean f)

    {   oldmove[moves%oldmovesize]=m;

        if(f)   undoinf[moves%oldmovesize]=v+8888;

        else    undoinf[moves%oldmovesize]=v;

    }

    int getstoredm(int m)   {return oldmove[m%oldmovesize];}



    int getstoredvictim()

    /*Get the piece captured in the former move.*/

    {int value=undoinf[moves%oldmovesize];

     if(value<8000) return value;

     return value-8888;

    }

    boolean getstoredfresh() {return (undoinf[moves%oldmovesize]>8000);}



    void domove(int m)

    {

        if(m==NULLMOVE)

        {   storemove(m,VOID,false);

            moves++;

            return;

        }

        int from=movefrom(m);

        int to=moveto(m);

        int newpiece=movenewpiece(m);

        boolean fresh=false;

        if(A[from]>100)

        {   fresh=true;

            A[from]=A[from]-100;

        }

        int victim=A[to];

        A[to]=A[from];

        A[from]=VOID;

        if(newpiece==ENPASSANT)

        {//pawn moves diagonal to empty field: enpassant

         A[to%12+12*(from/12)]=VOID;//remove a pawn;

        }

        else if(newpiece==CASTLE)

        {A[from-from%12+9]=VOID;

         A[from-from%12+7]=ROOK+getside();

        }

        else if(newpiece==CAASTLE)

        {A[from-from%12+2]=VOID;

         A[from-from%12+5]=ROOK+getside();

        }

        else if(newpiece!=0)

            A[to]=newpiece+getside();

        storemove(m,victim,fresh);

        moves++;



    }

    void undomove()

    {

        if(moves==0)

            return;

        moves--;

        int m=getstoredm(moves);

        if(m==NULLMOVE)

            return;

        int from=movefrom(m);

        int to=moveto(m);

        int newpiece=movenewpiece(m);

        A[from]=A[to];

        A[to]=getstoredvictim();

        if(getstoredfresh())    A[from]+=100;

        if(newpiece==ENPASSANT)

            A[to%12+12*(from/12)]=PAWN+(1-getside());

        else if(newpiece==CASTLE)

        {A[from-from%12+7]=VOID;

         A[from-from%12+9]=FRESHROOK+getside();

        }

        else if(newpiece==CAASTLE)

        {A[from-from%12+5]=VOID;

         A[from-from%12+2]=FRESHROOK+getside();

        }

        else if(newpiece!=0)

        {

            A[from]=PAWN+getside();

        }

    }



    /*Methods for a list of moves.*/

    /*A list is a special kind of array: the first element a[0]

    is the number of stored items, a[1],a[2],... contain the items.

    Adding is faster this way: we do not have to search the first empty

    spot. The constant maxlistsize is the maximum size of the list. It must

    be chosen large enough.*/

    static int[] newlist()

    {

        int[] L=new int[maxlistsize+1];//maxsize

        L[0]=0;

        return L;

    }

    static int getlength(int[] L)  {return L[0];}

    static void add(int[] L,int i)

    {

        if(L[0]>=maxlistsize)

            System.out.println("list overflow, raise maxlistsize.");

        L[0]++;

        L[L[0]]=i;

        //System.out.println("add "+movefrom(i)+" "+moveto(i));

    }

    static void delete(int[] L,int index)

    {

        L[index]=L[L[0]];

        L[0]--;

    }

    static void promotionadd(int[] l,int from,int to)

    /*Add four pawn promotion moves.*/

    {

        int[] choice={QUEEN,KNIGHT,ROOK,BISSHOP};

        for(int i=0;i<4;i++)

            add(l,newmove(from,to,choice[i]));

    }



    /*The add*moves: add the moves for sch a * piece of side

    standing on f.*/

    void addpawnmoves(int[] list,int side,int f)

    {int forward,firstline,lastline;

     if(side==WHITE)

     {forward=12;

      firstline=3;

      lastline=8;

     }

     else

     {forward=-12;

      firstline=8;

      lastline=3;

     }

     if(f/12==lastline)

     { if(getside(f+forward)==VOID)

            promotionadd(list,f,f+forward);

       if(inboard(f+forward+1)&&getside(f+forward+1)==1-side)

            promotionadd(list,f,f+forward+1);

       if(inboard(f+forward-1)&&getside(f+forward-1)==1-side)

            promotionadd(list,f,f+forward-1);

       return;

     }

     if(getside(f+forward)==VOID)

     {add(list,newmove(f,f+forward));

      if(f/12==firstline&&getside(f+2*forward)==VOID)

        add(list,newmove(f,f+2*forward));

     }

     if(inboard(f+forward+1)&&getside(f+forward+1)==1-side)

        add(list,newmove(f,f+forward+1));

     if(inboard(f+forward-1)&&getside(f+forward-1)==1-side)

        add(list,newmove(f,f+forward-1));

    }



    void addjumpmoves(int[] list,int side,int f,int[] jump)

    /*addknightmoves()=addjumpmoves(,,,knightvector)

      addkingmoves()=addjumpmoves(,,,kingvector)

    */

    {

     for(int i=0;i<8;i++)

      if(inboard(f+jump[i])&&getside(f+jump[i])!=side)

            add(list,newmove(f,f+jump[i]));

    }

    void addslidemoves(int[] list,int side,int f,int dir)

    /*addrookmoves= for each rookdir[] addslidemoves.

    addbisshopmoves= for each bisshopdir[] addslidemoves.

    addqueenmoves= for each rookdir and bisshopdir addslidemoves.*/

    {

     int x=f;

     for(int i=0;i<7;i++)

     {   x+=dir;

         if(!inboard(x)||getside(x)==side)

            break;

         add(list,newmove(f,x));

         if(getside(x)==1-side)

            break;

     }

    }

    boolean check(int side)

    //side is in check

    {

        int f=getking(side);

        if(f==-1)

            return false;

        return attack(f,1-side);

    }



    boolean illmovedone()

    /*for computerplayer, to filter ill pseudomoves.

    This method checks wether the former player puts itself in check

    */

        {return check(1-getside());}



    int[] listofmoves()

    /*return a list with all possible moves. It creates a list of

    pseudomoves and filters out the illegal ones.*/

    {int[] list=listofpseudomoves();

     boolean c;

     int m=1;

     while(m<=list[0])

     {domove(list[m]);

      c=illmovedone();

      undomove();

      if(c)

        delete(list,m);

      else

        m++;

     }

     return list;

    }



    int[] listofpseudomoves()

    /*create all moves that are possible, without looking at

    selfcheck. */

    {

        int[] list=newlist();

        if(status!=OPEN&&status!=CHECK)

            return list;

        int side=getside();

        for(int i=0;i<144;i++)

            if(inboard(i)&&getside(i)==getside())

            {switch(getpiece(i))

             {case PAWN:addpawnmoves(list,side,i);break;

              case KNIGHT:addjumpmoves(list,side,i,knightjump);break;

              case KING:addjumpmoves(list,side,i,kingjump);break;

              case ROOK:

               for(int d=0;d<4;d++) addslidemoves(list,side,i,rookdir[d]);

              break;

              case BISSHOP:

               for(int d=0;d<4;d++) addslidemoves(list,side,i,bisshopdir[d]);

              break;

              case QUEEN:

               for(int d=0;d<4;d++)

               {addslidemoves(list,side,i,rookdir[d]);

                addslidemoves(list,side,i,bisshopdir[d]);

               }

              break;

             }

            }

        //castling:

        int row=(side==WHITE)?2*12:9*12;

        if(getfresh(row+6))//the king hasn't left it's place

        {if(    getfresh(row+2)

                &&getside(row+3)==VOID

                &&getside(row+4)==VOID

                &&getside(row+5)==VOID

                &&!attack(row+4,1-side)

                &&!attack(row+5,1-side)

                &&!attack(row+6,1-side))

                    add(list,newmove(row+6,row+4,CAASTLE));

         if(    getfresh(row+9)

                &&getside(row+7)==VOID

                &&getside(row+8)==VOID

                &&!attack(row+6,1-side)

                &&!attack(row+7,1-side)

                &&!attack(row+8,1-side))

                    add(list,newmove(row+6,row+8,CASTLE));

        }

        if(moves!=0)

        {//try enpassant

         int forward= (side==WHITE)?12:-12;

         int m=getstoredm(moves-1);

         int to=moveto(m);

         int from=movefrom(m);

         if(getpiece(to)==PAWN&&from-to==2*forward)

         {if(inboard(to+1)&&getside(to+1)==side

                &&getpiece(to+1)==PAWN&&getside(to+forward)==VOID)

            add(list,newmove(to+1,to+forward,ENPASSANT));

          if(inboard(to-1)&&getside(to-1)==side

                &&getpiece(to-1)==PAWN&&getside(to+forward)==VOID)

            add(list,newmove(to-1,to+forward,ENPASSANT));

         }

        }

        return list;

    }





}





class superdiagram extends Canvas

/*this is a superclass for 2 kinds of diagrams

It can be magnified by changing scale, and knows to draw pieces.

*/

{

 int scale, ymax;

 Color backColor[];

 Color darkpieceColor[];// the colors used to paint the pieces

 Color lightpieceColor[];



 superdiagram()

 {

  backColor=new Color[2];

  backColor[0]=new Color(100,128,100);//dark green

  backColor[1]=new Color(128,160,128);//light green

  darkpieceColor=new Color[2];

  darkpieceColor[0]=new Color(255,255,255);//for WHITE

  darkpieceColor[1]=new Color(0,0,128);//for BLACK

  lightpieceColor=new Color[2];

  lightpieceColor[0]=new Color(200,200,64);//for WHITE

  lightpieceColor[1]=new Color(84,84,255);//for BLACK

 }



 void bar(int x,int y, int hSize, int vSize,Color c,Graphics g)

 {//draw a bar (filled rectangle).

  g.setColor(c);

  g.fillRect(   x*scale,    ymax-(y+vSize)*scale,

                hSize*scale,vSize*scale);

 }



 void paintfield(int fx,int fy, int piece,int side ,Graphics g)

 /*paint a field. Size: 16*scale pixels.*/

 {

  int x=fx*16;

  int y=fy*16;

  bar(x,y,16,16,backColor[(fx+fy)%2],g);

  if(side==chessboard.VOID) return;

  switch(piece)

  {case chessboard.PAWN:

    bar(x+4,y+4,8,8,darkpieceColor[side],g);

   break;

   case chessboard.ROOK:

    bar(x+1,y+4,14,3,darkpieceColor[side],g);

    bar(x+4,y+1,3,14,lightpieceColor[side],g);

    bar(x+1,y+9,14,3,darkpieceColor[side],g);

    bar(x+9,y+1,3,3,lightpieceColor[side],g);

    bar(x+9,y+7,3,8,lightpieceColor[side],g);

   break;

   case chessboard.KNIGHT:

    bar(x+7,y+1,2,5,darkpieceColor[side],g);

    bar(x+4,y+6,8,4,darkpieceColor[side],g);

    bar(x+10,y+10,4,2,lightpieceColor[side],g);

   break;

   case chessboard.BISSHOP:

    bar(x+3,y+1,10,4,darkpieceColor[side],g);

    bar(x+7,y+4,2,8,lightpieceColor[side],g);

   break;

   case chessboard.KING:

    bar(x+6,y+1,4,13,darkpieceColor[side],g);

    bar(x+3,y+8,10,3,lightpieceColor[side],g);

   break;

   case chessboard.QUEEN:

    bar(x+3,y+1,10,4,darkpieceColor[side],g);

    bar(x+7,y+5,2,6,lightpieceColor[side],g);

    bar(x+3,y+4,2,5,lightpieceColor[side],g);

    bar(x+11,y+4,2,5,lightpieceColor[side],g);

   break;

  }

 }



 public void update(Graphics g){paint(g);}

}



class pieceselector extends superdiagram

/*a pieceselector displays all pieces, and when the user clicks a

piece the pieceselector notifies the applet*/

{int[] piece;

 int[] side;

 simplechess applet;



 pieceselector(simplechess ds, int scalingfactor)

 {

  side= new int[14];

  piece=new int[14];

  applet=ds;

  for(int j=0;j<12;j++)

        side[j]=j%2;

  for(int i=0;i<2;i++)

  {

    piece[i]=chessboard.PAWN;

    piece[2+i]=chessboard.ROOK;

    piece[4+i]=chessboard.KNIGHT;

    piece[6+i]=chessboard.BISSHOP;

    piece[8+i]=chessboard.QUEEN;

    piece[10+i]=chessboard.KING;

    piece[12+i]=0;

    side[12+i]=chessboard.VOID;

  }

  scale(scalingfactor);

 }



 public boolean mouseDown(Event ev,int x,int y)

 {

  if(x<0||x>=32*scale||y<0||y>=ymax)

    return true;

  x=x/(16*scale);

  y=(ymax-y-1)/(16*scale);

  applet.piececlicked(side[2*y+x],piece[2*y+x]);

  return true;

 }



 public void paint(Graphics g)

 {

  for(int f=0;f<14;f++)

    paintfield(f%2,f/2,piece[f],side[f],g);

 }



 public void scale(int s)

 {

  if(s<1||s>5)      s=2;

  scale=s;

  ymax=7*16*scale;

  resize(32*scale,ymax);

 }

}





class chessview extends superdiagram

/*can display a chessboard, and when a field is clicked, the applet

is notified.*/

{

 chessboard board;

 simplechess applet;



 chessview(simplechess ds,chessboard b, int s)

 {

  applet=ds;

  board=b;

  scale(s);

 }



 public void scale(int s)

 {if(s<1||s>5)

    s=2;

  scale=s;

  ymax=8*16*scale;

  resize(ymax,ymax+15);

 }



 public boolean mouseDown(Event ev,int x,int y)

 {

  if(x<0||x>=ymax||y<0||y>=ymax)

    return true;

  x=x/(16*scale);

  y=(ymax-y-1)/(16*scale);

  applet.fieldclicked(12*(y+2)+x+2);

  return true;

 }



 public void paint(Graphics g)

 {int f;

  for(int x=0;x<8;x++)

    for(int y=0;y<8;y++)

        paintfield(x,y, board.getpiece(x+2+12*(y+2)),

                        board.getside(x+2+12*(y+2)),g);

  g.setColor(Color.black);

  g.fillRect(0,ymax,ymax,15);

  g.setColor(Color.white);

  String s;

  if(board.getside()==board.WHITE)

    s="White to move.";

  else

    s="Black to move.";

  if(board.status==board.CHECKMATE)

    s+=" Checkmate";

  else if(board.status==board.CHECK)

    s+=" Check!";

  else if(board.status==board.DRAW)

    s+=" Game ends in draw.";

  g.drawString(s,2,ymax+12);

 }

}





public class simplechess extends java.applet.Applet

/*The applet displays a board and buttons. It listens

to the buttons and mouseclicks.*/

{

    chessboard B;

    chessview Bview;

    pieceselector selector;

    Button newgame,edit,undo;

    int state;

    final static int USERGRABPIECE=0,USERRELEASEPIECE=1,

                USERSELECTPROMOTION=2,PCMOVING=3,

                    EDITING=4,NOTHINGTODO=5;

    int selectedfield1,selectedfield2;

    Label message;

    int[] list;

    mover Sieuwert;

    int pieceinhand;



    public void init()

    {



		setBackground(Color.black);	// added by J.A.W.

		setForeground(Color.white);	// added by J.A.W.



        B=new chessboard();

        Bview=new chessview(this,B,2);

        B.setview(Bview);

        selector=new pieceselector(this,2);



        setLayout(new BorderLayout());

        Panel buttonpanel=new Panel();//panel to keep buttons together

        buttonpanel.setLayout(new GridLayout(1,3));

            buttonpanel.add(newgame=new Button("new game"));

            buttonpanel.add(edit=new Button("edit"));

            buttonpanel.add(undo=new Button("undo"));

        Panel controlpanel=new Panel();

        controlpanel.setLayout(new GridLayout(2,1));

            controlpanel.add(buttonpanel);

            controlpanel.add(message=new Label());

        Panel diagrampanel=new Panel();

        diagrampanel.setLayout(new BorderLayout());

            diagrampanel.add("West",Bview);

            diagrampanel.add("East",selector);

        add("North",diagrampanel);

        add("South", controlpanel);

        state=USERGRABPIECE;

        setmessage("welcome");

        Sieuwert=new mover(B,this);

        pieceinhand=B.PAWN+B.WHITE;

    }



    void setmessage(String s)

    /*The string s will be shown to the user.*/

        {message.setText(s);}



    int seek(int[] L,int from,int to)

    /*seeks a move with that from and to in list. If not: -1*/

    {

        for(int i=L[0];i>0;i--)

            if(chessboard.movefrom(L[i])==from&&

                chessboard.moveto(L[i])==to)

                return i;

        return -1;

    }



    int seek(int[] L,int from,int to,int newpiece)

    /*seeks the move with that from, that to and that newpiece.*/

    {

        for(int i=L[0];i>0;i--)

            if(chessboard.movefrom(L[i])==from&&

                chessboard.moveto(L[i])==to&&

                chessboard.movenewpiece(L[i])==newpiece)

                return i;

        return -1;

    }



    public void fieldclicked(int f)

     /*If the computer is thinking, we do nothing.

     Else we try to do the user's move, and a computermove.*/

    {

       synchronized(this)

       {if(state==PCMOVING) return;}



       switch(state)

       {case USERGRABPIECE:

            if(B.getside(f)!=B.getside())

                {setmessage("You can't move that piece.");}

            else

                {selectedfield1=f;state=USERRELEASEPIECE;

                 setmessage("You got piece "+f);

                }

        break;

        case USERRELEASEPIECE:

            {

                if(list==null)

                    list=B.listofmoves();

                int m=seek(list,selectedfield1,f);

                if(m==-1)

                {   setmessage("Your piece cannot go there.");

                    state=USERGRABPIECE;

                    break;

                }

                int mod=chessboard.movenewpiece(list[m]);

                if(mod==chessboard.QUEEN||mod==chessboard.ROOK||

                        mod==chessboard.KNIGHT||mod==chessboard.BISSHOP)

                {   setmessage("Your pawn can promote to another piece.");

                    selectedfield2=f;

                    state=USERSELECTPROMOTION;

                    break;

                }

                else

                {   B.visualdomove(list[m]);

                    list=null;

                    computermove(Sieuwert);

                }

            }

        break;

        case EDITING:

            B.A[f]=pieceinhand;

            Bview.repaint();

            break;

        case NOTHINGTODO:

            setmessage("Press newgame for a rematch.");

        break;

       }

    }



    void piececlicked(int side,int piece)

    {

        if(state==USERSELECTPROMOTION)

        {

         int index=seek(list,selectedfield1,selectedfield2,piece);

         if(index!=-1)

         {B.visualdomove(list[index]);

          list=null;

          computermove(Sieuwert);

         }

        }

        else if(state==EDITING)

        {

            pieceinhand=side+piece;

        }



    }

    void computermove(mover M)

    {

        if(B.status==B.CHECKMATE||B.status==B.DRAW)

        {

         setmessage("Game over.");

         state=NOTHINGTODO;

        }

        else

        {

         state=PCMOVING;

         M.domove();

        }

    }



    void catchamove(int m)

    /*Some time after we ask the mover for a move,

    he will call this method to give us his move. We will

    do his move.*/

    {

     synchronized(this)

     {

        if(state==PCMOVING)

        {B.visualdomove(m);

         state=USERGRABPIECE;

        }

     }

    }



    void newgame()

    {

     B.setup();

     if(state!=EDITING)

        state=USERGRABPIECE;

    }



    void undo()

    {

        if(state!=EDITING)

        {B.visualundomove();

         setmessage("...");

        }

    }

    void switchedit()

    {

        if(state==EDITING&&B.getking(B.WHITE)!=-1&&B.getking(B.BLACK)!=-1)

        {

            B.reset();

            setmessage("play.");

            edit.setLabel("edit");

            state=USERGRABPIECE;

        }

        else if(state==USERGRABPIECE)

        {

            setmessage("edit.");

            edit.setLabel("play");

            state=EDITING;

        }



    }



    public boolean action(Event ev, Object O)

    /*If a button was clicked, the browser calls this function.

    If a computerplayer is thinking, we do nothing.

    Else, we do the right action.

    */

    {

        synchronized(this)

        {

        if(state==PCMOVING)

            return true;

        else if(ev.target==newgame)

            {newgame();}

        else if(ev.target==undo)

            {undo();}

        else if(ev.target==edit)

            {switchedit();}

        }

        return true;

    }

}



class mover extends Thread

/*mover is a thread, because that makes it possible to return the method

do a move before doing the move. this is more convenient for many browsers:

the mouseclicks that ask for moves are returned fast, so the user can click

other buttons while the computer is calculating.

the result is given to the applet by applet.catchamove();

*/

{

 chessboard original;            //the chessboard to do moves on

 chessboard b;                   // a copy to do calculations on

 static int minlevel=2,maxlevel=6;       //the maximum searchdepth

 simplechess applet;

 final static int HIGH=65534, SUPERHIGH=65535, ABOVEALL=65536;

 /*HIGH is the highest possible score. superhigh is

 a impossible score, that will act as a bottom

 to start searching. ABOVEALL is a score that can be returned if

 it is found out that an ill move was done.*/

 static int pawnscore[];//good places for pawns

 int nodes;



 mover(chessboard bb,simplechess l)

 //make a mover

 {

  makepawntable();

  original=bb;

  applet=l;

  start();         //get running.

 }



 static void makepawntable()

 {

  if(pawnscore!=null)

    return;

  pawnscore=new int[144];

  for(int i=0;i<144;i++)

    pawnscore[i]=100+5*((i-2)/12);

  for(int i=0;i<12;i++)

  {

    pawnscore[96+i]=240;

    pawnscore[84+i]=160;

  }

  pawnscore[66]=pawnscore[65]=pawnscore[78]=pawnscore[77]=180;

 }





 /*this trick intends to do the calculating in a second thread, so the

 main thread can handle other clicks. This is necessary in UNIX, because

 otherwise the applet would block all other applications from reading the mouse. In

 Netscape, this trick is unstable: NN can crash. In internet explorer, it

 works.*/

 public void run()

 /*the mover will get into sleep,

 when woke up do a move, sleep,move,etc*/

 {while(true)

  { this.suspend();

    domove_2();

  }

 }



 void domove()

 //this wakes up the mover. It will do a move

    {resume();}







 void domove_2()

 /*does a move on original. It tries all moves it can do, and

 then does the move that gave the highest prognosis.*/

 {

  nodes=0;

  b=new chessboard(original); //make a copy for calculations

  int[] list=b.listofmoves();

  int bestmoveindex=-1;

  int alpha=-SUPERHIGH;



  for(int c=1;c<=list[0];c++)

  {

    b.domove(list[c]);

        int s=-fullsearch(-SUPERHIGH,-alpha,1);

    b.undomove();

    if(s>alpha)

        {alpha=s;

         bestmoveindex=c;

        }

  }

  applet.catchamove(list[bestmoveindex]);

  System.out.println("nodes "+nodes);

 }



 void swap(int[] l,int[] v, int i,int j)

 /*swap the elements i and j in both l and v.*/

 {

    int dummy;

    dummy=l[i];

    l[i]=l[j];

    l[j]=dummy;

    dummy=v[i];

    v[i]=v[j];

    v[j]=dummy;

 }



 void sort(int[] l,int[] v) {quicksort(l,v,1,l[0]);}

 /*sorts v, keeping the corresponding elements l[i]

 and v[i] together. highest v first.*/



 void quicksort(int[] l,int[] v,int p,int r)

 //sort the interval [p,r] of v, like above

 {

  if(p<r)

  {

     int q=partition(l,v,p,r);

     quicksort(l,v,p,q);

     quicksort(l,v,q+1,r);

  }

 }



 int partition(int[] l,int[] v,int p,int r)

 /*split v (and l) in a high and low part.*/

 {int x=v[p];

  int i=p-1;

  int j=r+1;

  while(true)

  {

   do   j--;    while(!(v[j]>=x));

   do   i++;    while(!(v[i]<=x));

   if(i<j)

    swap(l,v,i,j);

   else

    return j;

  }

 }



 int[] estimates(int[] l)

 /*returns the estimated benefits of a move.*/

 {

    int[] v=new int[l[0]+1];

    int np;

    v[0]=l[0];

    for(int i=1;i<=l[0];i++)

    {

        if(l[i]==b.NULLMOVE)

            v[i]=0;

        else

        {

         np=b.movenewpiece(l[i]);

         if(np==0)

            v[i]=value(b.getpiece(b.moveto(l[i])));

         if(np==b.ENPASSANT)

            v[i]=110;

         else if(np==b.CASTLE)

            v[i]=20;

         else if(np==b.CAASTLE)

            v[i]=16;

         else v[i]=value(np);

        }

    }

    return v;

 }



 int[] makefulllist()

 /*make a list of moves to search.*/

 {

      int[] l=b.listofmoves();

      int[] v=estimates(l);

      sort(l,v);

      return l;

 }



 int fullsearch(int alpha, int beta, int level)

 /*Try to give a prognosis about how good the situation is for

 the player to move on the chessboard b. It does that

 by looking at the status(if the game is result),

 by doing any move and calling prognosis,

 by calling estimate (if it has called itself often enough)*/

 {

  if(level>=minlevel)

    return justattacksearch(alpha,beta,level);

  nodes++;



  /*try all moves and return the estimate

  we get when doing the best move*/

  int[] list=makefulllist();

  if(list[0]==0)//no moves

  {if(b.check(b.getside()))

        return -HIGH;//we lost.

    else return 0;//stalemate

  }

  for(int c=1;c<=list[0];c++)

  {

    b.domove(list[c]);

        int s=-fullsearch(-beta,-alpha,level+1);

    b.undomove();

    if(s>beta)        return s;

    if(s>alpha)        alpha=s;

  }

  return alpha;

 }



 int[] makejustattacklist()

 /*make a list for searching only attack moves.*/

 {

      int[] l=b.listofpseudomoves();

      int[] v=estimates(l);

      int i=1;

      while(i<=l[0])

        if(v[i]<50)

            b.delete(l,i);

        else

            i++;

      b.add(l,b.NULLMOVE);

      return l;

 }



 int justattacksearch(int alpha, int beta, int level)

 /*Try to give a prognosis about how good the situation is for

 the player to move on the chessboard b. It does that

 by looking at the status(if the game is result),

 by doing any move and calling prognosis,

 by calling estimate (if it has called itself often enough)*/

 {

  nodes++;

  if(level>=maxlevel)

    return justbacksearch(alpha,beta);



  /*try all moves and return the estimate

  we get when doing the best move*/

  int[] list=makejustattacklist();



  for(int c=1;c<=list[0];c++)

  {

    b.domove(list[c]);

        int s=-justattacksearch(-beta,-alpha,level+1);

    b.undomove();

    if(s>beta)        return s;

    if(s>alpha)        alpha=s;

  }

  return alpha;

 }





 int value(int piece)

 /*The value of a piece.*/

 {

    switch(piece)

    {

     case b.PAWN: return 105;

     case b.KNIGHT: return 300;

     case b.ROOK: return 500;

     case b.QUEEN: return 900;

     case b.BISSHOP:return 320;

     case b.KING: return 3200;

     case b.VOID: return 0;

    }

    return 0;

 }





 int justbacksearch(int alpha,int beta)

 //try to hit back, or use basicscore

 {

    if(b.illmovedone())

        return ABOVEALL;

    int to=b.moveto(b.getstoredm(b.moves-1));

    int s=basicscore();

    if(s>beta)        return s;

    if(s>alpha)       alpha=s;

    if(b.attack(to,b.getside()))

    {

        int[] list=hitbacklist();

        for(int c=1;c<=list[0];c++)

        {

         b.domove(list[c]);

            s=-justbacksearch(-beta,-alpha);

         b.undomove();

        if(s>beta)        return s;

        if(s>alpha)       alpha=s;

        }

    }

    return alpha;

 }





 int[] hitbacklist()

 {

    int[] l=b.listofpseudomoves();

    int to=b.moveto(b.getstoredm(b.moves-1));

    int i=1;

    while(i<=l[0])

        if(b.moveto(l[i])!=to)

            b.delete(l,i);

        else

            i++;

    b.add(l,b.NULLMOVE);

    return l;

 }





 int basicscore()

 /*calculate a score, non recursively*/

 {int score[]=new int[2];

  int pside;



  for(int x=24;x<120;x+=12)

  for(int field=x+2;field<x+10;field++)

  {

   pside=b.getside(field);

   if(pside!=chessboard.VOID)

   {

    switch(b.getpiece(field))

    {case chessboard.PAWN:

     if(pside==chessboard.WHITE)

        score[pside]+=pawnscore[field];

     else

        score[pside]+=pawnscore[144-field];

        //mirror the table to get the black values

     if(b.A[field+12]==chessboard.PAWN+pside)

        score[pside]-=20;//double pawn

     break;

     case chessboard.KNIGHT:

     score[pside]+=280;

     break;

     case chessboard.BISSHOP:

     score[pside]+=300;

     break;

     case chessboard.ROOK:

     score[pside]+=500;

     break;

     case chessboard.QUEEN:

     score[pside]+=900;

     break;

     case chessboard.KING:

     score[pside]+=3200;

     break;

    }

   }



  }

  int whiteresult=score[chessboard.WHITE]-score[chessboard.BLACK];

  return (b.getside()==chessboard.WHITE)?whiteresult:-whiteresult;

 }



}