/*simplechess.java: a very simple chess playing applet. made by Sieuwert van Otterloo, 1999. smotterl@cs.uu.nl Edited for Java 1.3 by Yves DebilloÎz */ 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=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() { 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=x)); do i++; while(!(v[i]<=x)); if(i=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 2: return 105; case 8: return 300; case 6: return 450; case 12: return 850; case 10:return 320; case 14: return 3200; case -1: 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