eae3b48567aca7e10b77bbb202c1fd6be5b9c15e
[capablanca.git] / board.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22
23 #include "includes.h"
24
25
26 const char *wpstring[] = {" ", "P", "N", "B", "R", "A", "C", "M", "Q", "E", "B", "Q", "W", "H", "N", "D", "H", "L", 
27                           "C", "S", "G", "H", "A", "F", "E", "H", "M", "S", "E", "W", "O", "G", "V", "S", "E", "A",
28                           "K", "H", "E", "W", "G", "L", "C", "H"};
29 const char *bpstring[] = {" ", "p", "n", "b", "r", "a", "c", "m", "q", "e", "b", "q", "w", "h", "n", "d", "h", "l", 
30                           "c", "s", "g", "h", "a", "f", "e", "h", "m", "s", "e", "w", "o", "g", "v", "s", "e", "a",
31                           "k", "h", "e", "w", "g", "l", "c", "h"};
32
33 int pieceValues[PIECES] = {0, 1, 3, 3, 5, 8, 9, 3, 9, 1, 1, 2, 2, 2, 1, 6, 5, 2, 3, 3, 3, 1, 5, 2, 1, 7, 7, 3, 3, 3, 7, 7, 7, 8, 9, 12,
34                            0, 8, 9, 8, 7, 3, 3, 1};
35
36 static const int mach_type = (1<<7) | (1<<8) | (1<<9) | (1<<10) | (1<<11);
37 #define IsMachineStyle(n) (((1<<(n)) & mach_type) != 0)
38
39 static char bstring[MAX_BOARD_STRING_LENGTH];
40
41 static int board_read_file(char *category, char *gname, struct game_state_t *gs);
42 static void wild_update(board_t b, int style);
43
44 static int style1(struct game_state_t *b, struct move_t *ml);
45 static int style2(struct game_state_t *b, struct move_t *ml);
46 static int style3(struct game_state_t *b, struct move_t *ml);
47 static int style4(struct game_state_t *b, struct move_t *ml);
48 static int style5(struct game_state_t *b, struct move_t *ml);
49 static int style6(struct game_state_t *b, struct move_t *ml);
50 static int style7(struct game_state_t *b, struct move_t *ml);
51 static int style8(struct game_state_t *b, struct move_t *ml);
52 static int style9(struct game_state_t *b, struct move_t *ml);
53 static int style10(struct game_state_t *b, struct move_t *ml);
54 static int style11(struct game_state_t *b, struct move_t *ml);
55 static int style12(struct game_state_t *b, struct move_t *ml);
56 static int style13(struct game_state_t *b, struct move_t *ml);
57
58 static int (*styleFuncs[MAX_STYLES])() = {
59         style1,
60         style2,
61         style3,
62         style4,
63         style5,
64         style6,
65         style7,
66         style8,
67         style9,
68         style10,
69         style11,
70         style12,
71         style13
72 };
73
74
75 static void reset_board_vars(struct game_state_t *gs)
76 {
77  int f,r;
78
79   if(gs->files <= 0) gs->files = 8; // [HGM] for pristine board, set default size
80   if(gs->ranks <= 0) gs->ranks = 8;
81   for (f = 0; f < 2; f++) {
82     for (r = 0; r < BW; r++)
83       gs->ep_possible[f][r] = 0;
84     for (r = PAWN; r <= PIECES-1; r++)
85       gs->holding[f][r-PAWN] = 0;
86   }
87   gs->wkmoved = gs->wqrmoved = gs->wkrmoved = -1; // [HGM] castle: no rights
88   gs->bkmoved = gs->bqrmoved = gs->bkrmoved = -1;
89   gs->onMove = WHITE;
90   gs->moveNum = 1;
91   gs->lastIrreversable = -1;
92   gs->gameNum = -1;
93 }
94
95 void board_clear(struct game_state_t *gs)
96 {
97  int f,r;
98
99  for (f = 0; f < BW; f++)
100     for (r = 0; r < BH; r++)
101       gs->board[f][r] = NOPIECE;
102  reset_board_vars(gs);
103 }
104
105 void board_standard(struct game_state_t *gs)
106 {
107  int f,r;
108
109  for (f = 0; f < BW; f++)
110     for (r = 0; r < BH; r++)
111       gs->board[f][r] = NOPIECE;
112  for (f = 0; f < gs->files; f++)
113    gs->board[f][gs->ranks-7] = W_PAWN;
114  for (f = 0; f < gs->files; f++)
115    gs->board[f][6] = B_PAWN;
116  gs->board[0][0] = W_ROOK;
117  gs->board[1][0] = W_KNIGHT;
118  gs->board[2][0] = W_BISHOP; 
119  gs->board[3][0] = W_QUEEN;
120  gs->board[gs->files/2][0] = W_KING;
121  gs->board[gs->files-3][0] = W_BISHOP;
122  gs->board[gs->files-2][0] = W_KNIGHT;
123  gs->board[gs->files-1][0] = W_ROOK;
124  gs->board[0][gs->ranks-1] = B_ROOK;
125  gs->board[1][gs->ranks-1] = B_KNIGHT;
126  gs->board[2][gs->ranks-1] = B_BISHOP;
127  gs->board[3][gs->ranks-1] = B_QUEEN;
128  gs->board[gs->files/2][gs->ranks-1] = B_KING;
129  gs->board[gs->files-3][gs->ranks-1] = B_BISHOP;
130  gs->board[gs->files-2][gs->ranks-1] = B_KNIGHT;
131  gs->board[gs->files-1][gs->ranks-1] = B_ROOK;
132 #if 1
133  if(gs->files == 10) {
134   gs->board[6][0] = W_CARDINAL;
135   gs->board[4][0] = W_MARSHALL;
136   gs->board[6][gs->ranks-1] = B_CARDINAL;
137   gs->board[4][gs->ranks-1] = B_MARSHALL;
138  }
139  if(gs->royalKnight) {
140    gs->board[1][0] = W_MAN;
141    gs->board[gs->files-2][0] = W_MAN;
142    gs->board[1][gs->ranks-1] = B_MAN;
143    gs->board[gs->files-2][gs->ranks-1] = B_MAN;
144  }
145 #endif
146
147  reset_board_vars(gs);
148  // [HGM] castle: standard setup has rights for corner Rooks and central King
149  gs->wkmoved = gs->files/2;
150  gs->bkmoved = gs->files/2;
151  gs->wkrmoved = gs->files-1;
152  gs->bkrmoved = gs->files-1;
153  gs->wqrmoved = 0;
154  gs->bqrmoved = 0;
155 }
156
157 int board_init(int g,struct game_state_t *b, char *category, char *board)
158 {
159   int retval = 0;
160   int wval, i, j;
161
162   b->files = b->ranks = 8;
163   b->pawnDblStep = (!category || strcmp(category, "shatranj")); 
164   b->royalKnight = (category && !strcmp(category, "knightmate"));
165   b->capablancaPieces = 0;
166   b->holdings = 0;
167   b->drops = 0;
168   b->castlingStyle = 1;
169   b->palace = 0;
170   b->setup = 0;
171   b->bareKingLoses = 0;
172   b->stalemate = 1;
173   b->promoType = 1;
174   b->promoZone = 1;
175   b->variant[0] = 0; // [HGM] variant: default is normal, if variant name is missing
176   if (!category || !board || !category[0] || !board[0]) 
177                                 /* accounts for bughouse too */
178     board_standard(b);
179   else {
180     if(category && category[0]) strcpy(b->variant, category); // [HGM] variant: remember category name
181     if (!strcmp(category, "wild") && sscanf(board, "%d", &wval) == 1) {
182         if(wval >= 1 && wval <= 4)
183             wild_update(b->board, wval);
184         sprintf(b->variant, "wild/%d", wval);
185     }
186
187     if (!strcmp(category, "knightmate")) {
188       board_standard(b);
189     } else if (!strcmp(category, "super")) {
190       board_standard(b);
191       b->holdings = 1;
192       b->promoType = 2;
193       for(i=CENTAUR; i<=AMAZON; i++) {
194         int placed = 0;
195         do { int p, newp;
196           j = random() % 8;
197           if((p = piecetype(b->board[j][0])) >= CENTAUR) continue; // includes King
198           b->holding[1][p-PAWN] = ++b->holding[0][p-PAWN]; // piece to holding
199           if(board && !strcmp(board, "1")) newp = i - CENTAUR + WOODY; else newp = i;
200           if(board && !strcmp(board, "2")) newp = WOODY + random()%7;
201           b->board[j][0] = newp | WHITE; // place replacements
202           b->board[j][7] = newp | BLACK;
203           placed = 1;
204         } while(!placed);
205       }
206       b->setup = 1;
207     } else if (!strcmp(category, "fischerandom")) {
208       wild_update(b->board, 22);
209       b->castlingStyle = 2;
210       b->setup = 1; // [HGM] FRC: even the default is a setup position, for which an initial board has to be printed
211     } else if (!strcmp(category, "caparandom")) {
212       b->files = 10;
213       wild_update(b->board, 46);
214       b->castlingStyle = 2;
215       b->setup = 1; 
216     } else retval = board_read_file(category, board, b); 
217   }
218   if(b->setup && game_globals.garray[g].FENstartPos[0])  // [HGM] use pre-existing start position, if one available
219     FEN_to_board(game_globals.garray[g].FENstartPos, b); //       (could be wild board, or shuffle variant)
220   if(b->castlingStyle == 1) {
221     b->wkmoved = b->files/2;
222     b->bkmoved = b->files/2;
223     b->wkrmoved = b->files-1;
224     b->bkrmoved = b->files-1;
225     b->wqrmoved = 0;
226     b->bqrmoved = 0;
227   } else if(b->castlingStyle == 2) {
228     for(i=j=0; i < b->files; i++) {
229       int p = b->board[i][0];
230       if(p == W_ROOK || p == W_KING) {
231         switch(j++) {
232           case 0: b->wqrmoved = b->bqrmoved = i; break;
233           case 1: b->wkmoved  = b->bkmoved  = i; break;
234           case 2: b->wkrmoved = b->bkrmoved = i; break;
235         }
236       }
237     }
238   }
239
240   MakeFENpos(g, game_globals.garray[g].FENstartPos);
241
242   return retval;
243 }
244
245 void board_calc_strength(struct game_state_t *b, int *ws, int *bs)
246 {
247   int r, f;
248   int *p;
249
250   *ws = *bs = 0;
251   for (f = 0; f < b->ranks; f++) {
252     for (r = 0; r < b->files; r++) {
253       if (colorval(b->board[r][f]) == WHITE)
254         p = ws;
255       else
256         p = bs;
257       *p += pieceValues[piecetype(b->board[r][f])];
258     }
259   }
260   for (r = PAWN; r < PIECES; r++) {
261     *ws += b->holding[0][r-1] * pieceValues[r];
262     *bs += b->holding[1][r-1] * pieceValues[r];
263   }
264 }
265
266 static char *holding_str(int *holding)
267 {
268         static char tmp[80];
269         int p,i,j;
270
271         i = 0;
272         for (p = PAWN; p < PIECES; p++) {
273                 for (j = 0; j < holding[p-1]; j++) {
274                         tmp[i++] = wpstring[p][0];
275                 }
276         }
277         tmp[i] = '\0';
278         return tmp;
279 }
280
281 static char *append_holding_machine(char *buf, int g, int c, int p)
282 {
283   struct game_state_t *gs = &game_globals.garray[g].game_state;
284   char tmp[160];
285
286   sprintf(tmp, "<b1> game %d white [%s] black [", g+1, holding_str(gs->holding[0]));
287   strcat(tmp, holding_str(gs->holding[1]));
288   strcat(buf, tmp);
289   if (p) {
290     sprintf(tmp, "] <- %c%s\n", "WB"[c], wpstring[p]);
291     strcat(buf, tmp);
292   } else
293     strcat(buf, "]\n");
294   return buf;
295 }
296
297 static char *append_holding_display(char *buf, struct game_state_t *gs, int white)
298 {
299   if (white)
300     strcat(buf, "White holding: [");
301   else
302     strcat(buf, "Black holding: [");
303   strcat(buf, holding_str(gs->holding[white ? 0 : 1]));
304   strcat(buf, "]\n");
305   return buf;
306 }
307
308 void update_holding(int g, int pieceCaptured)
309 {
310   int p = piecetype(pieceCaptured);
311   int c = colorval(pieceCaptured);
312   struct game_state_t *gs = &game_globals.garray[g].game_state;
313   int pp, pl;
314   char tmp1[160], tmp2[160];
315
316   if (c == WHITE) {
317     c = 0;
318     pp = game_globals.garray[g].white;
319   } else {
320     c = 1;
321     pp = game_globals.garray[g].black;
322   }
323   gs->holding[c][p-1]++;
324   tmp1[0] = '\0';
325   append_holding_machine(tmp1, g, c, p);
326   sprintf(tmp2, "Game %d %s received: %s -> [%s]\n", g+1,
327           player_globals.parray[pp].name, wpstring[p], holding_str(gs->holding[c]));
328   for (pl = 0; pl < player_globals.p_num; pl++) {
329     if (player_globals.parray[pl].status == PLAYER_EMPTY)
330       continue;
331     if (player_is_observe(pl, g) || (player_globals.parray[pl].game == g)) {
332       pprintf_prompt(pl, IsMachineStyle(player_globals.parray[pl].style) ? tmp1 : tmp2);
333         }
334   }
335 }
336
337
338 /* Globals used for each board */
339 static int wTime, bTime;
340 static int orient;
341 static int forPlayer;
342 static int myTurn;              /* 1 = my turn, 0 = observe, -1 = other turn */
343  /* 2 = examiner, -2 = observing examiner */
344  /* -3 = just send position (spos/refresh) */
345
346 char *board_to_string(char *wn, char *bn,
347                       int wt, int bt,
348                       struct game_state_t *b, struct move_t *ml, int style,
349                       int orientation, int relation,
350                       int p)
351 {
352   int bh = (b->gameNum >= 0 && game_globals.garray[b->gameNum].link >= 0
353              || b->holdings || b->drops == 2); // [HGM] zh: make sure holdings are printed (also in Seirawan)
354   orient = orientation;
355   myTurn = relation;
356
357   wTime = 0;
358   bTime = 0;
359
360   /* when examining we calculate times based on the time left when the 
361      move happened, not current time */
362   if (game_globals.garray[b->gameNum].status == GAME_EXAMINE) {
363           unsigned nhm = game_globals.garray[b->gameNum].numHalfMoves;
364
365           if (nhm > 0) {
366                   wTime = ml[nhm - 1].wTime;
367                   bTime = ml[nhm - 1].bTime;
368           } else {
369                   wTime = game_globals.garray[b->gameNum].wInitTime;
370                   bTime = game_globals.garray[b->gameNum].bInitTime;
371           }
372   }
373
374   /* cope with old stored games */
375   if (wTime == 0) wTime = wt;
376   if (bTime == 0) bTime = bt;
377
378   forPlayer = p;
379   if ((style < 0) || (style >= MAX_STYLES))
380     return NULL;
381
382   if (style != 11) {            /* game header */
383     sprintf(bstring, "Game %d (%s vs. %s)\n\n",
384           b->gameNum + 1,
385           game_globals.garray[b->gameNum].white_name,
386           game_globals.garray[b->gameNum].black_name);
387   } else
388     bstring[0] = '\0';
389
390   if (bh && !IsMachineStyle(style))
391     append_holding_display(bstring, b, orientation==BLACK);
392
393   if (styleFuncs[style] (b, ml))
394     return NULL;
395
396   if (bh) {
397     if (IsMachineStyle(style))
398       append_holding_machine(bstring, b->gameNum, 0, 0);
399     else
400       append_holding_display(bstring, b, orientation==WHITE);
401   }
402   return bstring;
403 }
404
405 char *move_and_time(struct move_t *m)
406 {
407         static char tmp[20];
408 #if 0
409         if(m->depth>0)
410              sprintf(tmp, "%-7s (%s%.2f/%d)", m->algString, /* tenth_str(m->tookTime, 0), */
411                                         m->score>0 ? "+" : "", m->score, m->depth);
412         else 
413 #endif
414         sprintf(tmp, "%-7s (%s)", m->algString, tenth_str(m->tookTime, 0));
415         return tmp;
416 }
417
418 /* The following take the game state and whole move list */
419
420 void Enlarge(char *a, int ss, int w)
421 {
422   int l, i;
423   char *p, *q;
424   if(strlen(a) < ss) return;
425   for(i=8; i<w; i++) {
426     l = strlen(a);
427     p = a + l; q = p + ss;
428     while(q != a+l-ss) *q-- = *p--;
429   }
430 }
431
432 static int genstyle(struct game_state_t *b, struct move_t *ml, const char *wp[], const char *bp[],
433                     const char *wsqr, const char *bsqr,
434                     const char *top, const char *mid, const char *start, const char *end, 
435                     const char *label,const char *blabel)
436 {
437   int f, r, count, i;
438   char tmp[80], mylabel[80], *p, *q, myTop[80], myMid[80];
439   int firstR, lastR, firstF, lastF, inc;
440   int ws, bs, sqrSize = strlen(wp[0]);
441
442   board_calc_strength(b, &ws, &bs);
443   if (orient == WHITE) {
444     firstR = b->ranks-1;
445     firstF = b->files-1;
446     lastR = lastF = 0;
447     inc = -1;
448   } else {
449     firstR = firstF = 0;
450     lastR = b->ranks-1;
451     lastF = b->files-1;
452     inc = 1;
453   }
454   strcpy(myTop, top);
455   strcpy(myMid, mid);
456   Enlarge(myTop, sqrSize, b->files);
457   Enlarge(myMid, sqrSize, b->files);
458   strcat(bstring, myTop);
459   for (f = firstR, count = b->ranks-1; f != lastR + inc; f += inc, count--) {
460     sprintf(tmp, "     %d  %s", f + (b->ranks < 10), start);
461     strcat(bstring, tmp);
462     for (r = lastF; r != firstF - inc; r = r - inc) {
463       if (square_color(r, f) == WHITE)
464         strcat(bstring, wsqr);
465       else
466         strcat(bstring, bsqr);
467       if (piecetype(b->board[r][f]) == NOPIECE) {
468         if (square_color(r, f) == WHITE)
469           strcat(bstring, bp[0]);
470         else
471           strcat(bstring, wp[0]);
472       } else {
473         int piece = piecetype(b->board[r][f]);
474 //      if(piece > QUEEN) piece = ELEPHANT + (piece == KING); // All fairies become elephants in ascii styles
475         if (colorval(b->board[r][f]) == WHITE)
476           strcat(bstring, wp[piece]);
477         else
478           strcat(bstring, bp[piece]);
479       }
480     }
481     sprintf(tmp, "%s", end);
482     strcat(bstring, tmp);
483     switch (count) {
484     case 7:
485       sprintf(tmp, "     Move # : %d (%s)", b->moveNum, CString(b->onMove));
486       strcat(bstring, tmp);
487       break;
488     case 6:
489 /*    if ((b->moveNum > 1) || (b->onMove == BLACK)) {  */
490 /* The change from the above line to the one below is a kludge by hersco. */
491       if (game_globals.garray[b->gameNum].numHalfMoves > 0) {
492 /* loon: think this fixes the crashing ascii board on takeback bug */
493         sprintf(tmp, "     %s Moves : '%s'", CString(CToggle(b->onMove)),
494                 move_and_time(&ml[game_globals.garray[b->gameNum].numHalfMoves - 1]));
495         strcat(bstring, tmp);
496       }
497       break;
498     case 5:
499       break;
500     case 4:
501       sprintf(tmp, "     Black Clock : %s", tenth_str(((bTime > 0) ? bTime : 0), 1));
502       strcat(bstring, tmp);
503       break;
504     case 3:
505       sprintf(tmp, "     White Clock : %s", tenth_str(((wTime > 0) ? wTime : 0), 1));
506       strcat(bstring, tmp);
507       break;
508     case 2:
509       sprintf(tmp, "     Black Strength : %d", bs);
510       strcat(bstring, tmp);
511       break;
512     case 1:
513       sprintf(tmp, "     White Strength : %d", ws);
514       strcat(bstring, tmp);
515       break;
516     case 0:
517       break;
518     }
519     strcat(bstring, "\n");
520     if (count != 0)
521       strcat(bstring, myMid);
522     else
523       strcat(bstring, myTop);
524   }
525   q = mylabel; i = 0;
526   if (orient == WHITE) {
527     p = label;
528     while(*p) {
529         switch(*p) {
530           case ' ':
531           case '\t':
532           case '\n':
533                 *q++ = *p++; break;
534           default:
535                 if(++i > b->files) { *q++ = '\n'; *q++ = 0; }
536                 *q++ = *p++;
537         }
538     }
539   } else {
540     p = blabel;
541     while(*p) {
542         switch(*p) {
543           case ' ':
544           case '\t':
545           case '\n':
546                 *q++ = *p++; break;
547           default:
548                 *q++ = *p++ + b->files - 12;
549                 if(++i >= b->files) { *q++ = '\n'; *q++ = 0; }
550         }
551     }
552   }
553   *q++ = 0;
554   strcat(bstring, mylabel);
555   return 0;
556 }
557
558 /* Experimental ANSI board for colour representation */
559 static int style13(struct game_state_t *b, struct move_t *ml)
560 {
561   static const char *wp[] = {"   ", "\033[37m\033[1m P ", "\033[37m\033[1m N ", "\033[37m\033[1m B ", "\033[37m\033[1m R ", "\033[37m\033[1m A ", "\033[37m\033[1m C ", "\033[37m\033[1m M ", "\033[37m\033[1m Q ", "\033[37m\033[1m E ", "\033[37m\033[1m K "};
562   static const char *bp[] = {"   ", "\033[21m\033[37m P ", "\033[21m\033[37m N ", "\033[21m\033[37m B ", "\033[21m\033[37m R ", "\033[21m\033[37m A ", "\033[21m\033[37m C ", "\033[21m\033[37m M ", "\033[21m\033[37m Q ", "\033[21m\033[37m E ", "\033[21m\033[37m K "};
563   static const char *wsqr = "\033[40m";
564   static const char *bsqr = "\033[45m";
565   static const char *top = "\t+------------------------+\n";
566   static const char *mid = "";
567   static const char *start = "|";
568   static const char *end = "\033[0m|";
569   static const char *label = "\t  a  b  c  d  e  f  g  h  i  j  k  l\n";
570   static const char *blabel = "\t  l  k  j  i  h  g  f  e  d  c  b  a\n";
571 return 0;
572   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
573 }
574
575 /* Standard ICS */
576 static int style1(struct game_state_t *b, struct move_t *ml)
577 {
578   static const char *wp[] = {"   |", " P |", " N |", " B |", " R |", " A |", " C |", " M |", " Q |", " E |", " B |", " Q |", 
579                              " W |", " H |", " N |", " D |", " H |", " L |", " C |", " S |", " G |", " H |", " A |", " F |",
580                              " E |", " H |", " M |", " S |", " E |", " W |", " O |", " G |", " V |", " S |", " E |", " A |",
581                              " K |", " H |", " E |", " W |", " G |", " L |", " C |", " H |"};
582   static const char *bp[] = {"   |", " *P|", " *N|", " *B|", " *R|", " *A|", " *C|", " *M|", " *Q|", " *E|", " *B|", " *Q|", 
583                              " *W|", " *H|", " *N|", " *D|", " *H|", " *L|", " *C|", " *S|", " *G|", " *H|", " *A|", " *F|",
584                              " *E|", " *H|", " *M|", " *S|", " *E|", " *W|", " *O|", " *G|", " *V|", " *S|", " *E|", " *A|",
585                              " *K|", " *H|", " *E|", " *W|", " *G|", " *L|", " *C|", " *H|"};
586   static char *wsqr = "";
587   static char *bsqr = "";
588   static char *top = "\t---------------------------------\n";
589   static char *mid = "\t|---+---+---+---+---+---+---+---|\n";
590   static char *start = "|";
591   static char *end = "";
592   static char *label = "\t  a   b   c   d   e   f   g   h   i   j   k   l\n";
593   static char *blabel = "\t  l   k   j   i   h   g   f   e   d   c   b   a\n";
594
595   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
596 }
597
598 /* USA-Today Sports Center-style board */
599 static int style2(struct game_state_t *b, struct move_t *ml)
600 {
601   static const char *wp[] = {"-  ", "P  ", "N  ", "B  ", "R  ", "A  ", "C  ", "M  ", "Q  ", "E  ", "B  ", "Q  ",
602                              "W  ", "H  ", "N  ", "D  ", "H  ", "L  ", "C  ", "S  ", "G  ", "H  ", "A  ", "F  ",
603                              "E  ", "H  ", "M  ", "S  ", "E  ", "W  ", "O  ", "G  ", "V  ", "S  ", "E  ", "A  ",
604                              "K  ", "H  ", "E  ", "W  ", "G  ", "L  ", "C  ", "H  "};
605   static const char *bp[] = {"+  ", "p' ", "n' ", "b' ", "r' ", "a' ", "c' ", "m' ", "q' ", "e' ", "b' ", "q' ",
606                              "w' ", "h' ", "n' ", "d' ", "h' ", "l' ", "c' ", "s' ", "g' ", "h' ", "a' ", "f' ",
607                              "e' ", "h' ", "m' ", "s' ", "e' ", "w' ", "o' ", "g' ", "v' ", "s' ", "e' ", "a' ",
608                              "k' ", "h' ", "e' ", "w' ", "g' ", "l' ", "c' ", "h' "};
609   static char *wsqr = "";
610   static char *bsqr = "";
611   static char *top = "";
612   static char *mid = "";
613   static char *start = "";
614   static char *end = "";
615   static char *label = "\ta  b  c  d  e  f  g  h  i  j  k  l\n";
616   static char *blabel = "\tl  k  j  i  h  g  f  e  d  c  b  a\n";
617
618   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
619 }
620
621 /* Experimental vt-100 ANSI board for dark backgrounds */
622 static int style3(struct game_state_t *b, struct move_t *ml)
623 {
624   static const char *wp[] = {"   ", " P ", " N ", " B ", " R ", " A ", " C ", " M ", " Q ", " E ", " B ", " Q ", 
625                              " W ", " H ", " N ", " D ", " H ", " L ", " C ", " S ", " G ", " H ", " A ", " F ",
626                              " E ", " H ", " M ", " S ", " E ", " W ", " O ", " G ", " V ", " S ", " E ", " A ",
627                              " K ", " H ", " E ", " W ", " G ", " L ", " C ", " H "};
628   static const char *bp[] = {"   ", " *P", " *N", " *B", " *R", " *A", " *C", " *M", " *Q", " *E", " *B", " *Q", 
629                              " *W", " *H", " *N", " *D", " *H", " *L", " *C", " *S", " *G", " *H", " *A", " *F",
630                              " *E", " *H", " *M", " *S", " *E", " *W", " *O", " *G", " *V", " *S", " *E", " *A",
631                              " *K", " *H", " *E", " *W", " *G", " *L", " *C", " *H"};
632   static char *wsqr = "\033[0m";
633   static char *bsqr = "\033[7m";
634   static char *top = "\t+------------------------+\n";
635   static char *mid = "";
636   static char *start = "|";
637   static char *end = "\033[0m|";
638   static char *label = "\t  a  b  c  d  e  f  g  h  i  j  k  l\n";
639   static char *blabel = "\t  l  k  j  i  h  g  f  e  d  c  b  a\n";
640
641   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
642 }
643
644 /* Experimental vt-100 ANSI board for light backgrounds */
645 static int style4(struct game_state_t *b, struct move_t *ml)
646 {
647   static const char *wp[] = {"   ", " P ", " N ", " B ", " R ", " A ", " C ", " M ", " Q ", " E ", " B ", " Q ", 
648                              " W ", " H ", " N ", " D ", " H ", " L ", " C ", " S ", " G ", " H ", " A ", " F ",
649                              " E ", " H ", " M ", " S ", " E ", " W ", " O ", " G ", " V ", " S ", " E ", " A ",
650                              " K ", " H ", " E ", " W ", " G ", " L ", " C ", " H "};
651   static const char *bp[] = {"   ", " *P", " *N", " *B", " *R", " *A", " *C", " *M", " *Q", " *E", " *B", " *Q", 
652                              " *W", " *H", " *N", " *D", " *H", " *L", " *C", " *S", " *G", " *H", " *A", " *F",
653                              " *E", " *H", " *M", " *S", " *E", " *W", " *O", " *G", " *V", " *S", " *E", " *A",
654                              " *K", " *H", " *E", " *W", " *G", " *L", " *C", " *H"};
655   static char *wsqr = "\033[7m";
656   static char *bsqr = "\033[0m";
657   static char *top = "\t+------------------------+\n";
658   static char *mid = "";
659   static char *start = "|";
660   static char *end = "\033[0m|";
661   static char *label = "\t  a  b  c  d  e  f  g  h  i  j  k  l\n";
662   static char *blabel = "\t  l  k  j  i  h  g  f  e  d  c  b  a\n";
663
664   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
665 }
666
667 /* Style suggested by ajpierce@med.unc.edu */
668 static int style5(struct game_state_t *b, struct move_t *ml)
669 {
670   static const char *wp[] = {"    ", "  o ", " :N:", " <B>", " |R|", " (A)", " [C]", " :M:", " {Q}", " !E!",
671                              " <B>", " {Q}", " .W.", " :H:", " :N:", " <H>", " |D|", " |L|", 
672                              " |C|", " !S!", " :G:", " :H:", " {A}", " {F}", " !E!", " (H)", " [M]", " :S:",
673                              " !E!", " |W|", " *O*", " {G}", " :V:", " (S)", " [E]", " &A&",
674                              " =K=", " (H)", " [E]", " (W)", " [G]", " <L>", " |C|", "  h "};
675   static const char *bp[] = {"    ", "  p ", " :n:", " <b>", " |r|", " (a)", " [c]", " :m:", " {q}", " !e!",
676                              " <b>", " {q}", " .w.", " :h:", " :n:", " <h>", " |d|", " |l|", 
677                              " |c|", " !s!", " :g:", " :h:", " {a}", " {f}", " !e!", " (h)", " [m]", " :s:",
678                              " !e!", " |w|", " *o*", " {g}", " :v:", " (s)", " [e]", " &a&",
679                              " =k=", " (f)", " [e]", " (w)", " [g]", " <l>", " |c|", "  h "};
680   static char *wsqr = "";
681   static char *bsqr = "";
682   static char *top = "        .   .   .   .   .   .   .   .   .\n";
683   static char *mid = "        .   .   .   .   .   .   .   .   .\n";
684   static char *start = "";
685   static char *end = "";
686   static char *label = "\t  a   b   c   d   e   f   g   h   i   j   k   l\n";
687   static char *blabel = "\t  l   k   j   i   h   g   f   e   d   c   b   a\n";
688
689   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
690 }
691
692 /* Email Board suggested by Thomas Fought (tlf@rsch.oclc.org) */
693 static int style6(struct game_state_t *b, struct move_t *ml)
694 {
695   static const char *wp[] = {"    |", " wp |", " WN |", " WB |", " WR |", " WA |", " WC |", " WM |", " WQ |", 
696                              " WE |", " WB |", " WQ |", " WW |", " WH |", " WN |", " WD |", " WH |", " WL |", 
697                              " WC |", " WS |", " WG |", " WH |", " WA |", " WF |", " WE |", " WH |", " WM |", 
698                              " WS |", " WE |", " WW |", " WO |", " WG |", " WV |", " WS |", " WE |", " WA |",
699                              " WK |", " WH |", " WE |", " WW |", " WG |", " WL |", " WC |", " Wh |"};
700   static const char *bp[] = {"    |", " bp |", " BN |", " BB |", " BR |", " BA |", " BC |", " BM |", " BQ |", 
701                              " BE |", " BB |", " BQ |", " BW |", " BH |", " BN |", " BD |", " BH |", " BL |", 
702                              " BC |", " BS |", " BG |", " BH |", " BA |", " BF |", " BE |", " BH |", " BM |", 
703                              " BS |", " BE |", " BW |", " BO |", " BG |", " BV |", " BS |", " BE |", " BA |",
704                              " BK |", " BH |", " BE |", " BW |", " BG |", " BL |", " BC |", " Bh |"};
705   static char *wsqr = "";
706   static char *bsqr = "";
707   static char *top = "\t-----------------------------------------\n";
708
709   static char *mid = "\t-----------------------------------------\n";
710   static char *start = "|";
711   static char *end = "";
712   static char *label = "\t  A    B    C    D    E    F    G    H    I    J    K    L\n";
713   static char *blabel = "\t  L    K    J    I    H    G    F    E    D    C    B    A\n";
714
715   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
716 }
717
718 /* Miniature board */
719 static int style7(struct game_state_t *b, struct move_t *ml)
720 {
721   static const char *wp[] = {"  ", " P", " N", " B", " R", " A", " C", " M", " Q", " E", " B", " Q", " W", " H", " N", " D", " H", " L", 
722                              " C", " S", " G", " H", " A", " F", " E", " H", " M", " S", " E", " W", " O", " G", " V", " S", " E", " A",
723                              " K", " H", " E", " W", " G", " L", " C", " H"};
724   static const char *bp[] = {" -", " p", " n", " b", " r", " a", " c", " m", " q", " e", " b", " q", " w", " h", " n", " d", " h", " l", 
725                              " c", " s", " g", " h", " a", " f", " e", " h", " m", " s", " e", " w", " o", " g", " v", " s", " e", " a",
726                              " k", " h", " e", " w", " g", " l", " c", " h"};
727   static char *wsqr = "";
728   static char *bsqr = "";
729   static char *top = "\t:::::::::::::::::::::\n";
730   static char *mid = "";
731   static char *start = "..";
732   static char *end = " ..";
733   static char *label = "\t   a b c d e f g h i j k l\n";
734   static char *blabel = "\t   l k j i h g f e d c b a\n";
735
736   return genstyle(b, ml, wp, bp, wsqr, bsqr, top, mid, start, end, label, blabel);
737 }
738
739 /* ICS interface maker board-- raw data dump */
740 static int style8(struct game_state_t *b, struct move_t *ml)
741 {
742   char tmp[160];
743   int f, r;
744   int ws, bs;
745
746   board_calc_strength(b, &ws, &bs);
747   sprintf(tmp, "#@#%03d%-16s%s%-16s%s", b->gameNum + 1,
748           game_globals.garray[b->gameNum].white_name,
749           (orient == WHITE) ? "*" : ":",
750           game_globals.garray[b->gameNum].black_name,
751           (orient == WHITE) ? ":" : "*");
752   strcat(bstring, tmp);
753   for (r = 0; r < b->ranks; r++) {
754     for (f = 0; f < b->files; f++) {
755       if (b->board[f][r] == NOPIECE) {
756         strcat(bstring, " ");
757       } else {
758         if (colorval(b->board[f][r]) == WHITE)
759           strcat(bstring, wpstring[piecetype(b->board[f][r])]);
760         else
761           strcat(bstring, bpstring[piecetype(b->board[f][r])]);
762       }
763     }
764   }
765   sprintf(tmp, "%03d%s%02d%02d%05d%05d%-7s(%s)@#@\n",
766           game_globals.garray[b->gameNum].numHalfMoves / 2 + 1,
767           (b->onMove == WHITE) ? "W" : "B",
768           ws,
769           bs,
770           (wTime + 5) / 10,
771           (bTime + 5) / 10,
772           game_globals.garray[b->gameNum].numHalfMoves ?
773           ml[game_globals.garray[b->gameNum].numHalfMoves - 1].algString :
774           "none",
775           game_globals.garray[b->gameNum].numHalfMoves ?
776           tenth_str(ml[game_globals.garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
777           "0:00");
778   strcat(bstring, tmp);
779   return 0;
780 }
781
782 /* last 2 moves only (previous non-verbose mode) */
783 static int style9(struct game_state_t *b, struct move_t *ml)
784 {
785   int i, count;
786   char tmp[160];
787   int startmove;
788
789   sprintf(tmp, "\nMove     %-23s%s\n",
790           game_globals.garray[b->gameNum].white_name,
791           game_globals.garray[b->gameNum].black_name);
792   strcat(bstring, tmp);
793   sprintf(tmp, "----     --------------         --------------\n");
794   strcat(bstring, tmp);
795   startmove = ((game_globals.garray[b->gameNum].numHalfMoves - 3) / 2) * 2;
796   if (startmove < 0)
797     startmove = 0;
798   for (i = startmove, count = 0;
799        i < game_globals.garray[b->gameNum].numHalfMoves && count < 4;
800        i++, count++) {
801     if (!(i & 0x01)) {
802       sprintf(tmp, "  %2d     ", i / 2 + 1);
803       strcat(bstring, tmp);
804     }
805     sprintf(tmp, "%-23s", move_and_time(&ml[i]));
806     strcat(bstring, tmp);
807     if (i & 0x01)
808       strcat(bstring, "\n");
809   }
810   if (i & 0x01)
811     strcat(bstring, "\n");
812   return 0;
813 }
814
815 /* Sleator's 'new and improved' raw dump format... */
816 static int style10(struct game_state_t *b, struct move_t *ml)
817 {
818   int f, r;
819   char tmp[160];
820   int ws, bs;
821
822   board_calc_strength(b, &ws, &bs);
823   sprintf(tmp, "<10>\n");
824   strcat(bstring, tmp);
825   for (r = b->ranks-1; r >= 0; r--) {
826     strcat(bstring, "|");
827     for (f = 0; f < b->files; f++) {
828       if (b->board[f][r] == NOPIECE) {
829         strcat(bstring, " ");
830       } else {
831         if (colorval(b->board[f][r]) == WHITE)
832           strcat(bstring, wpstring[piecetype(b->board[f][r])]);
833         else
834           strcat(bstring, bpstring[piecetype(b->board[f][r])]);
835       }
836     }
837     strcat(bstring, "|\n");
838   }
839   strcat(bstring, (b->onMove == WHITE) ? "W " : "B ");
840   if (game_globals.garray[b->gameNum].numHalfMoves) {
841     sprintf(tmp, "%d ",
842             ml[game_globals.garray[b->gameNum].numHalfMoves - 1].doublePawn);
843   } else {
844     sprintf(tmp, "-1 ");
845   }
846   strcat(bstring, tmp);
847   sprintf(tmp, "%d %d %d %d %d\n",
848           (b->wkmoved >= 0 && b->wkrmoved >= 0), // [HGM] castle: inverted the logic, both must have rights
849           (b->wkmoved >= 0 && b->wqrmoved >= 0),
850           (b->bkmoved >= 0 && b->bkrmoved >= 0),
851           (b->bkmoved >= 0 && b->bqrmoved >= 0),
852        (game_globals.garray[b->gameNum].numHalfMoves - ((b->lastIrreversable == -1) ? 0 :
853                                            b->lastIrreversable)));
854   strcat(bstring, tmp);
855   sprintf(tmp, "%d %s %s %d %d %d %d %d %d %d %d %s (%s) %s %d\n",
856           b->gameNum,
857           game_globals.garray[b->gameNum].white_name,
858           game_globals.garray[b->gameNum].black_name,
859           myTurn,
860           game_globals.garray[b->gameNum].wInitTime / 600,
861           game_globals.garray[b->gameNum].wIncrement / 10,
862           ws,
863           bs,
864           (wTime + 5) / 10,
865           (bTime + 5) / 10,
866           game_globals.garray[b->gameNum].numHalfMoves / 2 + 1,
867           game_globals.garray[b->gameNum].numHalfMoves ?
868           ml[game_globals.garray[b->gameNum].numHalfMoves - 1].moveString :
869           "none",
870           game_globals.garray[b->gameNum].numHalfMoves ?
871           tenth_str(ml[game_globals.garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
872           "0:00",
873           game_globals.garray[b->gameNum].numHalfMoves ?
874           ml[game_globals.garray[b->gameNum].numHalfMoves - 1].algString :
875           "none",
876           (orient == WHITE) ? 0 : 1);
877   strcat(bstring, tmp);
878   sprintf(tmp, ">10<\n");
879   strcat(bstring, tmp);
880   return 0;
881 }
882
883 /* Same as 8, but with verbose moves ("P/e3-e4", instead of "e4") */
884 static int style11(struct game_state_t *b, struct move_t *ml)
885 {
886   char tmp[160];
887   int f, r;
888   int ws, bs;
889
890   board_calc_strength(b, &ws, &bs);
891   sprintf(tmp, "#@#%03d%-16s%s%-16s%s", b->gameNum,
892           game_globals.garray[b->gameNum].white_name,
893           (orient == WHITE) ? "*" : ":",
894           game_globals.garray[b->gameNum].black_name,
895           (orient == WHITE) ? ":" : "*");
896   strcat(bstring, tmp);
897   for (r = 0; r < b->ranks; r++) {
898     for (f = 0; f < b->files; f++) {
899       if (b->board[f][r] == NOPIECE) {
900         strcat(bstring, " ");
901       } else {
902         if (colorval(b->board[f][r]) == WHITE)
903           strcat(bstring, wpstring[piecetype(b->board[f][r])]);
904         else
905           strcat(bstring, bpstring[piecetype(b->board[f][r])]);
906       }
907     }
908   }
909     sprintf(tmp, "%03d%s%02d%02d%05d%05d%-7s(%s)@#@\n",
910             game_globals.garray[b->gameNum].numHalfMoves / 2 + 1,
911             (b->onMove == WHITE) ? "W" : "B",
912             ws,
913             bs,
914             (wTime + 5) / 10,
915             (bTime + 5) / 10,
916             game_globals.garray[b->gameNum].numHalfMoves ?
917             ml[game_globals.garray[b->gameNum].numHalfMoves - 1].moveString :
918             "none",
919             game_globals.garray[b->gameNum].numHalfMoves ?
920             tenth_str(ml[game_globals.garray[b->gameNum].numHalfMoves - 1].tookTime, 0) :
921             "0:00");
922   strcat(bstring, tmp);
923   return 0;
924 }
925
926
927 int kludgeFlag = 0; 
928 /* Similar to style 10.  See the "style12" help file for information */
929 static int style12(struct game_state_t *b, struct move_t *ml)
930 {
931   int f, r;
932   char tmp[160]; // [HGM] 80 caused problems with long login names
933   int ws, bs; 
934   int nhm = kludgeFlag ? 0 : game_globals.garray[b->gameNum].numHalfMoves; 
935   // [HGM] setup: the number of half moves appeared in this routine an enormous number of times,
936   // and had to be dug out of the game_globals, so that this routine could only be used to print
937   // a board from a game, and not just any board given by an isolated game_state_t. This was very
938   // inconvenient for printing initial boards in move lists of shuffle variants, so I added the
939   // global kludgeFlag to signal that we want to print an initial position, and force nhm = 0.
940
941   board_calc_strength(b, &ws, &bs);
942
943   sprintf(bstring, "<12> ");
944   for (r = b->ranks-1; r >= 0; r--) {
945     for (f = 0; f < b->files; f++) {
946       if (b->board[f][r] == NOPIECE) {
947         strcat(bstring, "-");
948       } else {
949         if (colorval(b->board[f][r]) == WHITE)
950           strcat(bstring, wpstring[piecetype(b->board[f][r])]);
951         else
952           strcat(bstring, bpstring[piecetype(b->board[f][r])]);
953       }
954     }
955     strcat(bstring, " ");
956   }
957
958   strcat(bstring, (b->onMove == WHITE) ? "W " : "B ");
959   if (nhm) {
960     sprintf(tmp, "%d ",
961             ml[nhm - 1].doublePawn);
962   } else {
963     sprintf(tmp, "-1 ");
964   }
965   strcat(bstring, tmp);
966   sprintf(tmp, "%d %d %d %d %d ",
967           (b->wkmoved >= 0 && b->wkrmoved >= 0), // [HGM] castle: inverted the logic, both must have rights
968           (b->wkmoved >= 0 && b->wqrmoved >= 0),
969           (b->bkmoved >= 0 && b->bkrmoved >= 0),
970           (b->bkmoved >= 0 && b->bqrmoved >= 0),
971           (nhm - ((b->lastIrreversable == -1) ? 0 : b->lastIrreversable)));
972   strcat(bstring, tmp);
973   sprintf(tmp, "%d %s %s %d %d %d %d %d %d %d %d %s (%s) %s %d %d\n",
974           b->gameNum + 1,
975           game_globals.garray[b->gameNum].white_name,
976           game_globals.garray[b->gameNum].black_name,
977           myTurn,
978           game_globals.garray[b->gameNum].wInitTime / 600,
979           game_globals.garray[b->gameNum].wIncrement / 10,
980           ws,
981           bs,
982           (wTime / 10),
983           (bTime / 10),
984           nhm / 2 + 1,
985           nhm ?
986           ml[nhm - 1].moveString :
987           "none",
988           nhm ?
989           tenth_str(ml[nhm - 1].tookTime, 0) :
990           "0:00",
991           nhm ?
992           ml[nhm - 1].algString :
993           "none", (orient == WHITE) ? 0 : 1,
994           b->moveNum > 1 ? 1 : 0); /* ticking */
995
996   strcat(bstring, tmp);
997   return 0;
998 }
999
1000 static int board_read_file(char *category, char *gname, struct game_state_t *gs)
1001 {
1002   FILE *fp;
1003   int c;
1004   int onNewLine = 1;
1005   int onColor = -1;
1006   int onPiece = -1;
1007   int onFile = -1;
1008   int onRank = -1;
1009
1010   fp = fopen_p("%s/%s/%s", "r", BOARD_DIR, category, gname);
1011   if (!fp)
1012     return 1;
1013
1014   board_clear(gs);
1015   gs->setup = 1;
1016   if (gname && !strcmp(gname, "0"))
1017         gs->setup = 0; // [HGM] variant: any board in the default file "0" is supposed to be implied by the variant
1018
1019   while (!feof(fp)) {
1020     c = fgetc(fp);
1021     if (onNewLine) {
1022       if (c == 'W') {
1023         onColor = WHITE;
1024         if (gs->onMove < 0)
1025           gs->onMove = WHITE;
1026       } else if (c == 'B') {
1027         onColor = BLACK;
1028         if (gs->onMove < 0)
1029           gs->onMove = BLACK;
1030       } else if (c == 'S') { int f=8, r=8;
1031         // [HGM] rules: read rule modifiers
1032         fscanf(fp, "%dx%d", &f, &r); gs->files=f; gs->ranks = r;
1033         while (!feof(fp) && c != '\n') {
1034           c = fgetc(fp);
1035           switch(c) {
1036             case 'r':
1037                 gs->royalKnight = 1;
1038                 break;
1039             case 'c':
1040                 gs->capablancaPieces = 1;
1041                 break;
1042             case 'd':
1043                 gs->drops = 1;
1044                 break;
1045             case 'g':
1046                 gs->drops = 2;
1047                 break;
1048             case 'h':
1049                 gs->holdings = -1;     // color-flip holdings
1050                 break;
1051             case 'p':
1052                 gs->palace = 3;
1053                 break;
1054             case 'P':
1055                 gs->promoType = 2; // only promote to captured pieces
1056                 gs->holdings = 1;  // use holdings to hold own captured pieces
1057                 break;
1058             case 'S':
1059                 gs->promoType = 3; // Shogi-type promotions
1060                 break;
1061             case 'Z':
1062                 gs->promoZone = 3; // for Grand Chess
1063                 gs->pawnDblStep = 2;
1064                 break;
1065             case 'F':
1066                 gs->castlingStyle = 2; // FRC castling
1067                 break;
1068             case 'w':
1069                 gs->castlingStyle = 1; // wild castling, from both center files
1070                 break;
1071             case 'n':
1072                 gs->castlingStyle = 0; // no castling
1073                 break;
1074             case 'f':
1075                 gs->castlingStyle = 3; // free castling
1076                 break;
1077             case 'D':
1078                 gs->pawnDblStep = 0; // suppress pawn double step
1079                 break;
1080             case 'b':
1081                 gs->bareKingLoses = 1; // apply baring rule
1082                 break;
1083             case 's':
1084                 gs->stalemate = 0; // stalemate loses
1085                 break;
1086           }
1087         }       
1088         continue;
1089       } else if (c == '#') {
1090         while (!feof(fp) && c != '\n')
1091           c = fgetc(fp);        /* Comment line */
1092         continue;
1093       } else {                  /* Skip any line we don't understand */
1094         while (!feof(fp) && c != '\n')
1095           c = fgetc(fp);
1096         continue;
1097       }
1098       onNewLine = 0;
1099     } else {
1100       switch (c) {
1101       case 'A':
1102         onPiece = CARDINAL;
1103         break;
1104       case 'B':
1105         onPiece = BISHOP;
1106         break;
1107       case 'C':
1108         onPiece = MARSHALL;
1109         break;
1110       case 'D':
1111         onPiece = SELEPHANT;
1112         break;
1113       case 'E':
1114         onPiece = ALFIL;
1115         break;
1116       case 'F':
1117         onPiece = FERZ2;
1118         break;
1119       case 'G':
1120         onPiece = GOLD;
1121         break;
1122       case 'H':
1123         onPiece = HORSE;
1124         break;
1125       case 'I':
1126         onPiece = DRAGONHORSE;
1127         break;
1128       case 'J':
1129         onPiece = DRAGONKING;
1130         break;
1131       case 'K':
1132         onPiece = KING;
1133         break;
1134       case 'L':
1135         onPiece = LANCE;
1136         break;
1137       case 'M':
1138         onPiece = MAN;
1139         break;
1140       case 'N':
1141         onPiece = KNIGHT;
1142         break;
1143       case 'O':
1144         onPiece = CANNON;
1145         break;
1146       case 'P':
1147         onPiece = PAWN;
1148         break;
1149       case 'Q':
1150         onPiece = QUEEN;
1151         break;
1152       case 'R':
1153         onPiece = ROOK;
1154         break;
1155       case 'S':
1156         onPiece = SILVER;
1157         break;
1158       case 'T':
1159         onPiece = ELEPHANT;
1160         break;
1161       case 'U':
1162         onPiece = HAWK;
1163         break;
1164       case 'V':
1165         onPiece = CENTAUR;
1166         break;
1167       case 'W':
1168         onPiece = WAZIR;
1169         break;
1170       case 'X':
1171         onPiece = WARLORD;
1172         break;
1173       case 'Y':
1174         onPiece = GENERAL;
1175         break;
1176       case 'Z':
1177         onPiece = AMAZON;
1178         break;
1179       case 'm':
1180         onPiece = MANDARIN;
1181         break;
1182       case 'n':
1183         onPiece = HONORABLEHORSE;
1184         break;
1185       case 'o':
1186         onPiece = MODERNELEPHANT;
1187         break;
1188       case 'p':
1189         onPiece = PRIESTESS;
1190         break;
1191       case 'q':
1192         onPiece = FERZ;
1193         break;
1194       case 'r':
1195         onPiece = MINISTER;
1196         break;
1197       case 's':
1198         onPiece = MASTODON;
1199         break;
1200       case 't':
1201         onPiece = ALFIL2;
1202         break;
1203       case 'u':
1204         onPiece = NIGHTRIDER;
1205         break;
1206       case 'v':
1207         onPiece = HOPLITE;
1208         break;
1209       case 'w':
1210         onPiece = WOODY;
1211         break;
1212       case 'x':
1213         onPiece = LIEUTENANT;
1214         break;
1215       case 'y':
1216         onPiece = CAPTAIN;
1217         break;
1218       case 'z':
1219         onPiece = MAN2;
1220         break;
1221       case 'a':
1222       case 'b':
1223       case 'c':
1224       case 'd':
1225       case 'e':
1226       case 'f':
1227       case 'g':
1228       case 'h':
1229       case 'i':
1230       case 'j':
1231       case 'k':
1232       case 'l':
1233         onFile = c - 'a';
1234         if(onFile >= gs->files) { onFile = -1; break; }
1235         onRank = -1;
1236         break;
1237       case '@':
1238         if (onColor >= 0 && onPiece >= 0) // allow placement in holdings
1239           gs->holding[onColor == BLACK][onPiece-1]++;
1240         break;
1241       case '1':
1242       case '2':
1243       case '3':
1244       case '4':
1245       case '5':
1246       case '6':
1247       case '7':
1248       case '8':
1249       case '9':
1250       case '0':
1251         onRank = c - '1' + (gs->ranks > 9);
1252         if(onRank < 0 || onRank >= gs->ranks) { onRank = -1; break; }
1253         if (onFile >= 0 && onColor >= 0 && onPiece >= 0)
1254           gs->board[onFile][onRank] = onPiece | onColor;
1255         break;
1256       case '#':
1257         while (!feof(fp) && c != '\n')
1258           c = fgetc(fp);        /* Comment line */
1259       case '\n':
1260         onNewLine = 1;
1261         onColor = -1;
1262         onPiece = -1;
1263         onFile = -1;
1264         onRank = -1;
1265         break;
1266       default:
1267         break;
1268       }
1269     }
1270   }
1271   fclose(fp);
1272   return 0;
1273 }
1274
1275 #define WHITE_SQUARE 1
1276 #define BLACK_SQUARE 0
1277 #define ANY_SQUARE -1
1278 #define SquareColor(f, r) ((f ^ r) & 1)
1279
1280 static void place_piece(board_t b, int piece, int squareColor, int width)
1281 { //[HGM] board: make width a variable
1282   int r, f;
1283   int placed = 0;
1284
1285   if (iscolor(piece, BLACK))
1286     r = 7;
1287   else
1288     r = 0;
1289
1290   while (!placed) {
1291     if (squareColor == ANY_SQUARE) {
1292       f = random() % width;
1293     } else {
1294       f = (random() % ((width+1)/2)) * 2; // to not overflow odd-width boards
1295       if (SquareColor(f, r) != squareColor)
1296         f++;
1297     }
1298     if ((b)[f][r] == NOPIECE) {
1299       (b)[f][r] = piece;
1300       placed = 1;
1301     }
1302   }
1303 }
1304
1305 static void wild_update(board_t b, int style)
1306 {
1307   int f, r, i, j;
1308
1309   for (f = 0; f < BW; f++) // [HGM] board: make sure also works with wider boards
1310     for (r = 0; r < 8; r++)
1311       b[f][r] = NOPIECE;
1312   for (f = 0; f < 8; f++) {
1313     b[f][1] = W_PAWN;
1314     b[f][6] = B_PAWN;
1315   }
1316   switch (style) {
1317   case 1:
1318     if (random() & 0x01) {
1319       b[4][0] = W_KING;
1320       b[3][0] = W_QUEEN;
1321     } else {
1322       b[3][0] = W_KING;
1323       b[4][0] = W_QUEEN;
1324     }
1325     if (random() & 0x01) {
1326       b[4][7] = B_KING;
1327       b[3][7] = B_QUEEN;
1328     } else {
1329       b[3][7] = B_KING;
1330       b[4][7] = B_QUEEN;
1331     }
1332     b[0][0] = b[7][0] = W_ROOK;
1333     b[0][7] = b[7][7] = B_ROOK;
1334     /* Must do bishops before knights to be sure opposite colored squares are
1335        available. */
1336     place_piece(b, W_BISHOP, WHITE_SQUARE, 8);
1337     place_piece(b, W_BISHOP, BLACK_SQUARE, 8);
1338     place_piece(b, W_KNIGHT, ANY_SQUARE, 8);
1339     place_piece(b, W_KNIGHT, ANY_SQUARE, 8);
1340     place_piece(b, B_BISHOP, WHITE_SQUARE, 8);
1341     place_piece(b, B_BISHOP, BLACK_SQUARE, 8);
1342     place_piece(b, B_KNIGHT, ANY_SQUARE, 8);
1343     place_piece(b, B_KNIGHT, ANY_SQUARE, 8);
1344     break;
1345   case 2:
1346     place_piece(b, W_KING, ANY_SQUARE, 8);
1347     place_piece(b, W_QUEEN, ANY_SQUARE, 8);
1348     place_piece(b, W_ROOK, ANY_SQUARE, 8);
1349     place_piece(b, W_ROOK, ANY_SQUARE, 8);
1350     place_piece(b, W_BISHOP, ANY_SQUARE, 8);
1351     place_piece(b, W_BISHOP, ANY_SQUARE, 8);
1352     place_piece(b, W_KNIGHT, ANY_SQUARE, 8);
1353     place_piece(b, W_KNIGHT, ANY_SQUARE, 8);
1354     /* Black mirrors White */
1355     for (i = 0; i < 8; i++) {
1356       b[i][7] = b[i][0] | BLACK;
1357     }
1358     break;
1359   case 3:
1360     /* Generate White king on random square plus random set of pieces */
1361     place_piece(b, W_KING, ANY_SQUARE, 8);
1362     for (i = 0; i < 8; i++) {
1363       if (b[i][0] != W_KING) {
1364         b[i][0] = (random() % 4) + 2; if(b[i][0] == 5) b[i][0] += 3;
1365       }
1366     }
1367     /* Black mirrors White */
1368     for (i = 0; i < 8; i++) {
1369       b[i][7] = b[i][0] | BLACK;
1370     }
1371     break;
1372   case 4:
1373     /* Generate White king on random square plus random set of pieces */
1374     place_piece(b, W_KING, ANY_SQUARE, 8);
1375     for (i = 0; i < 8; i++) {
1376       if (b[i][0] != W_KING) {
1377         b[i][0] = (random() % 4) + 2; if(b[i][0] == 5) b[i][0] += 3;
1378       }
1379     }
1380     /* Black has same set of pieces, but randomly permuted, except that Black
1381        must have the same number of bishops on white squares as White has on
1382        black squares, and vice versa.  So we must place Black's bishops first
1383        to be sure there are enough squares left of the correct color. */
1384     for (i = 0; i < 8; i++) {
1385       if (b[i][0] == W_BISHOP) {
1386         place_piece(b, B_BISHOP, !SquareColor(i, 0), 8);
1387       }
1388     }
1389     for (i = 0; i < 8; i++) {
1390       if (b[i][0] != W_BISHOP) {
1391         place_piece(b, b[i][0] | BLACK, ANY_SQUARE, 8);
1392       }
1393     }
1394     break;
1395   case 22:
1396     /* Chess960 placement: King between R */
1397     place_piece(b, W_BISHOP, WHITE_SQUARE, 8);
1398     place_piece(b, W_BISHOP, BLACK_SQUARE, 8);
1399     place_piece(b, W_QUEEN,  ANY_SQUARE, 8);
1400     place_piece(b, W_KNIGHT, ANY_SQUARE, 8);
1401     place_piece(b, W_KNIGHT, ANY_SQUARE, 8);
1402     for (i = j = 0; i < 8; i++) {
1403       if(b[i][0] == NOPIECE) b[i][0] = (j++ == 1 ? W_KING : W_ROOK);
1404     }
1405     /* Black mirrors White */
1406     for (i = 0; i < 8; i++) {
1407       b[i][7] = b[i][0] | BLACK;
1408     }
1409     break;
1410   case 46:
1411     /* Chess960 placement: King between R */
1412     place_piece(b, W_BISHOP, WHITE_SQUARE, 10);
1413     place_piece(b, W_BISHOP, BLACK_SQUARE, 10);
1414     place_piece(b, W_QUEEN,  ANY_SQUARE, 10);
1415     place_piece(b, W_MARSHALL, ANY_SQUARE, 10);
1416     place_piece(b, W_CARDINAL, ANY_SQUARE, 10);
1417     place_piece(b, W_KNIGHT, ANY_SQUARE, 10);
1418     place_piece(b, W_KNIGHT, ANY_SQUARE, 10);
1419     for (i = j = 0; i < 10; i++) {
1420       if(b[i][0] == NOPIECE) j++ == 1 ? W_KING : W_ROOK;
1421     }
1422     /* Black mirrors White */
1423     for (i = 0; i < 10; i++) {
1424       b[i][7] = b[i][0] | BLACK;
1425     }
1426     break;
1427   default:
1428     return;
1429     break;
1430   }
1431   {
1432     FILE *fp;
1433     int onPiece;
1434
1435     fp = fopen_p("%s/wild/%d", "w", BOARD_DIR, style);
1436     if (!fp) {
1437       d_printf( "CHESSD: Can't write wild style %d\n", style);
1438       return;
1439     }
1440     fprintf(fp, "W:");
1441     onPiece = -1;
1442     for (r = 1; r >= 0; r--) {
1443       for (f = 0; f < 8; f++) {
1444         if (onPiece < 0 || b[f][r] != onPiece) {
1445           onPiece = b[f][r];
1446           fprintf(fp, " %s", wpstring[piecetype(b[f][r])]);
1447         }
1448         fprintf(fp, " %c%c", f + 'a', r + '1');
1449       }
1450     }
1451     fprintf(fp, "\nB:");
1452     onPiece = -1;
1453     for (r = 6; r < 8; r++) {
1454       for (f = 0; f < 8; f++) {
1455         if (onPiece < 0 || b[f][r] != onPiece) {
1456           onPiece = b[f][r];
1457           fprintf(fp, " %s", wpstring[piecetype(b[f][r])]);
1458         }
1459         fprintf(fp, " %c%c", f + 'a', r + '1');
1460       }
1461     }
1462     fprintf(fp, "\n");
1463     fclose(fp);
1464   }
1465 }
1466
1467 void wild_init(void)
1468 {
1469         board_t b;
1470         wild_update(b, 1);
1471         wild_update(b, 2);
1472         wild_update(b, 3);
1473         wild_update(b, 4);
1474 }
1475