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 */
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 QINI_FILE "qmax.ini"
97 #include <windows.h>
\r
98 #define QINI_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 = QINI_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
333 if(cnt==0 && K==0 && L==0) {
\r
334 printf("1/2-1/2 {Stalemate}\n");
\r
339 printf("0-1 {Black mates}\n");
\r
341 printf("1-0 {White mates}\n");
\r
345 printf("1/2-1/2 {Draw by fifty move rule}\n");
\r
356 N=8100;W(N-->256)T[N]=rand()>>9;
\r
357 srand(GetTickCount());
\r
364 for(i=0;i<16*BH;i++)b[i]=0; /* clear board */
\r
365 b[23]=b[119]=10;b[BW+8]=b[BW+104]=26; /* place Cannons */
\r
367 {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
368 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
369 } /*(in unused half b[])*/
\r
370 Side = WHITE; Q=0; O=S;
\r
372 for(i=0; i<BW; i++) if(i!=3) R += (w[oo[i]]/FAC) + (w[oo[i+16]]/FAC);
\r
376 void CopyBoard(int s)
\r
378 int i, j, k, cnt=0;
\r
380 /* copy game representation of engine to HistoryBoard */
\r
381 /* don't forget castling rights and e.p. state! */
\r
382 for(i=0; i<BH; i++)
\r
383 for(j=0; j<BW; j++) /* board squares */
\r
384 HistoryBoards[s][BW*i+j] = b[16*i+j]|64*(16*i+j==O);
\r
387 void PrintVariants()
\r
389 int i, j, count=0; char c, buf[80];
\r
392 f = fopen(inifile, "r");
\r
393 if(f==NULL) return;
\r
395 /* search for game names in definition file */
\r
397 while(fscanf(f, "Game: %s", buf) != 1 && c != EOF)
\r
398 while((c = fgetc(f)) != EOF && c != '\n');
\r
399 if(c == EOF) break;
\r
400 if(count++) printf(",");
\r
407 int LoadGame(char *name)
\r
409 int i, j, count=0; char c, buf[80];
\r
410 static int currentVariant;
\r
413 f = fopen(inifile, "r");
\r
415 { printf("telluser piece-desription file '%s' not found\n", inifile);
\r
418 if(fscanf(f, "version 4.8(%c)", &c)!=1 || c != 'w')
\r
419 { printf("telluser incompatible qmax.ini file\n"); exit(0); }
\r
422 { /* search for game name in definition file */
\r
423 while(fscanf(f, "Game: %s", buf)!=1 || strcmp(name, buf) ) {
\r
424 while((c = fgetc(f)) != EOF && c != '\n');
\r
427 printf("telluser variant %s not supported\n", name);
\r
429 return; /* keep old settings */
\r
432 currentVariant = count;
\r
435 /* We have found variant, or if none specified, are at beginning of file */
\r
436 if(fscanf(f, "%dx%d", &BW, &BH)!=2 || BW>12 || BH>9)
\r
437 { printf("telluser unsupported board size %dx%d\n",BW,BH); exit(0); }
\r
439 for(i=0; i<BH; i++) fscanf(f, "%d", oo+i);
\r
440 for(i=0; i<BH; i++) fscanf(f, "%d", oo+i+16);
\r
442 for(i= 0; i<=U; i++)
\r
443 A[i].K = A[i].D = A[i].X = A[i].Y = A[i].F = 0; /* clear hash */
\r
444 for(i=0; i<32; i++) piecetype[i] = 0;
\r
447 while(fscanf(f, "%d,%x", o+j, of+j)==2 ||
\r
448 fscanf(f,"%c:%d",&c, w+i+1)==2)
\r
450 { od[++i]=j; centr[i] = c>='a';
\r
451 piecetype[c&31]=i; piecename[i]=c&31;
\r
454 /* 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
455 c=0; if(i>15 || j>255) break;
\r
459 sh = w[7] < 250 ? 3 : 0;
\r
462 int main(int argc, char **argv)
\r
464 int Computer, MaxTime, MaxMoves, TimeInc, sec, i, j;
\r
465 char line[256], command[256], c, cc;
\r
469 if(argc>1 && sscanf(argv[1], "%d", &m)==1)
\r
470 { U = (1<<m)-1; argc--; argv++; }
\r
471 A = (struct _ *) calloc(U+1, sizeof(struct _));
\r
472 if(argc>1) inifile = argv[1];
\r
474 signal(SIGINT, SIG_IGN);
\r
475 printf("tellics say MaxQi 4.8 (G)\n");
\r
476 printf("tellics say by H.G. Muller\n");
\r
481 MaxTime = 10000; /* 10 sec */
\r
482 MaxDepth = 30; /* maximum depth of your search */
\r
486 if (Side == Computer) {
\r
487 /* think up & do move, measure time used */
\r
488 /* it is the responsibility of the engine */
\r
489 /* to control its search time based on */
\r
490 /* MovesLeft, TimeLeft, MaxMoves, TimeInc */
\r
491 /* Next 'MovesLeft' moves have to be done */
\r
492 /* within TimeLeft+(MovesLeft-1)*TimeInc */
\r
493 /* If MovesLeft<0 all remaining moves of */
\r
494 /* the game have to be done in this time. */
\r
495 /* If MaxMoves=1 any leftover time is lost*/
\r
496 Ticks = GetTickCount();
\r
497 m = MovesLeft<=0 ? 40 : MovesLeft;
\r
498 tlim = (0.6-0.06*(BW-8))*(TimeLeft+(m-1)*TimeInc)/(m+7);
\r
499 if(tlim>TimeLeft/15) tlim = TimeLeft/15;
\r
500 PromPiece = 0; /* Always promote to Queen ourselves */
\r
502 if (D(Side,-I,I,Q,S,3)==I) {
\r
503 Side ^= BLACK^WHITE;
\r
504 if(UnderProm>=0 && UnderProm != L)
\r
505 { printf("tellics I hate under-promotions!\n");
\r
506 printf("resign { underpromotion } \n");
\r
509 } else UnderProm = -1;
\r
511 printf("%c%c%c%c",'i'-(K>>4),'9'-(K&15),
\r
512 'i'-(L>>4&15),'9'-(L&15));
\r
514 m = GetTickCount() - Ticks;
\r
516 /* time-control accounting */
\r
518 TimeLeft += TimeInc;
\r
519 if(--MovesLeft == 0) {
\r
520 MovesLeft = MaxMoves;
\r
522 TimeLeft = MaxTime;
\r
523 else TimeLeft += MaxTime;
\r
526 GameHistory[GamePtr++] = PACK_MOVE;
\r
527 CopyBoard(HistPtr=HistPtr+1&1023);
\r
528 if(PrintResult(Side))
\r
531 if(!PrintResult(Side))
\r
532 printf("resign { refuses own move }\n");
\r
537 if (!fgets(line, 256, stdin))
\r
539 if (line[0] == '\n')
\r
541 sscanf(line, "%s", command);
\r
542 if (!strcmp(command, "xboard"))
\r
544 if (!strcmp(command, "protover")) {
\r
545 printf("feature myname=\"MaxQi 4.8G\"\n");
\r
546 printf("feature memory=1\n");
\r
547 printf("feature smp=1\n");
\r
548 printf("feature setboard=0 ping=1 done=0\n");
\r
549 printf("feature variants=\"");
\r
551 printf("\" done=1\n");
\r
554 if (!strcmp(command, "ping")) { int nr=0;
\r
555 sscanf(line, "ping %d", &nr);
\r
556 printf("pong %d\n", nr);
\r
559 if (!strcmp(command, "p")) {
\r
563 if (!strcmp(command, "memory")) {
\r
565 sscanf(line+6, "%d", &mem); mem = (mem*1024*1024)/12; // max nr of hash entries
\r
566 mask = 0x7FFFFFFF; while(mask > mem) mask >>= 1;
\r
569 A = (struct _ *) calloc(U+1, sizeof(struct _));
\r
573 if (!strcmp(command, "new")) {
\r
574 /* start new game */
\r
575 LoadGame("xiangqi");
\r
577 GamePtr = Setup = 0;
\r
581 TimeLeft = MaxTime;
\r
582 MovesLeft = MaxMoves;
\r
583 for(nr=0; nr<1024; nr++)
\r
584 for(m=0; m<STATE; m++)
\r
585 HistoryBoards[nr][m] = 0;
\r
588 if (!strcmp(command, "quit"))
\r
591 if (!strcmp(command, "force")) {
\r
592 /* computer plays neither */
\r
596 if (!strcmp(command, "white")) {
\r
597 /* set white to move in current position */
\r
602 if (!strcmp(command, "black")) {
\r
603 /* set blck to move in current position */
\r
608 if (!strcmp(command, "st")) {
\r
609 /* move-on-the-bell mode */
\r
610 /* indicated by MaxMoves = 1 */
\r
611 sscanf(line, "st %d", &MaxTime);
\r
612 MovesLeft = MaxMoves = 1;
\r
613 TimeLeft = MaxTime *= 1000;
\r
617 if (!strcmp(command, "sd")) {
\r
618 /* set depth limit (remains in force */
\r
619 /* until next 'sd n' command) */
\r
620 sscanf(line, "sd %d", &MaxDepth);
\r
621 MaxDepth += 2; /* QS depth */
\r
624 if (!strcmp(command, "level")) {
\r
625 /* normal or blitz time control */
\r
627 if(sscanf(line, "level %d %d %d",
\r
628 &MaxMoves, &MaxTime, &TimeInc)!=3 &&
\r
629 sscanf(line, "level %d %d:%d %d",
\r
630 &MaxMoves, &MaxTime, &sec, &TimeInc)!=4)
\r
632 MovesLeft = MaxMoves;
\r
633 TimeLeft = MaxTime = 60000*MaxTime + 1000*sec;
\r
637 if (!strcmp(command, "time")) {
\r
638 /* set time left on clock */
\r
639 sscanf(line, "time %d", &TimeLeft);
\r
640 TimeLeft *= 10; /* centi-sec to ms */
\r
643 if (!strcmp(command, "otim")) {
\r
644 /* opponent's time (not kept, so ignore) */
\r
647 if (!strcmp(command, "easy")) {
\r
650 if (!strcmp(command, "hard")) {
\r
653 if (!strcmp(command, "accepted")) {
\r
656 if (!strcmp(command, "rejected")) {
\r
659 if (!strcmp(command, "random")) {
\r
662 if (!strcmp(command, "go")) {
\r
663 /* set computer to play current side to move */
\r
665 MovesLeft = -(GamePtr+(Side==WHITE)>>1);
\r
666 while(MaxMoves>0 && MovesLeft<=0)
\r
667 MovesLeft += MaxMoves;
\r
670 if (!strcmp(command, "hint")) {
\r
671 Ticks = GetTickCount(); tlim = 1000;
\r
672 D(Side,-I,I,Q,4*S,6);
\r
676 printf("%c%c%c%c",'a'+(K&7),'8'-(K>>4),
\r
677 'a'+(L&7),'8'-(L>>4));
\r
681 if (!strcmp(command, "undo") && (nr=1) ||
\r
682 !strcmp(command, "remove") && (nr=2) ) {
\r
683 /* 'take back' moves by replaying game */
\r
684 /* from history until desired ply */
\r
685 if (GamePtr - nr < 0)
\r
688 HistPtr -= nr; /* erase history boards */
\r
690 for(m=0; m<STATE; m++)
\r
691 HistoryBoards[HistPtr+nr+1&1023][m] = 0;
\r
694 for(i=0; i<128; i++) b[i] = setupPosition[i];
\r
695 Side = setupPosition[128]; Q = SetupQ;
\r
697 for(i=0; i<=U; i++) A[i].D = A[i].K = 0; // clear hash table
\r
698 for(nr=0; nr<GamePtr; nr++) {
\r
699 UNPACK_MOVE(GameHistory[nr]);
\r
700 D(Side,-I,I,Q,S,3);
\r
701 Side ^= BLACK^WHITE;
\r
705 if (!strcmp(command, "post")) {
\r
709 if (!strcmp(command, "nopost")) {
\r
713 if (!strcmp(command, "variant")) {
\r
714 sscanf(line, "variant %s", command);
\r
716 InitGame(); Setup = 0;
\r
719 if (!strcmp(command, "edit")) {
\r
720 int color = WHITE, p;
\r
722 while(fgets(line, 256, stdin)) {
\r
726 for(i=0; i<128; i++) b[i]=0;
\r
731 color = WHITE+BLACK - color;
\r
735 if( m >= 'A' && m <= 'Z'
\r
736 && line[1] >= 'a' && line[1] <= 'a'+BH-1
\r
737 && line[2] >= '0' && line[2] <= '0'+BW-1) {
\r
738 m = 16*('i'-line[1])+'9'-line[2];
\r
739 p = 4; /* Elephant code */
\r
750 Q+=w[p]; R+=w[p]/FAC;
\r
753 if((color==WHITE) == ((m&15)<5))
\r
755 case 'E': p+=(color==BLACK);
\r
757 Q+=w[p]; R+=w[p]/FAC;
\r
763 if(Side != color) Q = -Q;
\r
764 GamePtr = HistPtr = 0; Setup = 1; SetupQ = Q; // start anew
\r
765 for(i=0; i<128; i++) setupPosition[i] = b[i]; // remember position
\r
766 setupPosition[128] = Side;
\r
769 /* command not recognized, assume input move */
\r
770 m = line[0]<'a' | line[0]>='a'+BH | line[1]<'0' | line[1]>='0'+BW |
\r
771 line[2]<'a' | line[2]>='a'+BH | line[3]<'0' | line[3]>='0'+BW |
\r
773 {char *c=line; K=16*('i'-c[0])+'9'-c[1];
\r
774 L=16*('i'-c[2])+'9'-c[3]; }
\r
776 /* doesn't have move syntax */
\r
777 printf("Error (unknown command): %s\n", command);
\r
778 else if(D(Side,-I,I,Q,S,3)!=I) {
\r
779 /* did have move syntax, but illegal move */
\r
780 printf("Illegal move:%s\n", line);
\r
781 } else { /* legal move, perform it */
\r
782 GameHistory[GamePtr++] = PACK_MOVE;
\r
783 Side ^= BLACK^WHITE;
\r
784 CopyBoard(HistPtr=HistPtr+1&1023);
\r
785 if(PrintResult(Side)) Computer = EMPTY;
\r