/* http://home.hccnet.nl/h.g.muller/umax4_8w.c */ /* ported to plan9 by umbraticus */ /***************************************************************************/ /* micro-Max, */ /* A chess program smaller than 2KB (of non-blank source), by H.G. Muller */ /***************************************************************************/ /* version 4.8 (~1900 characters) features: */ /* - recursive negamax search */ /* - all-capture quiescence search with MVV/LVA priority */ /* - (internal) iterative deepening */ /* - best-move-first 'sorting' */ /* - a hash table storing score and best move */ /* - futility pruning */ /* - king safety through magnetic frozen king */ /* - null-move pruning */ /* - Late-move reductions */ /* - full FIDE rules (expt minor promotion) and move-legality checking */ /* - keep hash + rep-draw detect */ /* - end-game Pawn-push bonus, new piece values, gradual promotion */ /* Rehash sacrificed, simpler retrieval. Some characters squeezed out. */ /* No hash-table clear, single-call move-legality checking based on K==I */ /* fused to generic Winboard driver */ #include #include int StartKey; #define EMPTY 0 #define WHITE 8 #define BLACK 16 #define STATE 64 /* make unique integer from engine move representation */ #define PACK_MOVE 256*K + L; /* convert intger argument back to engine move representation */ #define UNPACK_MOVE(A) K = (A)>>8 & 255; L = (A) & 255; /* Global variables visible to engine. Normally they */ /* would be replaced by the names under which these */ /* are known to your engine, so that they can be */ /* manipulated directly by the interface. */ int Side; int Move; int PromPiece; int Result; int TimeLeft; int MovesLeft; int MaxDepth; int Post; int Fifty; int UnderProm; long Ticks, tlim; int GameHistory[1024]; char HistoryBoards[1024][STATE]; int GamePtr, HistPtr; #define W while #define blah(A,B) *(int*)(T+A+(B&8)+S*(B&7)) #define Jerk(A) blah(y+A,b[y])-blah(x+A,u)-blah(H+A,t) int U=(1<<23)-1; struct _ {int K,V;char X,Y,D;} *A; /* hash table, 16M+8 entries*/ int M=136,S=128,I=8e3,Q,O,K,N,j,R,J,Z; /* M=0x88 */ char L, w[]={0,2,2,7,-1,8,12,23}, /* relative piece values */ o[]={-16,-15,-17,0,1,16,0,1,16,15,17,0,14,18,31,33,0, /* step-vector lists */ 7,-1,11,6,8,3,6, /* 1st dir. in o[] per piece*/ 6,3,5,7,4,5,3,6}, /* initial piece setup */ b[129], /* board: half of 16x8+dummy*/ T[1035], /* hash translation table */ n[]=".?+nkbrq?*?NKBRQ"; /* piece symbols on printout*/ /* recursive minimax search, k=moving side, n=depth*/ /* (q,l)=window, e=current eval. score, E=e.p. sqr.*/ /* e=score, z=prev.dest; J,Z=hashkeys; return score*/ int D(int k, int q, int l, int e, int E, int z, int n){ int j,r,m,v,d,h,i,F,G,P,V,f=J,g=Z,C,s; char t,p,u,x,y,X,Y,H,B; struct _*a=A+(J+k*E&U-1); /* lookup pos. in hash table*/ q-=qD;m=a->V;X=a->X;Y=a->Y; /* resume at stored depth */ if(a->K-Z|z&8| /* miss: other pos. or empty*/ !(m<=q|X&8&&m>=l|X&S)) /* or window incompatible */ d=Y=0; /* start iter. from scratch */ X&=~M; /* start at best-move hint */ W(d++2&&l+I?D(24-k,-l,1-l,-e,S,S,d-3):I; /* search null move */ m=-P35?d-2?-I:e:-P; /*** prune if > beta unconsidered:static eval */ N++; /* node count (for timing) */ do{u=b[x]; /* scan board looking for */ if(u&k) /* own piece (inefficient!)*/ {r=p=u&7; /* p = piece type (set r>0) */ j=o[p+16]; /* first step vector f.piece*/ W(r=p>2&r<0?-r:-o[++j]) /* loop over directions o[] */ {A: /* resume normal after best */ y=x;F=G=S; /* (x,y)=move, (F,G)=castl.R*/ do{ /* y traverses ray, or: */ H=y=h?Y^h:y+r; /* sneak in prev. best move */ if(y&M)break; /* board edge hit */ m=E-S&b[E]&&y-E<2&E-y<2?I:m; /* bad castling */ if(p<3&y==E)H^=16; /* shift capt.sqr. H if e.p.*/ t=b[H];if(t&k|p<3&!(y-x&7)-!t)break; /* capt. own, bad pawn mode */ i=37*w[t&7]+(t&192); /* value of capt. piece t */ if(i<0)m=I,d=98; /* K capture */ if(m>=l&d>1)goto C; /* abort on fail high */ v=d-1?e:i-p; /*** MVV/LVA scoring if d=1**/ if(d-!t>1) /*** all captures if d=2 ***/ {v=p<6?b[x+8]-b[y+8]:0; /* center positional pts. */ b[G]=b[H]=b[x]=0;b[y]=u|32; /* do move, set non-virgin */ if(!(G&M))b[F]=k+6,v+=50; /* castling: put R & score */ v-=p-4|R>30?0:20; /*** freeze K in mid-game ***/ if(p<3) /* pawns: */ {v-=9*((x-2&M||b[x-2]-u)+ /* structure, undefended */ (x+2&M||b[x+2]-u)-1 /* squares plus bias */ +(b[x^16]==k+36)) /*** cling to magnetic K ***/ -(R>>2); /* end-game Pawn-push bonus */ i+=V=y+r+1&S?647-p:2*(u&y+16&32); /* promotion / passer bonus */ b[y]+=V; /* upgrade P or convert to Q*/ } J+=Jerk(0);Z+=Jerk(8)+G-S; v+=e+i;V=m>q?m:q; /*** new eval & alpha ****/ C=d-1-(d>5&p>2&!t&!h); /* nw depth, reduce non-cpt.*/ C=R>30|P-I|d<3||t&&p-4?C:d; /* extend 1 ply if in-check */ do s=C>2|v>V?-D(24-k,-l,-V,-v,/*** futility, recursive eval. of reply */ F,y,C):v; W(s>q&++CD=99;a->V=0; /* lock game in hash as draw*/ R+=i>>7; /*** total captd material ***/ if((b[y]&7)!=p && PromPiece == 'n') UnderProm = y; if((b[y]&7)!=p) print("tellics kibitz promotion\n"); Fifty = t|p<3?0:Fifty+1; return l;} /* & not in check, signal */ v=m; /* (prevent fail-lows on */ } /* K-capt. replies) */ J=f;Z=g; b[G]=k+6;b[F]=b[y]=0;b[x]=u;b[H]=t; /* undo move,G can be dummy */ } /* if non-castling */ if(v>m) /* new best, update max,best*/ m=v,X=x,Y=y|S&F; /* mark non-double with S */ if(h){h=0;goto A;} /* redo after doing old best*/ if(x+r-y|u&32| /* not 1st step,moved before*/ p>2&(p-4|j-7|| /* no P & no lateral K move,*/ b[G=x+3^r>>1&7]-k-6 /* no virgin R in corner G, */ ||b[G^1]|b[G^2]) /* no 2 empty sq. next to R */ )t+=p<5; /* fake capt. for nonsliding*/ else F=y; /* enable e.p. */ }W(!t); /* if not capt. continue ray*/ }}}W((x=x+9&~M)-B); /* next sqr. of board, wrap */ C:m=m+I|P==I?m:0; /*** check test thru NM best loses K: (stale)mate*/ if(a->D<99) /* protect game history */ a->K=Z,a->V=m,a->D=d, /* always store in hash tab */ a->X=X|8*(m>q)|S*(mY=Y; /* move, type (bound/exact),*/ if(z==8&Post) print("%2d %6d %8ld %10d %c%c%c%c\n",d-2,m,(time(0)-Ticks)/10,N, 'a'+(X&7),'8'-(X>>4),'a'+(Y&7),'8'-(Y>>4&7)); } /* encoded in X S,8 bits */ if(z==S+1)K=X,L=Y&~M; return m+=m=100) { print("1/2-1/2 {Draw by fifty move rule}\n"); return 4; } return 0; } void InitEngine(void) { K=8;W(K--) {L=8;W(L--)b[16*L+K+8]=(K-4)*(K-4)+(L-3.5)*(L-3.5); /* center-pts table */ } /*(in unused half b[])*/ N=1035;W(N-->M)T[N]=rand()>>9; } void InitGame(void) { int i; for(i=0;i<128;i++)b[i&~M]=0; K=8;W(K--) {b[K]=(b[K+112]=o[K+24]+8)+8;b[K+16]=18;b[K+96]=9; /* initial board setup*/ } /*(in unused half b[])*/ Side = WHITE; Q=0; O=S; Fifty = R = 0; UnderProm = -1; } void CopyBoard(int s) { int j; /* copy game representation of engine to HistoryBoard */ /* don't forget castling rights and e.p. state! */ for(j=0; j<64; j++) HistoryBoards[s][j] = b[j+(j&0x38)]; /* board squares */ if(!(O&M)) HistoryBoards[s][O+(O&7)>>1] |= 64; /* mark ep square */ } void main(int argc, char **argv) { int Computer, MaxTime, MaxMoves, TimeInc, sec, i; char line[256]; int m, nr, len; if(argc > 1){m = atoi(argv[1]); U = (1<=0 && UnderProm != L) { print("tellics I hate under-promotions!\n"); print("resign\n"); Computer = EMPTY; continue; } else UnderProm = -1; print("move %c%c%c%c\n",'a'+(K&7),'8'-(K>>4), 'a'+(L&7),'8'-(L>>4)); m = time(0) - Ticks; /* time-control accounting */ TimeLeft -= m; TimeLeft += TimeInc; if(--MovesLeft == 0) { MovesLeft = MaxMoves; if(MaxMoves == 1) TimeLeft = MaxTime; else TimeLeft += MaxTime; } GameHistory[GamePtr++] = PACK_MOVE; CopyBoard(HistPtr=HistPtr+1&1023); if(PrintResult(Side)) Computer = EMPTY; } else { if(!PrintResult(Side)) print("resign\n"); Computer = EMPTY; } continue; } len = read(0, line, 256); if (len <= 0) exits(nil); line[len - 1] = '\0'; if (len == 1 || !strcmp(line, "xboard")) continue; if (!strcmp(line, "new")) { /* start new game */ InitGame(); GamePtr = 0; HistPtr = 0; Computer = BLACK; TimeLeft = MaxTime; MovesLeft = MaxMoves; for(nr=0; nr<1024; nr++) for(m=0; m= '0' && line[len] <= '9'; len++); if(line[len++] == ' '){ MaxTime = atoi(line + len); while(line[len] >= '0' && line[len] <= '9') len++; if(line[len] == ':'){ sec = atoi(line + ++len); while(line[len] >= '0' && line[len] <= '9') len++; } if(line[len++] == ' ') TimeInc = atoi(line + len); } MovesLeft = MaxMoves; TimeLeft = MaxTime = 60000*MaxTime + 1000*sec; TimeInc *= 1000; continue; } if (!strncmp(line, "time ", 5)) { /* set time left on clock */ TimeLeft = atoi(line + 5) * 10; /* centi-sec to ms */ continue; } if (!strncmp(line, "otim", 4)) { /* opponent's time (not kept, so ignore) */ continue; } if (!strcmp(line, "go")) { /* set computer to play current side to move */ Computer = Side; MovesLeft = -(GamePtr+(Side==WHITE)>>1); while(MaxMoves>0 && MovesLeft<=0) MovesLeft += MaxMoves; continue; } if (!strcmp(line, "hint")) { Ticks = time(0); tlim = 1000; D(Side,-I,I,Q,O,S+1,6); if (K==0 && L==0) continue; print("Hint: %c%c%c%c\n",'a'+(K&7),'8'-(K>>4), 'a'+(L&7),'8'-(L>>4)); continue; } if (!strcmp(line, "undo") && (nr=1) || !strcmp(line, "remove") && (nr=2) ) { /* 'take back' moves by replaying game */ /* from history until desired ply */ if (GamePtr < nr) continue; GamePtr -= nr; HistPtr -= nr; /* erase history boards */ while(nr-- > 0) for(m=0; m 0) { m = line[0]; if(m=='.') break; if(m=='#') { for(i=0; i<128; i++) b[i&0x77]=0; R=40; Q=0; O=S; continue; } if(m=='c') { color = WHITE+BLACK - color; Q = -Q; continue; } if((m=='P' || m=='N' || m=='B' || m=='R' || m=='Q' || m=='K') && line[1] >= 'a' && line[1] <= 'h' && line[2] >= '1' && line[2] <= '8') { m = line[1]-16*line[2]+799; switch(line[0]) { case 'P': if(color==WHITE) b[m]=(m&0x70)==0x60?9:41; else b[m]=(m&0x70)==0x10?18:50; Q+=2*37; break; case 'N': b[m]=3+color; Q+=7*37; R-=2; break; case 'B': b[m]=5+color; Q+=8*37; R-=2; break; case 'R': b[m]=6+color+32; if((m==0x00 || m==0x07) && color==BLACK || (m==0x70 || m==0x77) && color==WHITE) b[m] -= 32; Q+=12*37; R-=3; break; case 'Q': b[m]=7+color; Q+=23*37; R-=6; break; case 'K': b[m]=4+color+32; if(m==0x04 && color==BLACK || m==0x74 && color==WHITE) b[m] -= 32; break; } continue; } } if(Side != color) Q = -Q; continue; } /* command not recognized, assume input move */ m = line[0]<'a' | line[0]>'h' | line[1]<'1' | line[1]>'8' | line[2]<'a' | line[2]>'h' | line[3]<'1' | line[3]>'8'; PromPiece = line[4]; {char *c=line; K=c[0]-16*c[1]+799;L=c[2]-16*c[3]+799; } if (m) /* doesn't have move syntax */ print("Error (unknown command): %s\n", line); else if(D(Side,-I,I,Q,O,8,3)!=I) { /* did have move syntax, but illegal move */ print("Illegal move:%s\n", line); } else { /* legal move, perform it */ GameHistory[GamePtr++] = PACK_MOVE; Side ^= 24; CopyBoard(HistPtr=HistPtr+1&1023); if(PrintResult(Side)) Computer = EMPTY; } } }