1 /***************************************************************************/
\r
3 /* Version of the sub-2KB (source) micro-Max Chess program, fused to a */
\r
4 /* generic WinBoard interface, loading its move-generator tables from file */
\r
5 /* Adapted to play Xiang Qi, which required rather specialized changes */
\r
6 /***************************************************************************/
\r
7 /* micro-Max version 4.8 (~1950 characters) features: */
\r
8 /* - recursive negamax search */
\r
9 /* - all-capture quiescence search with MVV/LVA priority */
\r
10 /* - (internal) iterative deepening */
\r
11 /* - best-move-first 'sorting' */
\r
12 /* - a hash table storing score and best move */
\r
13 /* - futility pruning */
\r
14 /* - king safety through magnetic frozen king */
\r
15 /* - null-move pruning */
\r
16 /* - Late-move reductions */
\r
17 /* - full FIDE rules (expt minor promotion) and move-legality checking */
\r
18 /* - keep hash + rep-draw detect */
\r
19 /* - end-game Pawn-push bonus, new piece values, gradual promotion */
\r
20 /***************************************************************************/
\r
21 /* This version reads the piece description from a file fmax.ini */
\r
22 /* The format supports many fairy pieces, including hoppers. */
\r
23 /* f) now supports 15 piece types, by requisitioning WHITE bit */
\r
24 /* g) supports larger board width. */
\r
25 /* h) castling bug ('in-check by non-captures') corrected */
\r
26 /* i) rep-draw bug ('side-to-move') corrected */
\r
27 /* k) allow user underpromotions, recognize & ignore 'variant' command */
\r
28 /* l) edit bug corrected (i & j file clear) */
\r
29 /* m) piece values no longer quantized, game-stage counting bug corrected */
\r
30 /* n) edit-menu K-side castling bug corrected. */
\r
31 /* o) retrieve the requested variant from the .ini file */
\r
32 /* p) clear hash table on variant switch */
\r
33 /* q) reduced piece-material count for better Pawn push */
\r
34 /* r) hash-table bug corrected (X still ORed with flags) */
\r
35 /* s) Bug that prevented initialization center points corrected */
\r
36 /* t) castling bug after edit fixed */
\r
37 /* u) converted to protocol 2; ping implemented */
\r
38 /* v) white e.p. rights hash bug fixed; */
\r
39 /* w) piece indicators programable, multi-path support */
\r
40 /* x) e.p. changed to support Berolina Pawns */
\r
41 /* y) capture vlue of 6-7th-rank Pawn reduced in Shatranj */
\r
42 /* z) bug in promotion input corrected */
\r
43 /* A) stalemate-detection bug in printResult fixed */
\r
44 /* B) Invalidate hash on game-level promotion (might be under-promotion!) */
\r
45 /* C) King move evaluation based on negative piece value in stead of nr */
\r
46 /* D) WB memory command added, undo fixed */
\r
47 /* E) 15th piece read in */
\r
48 /* F) accepts ini fileargument */
\r
49 /* Xiang Qi adaptations: */
\r
50 /* orient board sideways for easy implementation of King-facing rule */
\r
51 /* allow board to have 9 rows (= files) */
\r
52 /* add array for specifying board zones */
\r
53 /* add zone limiter for each piece */
\r
54 /* change promotion code to act when crossing river */
\r
55 /* remove stalemate code */
\r
56 /* G) o[] and oo[] made int, to work on big-endian machines */
\r
57 /***************************************************************************/
\r
59 /*****************************************************************/
\r
60 /* LICENCE NOTIFICATION */
\r
61 /* Fairy-Max 4.8 is free software, and you have my permission do */
\r
62 /* with it whatever you want, whether it is commercial or not. */
\r
63 /* Note, however, that Fairy-Max can easily be configured through*/
\r
64 /* its fmax.ini file to play Chess variants that are legally pro-*/
\r
65 /* tected by patents, and that to do so would also require per- */
\r
66 /* mission of the holders of such patents. No guarantees are */
\r
67 /* given that Fairy-Max does anything in particular, or that it */
\r
68 /* would not wreck the hardware it runs on, and running it is */
\r
69 /* entirely for your own risk. H.G,Muller, author of Fairy-Max */
\r
70 /*****************************************************************/
\r
75 /* fused to generic Winboard driver */
\r
85 #include <sys/time.h>
\r
86 int GetTickCount() // with thanks to Tord
\r
88 gettimeofday(&t, NULL);
\r
89 return t.tv_sec*1000 + t.tv_usec/1000;
\r
92 #define INI_FILE "qmax.ini"
\r
97 #include <windows.h>
\r
98 #define INI_FILE "qmax.ini"
\r
112 /* make unique integer from engine move representation */
\r
113 #define PACK_MOVE 256*K + L;
\r
115 /* convert intger argument back to engine move representation */
\r
116 #define UNPACK_MOVE(A) K = (A)>>8 & 255; L = (A) & 255;
\r
118 /* Global variables visible to engine. Normally they */
\r
119 /* would be replaced by the names under which these */
\r
120 /* are known to your engine, so that they can be */
\r
121 /* manipulated directly by the interface. */
\r
134 char piecename[32], piecetype[32], defaultchar[]=".PPKNBRQEWFMACHG";
\r
135 char *inifile = INI_FILE;
\r
137 int Ticks, tlim, Setup, SetupQ;
\r
139 int GameHistory[1024];
\r
140 char HistoryBoards[1024][STATE], setupPosition[129];
\r
141 int GamePtr, HistPtr;
\r
144 #define K(A,B) *(int*)(T+A+((B&31)<<8))
\r
145 #define J(A) K(y+A,b[y])-K(x+A,u)-K(y+A,t)
\r
148 struct _ {int K,V;char X,Y,D,F;} *A; /* hash table, 16M+8 entries*/
\r
150 int M=136,S=128,I=8e3,Q,O,K,N,j,R,J,Z,LL,L, /* M=0x88 */
\r
152 w[16]={0,2,2,-1,7,8,12,23,7,5}, /* relative piece values */
\r
154 oo[32], /* initial piece setup */
\r
156 od[16]; /* 1st dir. in o[] per piece*/
\r
159 b[513], /* board: 16x8+dummy, + PST */
\r
160 T[8200], /* hash translation table */
\r
162 n[]=".P*KEEQQAHCR????x+pkeeqqahcr????"; /* piece symbols on printout*/
\r
164 char zn[] = { /* zones of xiangqi board */
\r
165 1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0,
\r
166 1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0,
\r
167 1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0,
\r
168 0,0,0,1,1,2,2,0,0,0, 0,0,0,0,0,0,
\r
169 0,0,0,1,1,2,2,0,0,0, 0,0,0,0,0,0,
\r
170 0,0,0,1,1,2,2,0,0,0, 0,0,0,0,0,0,
\r
171 1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0,
\r
172 1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0,
\r
173 1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0
\r
178 i=-1;W(++i<144)printf(" %c",(i&15)==BW&&(i+=15-BW)?10:n[b[i]&31]);
\r
181 D(k,q,l,e,z,n) /* recursive minimax search, k=moving side, n=depth*/
\r
182 int k,q,l,e,z,n; /* (q,l)=window, e=current eval. score, E=e.p. sqr.*/
\r
183 { /* e=score, z=prev.dest; J,Z=hashkeys; return score*/
\r
184 int j,r,m,v,d,h,i,P,V,f=J,g=Z,C,s,flag,F;
\r
185 unsigned char t,p,u,x,y,X,Y,B,lu;
\r
186 struct _*a=A+(J+k&U-1); /* lookup pos. in hash table*/
\r
187 q-=q<e;l-=l<=e; /* adj. window: delay bonus */
\r
188 d=a->D;m=a->V;F=a->F; /* resume at stored depth */
\r
189 X=a->X;Y=a->Y; /* start at best-move hint */
\r
190 if(z&S&&a->K==Z)printf("# root hit %d %d %x\n",a->D,a->V,a->F);
\r
191 if(a->K-Z|z&S | /* miss: other pos. or empty*/
\r
192 !(m<=q|F&8&&m>=l|F&S)) /* or window incompatible */
\r
193 d=X=0,Y=-1; /* start iter. from scratch */
\r
194 W(d++<n||d<3|| /*** min depth = 2 iterative deepening loop */
\r
195 z&S&&K==I&&(GetTickCount()-Ticks<tlim&d<=MaxDepth|| /* root: deepen upto time */
\r
196 (K=X,L=Y,d=3))) /* time's up: go do best */
\r
197 {x=B=X;lu=1; /* start scan at prev. best */
\r
198 h=Y-255; /* if move, request 1st try */
\r
199 P=d>2&&l+I?D(16-k,-l,1-l,-e,2*S,d-3):I; /* search null move */
\r
200 m=-P<l|R<5?d-2?-I:e:-P; /*** prune if > beta unconsidered:static eval */
\r
201 N++; /* node count (for timing) */
\r
202 do{u=b[x]; /* scan board looking for */
\r
203 if(u)m=lu|u&15^3?m:(d=98,I),lu=u&15^3; /* Kings facing each other */
\r
204 if(u&&(u&16)==k) /* own piece (inefficient!)*/
\r
205 {r=p=u&15; /* p = piece type (set r>0) */
\r
206 j=od[p]; /* first step vector f.piece*/
\r
207 W(r=o[++j]) /* loop over directions o[] */
\r
208 {A: /* resume normal after best */
\r
209 flag=h?3:of[j]; /* move modes (for fairies) */
\r
210 y=x; /* (x,y)=move */
\r
211 do{ /* y traverses ray, or: */
\r
212 y=h?Y:y+r; /* sneak in prev. best move */
\r
213 if(y>=16*BH|(y&15)>=BW)break; /* board edge hit */
\r
214 t=b[y]; /* captured piece */
\r
215 if(flag&1+!t) /* mode (capt/nonc) allowed?*/
\r
216 {if(t&&(t&16)==k||flag>>10&zn[y])break; /* capture own or bad zone */
\r
217 i=w[t&15]; /* value of capt. piece t */
\r
218 if(i<0)m=I,d=98; /* K capture */
\r
219 if(m>=l&d>1)goto C; /* abort on fail high */
\r
220 v=d-1?e:i-p; /*** MVV/LVA scoring if d=1**/
\r
221 if(d-!t>1) /*** all captures if d=2 ***/
\r
222 {v=centr[p]?b[x+257]-b[y+257]:0; /* center positional pts. */
\r
223 b[x]=0;b[y]=u; /* do move */
\r
224 v-=w[p]>0|R<10?0:20; /*** freeze K in mid-game ***/
\r
225 if(p<3) /* pawns: */
\r
226 {v+=2; /* end-game Pawn-push bonus */
\r
227 if(zn[x]-zn[y])b[y]+=5, /* upgrade Pawn and */
\r
228 i+=w[p+5]-w[p]; /* promotion bonus */
\r
230 if(z&S && GamePtr<6) v+=(rand()>>10&31)-16; // randomize in root
\r
232 v+=e+i;V=m>q?m:q; /*** new eval & alpha ****/
\r
233 C=d-1-(d>5&p>2&!t&!h); /* nw depth, reduce non-cpt.*/
\r
234 C=R<10|P-I|d<3||t&&p-3?C:d; /* extend 1 ply if in-check */
\r
236 s=C>2|v>V?-D(16-k,-l,-V,-v,/*** futility, recursive eval. of reply */
\r
238 W(s>q&++C<d); v=s; /* no fail:re-srch unreduced*/
\r
239 if(z&S&&K-I) /* move pending: check legal*/
\r
240 {if(v+I&&x==K&y==L) /* if move found */
\r
242 a->D=99;a->V=500; /* lock game in hash as loss*/
\r
243 R-=i/FAC; /*** total captd material ***/
\r
244 Fifty = t|p<3?0:Fifty+1;
\r
245 return l;} /* & not in check, signal */
\r
246 v=m; /* (prevent fail-lows on */
\r
247 } /* K-capt. replies) */
\r
249 b[y]=t;b[x]=u; /* undo move */
\r
250 } /* if non-castling */
\r
251 if(v>m) /* new best, update max,best*/
\r
252 m=v,X=x,Y=y; /* no marking! */
\r
253 if(h){h=0;goto A;} /* redo after doing old best*/
\r
256 t+=flag&4; /* fake capt. for nonsliding*/
\r
257 if(s&&flag&8)t=0,flag^=flag>>4&15; /* hoppers go to next phase */
\r
258 if(!(flag&S)) /* zig-zag piece? */
\r
259 r^=flag>>12,flag^=flag>>4&15; /* alternate vector & mode */
\r
260 }W(!t); /* if not capt. continue ray*/
\r
262 if((++x&15)>=BW)x=x+16&240,lu=1; /* next sqr. of board, wrap */
\r
265 C:if(a->D<99) /* protect game history */
\r
266 a->K=Z,a->V=m,a->D=d,a->X=X, /* always store in hash tab */
\r
267 a->F=8*(m>q)|S*(m<l),a->Y=Y; /* move, type (bound/exact),*/
\r
269 printf("%2d ",d-2);
\r
271 printf("%8d %10d %c%c%c%c\n",(GetTickCount()-Ticks)/10,N,
\r
272 'i'-(X>>4&15),'9'-(X&15),'i'-(Y>>4&15),'9'-(Y&15)),fflush(stdout);}
\r
273 } /* encoded in X S,8 bits */
\r
274 if(z&4*S)K=X,L=Y&~S;
\r
275 return m+=m<e; /* delayed-loss bonus */
\r
279 /* Generic main() for Winboard-compatible engine */
\r
280 /* (Inspired by TSCP) */
\r
281 /* Author: H.G. Muller */
\r
283 /* The engine is invoked through the following */
\r
284 /* subroutines, that can draw on the global vaiables */
\r
285 /* that are maintained by the interface: */
\r
286 /* Side side to move */
\r
287 /* Move move input to or output from engine */
\r
288 /* PromPiece requested piece on promotion move */
\r
289 /* TimeLeft ms left to next time control */
\r
290 /* MovesLeft nr of moves to play within TimeLeft */
\r
291 /* MaxDepth search-depth limit in ply */
\r
292 /* Post boolean to invite engine babble */
\r
294 /* InitEngine() progran start-up initialization */
\r
295 /* InitGame() initialization to start new game */
\r
296 /* (sets Side, but not time control) */
\r
297 /* Think() think up move from current position */
\r
298 /* (leaves move in Move, can be invalid */
\r
299 /* if position is check- or stalemate) */
\r
300 /* DoMove() perform the move in Move */
\r
301 /* (togglese Side) */
\r
302 /* ReadMove() convert input move to engine format */
\r
303 /* PrintMove() print Move on standard output */
\r
304 /* Legal() check Move for legality */
\r
305 /* ClearBoard() make board empty */
\r
306 /* PutPiece() put a piece on the board */
\r
308 /* define this to the codes used in your engine, */
\r
309 /* if the engine hasn't defined it already. */
\r
311 int PrintResult(int s)
\r
313 int i, j, k, cnt=0;
\r
315 /* search last 50 states with this stm for third repeat */
\r
316 for(j=2; j<=100 && j <= HistPtr; j+=2)
\r
318 for(k=0; k<STATE; k++)
\r
319 if(HistoryBoards[HistPtr][k] !=
\r
320 HistoryBoards[HistPtr-j&1023][k] )
\r
323 /* is the same, count it */
\r
324 if(++cnt > 1) /* third repeat */
\r
326 printf("1/2-1/2 {Draw by repetition}\n");
\r
332 cnt = D(s,-I,I,Q,4*S,3);
\r
335 printf("0-1 {Black mates}\n");
\r
337 printf("1-0 {White mates}\n");
\r
341 printf("1/2-1/2 {Draw by fifty move rule}\n");
\r
352 N=8100;W(N-->256)T[N]=rand()>>9;
\r
353 srand(GetTickCount());
\r
360 for(i=0;i<16*BH;i++)b[i]=0; /* clear board */
\r
361 b[23]=b[119]=10;b[BW+8]=b[BW+104]=26; /* place Cannons */
\r
363 {b[16*K]=oo[K+16]+16;b[16*K+BW-1]=oo[K];if(!(K&1))b[16*K+BW-7]=18,b[16*K+6]=1; /* initial board setup*/
\r
364 L=BW;W(L--)b[L+16*K+257]=(K-(BW-1)/2.)*(K-(BW-1)/2.)+(L-(BH-1)/2.)*(L-(BH-1)/2.); /* center-pts table */
\r
365 } /*(in unused half b[])*/
\r
366 Side = WHITE; Q=0; O=S;
\r
368 for(i=0; i<BW; i++) if(i!=3) R += (w[oo[i]]/FAC) + (w[oo[i+16]]/FAC);
\r
372 void CopyBoard(int s)
\r
374 int i, j, k, cnt=0;
\r
376 /* copy game representation of engine to HistoryBoard */
\r
377 /* don't forget castling rights and e.p. state! */
\r
378 for(i=0; i<BH; i++)
\r
379 for(j=0; j<BW; j++) /* board squares */
\r
380 HistoryBoards[s][BW*i+j] = b[16*i+j]|64*(16*i+j==O);
\r
383 void PrintVariants()
\r
385 int i, j, count=0; char c = EOF+1, buf[80];
\r
388 f = fopen(inifile, "r");
\r
389 if(f==NULL) return;
\r
391 /* search for game names in definition file */
\r
393 while(fscanf(f, "Game: %s", buf) != 1 && c != EOF)
\r
394 while((c = fgetc(f)) != EOF && c != '\n');
\r
395 if(c == EOF) break;
\r
396 if(count++) printf(",");
\r
403 void LoadGame(char *name)
\r
405 int i, j, count=0; char c, buf[80];
\r
406 static int currentVariant;
\r
409 f = fopen(inifile, "r");
\r
411 { printf("telluser piece-desription file '%s' not found\n", inifile);
\r
414 if(fscanf(f, "version 4.8(%c)", &c)!=1 || c != 'w')
\r
415 { printf("telluser incompatible qmax.ini file\n"); exit(0); }
\r
418 { /* search for game name in definition file */
\r
419 while(fscanf(f, "Game: %s", buf)!=1 || strcmp(name, buf) ) {
\r
420 while((c = fgetc(f)) != EOF && c != '\n');
\r
423 printf("telluser variant %s not supported\n", name);
\r
425 return; /* keep old settings */
\r
428 currentVariant = count;
\r
431 /* We have found variant, or if none specified, are at beginning of file */
\r
432 if(fscanf(f, "%dx%d", &BW, &BH)!=2 || BW>12 || BH>9)
\r
433 { printf("telluser unsupported board size %dx%d\n",BW,BH); exit(0); }
\r
435 for(i=0; i<BH; i++) fscanf(f, "%d", oo+i);
\r
436 for(i=0; i<BH; i++) fscanf(f, "%d", oo+i+16);
\r
438 for(i= 0; i<=U; i++)
\r
439 A[i].K = A[i].D = A[i].X = A[i].Y = A[i].F = 0; /* clear hash */
\r
440 for(i=0; i<32; i++) piecetype[i] = 0;
\r
443 while(fscanf(f, "%d,%x", o+j, of+j)==2 ||
\r
444 fscanf(f,"%c:%d",&c, w+i+1)==2)
\r
446 { od[++i]=j; centr[i] = c>='a';
\r
447 piecetype[c&31]=i; piecename[i]=c&31;
\r
450 /* printf("tell c='%c' i=%d od[i]=%d j=%d (%3d,%8x)\n",c,i,od[i],j,o[j-1],of[j-1]); /**/
\r
451 c=0; if(i>15 || j>255) break;
\r
455 sh = w[7] < 250 ? 3 : 0;
\r
458 int main(int argc, char **argv)
\r
460 int Computer, MaxTime, MaxMoves, TimeInc, sec, i, j;
\r
461 char line[256], command[256], c, cc;
\r
465 if(argc>1 && sscanf(argv[1], "%d", &m)==1)
\r
466 { U = (1<<m)-1; argc--; argv++; }
\r
467 A = (struct _ *) calloc(U+1, sizeof(struct _));
\r
468 if(argc>1) inifile = argv[1];
\r
470 signal(SIGINT, SIG_IGN);
\r
471 printf("tellics say MaxQi 4.8 (G)\n");
\r
472 printf("tellics say by H.G. Muller\n");
\r
477 MaxTime = 10000; /* 10 sec */
\r
478 MaxDepth = 30; /* maximum depth of your search */
\r
482 if (Side == Computer) {
\r
483 /* think up & do move, measure time used */
\r
484 /* it is the responsibility of the engine */
\r
485 /* to control its search time based on */
\r
486 /* MovesLeft, TimeLeft, MaxMoves, TimeInc */
\r
487 /* Next 'MovesLeft' moves have to be done */
\r
488 /* within TimeLeft+(MovesLeft-1)*TimeInc */
\r
489 /* If MovesLeft<0 all remaining moves of */
\r
490 /* the game have to be done in this time. */
\r
491 /* If MaxMoves=1 any leftover time is lost*/
\r
492 Ticks = GetTickCount();
\r
493 m = MovesLeft<=0 ? 40 : MovesLeft;
\r
494 tlim = (0.6-0.06*(BW-8))*(TimeLeft+(m-1)*TimeInc)/(m+7);
\r
495 if(tlim>TimeLeft/15) tlim = TimeLeft/15;
\r
496 PromPiece = 0; /* Always promote to Queen ourselves */
\r
498 if (D(Side,-I,I,Q,S,3)==I) {
\r
499 Side ^= BLACK^WHITE;
\r
500 if(UnderProm>=0 && UnderProm != L)
\r
501 { printf("tellics I hate under-promotions!\n");
\r
502 printf("resign { underpromotion } \n");
\r
505 } else UnderProm = -1;
\r
507 printf("%c%c%c%c",'i'-(K>>4),'9'-(K&15),
\r
508 'i'-(L>>4&15),'9'-(L&15));
\r
510 m = GetTickCount() - Ticks;
\r
512 /* time-control accounting */
\r
514 TimeLeft += TimeInc;
\r
515 if(--MovesLeft == 0) {
\r
516 MovesLeft = MaxMoves;
\r
518 TimeLeft = MaxTime;
\r
519 else TimeLeft += MaxTime;
\r
522 GameHistory[GamePtr++] = PACK_MOVE;
\r
523 CopyBoard(HistPtr=HistPtr+1&1023);
\r
524 if(PrintResult(Side))
\r
527 if(!PrintResult(Side))
\r
528 printf("resign { refuses own move }\n");
\r
533 if (!fgets(line, 256, stdin))
\r
535 if (line[0] == '\n')
\r
537 sscanf(line, "%s", command);
\r
538 if (!strcmp(command, "xboard"))
\r
540 if (!strcmp(command, "protover")) {
\r
541 printf("feature myname=\"MaxQi 4.8G\"\n");
\r
542 printf("feature memory=1\n");
\r
543 printf("feature smp=1\n");
\r
544 printf("feature setboard=0 ping=1 done=0\n");
\r
545 printf("feature variants=\"");
\r
547 printf("\" done=1\n");
\r
550 if (!strcmp(command, "ping")) { int nr=0;
\r
551 sscanf(line, "ping %d", &nr);
\r
552 printf("pong %d\n", nr);
\r
555 if (!strcmp(command, "p")) {
\r
559 if (!strcmp(command, "memory")) {
\r
561 sscanf(line+6, "%d", &mem); mem = (mem*1024*1024)/12; // max nr of hash entries
\r
562 mask = 0x7FFFFFFF; while(mask > mem) mask >>= 1;
\r
565 A = (struct _ *) calloc(U+1, sizeof(struct _));
\r
569 if (!strcmp(command, "new")) {
\r
570 /* start new game */
\r
571 LoadGame("xiangqi");
\r
573 GamePtr = Setup = 0;
\r
577 TimeLeft = MaxTime;
\r
578 MovesLeft = MaxMoves;
\r
579 for(nr=0; nr<1024; nr++)
\r
580 for(m=0; m<STATE; m++)
\r
581 HistoryBoards[nr][m] = 0;
\r
584 if (!strcmp(command, "quit"))
\r
587 if (!strcmp(command, "force")) {
\r
588 /* computer plays neither */
\r
592 if (!strcmp(command, "white")) {
\r
593 /* set white to move in current position */
\r
598 if (!strcmp(command, "black")) {
\r
599 /* set blck to move in current position */
\r
604 if (!strcmp(command, "st")) {
\r
605 /* move-on-the-bell mode */
\r
606 /* indicated by MaxMoves = 1 */
\r
607 sscanf(line, "st %d", &MaxTime);
\r
608 MovesLeft = MaxMoves = 1;
\r
609 TimeLeft = MaxTime *= 1000;
\r
613 if (!strcmp(command, "sd")) {
\r
614 /* set depth limit (remains in force */
\r
615 /* until next 'sd n' command) */
\r
616 sscanf(line, "sd %d", &MaxDepth);
\r
617 MaxDepth += 2; /* QS depth */
\r
620 if (!strcmp(command, "level")) {
\r
621 /* normal or blitz time control */
\r
623 if(sscanf(line, "level %d %d %d",
\r
624 &MaxMoves, &MaxTime, &TimeInc)!=3 &&
\r
625 sscanf(line, "level %d %d:%d %d",
\r
626 &MaxMoves, &MaxTime, &sec, &TimeInc)!=4)
\r
628 MovesLeft = MaxMoves;
\r
629 TimeLeft = MaxTime = 60000*MaxTime + 1000*sec;
\r
633 if (!strcmp(command, "time")) {
\r
634 /* set time left on clock */
\r
635 sscanf(line, "time %d", &TimeLeft);
\r
636 TimeLeft *= 10; /* centi-sec to ms */
\r
639 if (!strcmp(command, "otim")) {
\r
640 /* opponent's time (not kept, so ignore) */
\r
643 if (!strcmp(command, "easy")) {
\r
646 if (!strcmp(command, "hard")) {
\r
649 if (!strcmp(command, "accepted")) {
\r
652 if (!strcmp(command, "rejected")) {
\r
655 if (!strcmp(command, "random")) {
\r
658 if (!strcmp(command, "go")) {
\r
659 /* set computer to play current side to move */
\r
661 MovesLeft = -(GamePtr+(Side==WHITE)>>1);
\r
662 while(MaxMoves>0 && MovesLeft<=0)
\r
663 MovesLeft += MaxMoves;
\r
666 if (!strcmp(command, "hint")) {
\r
667 Ticks = GetTickCount(); tlim = 1000;
\r
668 D(Side,-I,I,Q,4*S,6);
\r
672 printf("%c%c%c%c",'a'+(K&7),'8'-(K>>4),
\r
673 'a'+(L&7),'8'-(L>>4));
\r
677 if (!strcmp(command, "undo") && (nr=1) ||
\r
678 !strcmp(command, "remove") && (nr=2) ) {
\r
679 /* 'take back' moves by replaying game */
\r
680 /* from history until desired ply */
\r
681 if (GamePtr - nr < 0)
\r
684 HistPtr -= nr; /* erase history boards */
\r
686 for(m=0; m<STATE; m++)
\r
687 HistoryBoards[HistPtr+nr+1&1023][m] = 0;
\r
690 for(i=0; i<128; i++) b[i] = setupPosition[i];
\r
691 Side = setupPosition[128]; Q = SetupQ;
\r
693 for(i=0; i<=U; i++) A[i].D = A[i].K = 0; // clear hash table
\r
694 for(nr=0; nr<GamePtr; nr++) {
\r
695 UNPACK_MOVE(GameHistory[nr]);
\r
696 D(Side,-I,I,Q,S,3);
\r
697 Side ^= BLACK^WHITE;
\r
701 if (!strcmp(command, "post")) {
\r
705 if (!strcmp(command, "nopost")) {
\r
709 if (!strcmp(command, "variant")) {
\r
710 sscanf(line, "variant %s", command);
\r
712 InitGame(); Setup = 0;
\r
715 if (!strcmp(command, "edit")) {
\r
716 int color = WHITE, p;
\r
718 while(fgets(line, 256, stdin)) {
\r
722 for(i=0; i<128; i++) b[i]=0;
\r
727 color = WHITE+BLACK - color;
\r
731 if( m >= 'A' && m <= 'Z'
\r
732 && line[1] >= 'a' && line[1] <= 'a'+BH-1
\r
733 && line[2] >= '0' && line[2] <= '0'+BW-1) {
\r
734 m = 16*('i'-line[1])+'9'-line[2];
\r
735 p = 4; /* Elephant code */
\r
746 Q+=w[p]; R+=w[p]/FAC;
\r
749 if((color==WHITE) == ((m&15)<5))
\r
751 case 'E': p+=(color==BLACK);
\r
753 Q+=w[p]; R+=w[p]/FAC;
\r
759 if(Side != color) Q = -Q;
\r
760 GamePtr = HistPtr = 0; Setup = 1; SetupQ = Q; // start anew
\r
761 for(i=0; i<128; i++) setupPosition[i] = b[i]; // remember position
\r
762 setupPosition[128] = Side;
\r
765 /* command not recognized, assume input move */
\r
766 m = line[0]<'a' | line[0]>='a'+BH | line[1]<'0' | line[1]>='0'+BW |
\r
767 line[2]<'a' | line[2]>='a'+BH | line[3]<'0' | line[3]>='0'+BW |
\r
769 {char *c=line; K=16*('i'-c[0])+'9'-c[1];
\r
770 L=16*('i'-c[2])+'9'-c[3]; }
\r
772 /* doesn't have move syntax */
\r
773 printf("Error (unknown command): %s\n", command);
\r
774 else if(D(Side,-I,I,Q,S,3)!=I) {
\r
775 /* did have move syntax, but illegal move */
\r
776 printf("Illegal move:%s\n", line);
\r
777 } else { /* legal move, perform it */
\r
778 GameHistory[GamePtr++] = PACK_MOVE;
\r
779 Side ^= BLACK^WHITE;
\r
780 CopyBoard(HistPtr=HistPtr+1&1023);
\r
781 if(PrintResult(Side)) Computer = EMPTY;
\r