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