c5f5d35b3c9859df81dc141ccffee3bbbc01f892
[capablanca.git] / lasker-2.2.3 / src / gameproc.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 #include "includes.h"
22
23 /*Let users know that someone is available (format the message)*/
24 static void getavailmess(int p, char* message)
25 {
26         struct player *pp = &player_globals.parray[p];
27         char titles[100];
28
29         titles [0]='\0';
30         AddPlayerLists(p,titles);
31         sprintf (message,"%s%s Blitz (%s), Std (%s), Wild (%s), Light(%s), Bug(%s)\n"
32                  "  is now available for matches.",
33                  pp->name, titles,
34         ratstrii(pp->b_stats.rating, p),
35         ratstrii(pp->s_stats.rating, p),
36         ratstrii(pp->w_stats.rating, p),
37         ratstrii(pp->l_stats.rating, p),
38         ratstrii(pp->bug_stats.rating, p));
39 }
40
41 void getnotavailmess(int p, char* message)
42 {
43         struct player *pp = &player_globals.parray[p];
44         char titles[100];
45
46         titles[0]='\0';
47         AddPlayerLists(p,titles);
48         sprintf (message,"%s%s is no longer available for matches.",
49                  pp->name, titles);
50 }
51
52 void announce_avail(int p)
53 {
54         struct player *pp = &player_globals.parray[p];
55         char avail[200];
56         int p1;
57         if ((pp->game < 0) && (CheckPFlag(p, PFLAG_OPEN))) {
58                 getavailmess (p,avail);
59                 
60                 for (p1 = 0; p1 < player_globals.p_num; p1++) {
61                         if (p == p1)
62                                 continue;
63                         if (player_globals.parray[p1].status != PLAYER_PROMPT)
64                                 continue;
65                         if (CheckPFlag(p1, PFLAG_AVAIL) && CheckPFlag(p1, PFLAG_OPEN)
66                             && (player_globals.parray[p1].game < 0))
67                                 if (((pp->b_stats.rating <= player_globals.parray[p1].availmax) && (pp->b_stats.rating >= player_globals.parray[p1].availmin)) || (!player_globals.parray[p1].availmax))
68                                         pprintf_prompt (p1,"\n%s\n",avail);
69                 }
70         }
71 }
72
73 void announce_notavail(int p)
74 {
75         struct player *pp = &player_globals.parray[p];
76         char avail[200];
77         int p1;
78         
79         getnotavailmess (p,avail);
80         
81         for (p1 = 0; p1 < player_globals.p_num; p1++) {
82                 if (p == p1)
83                         continue;
84                 if (player_globals.parray[p1].status != PLAYER_PROMPT)
85                         continue;
86                 if (CheckPFlag(p1, PFLAG_AVAIL) && CheckPFlag(p1, PFLAG_OPEN)
87                     && (player_globals.parray[p1].game < 0))
88                         if (((pp->b_stats.rating <= player_globals.parray[p1].availmax) && (pp->b_stats.rating >= player_globals.parray[p1].availmin)) || (!player_globals.parray[p1].availmax))
89                                 pprintf_prompt (p1,"\n%s\n",avail);
90         }
91 }
92
93 void game_ended(int g, int winner, int why)
94 {
95   struct game *gg = &game_globals.garray[g];
96   char outstr[200];
97   char avail_black[200]; /* for announcing white/black avail */
98   char avail_white[200];
99   char avail_bugwhite[200];
100   char avail_bugblack[200];
101   char tmp[200];
102   int p;
103   int gl = gg->link;
104   int rate_change = 0;
105   int isDraw = 0;
106   int whiteResult;
107   char winSymbol[10];
108   char EndSymbol[10];
109   char *NameOfWinner, *NameOfLoser;
110   int beingplayed = 0;          /* i.e. it wasn't loaded for adjudication */
111   int print_avail = 0;
112
113   avail_white[0] = '\0';
114   avail_black[0] = '\0';
115   avail_bugwhite[0] = '\0';
116   avail_bugblack[0] = '\0';
117
118   beingplayed = (player_globals.parray[gg->black].game == g);
119
120   sprintf(outstr, "\n{Game %d (%s vs. %s) ", g + 1,
121           player_globals.parray[gg->white].name,
122           player_globals.parray[gg->black].name);
123   gg->result = why;
124   gg->winner = winner;
125   if (winner == WHITE) {
126     whiteResult = RESULT_WIN;
127     strcpy(winSymbol, "1-0");
128     NameOfWinner = player_globals.parray[gg->white].name;
129     NameOfLoser = player_globals.parray[gg->black].name;
130   } else {
131     whiteResult = RESULT_LOSS;
132     strcpy(winSymbol, "0-1");
133     NameOfWinner = player_globals.parray[gg->black].name;
134     NameOfLoser = player_globals.parray[gg->white].name;
135   }
136   switch (why) {
137   case END_CHECKMATE:
138     sprintf(tmp, "%s checkmated} %s", NameOfLoser, winSymbol);
139     strcpy(EndSymbol, "Mat");
140     rate_change = 1;
141     break;
142   case END_BARE:
143     sprintf(tmp, "%s bared} %s", NameOfLoser, winSymbol);
144     strcpy(EndSymbol, "Bar");
145     rate_change = 1;
146     break;
147   case END_RESIGN:
148     sprintf(tmp, "%s resigns} %s", NameOfLoser, winSymbol);
149     strcpy(EndSymbol, "Res");
150     rate_change = 1;
151     break;
152   case END_FLAG:
153     sprintf(tmp, "%s forfeits on time} %s", NameOfLoser, winSymbol);
154     strcpy(EndSymbol, "Fla");
155     rate_change = 1;
156     break;
157   case END_STALEMATE:
158     sprintf(tmp, "Game drawn by stalemate} 1/2-1/2");
159     isDraw = 1;
160     strcpy(EndSymbol, "Sta");
161     rate_change = 1;
162     whiteResult = RESULT_DRAW;
163     break;
164   case END_AGREEDDRAW:
165     sprintf(tmp, "Game drawn by mutual agreement} 1/2-1/2");
166     isDraw = 1;
167     strcpy(EndSymbol, "Agr");
168     rate_change = 1;
169     whiteResult = RESULT_DRAW;
170     break;
171   case END_BOTHFLAG:
172     sprintf(tmp, "Game drawn because both players ran out of time} 1/2-1/2");
173     isDraw = 1;
174     strcpy(EndSymbol, "Fla");
175     rate_change = 1;
176     whiteResult = RESULT_DRAW;
177     break;
178   case END_REPETITION:
179     sprintf(tmp, "Game drawn by repetition} 1/2-1/2");
180     isDraw = 1;
181     strcpy(EndSymbol, "Rep");
182     rate_change = 1;
183     whiteResult = RESULT_DRAW;
184     break;
185   case END_50MOVERULE:
186     sprintf(tmp, "Game drawn by the 50 move rule} 1/2-1/2");
187     isDraw = 1;
188     strcpy(EndSymbol, "50");
189     rate_change = 1;
190     whiteResult = RESULT_DRAW;
191     break;
192   case END_ADJOURN:
193     if (gl >= 0) {
194       sprintf(tmp, "Bughouse game aborted.} *");
195       whiteResult = RESULT_ABORT;
196     } else {
197     sprintf(tmp, "Game adjourned by mutual agreement} *");
198     game_save(g);
199     }
200     break;
201   case END_LOSTCONNECTION:
202     sprintf(tmp, "%s lost connection; game ", NameOfWinner);
203     if (CheckPFlag(gg->white, PFLAG_REG)
204         && CheckPFlag(gg->black, PFLAG_REG)
205         && gl < 0) {
206       sprintf(tmp, "adjourned} *");
207       game_save(g);
208     } else
209       sprintf(tmp, "aborted} *");
210     whiteResult = RESULT_ABORT;
211     break;
212   case END_ABORT:
213     sprintf(tmp, "Game aborted by mutual agreement} *");
214     whiteResult = RESULT_ABORT;
215     break;
216   case END_COURTESY:
217     sprintf(tmp, "Game courtesyaborted by %s} *", NameOfWinner);
218     whiteResult = RESULT_ABORT;
219     break;
220   case END_COURTESYADJOURN:
221     if (gl >= 0) {
222       sprintf(tmp, "Bughouse game courtesyaborted by %s.} *", NameOfWinner);
223       whiteResult = RESULT_ABORT;
224     } else {
225     sprintf(tmp, "Game courtesyadjourned by %s} *", NameOfWinner);
226     game_save(g);
227     }
228     break;
229   case END_NOMATERIAL:
230     /* Draw by insufficient material (e.g., lone K vs. lone K) */
231     sprintf(tmp, "Neither player has mating material} 1/2-1/2");
232     isDraw = 1;
233     strcpy(EndSymbol, "NM ");
234     rate_change = 1;
235     whiteResult = RESULT_DRAW;
236     break;
237   case END_FLAGNOMATERIAL:
238     sprintf(tmp, "%s ran out of time and %s has no material to mate} 1/2-1/2",
239             NameOfLoser, NameOfWinner);
240     isDraw = 1;
241     strcpy(EndSymbol, "TM ");
242     rate_change = 1;
243     whiteResult = RESULT_DRAW;
244     break;
245   case END_ADJWIN:
246     sprintf(tmp, "%s wins by adjudication} %s", NameOfWinner, winSymbol);
247     strcpy(EndSymbol, "Adj");
248     rate_change = 1;
249     break;
250   case END_ADJDRAW:
251     sprintf(tmp, "Game drawn by adjudication} 1/2-1/2");
252     isDraw = 1;
253     strcpy(EndSymbol, "Adj");
254     rate_change = 1;
255     whiteResult = RESULT_DRAW;
256     break;
257   case END_ADJABORT:
258     sprintf(tmp, "Game aborted by adjudication} *");
259     whiteResult = RESULT_ABORT;
260     break;
261   default:
262     sprintf(tmp, "Hmm, the game ended and I don't know why} *");
263     break;
264   }
265   strcat(outstr, tmp);
266
267   if (CheckPFlag(gg->white, PFLAG_TOURNEY) &&
268       CheckPFlag(gg->black, PFLAG_TOURNEY)) {
269           /* mamer wants more info */
270           sprintf(tmp," [%d %d %d %d %d]",
271                   gg->wInitTime/(60*10), gg->wIncrement/10, gg->rated, gg->private, (int)gg->type);
272           strcat(outstr, tmp);
273   }
274
275   strcat(outstr, "\n");
276
277   if (gg->rated && rate_change && gg->type != TYPE_BUGHOUSE)
278     /* Adjust ratings; bughouse gets done later. */
279     rating_update(g, -1);
280
281   if (beingplayed) {
282     int printed = 0;
283     int avail_printed = 0;
284
285     pprintf_noformat(gg->white, outstr);
286     pprintf_noformat(gg->black, outstr);
287     Bell (gg->white);
288     Bell (gg->black);
289
290     gg->link = -1;              /*IanO: avoids recursion */
291     if (gl >= 0 && game_globals.garray[gl].link >= 0) {
292       pprintf_noformat(game_globals.garray[gl].white, outstr);
293       pprintf_noformat(game_globals.garray[gl].black, outstr);
294       if (CheckPFlag(game_globals.garray[gl].white, PFLAG_OPEN)) {
295         getavailmess (game_globals.garray[gl].white, avail_bugwhite);
296         print_avail = 1;
297       }
298       if (CheckPFlag(game_globals.garray[gl].black, PFLAG_OPEN)) {
299         getavailmess (game_globals.garray[gl].black, avail_bugblack);
300         print_avail = 1;
301       }
302       if ((gg->rated) && (rate_change)) {
303         /* Adjust ratings */
304         rating_update(g, gl);
305       }
306       game_ended(gl, CToggle(winner), why);
307     }
308
309     if ((player_num_active_boards(gg->white) <= 1) /* not a simul or */
310          && CheckPFlag(gg->white, PFLAG_OPEN)) {   /* simul is over? */
311       getavailmess (gg->white,avail_white);
312       print_avail = 1;
313     } else {    /* Part of an ongoing simul!  Let's shrink the array. */
314       
315     }
316     
317     if (CheckPFlag(gg->black, PFLAG_OPEN)) {
318       getavailmess (gg->black,avail_black);
319       print_avail = 1;
320     }
321
322     for (p = 0; p < player_globals.p_num; p++) {
323       struct player *pp = &player_globals.parray[p];
324       if ((p == gg->white) || (p == gg->black))
325         continue;
326       if (pp->status != PLAYER_PROMPT)
327         continue;
328
329       if (CheckPFlag(p, PFLAG_GIN) || player_is_observe(p, g)) {
330         pprintf_noformat(p, outstr);
331         printed = 1;
332       }
333
334       if (CheckPFlag(p, PFLAG_AVAIL) && (CheckPFlag(p, PFLAG_OPEN)) && (pp->game < 0) && (print_avail)) {
335         if (((player_globals.parray[gg->white].b_stats.rating <= pp->availmax) && (player_globals.parray[gg->white].b_stats.rating >= pp->availmin)) || (!pp->availmax)) {
336           pprintf (p,"\n%s",avail_white);
337           avail_printed = 1;
338         }
339         if (((player_globals.parray[gg->black].b_stats.rating <= pp->availmax) && (player_globals.parray[gg->black].b_stats.rating >= pp->availmin)) || (!pp->availmax)) {
340           pprintf (p,"\n%s",avail_black);
341           avail_printed = 1;
342         }
343         if (gl == -1) /* bughouse ? */ {
344           if (((player_globals.parray[game_globals.garray[gl].white].b_stats.rating <= pp->availmax) && (player_globals.parray[game_globals.garray[gl].white].b_stats.rating >= pp->availmin)) || (!pp->availmax)) {
345             pprintf (p,"\n%s",avail_bugwhite);
346             avail_printed = 1;
347           }
348           if (((player_globals.parray[game_globals.garray[gl].black].b_stats.rating <= pp->availmax) && (player_globals.parray[game_globals.garray[gl].black].b_stats.rating >= pp->availmin)) || (!pp->availmax)) {
349             pprintf (p,"\n%s",avail_bugblack);
350             avail_printed = 1;
351           }
352         }
353         if (avail_printed) {
354           avail_printed = 0;
355           printed = 1; 
356           pprintf (p,"\n");
357         }
358       }
359
360       if (printed) {
361         send_prompt(p);
362         printed = 0;
363       }
364     }
365
366     if (!(gg->rated && rate_change)) {
367       pprintf(gg->white, "No ratings adjustment done.\n");
368       pprintf(gg->black, "No ratings adjustment done.\n");
369     } 
370   }
371
372   if (rate_change && gl < 0)
373     game_write_complete(g, isDraw, EndSymbol);
374   /* Mail off the moves */
375   if (CheckPFlag(gg->white, PFLAG_AUTOMAIL)) {
376     pcommand(gg->white, "mailmoves");
377   }
378   if (CheckPFlag(gg->black, PFLAG_AUTOMAIL)) {
379     pcommand(gg->black, "mailmoves");
380   }
381   if (!((player_globals.parray[gg->white].simul_info != NULL) &&
382          (player_globals.parray[gg->white].simul_info->numBoards))) {
383     player_globals.parray[gg->white].num_white++;
384     PFlagOFF(gg->white, PFLAG_LASTBLACK);
385     player_globals.parray[gg->black].num_black++;
386     PFlagON(gg->black, PFLAG_LASTBLACK);
387   }
388   player_globals.parray[gg->white].last_opponent = 
389           strdup(gg->black_name);
390   player_globals.parray[gg->black].last_opponent = 
391           strdup(gg->white_name);
392   if (beingplayed) {
393     player_globals.parray[gg->white].game = -1;
394     player_globals.parray[gg->black].game = -1;
395     player_globals.parray[gg->white].opponent = -1;
396     player_globals.parray[gg->black].opponent = -1;
397     if (gg->white != command_globals.commanding_player)
398       send_prompt(gg->white);
399     if (gg->black != command_globals.commanding_player)
400       send_prompt(gg->black);
401     if ((player_globals.parray[gg->white].simul_info != NULL) && 
402          (player_globals.parray[gg->white].simul_info->numBoards))
403       player_simul_over(gg->white, g, whiteResult);
404   }
405   game_finish(g); 
406 }
407
408 static int was_promoted(struct game *g, int f, int r)
409 {
410 #define BUGHOUSE_PAWN_REVERT 1
411 #ifdef BUGHOUSE_PAWN_REVERT
412   int i;
413
414   for (i = g->numHalfMoves-2; i > 0; i -= 2) {
415     if (g->moveList[i].toFile == f && g->moveList[i].toRank == r) {
416       if (g->moveList[i].piecePromotionTo) {
417         switch(g->moveList[i].moveString[0]) { // [HGM] return original piece type rather than just TRUE
418           case 'P': return PAWN;
419           case 'N': return HONORABLEHORSE; // !!! this is Shogi, so no KNIGHT !!!
420           case 'B': return BISHOP;
421           case 'R': return ROOK;
422           case 'L': return LANCE;
423           case 'S': return SILVER;
424           default:  return GOLD;
425         }
426       }
427       if (g->moveList[i].fromFile == ALG_DROP)
428         return 0;
429       f = g->moveList[i].fromFile;
430       r = g->moveList[i].fromRank;
431     }
432   }
433 #endif
434   return 0;
435 }
436
437 int pIsPlaying (int p)
438 {
439         struct player *pp = &player_globals.parray[p];
440         int g = pp->game;
441         int p1 = pp->opponent;
442         
443         if (g < 0 || game_globals.garray[g].status != GAME_ACTIVE) {
444                 pprintf (p, "You are not playing a game.\n");
445                 return 0;
446         } 
447
448         if (game_globals.garray[g].white != p && game_globals.garray[g].black != p) {
449                 /* oh oh; big bad game bug. */
450                 d_printf("BUG:  Player %s playing game %d according to player_globals.parray,"
451                          "\n      but not according to game_globals.garray.\n", pp->name, g+1);
452                 pprintf (p, "Disconnecting you from game number %d.\n", g+1);
453                 pp->game = -1;
454                 if (p1 >= 0 && player_globals.parray[p1].game == g
455                     && game_globals.garray[g].white != p1 && game_globals.garray[g].black != p1) {
456                         pprintf (p1, "Disconnecting you from game number %d.\n", g+1);
457                         player_globals.parray[p1].game = -1;
458                 }
459                 return 0;
460         }
461         return 1;
462 }
463
464 /* add clock increments */
465 static void game_add_increment(struct player *pp, struct game *gg)
466 {
467         /* no update on first move */
468         if (gg->game_state.moveNum == 1) return;
469
470         if (net_globals.con[pp->socket]->timeseal) {    /* does he use timeseal? */
471                 if (pp->side == WHITE) {
472                         gg->wRealTime += gg->wIncrement * 100;
473                         gg->wTime = gg->wRealTime / 100;        /* remember to conv to
474                                                                                                    tenth secs */
475                 } else if (pp->side == BLACK) {
476                         gg->bRealTime += gg->bIncrement * 100;  /* conv to ms */
477                         gg->bTime = gg->bRealTime / 100;        /* remember to conv to
478                                                                                                    tenth secs */
479                 }
480         } else {
481                 if (gg->game_state.onMove == BLACK) {
482                         gg->bTime += gg->bIncrement;
483                 }
484                 if (gg->game_state.onMove == WHITE) {
485                         gg->wTime += gg->wIncrement;
486                 }
487         }
488 }
489
490 /* updates clocks for a game with timeseal */
491 void timeseal_update_clocks(struct player *pp, struct game *gg)
492 {
493         /* no update on first move */
494         if (gg->game_state.moveNum == 1) return;
495
496         if (pp->side == WHITE) {
497                 gg->wLastRealTime = gg->wRealTime;
498                 gg->wTimeWhenMoved = net_globals.con[pp->socket]->time;
499                 if (((gg->wTimeWhenMoved - gg->wTimeWhenReceivedMove) < 0) ||
500                     (gg->wTimeWhenReceivedMove == 0)) {
501                         /* might seem weird - but could be caused by a person moving BEFORE
502                            he receives the board pos (this is possible due to lag) but it's
503                            safe to say he moved in 0 secs :-) */
504                         gg->wTimeWhenReceivedMove = gg->wTimeWhenMoved;
505                 } else {
506                         gg->wRealTime -= gg->wTimeWhenMoved - gg->wTimeWhenReceivedMove;
507                 }
508         } else if (pp->side == BLACK) {
509                 gg->bLastRealTime = gg->bRealTime;
510                 gg->bTimeWhenMoved = net_globals.con[pp->socket]->time;
511                 if (((gg->bTimeWhenMoved - gg->bTimeWhenReceivedMove) < 0) ||
512                     (gg->bTimeWhenReceivedMove == 0)) {
513                         /* might seem weird - but could be caused by a person moving BEFORE
514                            he receives the board pos (this is possible due to lag) but it's
515                            safe to say he moved in 0 secs :-) */
516                         gg->bTimeWhenReceivedMove = gg->bTimeWhenMoved;
517                 } else {
518                         gg->bRealTime -= gg->bTimeWhenMoved - gg->bTimeWhenReceivedMove;
519                 }
520         }
521 }
522
523
524 void process_move(int p, char *command)
525 {
526   struct player *pp = &player_globals.parray[p];
527   struct game *gg;
528   int g, result, len, i, f;
529   struct move_t move;
530   unsigned now = 0;
531
532   if (pp->game < 0) {
533     pprintf(p, "You are not playing or examining a game.\n");
534     return;
535   }
536   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
537
538   g = pp->game;
539   gg = &game_globals.garray[g];
540
541   if (gg->status == GAME_SETUP) {
542     if (!attempt_drop(p,g,command)) {
543       pprintf(p, "You are still setting up the position.\n");
544       pprintf(p, "Type: 'setup done' when you are finished editing.\n");
545     } else
546     send_board_to(g, p); 
547     return;
548   }
549
550   if (gg->status != GAME_EXAMINE) {
551     if (!pIsPlaying(p)) return;
552
553     if (pp->side != gg->game_state.onMove) {
554       pprintf(p, "It is not your move.\n");
555       return;
556     }
557     if (gg->clockStopped) {
558       pprintf(p, "Game clock is paused, use \"unpause\" to resume.\n");
559       return;
560     }
561   }
562   pp->promote = NOPIECE; // [HGM] this seemed to be uninitialized, which caused spurious promotion in Shogi
563   if ((len = strlen(command)) > 1) {
564     if (command[len - 2] == '=' || gg->game_state.drops == 2 && command[len - 2] == '/') { // [HGM] encode gating as promotion
565 printf("promo '%s'\n", command);
566       switch (tolower(command[len - 1])) {
567       case 'n':
568         pp->promote = KNIGHT;
569         break;
570       case 'b':
571         pp->promote = BISHOP;
572         break;
573       case 'r':
574         pp->promote = ROOK;
575         break;
576       case 'a':
577         pp->promote = CARDINAL;
578         break;
579       case 'c':
580         pp->promote = MARSHALL;
581         break;
582       case 'm':
583         pp->promote = MAN;
584         break;
585       case 'q':
586         pp->promote = QUEEN;
587         break;
588       // courier promotion
589       case 'f':
590         pp->promote = FERZ2;
591         break;
592       // Superchess promotions
593       case 'e':
594         pp->promote = EMPRESS;
595         break;
596       case 's':
597         pp->promote = PRINCESS;
598         break;
599       case 'v':
600         pp->promote = CENTAUR;
601         break;
602       case 'w':
603         pp->promote = WOODY;
604         break;
605       case 'o':
606         pp->promote = SQUIRREL;
607         break;
608       case 'g':
609         pp->promote = MASTODON;
610         break;
611       case 'l':
612         pp->promote = LIEUTENANT;
613         break;
614       case 'k':
615         pp->promote = KING;
616         break;
617       // Shogi promotions
618       case 'h':
619         pp->promote = DRAGONHORSE;
620         break;
621       case 'd':
622         pp->promote = DRAGONKING;
623         break;
624       case '^':
625       case '+':
626         pp->promote = GOLD;
627         break;
628       case '=':
629         pp->promote = NOPIECE;
630         break;
631       default:
632         pprintf(p, "Don't understand that move.\n");
633         return;
634         break;
635       }
636     }
637   }
638
639   switch (parse_move(command, &gg->game_state, &move, pp->promote)) {
640   case MOVE_ILLEGAL:
641     pprintf(p, "Illegal move.\n");
642     return;
643     break;
644   case MOVE_AMBIGUOUS:
645     pprintf(p, "Ambiguous move.\n");
646     return;
647     break;
648   default:
649     break;
650   }
651
652   if (gg->status == GAME_EXAMINE) {
653     gg->numHalfMoves++;
654     if (gg->numHalfMoves > gg->examMoveListSize) {
655       gg->examMoveListSize += 20;       /* Allocate 20 moves at a time */
656       gg->examMoveList = (struct move_t *) realloc(gg->examMoveList, sizeof(struct move_t) * gg->examMoveListSize);
657     }
658     result = execute_move(&gg->game_state, &move, 1);
659     move.atTime = now;
660     move.tookTime = 0;
661     MakeFENpos(g, move.FENpos);
662     gg->examMoveList[gg->numHalfMoves - 1] = move;
663     /* roll back time */
664     if (gg->game_state.onMove == WHITE) {
665       gg->wTime += (gg->lastDecTime - gg->lastMoveTime);
666     } else {
667       gg->bTime += (gg->lastDecTime - gg->lastMoveTime);
668     }
669     now = tenth_secs();
670     if (gg->numHalfMoves == 0)
671       gg->timeOfStart = now;
672     gg->lastMoveTime = now;
673     gg->lastDecTime = now;
674
675   } else {                      /* real game */
676     i = pp->opponent;
677     if ((player_globals.parray[i].simul_info != NULL) && (player_globals.parray[i].simul_info->numBoards &&
678          (player_globals.parray[i].simul_info->boards[player_globals.parray[i].simul_info->onBoard] != g))) {
679       pprintf(p, "It isn't your turn: wait until the simul giver is at your board.\n");
680       return;
681     }
682     if (net_globals.con[pp->socket]->timeseal) {        /* does he use timeseal? */
683             timeseal_update_clocks(pp, &game_globals.garray[g]);
684     }
685     /* we need to reset the opp's time for receiving the board since the
686        timeseal decoder only alters the time if it's 0 Otherwise the time
687        would be changed if the player did a refresh which would screw up
688        the timings */
689     if (pp->side == WHITE) {
690       gg->bTimeWhenReceivedMove = 0;
691     } else {
692       gg->wTimeWhenReceivedMove = 0;
693     }
694
695     game_update_time(g);
696     game_add_increment(pp, gg);
697
698     /* Do the move */
699     gg->numHalfMoves++;
700     if (gg->numHalfMoves > gg->moveListSize) {
701       gg->moveListSize += 20;   /* Allocate 20 moves at a time */
702       gg->moveList = (struct move_t *) realloc(gg->moveList, sizeof(struct move_t) * gg->moveListSize);
703     }
704     result = execute_move(&gg->game_state, &move, 1);
705     if (result == MOVE_OK && (gg->link >= 0 || gg->game_state.holdings) && move.pieceCaptured != NOPIECE) {
706       /* transfer captured piece to partner */
707       /* check if piece reverts to a pawn */
708       int victim = move.pieceCaptured, partner = gg->link, demoted;
709       // [HGM] zh: if not Bughouse, the game_state.holdings field decides what happens
710       if(gg->link < 0) { 
711         partner = g; // pieces stay with current board
712         if(gg->game_state.holdings == -1) victim ^= WHITE|BLACK; // flip color
713       } 
714       if (demoted = was_promoted(&game_globals.garray[g], move.toFile, move.toRank))
715         update_holding(partner, colorval(victim) | demoted); // [HGM] was_promoted now returns original piece type
716       else
717         update_holding(partner, victim);
718     }
719     now = tenth_secs();
720     move.atTime = now;
721     if (gg->numHalfMoves > 1) {
722       move.tookTime = move.atTime - gg->lastMoveTime;
723     } else {
724       move.tookTime = move.atTime - gg->startTime;
725     }
726     gg->lastMoveTime = now;
727     gg->lastDecTime = now;
728     move.wTime = gg->wTime;
729     move.bTime = gg->bTime;
730
731     if (net_globals.con[pp->socket]->timeseal) {        /* does he use timeseal? */
732       if (pp->side == WHITE) {
733         move.tookTime = (game_globals.garray[pp->game].wTimeWhenMoved -
734                          game_globals.garray[pp->game].wTimeWhenReceivedMove) / 100;
735       } else {
736         move.tookTime = (game_globals.garray[pp->game].bTimeWhenMoved -
737                          game_globals.garray[pp->game].bTimeWhenReceivedMove) / 100;
738       }
739     }
740
741     if (gg->numHalfMoves <= 2) {
742             move.tookTime = 0;
743     }
744
745     MakeFENpos(g, move.FENpos);
746     gg->moveList[gg->numHalfMoves - 1] = move;
747   }
748
749   send_boards(g);
750
751   if (result == MOVE_ILLEGAL) {
752     pprintf(p, "Internal error, illegal move accepted!\n");
753   }
754   if ((result == MOVE_OK) && (gg->status == GAME_EXAMINE)) {
755     int p1;
756
757     for (p1 = 0; p1 < player_globals.p_num; p1++) {
758       if (player_globals.parray[p1].status != PLAYER_PROMPT)
759         continue;
760       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
761         pprintf(p1, "%s moves: %s\n", pp->name, move.algString);
762       }
763     }
764   }
765   if (result == MOVE_CHECKMATE) {
766     if (gg->status == GAME_EXAMINE) {
767       int p1;
768
769       for (p1 = 0; p1 < player_globals.p_num; p1++) {
770         if (player_globals.parray[p1].status != PLAYER_PROMPT)
771           continue;
772         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
773           pprintf(p1, "%s has been checkmated.\n",
774                   (CToggle(gg->game_state.onMove) == BLACK) ? "White" : "Black");
775         }
776       }
777     } else {
778       game_ended(g, CToggle(gg->game_state.onMove), END_CHECKMATE);
779     }
780   }
781   if (result == MOVE_STALEMATE) {
782     if (gg->status == GAME_EXAMINE) {
783       int p1;
784
785       for (p1 = 0; p1 < player_globals.p_num; p1++) {
786         if (player_globals.parray[p1].status != PLAYER_PROMPT)
787           continue;
788         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
789           pprintf(p1, "Stalemate.\n");
790         }
791       }
792     } else {
793       game_ended(g, CToggle(gg->game_state.onMove), END_STALEMATE);
794     }
795   }
796   if (result == MOVE_NOMATERIAL) {
797     if (gg->status == GAME_EXAMINE) {
798       int p1;
799
800       for (p1 = 0; p1 < player_globals.p_num; p1++) {
801         if (player_globals.parray[p1].status != PLAYER_PROMPT)
802           continue;
803         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
804           pprintf(p1, "No mating material.\n");
805         }
806       }
807     } else {
808       game_ended(g, CToggle(gg->game_state.onMove), END_NOMATERIAL);
809     }
810   }
811   if (result == MOVE_BARE) {
812     if (gg->status == GAME_EXAMINE) {
813       int p1;
814
815       for (p1 = 0; p1 < player_globals.p_num; p1++) {
816         if (player_globals.parray[p1].status != PLAYER_PROMPT)
817           continue;
818         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
819           pprintf(p1, "%s bared.\n",
820                   (gg->game_state.onMove == BLACK) ? "White" : "Black");
821         }
822       }
823     } else {
824       game_ended(g, gg->game_state.onMove, END_BARE);
825     }
826   }
827 }
828
829 int com_resign(int p, param_list param)
830 {
831   struct player *pp = &player_globals.parray[p];
832   int g, o, oconnected;
833
834   if (param[0].type == TYPE_NULL) {
835     g = pp->game;
836     if (!pIsPlaying(p))
837       return COM_OK;
838     else {
839       decline_withdraw_offers(p, -1, -1, DO_DECLINE);
840       game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
841     }
842   } else if (FindPlayer(p, param[0].val.word, &o, &oconnected)) {
843     g = game_new();
844     if (game_read(g, p, o) < 0) {
845       if (game_read(g, o, p) < 0) {
846         pprintf(p, "You have no stored game with %s\n", player_globals.parray[o].name);
847         if (!oconnected)
848           player_remove(o);
849         return COM_OK;
850       } else {
851         game_globals.garray[g].white = o;
852         game_globals.garray[g].black = p;
853       }
854     } else {
855       game_globals.garray[g].white = p;
856       game_globals.garray[g].black = o;
857     }
858     pprintf(p, "You resign your stored game with %s\n", player_globals.parray[o].name);
859     pcommand(p, "message %s I have resigned our stored game \"%s vs. %s.\"",
860              player_globals.parray[o].name,
861              player_globals.parray[game_globals.garray[g].white].name,
862              player_globals.parray[game_globals.garray[g].black].name);
863     game_delete(game_globals.garray[g].white, game_globals.garray[g].black);
864     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
865     if (!oconnected)
866       player_remove(o);
867   }
868   return COM_OK;
869 }
870
871 static int Check50MoveRule (int p, int g)
872 {
873   int num_reversible = game_globals.garray[g].numHalfMoves;
874
875   if (game_globals.garray[g].game_state.lastIrreversable >= 0) {
876     num_reversible -= game_globals.garray[g].game_state.lastIrreversable;
877   }
878   if (num_reversible > 99) {
879     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_50MOVERULE);
880     return 1;
881   }
882   return 0;
883 }
884
885 static char *GetFENpos (int g, int half_move)
886 {
887   if (half_move < 0)
888     return game_globals.garray[g].FENstartPos;
889   else return game_globals.garray[g].moveList[half_move].FENpos;
890 }
891
892 static int CheckRepetition (int p, int g)
893 {
894   struct player *pp = &player_globals.parray[p];
895   struct pending* pend;
896   int move_num;
897   int flag1 = 1, flag2 = 1;
898   char *pos1 = GetFENpos (g, game_globals.garray[g].numHalfMoves - 1);
899   char *pos2 = GetFENpos (g, game_globals.garray[g].numHalfMoves);
900   char *pos;
901
902   if (game_globals.garray[g].numHalfMoves < 8)  /* can't have three repeats any quicker. */
903     return 0;
904
905   for (move_num = game_globals.garray[g].game_state.lastIrreversable - 1; // [HGM] FEN stored in moveList[numHalfMoves-1] !
906        move_num < game_globals.garray[g].numHalfMoves - 1; move_num++) {
907     pos = GetFENpos (g, move_num);
908     if (strlen(pos1) == strlen(pos) && !strcmp(pos1, pos))
909       flag1++;
910     if (strlen(pos2) == strlen(pos) && !strcmp(pos2, pos))
911       flag2++;
912 printf("%2d. %d-%d %s %s %s\n", move_num, flag1, flag2, pos1,pos2,pos);
913   }
914   if (flag1 >= 3 || flag2 >= 3) {
915     if ((pend = find_pend(pp->opponent, p, PEND_DRAW)) != NULL) {
916       delete_pending(pend);
917       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
918     }
919     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_REPETITION);
920     return 1;
921   }
922   else return 0;
923 }
924
925 int com_draw(int p, param_list param)
926 {
927   struct player *pp = &player_globals.parray[p];
928   struct pending* pend;
929   int p1, g = pp->game;
930
931   if (!pIsPlaying(p)) {
932     return COM_OK;
933   }
934   if (Check50MoveRule (p, g) || CheckRepetition(p, g)) {
935     return COM_OK;
936   }
937   p1 = pp->opponent;
938
939   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards &&
940         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] != g)) {
941     pprintf(p, "You can only make requests when the simul player is at your board.\n");
942     return COM_OK;
943   }
944
945   if ((pend = (find_pend(pp->opponent, p, PEND_DRAW))) != NULL) {
946     delete_pending(pend);
947     decline_withdraw_offers(p, -1, -1,DO_DECLINE);
948     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_AGREEDDRAW);
949   } else {
950     pprintf(pp->opponent, "\n");
951     pprintf_highlight(pp->opponent, "%s", pp->name);
952     pprintf_prompt(pp->opponent, " offers you a draw.\n");
953     pprintf(p, "Draw request sent.\n");
954     add_request(p, pp->opponent, PEND_DRAW);
955   }
956   return COM_OK;
957 }
958
959 int com_pause(int p, param_list param)
960 {
961   struct player *pp = &player_globals.parray[p];
962   int g, now;
963   struct pending* pend;
964
965   if (!pIsPlaying(p)) {
966     return COM_OK;
967   }
968   g = pp->game;
969   if (game_globals.garray[g].wTime == 0) {
970     pprintf(p, "You can't pause untimed games.\n");
971     return COM_OK;
972   }
973   if (game_globals.garray[g].clockStopped) {
974     pprintf(p, "Game is already paused, use \"unpause\" to resume.\n");
975     return COM_OK;
976   }
977   if ((pend = find_pend(pp->opponent, p, PEND_PAUSE)) != NULL) {
978     delete_pending(pend);
979     game_globals.garray[g].clockStopped = 1;
980     /* Roll back the time */
981     if (game_globals.garray[g].game_state.onMove == WHITE) {
982       game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
983     } else {
984       game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
985     }
986     now = tenth_secs();
987     if (game_globals.garray[g].numHalfMoves == 0)
988       game_globals.garray[g].timeOfStart = now;
989     game_globals.garray[g].lastMoveTime = now;
990     game_globals.garray[g].lastDecTime = now;
991     send_boards(g);
992     pprintf_prompt(pp->opponent, "\n%s accepted pause. Game clock paused.\n",
993                    pp->name);
994     pprintf(p, "Game clock paused.\n");
995   } else {
996     pprintf(pp->opponent, "\n");
997     pprintf_highlight(pp->opponent, "%s", pp->name);
998     pprintf_prompt(pp->opponent, " requests to pause the game.\n");
999     pprintf(p, "Pause request sent.\n");
1000     add_request(p, pp->opponent, PEND_PAUSE);
1001   }
1002   return COM_OK;
1003 }
1004
1005 int com_unpause(int p, param_list param)
1006 {
1007   struct player *pp = &player_globals.parray[p];
1008   int g;
1009   int now;
1010   struct pending* pend;
1011
1012   if (!pIsPlaying(p)) {
1013     return COM_OK;
1014   }
1015
1016   g = pp->game;
1017
1018   if (!game_globals.garray[g].clockStopped) {
1019     pprintf(p, "Game is not paused.\n");
1020     return COM_OK;
1021   }
1022   if ((pend = find_pend(pp->opponent, p, PEND_UNPAUSE)) != NULL) {
1023     delete_pending(pend);
1024     game_globals.garray[g].clockStopped = 0;
1025     now = tenth_secs();
1026     if (game_globals.garray[g].numHalfMoves == 0)
1027       game_globals.garray[g].timeOfStart = now;
1028     game_globals.garray[g].lastMoveTime = now;
1029     game_globals.garray[g].lastDecTime = now;
1030     send_boards(g);
1031     pprintf(p, "Game clock resumed.\n");
1032     pprintf_prompt(pp->opponent, "\nGame clock resumed.\n");
1033   } else {
1034     pprintf(pp->opponent, "\n");
1035     pprintf_highlight(pp->opponent, "%s", pp->name);
1036     pprintf_prompt(pp->opponent, " requests to unpause the game.\n");
1037     pprintf(p, "Unpause request sent.\n");
1038     add_request(p, pp->opponent, PEND_UNPAUSE);
1039   }
1040   return COM_OK;
1041 }
1042
1043 int com_abort(int p, param_list param)
1044 {
1045   struct player *pp = &player_globals.parray[p];
1046   struct pending* pend;
1047   int p1, g, myColor, yourColor, myGTime, yourGTime;
1048   int courtesyOK = 1;
1049
1050   g = pp->game;
1051   if (!pIsPlaying(p))
1052     return COM_OK;
1053
1054   p1 = pp->opponent;
1055   if (p == game_globals.garray[g].white) {
1056     myColor = WHITE;
1057     yourColor = BLACK;
1058     myGTime = game_globals.garray[g].wTime;
1059     yourGTime = game_globals.garray[g].bTime;
1060   } else {
1061     myColor = BLACK;
1062     yourColor = WHITE;
1063     myGTime = game_globals.garray[g].bTime;
1064     yourGTime = game_globals.garray[g].wTime;
1065   }
1066   if ((player_globals.parray[p1].simul_info != NULL) && 
1067      (player_globals.parray[p1].simul_info->numBoards &&
1068         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] != g)) {
1069     pprintf(p, "You can only make requests when the simul player is at your board.\n");
1070     return COM_OK;
1071   }
1072   if ((pend = find_pend(p1, p, PEND_ABORT)) != NULL) {
1073     delete_pending(pend);
1074     decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1075     game_ended(g, yourColor, END_ABORT);
1076   } else {
1077     game_update_time(g);
1078
1079     if (net_globals.con[pp->socket]->timeseal
1080         && game_globals.garray[g].game_state.onMove == myColor
1081         && game_globals.garray[g].flag_pending == FLAG_ABORT) {
1082       /* It's my move, opponent has asked for abort; I lagged out,
1083          my timeseal prevented courtesyabort, and I am sending an abort
1084          request before acknowledging (and processing) my opponent's
1085          courtesyabort.  OK, let's abort already :-). */
1086       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1087       game_ended(g, yourColor, END_ABORT);
1088     }
1089
1090     if (net_globals.con[player_globals.parray[p1].socket]->timeseal) {  /* opp uses timeseal? */
1091
1092       int yourRealTime = (myColor == WHITE  ?  game_globals.garray[g].bRealTime
1093                                             :  game_globals.garray[g].wRealTime);
1094       if (myGTime > 0 && yourGTime <= 0 && yourRealTime > 0) {
1095         /* Override courtesyabort; opponent still has time.  Check for lag. */
1096         courtesyOK = 0;
1097
1098         if (game_globals.garray[g].game_state.onMove != myColor
1099             && game_globals.garray[g].flag_pending != FLAG_CHECKING) {
1100           /* Opponent may be lagging; let's ask. */
1101           game_globals.garray[g].flag_pending = FLAG_ABORT;
1102           game_globals.garray[g].flag_check_time = time(0);
1103           pprintf(p, "Opponent has timeseal; trying to courtesyabort.\n");
1104           pprintf(p1, "\n[G]\n");
1105           return COM_OK;
1106         }
1107       }
1108     }
1109
1110     if (myGTime > 0 && yourGTime <= 0 && courtesyOK) {
1111       /* player wants to abort + opponent is out of time = courtesyabort */
1112       pprintf(p, "Since you have time, and your opponent has none, the game has been aborted.");
1113       pprintf(p1, "Your opponent has aborted the game rather than calling your flag.");
1114       decline_withdraw_offers(p, -1, -1, DO_DECLINE);
1115       game_ended(g, myColor, END_COURTESY);
1116     } else {
1117       pprintf(p1, "\n");
1118       pprintf_highlight(p1, "%s", pp->name);
1119       pprintf(p1, " would like to abort the game; ");
1120       pprintf_prompt(p1, "type \"abort\" to accept.\n");
1121       pprintf(p, "Abort request sent.\n");
1122       add_request(p, p1, PEND_ABORT);
1123     }
1124   }
1125   return COM_OK;
1126 }
1127
1128 static int player_has_mating_material(struct game_state_t *gs, int color)
1129 {
1130   int i, j;
1131   int piece;
1132   int minor_pieces = 0;
1133
1134   for (i = 0; i < gs->files; i++)
1135     for (j = 0; j < gs->ranks; j++) {
1136       piece = gs->board[i][j];
1137       switch (piecetype(piece)) {
1138       case BISHOP:
1139       case KNIGHT:
1140         if (iscolor(piece, color))
1141           minor_pieces++;
1142         break;
1143       case KING:
1144       case NOPIECE:
1145         break;
1146       default:
1147         if (iscolor(piece, color))
1148           return 1;
1149       }
1150     }
1151   return ((minor_pieces > 1) ? 1 : 0);
1152 }
1153
1154 int com_flag(int p, param_list param)
1155 {
1156         struct player *pp = &player_globals.parray[p];
1157         struct game *gg;
1158         int g;
1159         int myColor;
1160
1161         if (!pIsPlaying(p)) {
1162                 return COM_OK;
1163         }
1164         g = pp->game;
1165
1166         gg = &game_globals.garray[g];
1167
1168         myColor = (p == gg->white ? WHITE : BLACK);
1169         if (gg->type == TYPE_UNTIMED) {
1170                 pprintf(p, "You can't flag an untimed game.\n");
1171                 return COM_OK;
1172         }
1173         if (gg->numHalfMoves < 2) {
1174                 pprintf(p, "You cannot flag before both players have moved.\nUse abort instead.\n");
1175                 return COM_OK;
1176         }
1177         game_update_time(g);
1178         
1179         {
1180                 int myTime, yourTime, opp = pp->opponent, serverTime;
1181                 
1182                 if (net_globals.con[pp->socket]->timeseal) {    /* does caller use timeseal? */
1183                         myTime = (myColor==WHITE?gg->wRealTime:gg->bRealTime);
1184                 } else {
1185                         myTime = (myColor == WHITE?gg->wTime:gg->bTime);
1186                 }
1187                 serverTime = (myColor == WHITE?gg->bTime:gg->wTime);
1188                 
1189                 if (net_globals.con[player_globals.parray[opp].socket]->timeseal) {     /* opp uses timeseal? */
1190                         yourTime = (myColor == WHITE?gg->bRealTime:gg->wRealTime);
1191                 } else {
1192                         yourTime = serverTime;
1193                 }
1194
1195                 /* the clocks to compare are now in myTime and yourTime */
1196                 if ((myTime <= 0) && (yourTime <= 0)) {
1197                         decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1198                         game_ended(g, myColor, END_BOTHFLAG);
1199                         return COM_OK;
1200                 }
1201
1202                 if (yourTime > 0) {
1203                         /* Opponent still has time, but if that's only because s/he
1204                          * may be lagging, we should ask for an acknowledgement and then
1205                          * try to call the flag. */
1206                         
1207                         if (serverTime <= 0 && gg->game_state.onMove != myColor
1208                             && gg->flag_pending != FLAG_CHECKING) {                             
1209                                 /* server time thinks opponent is down, but RealTIme disagrees.
1210                                  * ask client to acknowledge it's alive. */                             
1211                                 gg->flag_pending = FLAG_CALLED;
1212                                 gg->flag_check_time = time(0);
1213                                 pprintf(p, "Opponent has timeseal; checking if (s)he's lagging.\n");
1214                                 pprintf (opp, "\n[G]\n");
1215                                 return COM_OK;
1216                         }
1217                         
1218                         /* if we're here, it means one of:
1219                          * 1. the server agrees opponent has time, whether lagging or not.
1220                          * 2. opp. has timeseal (if yourTime != serverTime), had time left
1221                          *    after the last move (yourTime > 0), and it's still your move.
1222                          * 3. we're currently checking a flag call after having receiving
1223                          *    acknowledgement from the other timeseal (and would have reset
1224                          *    yourTime if the flag were down). */
1225                         
1226                         pprintf(p, "Your opponent is not out of time!\n");
1227                         return COM_OK;
1228                 }
1229         }
1230         
1231         decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1232         if (player_has_mating_material(&gg->game_state, myColor))
1233                 game_ended(g, myColor, END_FLAG);
1234         else
1235                 game_ended(g, myColor, END_FLAGNOMATERIAL);
1236         return COM_OK;
1237 }
1238
1239 int com_adjourn(int p, param_list param)
1240 {
1241   struct player *pp = &player_globals.parray[p];
1242   struct pending* pend;
1243   int p1, g, myColor, yourColor;
1244
1245   if (!pIsPlaying(p))
1246     return COM_OK;
1247
1248   p1 = pp->opponent;
1249   g = pp->game;
1250   if (!CheckPFlag(p, PFLAG_REG) || !CheckPFlag(p, PFLAG_REG)) {
1251     pprintf(p, "Both players must be registered to adjourn a game.  Use \"abort\".\n");
1252     return COM_OK;
1253   }
1254   if (game_globals.garray[g].link >= 0) {
1255     pprintf(p, "Bughouse games cannot be adjourned.\n");
1256     return COM_OK;
1257   }
1258   myColor = (p == game_globals.garray[g].white ? WHITE : BLACK);
1259   yourColor = (myColor == WHITE ? BLACK : WHITE);
1260
1261   if ((pend = find_pend(p1, p, PEND_ADJOURN)) != NULL) {
1262     delete_pending(pend);
1263     decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1264     game_ended(pp->game, yourColor, END_ADJOURN);
1265   } else {
1266     game_update_time(g);
1267     if (((myColor == WHITE) && (game_globals.garray[g].wTime > 0) && (game_globals.garray[g].bTime <= 0))
1268         || ((myColor == BLACK) && (game_globals.garray[g].bTime > 0) && (game_globals.garray[g].wTime <= 0))) {
1269 /* player wants to adjourn + opponent is out of time = courtesyadjourn */
1270       pprintf(p, "Since you have time, and your opponent has none, the game has been adjourned.");
1271       pprintf(p1, "Your opponent has adjourned the game rather than calling your flag.");
1272       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1273       game_ended(g, myColor, END_COURTESYADJOURN);
1274     } else {
1275       pprintf(p1, "\n");
1276       pprintf_highlight(p1, "%s", pp->name);
1277       pprintf(p1, " would like to adjourn the game; ");
1278       pprintf_prompt(p1, "type \"adjourn\" to accept.\n");
1279       pprintf(p, "Adjourn request sent.\n");
1280       add_request(p, p1, PEND_ADJOURN);
1281     }
1282   }
1283   return COM_OK;
1284 }
1285
1286 int com_takeback(int p, param_list param)
1287 {
1288   struct player *pp = &player_globals.parray[p];
1289   int nHalfMoves = 1, g, i, p1, pend_half_moves;
1290   struct pending* from;
1291
1292   if (!pIsPlaying(p))
1293     return COM_OK;
1294
1295   p1 = pp->opponent;
1296   if ((player_globals.parray[p1].simul_info != NULL) && 
1297      (player_globals.parray[p1].simul_info->numBoards &&
1298         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] !=
1299         pp->game)) {
1300     pprintf(p, "You can only make requests when the simul player is at your board.\n");
1301     return COM_OK;
1302   }
1303
1304   g = pp->game;
1305   if (game_globals.garray[g].link >= 0) {
1306     pprintf(p, "Takeback not implemented for bughouse games yet.\n");
1307     return COM_OK;
1308   }
1309   if (param[0].type == TYPE_INT) {
1310     nHalfMoves = param[0].val.integer;
1311     if (nHalfMoves <= 0) {
1312       pprintf (p,"You can't takeback less than 1 move.\n");
1313       return COM_OK;
1314     }
1315   }
1316   if ((from = find_pend(pp->opponent, p, PEND_TAKEBACK)) != NULL) {
1317     pend_half_moves = from->wtime;
1318     delete_pending(from);
1319     if (pend_half_moves == nHalfMoves) {
1320       /* Doing the takeback */
1321       decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1322       for (i = 0; i < nHalfMoves; i++) {
1323         if (backup_move(g, REL_GAME) != MOVE_OK) {
1324           pprintf(game_globals.garray[g].white, "Can only backup %d moves\n", i);
1325           pprintf(game_globals.garray[g].black, "Can only backup %d moves\n", i);
1326           break;
1327         }
1328       }
1329
1330       game_globals.garray[g].wTimeWhenReceivedMove = 0;
1331       game_globals.garray[g].bTimeWhenReceivedMove = 0;
1332
1333       send_boards(g);
1334     } else {
1335
1336       if (!game_globals.garray[g].numHalfMoves) {
1337         pprintf(p, "There are no moves in your game.\n");
1338         pprintf_prompt(pp->opponent, "\n%s has declined the takeback request.\n", 
1339                        pp->name);
1340         return COM_OK;
1341       }
1342  
1343       if (game_globals.garray[g].numHalfMoves < nHalfMoves) {
1344         pprintf(p, "There are only %d half moves in your game.\n", game_globals.garray[g].numHalfMoves);
1345         pprintf_prompt(pp->opponent, "\n%s has declined the takeback request.\n", 
1346                        pp->name);
1347         return COM_OK;
1348       }
1349       pprintf(p, "You disagree on the number of half-moves to takeback.\n");
1350       pprintf(p, "Alternate takeback request sent.\n");
1351       pprintf_prompt(pp->opponent, "\n%s proposes a different number (%d) of half-move(s).\n", pp->name, nHalfMoves);
1352       from = add_request(p, pp->opponent, PEND_TAKEBACK);
1353       from->wtime = nHalfMoves;
1354     }
1355   } else {
1356
1357     if (!game_globals.garray[g].numHalfMoves) {
1358       pprintf(p, "There are no moves in your game.\n");
1359       return COM_OK;
1360     }
1361     if (game_globals.garray[g].numHalfMoves < nHalfMoves) {
1362       pprintf(p, "There are only %d half moves in your game.\n", game_globals.garray[g].numHalfMoves);
1363       return COM_OK;
1364     }
1365     pprintf(pp->opponent, "\n");
1366     pprintf_highlight(pp->opponent, "%s", pp->name);
1367     pprintf_prompt(pp->opponent, " would like to take back %d half move(s).\n",
1368            nHalfMoves);
1369     pprintf(p, "Takeback request sent.\n");
1370     from = add_request(p, pp->opponent, PEND_TAKEBACK);
1371     from->wtime = nHalfMoves;
1372   }
1373   return COM_OK;
1374 }
1375
1376
1377 int com_switch(int p, param_list param)
1378 {
1379   struct player *pp = &player_globals.parray[p];
1380   int g = pp->game, tmp, now, p1;
1381   char *strTmp;
1382   struct pending* pend;
1383
1384   if (!pIsPlaying(p))
1385     return COM_OK;
1386
1387   p1 = pp->opponent;
1388   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards &&
1389         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] != g)) {
1390     pprintf(p, "You can only make requests when the simul player is at your board.\n");
1391     return COM_OK;
1392   }
1393
1394   if (game_globals.garray[g].link >= 0) {
1395     pprintf(p, "Switch not implemented for bughouse games.\n");
1396     return COM_OK;
1397   }
1398   if ((pend = find_pend(pp->opponent, p, PEND_SWITCH)) != NULL) {
1399     delete_pending(pend);
1400     /* Doing the switch */
1401     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1402
1403     tmp = game_globals.garray[g].white;
1404     game_globals.garray[g].white = game_globals.garray[g].black;
1405     game_globals.garray[g].black = tmp;
1406     pp->side = (pp->side == WHITE) ? BLACK : WHITE;
1407     strTmp = strdup(game_globals.garray[g].white_name);
1408     strcpy(game_globals.garray[g].white_name, game_globals.garray[g].black_name);
1409     strcpy(game_globals.garray[g].black_name, strTmp);
1410     free(strTmp);
1411
1412     player_globals.parray[pp->opponent].side =
1413       (player_globals.parray[pp->opponent].side == WHITE) ? BLACK : WHITE;
1414     /* Roll back the time */
1415     if (game_globals.garray[g].game_state.onMove == WHITE) {
1416       game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1417     } else {
1418       game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1419     }
1420     now = tenth_secs();
1421     if (game_globals.garray[g].numHalfMoves == 0)
1422       game_globals.garray[g].timeOfStart = now;
1423     game_globals.garray[g].lastMoveTime = now;
1424     game_globals.garray[g].lastDecTime = now;
1425     send_boards(g);
1426     return COM_OK;
1427   }
1428   if (game_globals.garray[g].rated && game_globals.garray[g].numHalfMoves > 0) {
1429     pprintf(p, "You cannot switch sides once a rated game is underway.\n");
1430     return COM_OK;
1431   }
1432   pprintf(pp->opponent, "\n");
1433   pprintf_highlight(pp->opponent, "%s", pp->name);
1434   pprintf_prompt(pp->opponent, " would like to switch sides.\nType \"accept\" to switch sides, or \"decline\" to refuse.\n");
1435   pprintf(p, "Switch request sent.\n");
1436   add_request(p, pp->opponent, PEND_SWITCH);
1437   return COM_OK;
1438 }
1439
1440 int com_time(int p, param_list param)
1441 {
1442   struct player *pp = &player_globals.parray[p];
1443   int p1, g;
1444
1445   if (param[0].type == TYPE_NULL) {
1446     g = pp->game;
1447     if (!pIsPlaying(p))
1448       return COM_OK;
1449   } else {
1450     g = GameNumFromParam(p, &p1, &param[0]);
1451     if (g < 0)
1452       return COM_OK;
1453   }
1454   if ((g < 0) || (g >= game_globals.g_num) || (game_globals.garray[g].status != GAME_ACTIVE)) {
1455     pprintf(p, "There is no such game.\n");
1456     return COM_OK;
1457   }
1458   game_update_time(g);
1459   pprintf(p, "White (%s) : %d mins, %d secs\n",
1460           player_globals.parray[game_globals.garray[g].white].name,
1461           game_globals.garray[g].wTime / 600,
1462           (game_globals.garray[g].wTime - ((game_globals.garray[g].wTime / 600) * 600)) / 10);
1463   pprintf(p, "Black (%s) : %d mins, %d secs\n",
1464           player_globals.parray[game_globals.garray[g].black].name,
1465           game_globals.garray[g].bTime / 600,
1466           (game_globals.garray[g].bTime - ((game_globals.garray[g].bTime / 600) * 600)) / 10);
1467   return COM_OK;
1468 }
1469
1470 int com_ptime(int p, param_list param)
1471 {
1472   struct player *pp = &player_globals.parray[p];
1473   int retval, part = pp->partner;
1474
1475   if (part < 0) {
1476     pprintf(p, "You do not have a partner.\n");
1477     return COM_OK;
1478   }
1479   retval = pcommand (p, "time %s", player_globals.parray[part].name);
1480   if (retval == COM_OK)
1481     return COM_OK_NOPROMPT;
1482   else
1483     return retval;
1484 }
1485
1486 int com_boards(int p, param_list param)
1487 {
1488   char *category = NULL;
1489   char dname[MAX_FILENAME_SIZE];
1490   DIR *dirp;
1491   struct dirent *dp;
1492
1493   if (param[0].type == TYPE_WORD)
1494     category = param[0].val.word;
1495   if (category) {
1496     pprintf(p, "Boards Available For Category %s:\n", category);
1497     sprintf(dname, "%s/%s", BOARD_DIR, category);
1498   } else {
1499     pprintf(p, "Categories Available:\n");
1500     sprintf(dname, "%s", BOARD_DIR);
1501   }
1502   dirp = opendir(dname);
1503   if (!dirp) {
1504     pprintf(p, "No such category %s, try \"boards\".\n", category);
1505     return COM_OK;
1506   }
1507
1508 /* YUK! what a mess, how about printing an ordered directory? - DAV*/
1509
1510   for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1511     if (!strcmp(dp->d_name, "."))
1512       continue;
1513     if (!strcmp(dp->d_name, ".."))
1514       continue;
1515     pprintf(p, "%s\n", dp->d_name);
1516   }
1517   closedir(dirp);
1518   return COM_OK;
1519 }
1520
1521 int com_simmatch(int p, param_list param)
1522 {
1523   struct player *pp = &player_globals.parray[p];
1524   int p1, g, adjourned;
1525   int num;
1526   char tmp[100];
1527   struct pending* pend;
1528   char* board = NULL;
1529   char* category = NULL;
1530   char fname[MAX_FILENAME_SIZE];
1531
1532   if (pp->game >=0) {
1533     if (game_globals.garray[pp->game].status == GAME_EXAMINE) {
1534       pprintf(p, "You are still examining a game.\n");
1535       return COM_OK;
1536     }
1537     if (game_globals.garray[pp->game].status == GAME_SETUP) {
1538       pprintf(p, "You are still setting up a position.\n");
1539       return COM_OK;
1540     }
1541   } 
1542   p1 = player_find_part_login(param[0].val.word);
1543   if (p1 < 0) {
1544     pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
1545     return COM_OK;
1546   }
1547   if (p == p1) {
1548     pprintf(p, "You can't simmatch yourself!\n");
1549     return COM_OK;
1550   }
1551   if ((pend = find_pend(p1, p, PEND_SIMUL)) != NULL) {
1552
1553     /* Accepting Simul ! */
1554
1555     if ((pp->simul_info != NULL) && 
1556        (pp->simul_info->numBoards >= MAX_SIMUL)) {
1557       pprintf(p, "You are already playing the maximum of %d boards.\n", MAX_SIMUL);
1558       pprintf(p1, "Simul request removed, boards filled.\n");
1559       delete_pending(pend);
1560       return COM_OK;
1561     }
1562     unobserveAll(p);            /* stop observing when match starts */
1563     unobserveAll(p1);
1564
1565     g = game_new();
1566     adjourned = 0;
1567     if (game_read(g, p, p1) >= 0) {
1568       adjourned = 1;
1569       delete_pending(pend);
1570     }
1571
1572     if (!adjourned) {           /* no adjourned game, so begin a new game */
1573
1574       if ((pend->category != NULL) && (pend->board_type != NULL)) {
1575         board = strdup(pend->category);
1576         category = strdup(pend->board_type);
1577       } 
1578
1579       delete_pending(pend);
1580
1581       if (create_new_match(g,p, p1, 0, 0, 0, 0, 0, ((board == NULL) ? "\0" : board), ((category == NULL) ? "\0" : category), 1,1) == COM_FAILED) {
1582         pprintf(p, "There was a problem creating the new match.\n");
1583         pprintf_prompt(p1, "There was a problem creating the new match.\n");
1584         game_remove(g);
1585
1586         if (board != NULL) {
1587           free (board);
1588           free (category);
1589         }
1590         return COM_OK;
1591       }
1592
1593       if (board != NULL) {
1594         free (board);
1595         free (category);
1596       }
1597
1598     } else {                    /* resume adjourned game */
1599       game_delete(p, p1);
1600
1601       sprintf(tmp, "{Game %d (%s vs. %s) Continuing %s %s simul.}\n", g + 1, pp->name, player_globals.parray[p1].name, rstr[game_globals.garray[g].rated], bstr[game_globals.garray[g].type]);
1602       pprintf(p, tmp);
1603       pprintf(p1, tmp);
1604
1605       game_globals.garray[g].white = p;
1606       game_globals.garray[g].black = p1;
1607       game_globals.garray[g].status = GAME_ACTIVE;
1608       game_globals.garray[g].startTime = tenth_secs();
1609       game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1610       game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1611       pp->game = g;
1612       pp->opponent = p1;
1613       pp->side = WHITE;
1614       player_globals.parray[p1].game = g;
1615       player_globals.parray[p1].opponent = p;
1616       player_globals.parray[p1].side = BLACK;
1617       send_boards(g);
1618     }
1619
1620     if (pp->simul_info == NULL) {
1621       pp->simul_info = (struct simul_info_t *) malloc(sizeof(struct simul_info_t));
1622       pp->simul_info->numBoards = 0;
1623       pp->simul_info->onBoard = 0;
1624       pp->simul_info->num_wins = pp->simul_info->num_draws
1625         = pp->simul_info->num_losses = 0;
1626     }
1627     num = pp->simul_info->numBoards;
1628     /*    pp->simul_info->results[num] = -1; */
1629     pp->simul_info->boards[num] = pp->game;
1630     pp->simul_info->numBoards++;
1631     if (pp->simul_info->numBoards > 1 &&
1632         pp->simul_info->onBoard >= 0)
1633       player_goto_board(p, pp->simul_info->onBoard);
1634     else
1635       pp->simul_info->onBoard = 0;
1636     return COM_OK;
1637   }
1638   if (find_pend(-1, p, PEND_SIMUL) != NULL) {
1639     pprintf(p, "You cannot be the simul giver and request to join another simul.\nThat would just be too confusing for me and you.\n");
1640     return COM_OK;
1641   }
1642   if (pp->simul_info != NULL) {
1643     if (pp->simul_info->numBoards) {
1644       pprintf(p, "You cannot be the simul giver and request to join another simul.\nThat would just be too confusing for me and you.\n");
1645       return COM_OK;
1646     }
1647   }
1648   if (pp->game >=0) {
1649     pprintf(p, "You are already playing a game.\n");
1650     return COM_OK;
1651   }
1652   if (!CheckPFlag(p1, PFLAG_SIMOPEN)) {
1653     pprintf_highlight(p, "%s", player_globals.parray[p1].name);
1654     pprintf(p, " is not open to receiving simul requests.\n");
1655     return COM_OK;
1656   }
1657   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards >= MAX_SIMUL)) {
1658     pprintf_highlight(p, "%s", player_globals.parray[p1].name);
1659     pprintf(p, " is already playing the maximum of %d boards.\n", MAX_SIMUL);
1660     return COM_OK;
1661   }
1662
1663 /* loon: checking for some crazy situations we can't allow :) */
1664
1665   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].game >=0) && (player_globals.parray[p1].simul_info->numBoards == 0)) {
1666     pprintf_highlight(p, "%s", player_globals.parray[p1].name);
1667     if (player_globals.parray[game_globals.garray[player_globals.parray[p1].game].white].simul_info->numBoards) {
1668       pprintf(p, " is playing in ");
1669       pprintf_highlight(p, "%s", player_globals.parray[player_globals.parray[p1].opponent].name);
1670       pprintf(p, "'s simul, and can't accept.\n");
1671     } else {
1672       pprintf(p, " can't begin a simul while playing a non-simul game.\n");
1673     }
1674     return COM_OK;
1675   }
1676
1677   g = game_new();               /* Check if an adjourned untimed game */
1678   adjourned = ((game_read(g, p, p1) < 0) && (game_read(g, p1, p) < 0)) ? 0 : 1;
1679   if (adjourned) {
1680     if (!(game_globals.garray[g].type == TYPE_UNTIMED))
1681       adjourned = 0;
1682   }
1683   game_remove(g);
1684
1685   pend = add_request(p, p1, PEND_SIMUL);
1686
1687   if ((param[1].type == TYPE_WORD) && (param[2].type == TYPE_WORD)) {
1688
1689     sprintf(fname, "%s/%s/%s", BOARD_DIR, param[1].val.word , param[2].val.word);
1690     if (!file_exists(fname)) {
1691       pprintf(p, "No such category/board: %s/%s\n", param[1].val.word , param[2].val.word);
1692       return COM_OK;
1693     }
1694     pend->category = strdup(param[1].val.word);
1695     pend->board_type = strdup(param[2].val.word);
1696   } else {
1697     pend->category = NULL;
1698     pend->board_type = NULL;
1699   }
1700  
1701   pprintf(p1, "\n");
1702   pprintf_highlight(p1, "%s", pp->name);
1703   if (adjourned) {
1704     pprintf_prompt(p1, " requests to continue an adjourned simul game.\n");
1705     pprintf(p, "Request to resume simul sent. Adjourned game found.\n");
1706   } else {
1707     if (pend->category == NULL)
1708       pprintf_prompt(p1, " requests to join a simul match with you.\n");
1709     else
1710       pprintf_prompt(p1, " requests to join a %s %s simul match with you.\n",
1711                 pend->category,pend->board_type);
1712     pprintf(p, "Simul match request sent.\n");
1713   }
1714   return COM_OK;
1715 }
1716
1717 int com_goboard(int p, param_list param)
1718 {
1719   struct player *pp = &player_globals.parray[p];
1720   int on, g, p1, gamenum;
1721
1722   if (pp->simul_info == NULL) {
1723     pprintf(p, "You are not giving a simul.\n");
1724     return COM_OK;
1725   }
1726
1727   if (!pp->simul_info->numBoards) {
1728     pprintf(p, "You are not giving a simul.\n");
1729     return COM_OK;
1730   }
1731
1732   if (param[0].type == TYPE_WORD) {
1733
1734     p1 = player_find_part_login(param[0].val.word);
1735     if (p1 < 0) {
1736       pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
1737       return COM_OK;
1738     }
1739     if (p == p1) {
1740       pprintf(p, "You can't goboard yourself!\n");
1741       return COM_OK;
1742     }
1743
1744     gamenum = player_globals.parray[p1].game;
1745     if (gamenum < 0) {
1746       pprintf (p,"%s is not playing a game.\n", player_globals.parray[p1].login);
1747       return COM_OK;
1748     }
1749
1750   } else { 
1751     gamenum = param[0].val.integer - 1;
1752     if (gamenum < 0)
1753       gamenum = 0;
1754   }
1755
1756   on = pp->simul_info->onBoard;
1757   g = pp->simul_info->boards[on];
1758   if (gamenum == g) {
1759     pprintf(p, "You are already at that board!\n");
1760     return COM_OK;
1761   }
1762   if (pp->simul_info->numBoards > 1) {
1763     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1764     if (player_goto_simulgame_bynum(p, gamenum) !=-1) {
1765       if (g >= 0) {
1766         pprintf(game_globals.garray[g].black, "\n");
1767         pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
1768         pprintf_prompt(game_globals.garray[g].black, " has moved away from your board.\n");
1769       }
1770     } else
1771     pprintf(p, "You are not playing that game/person.\n");
1772   } else
1773     pprintf(p, "You are only playing one board!\n");
1774   return COM_OK;
1775 }
1776
1777 int com_simnext(int p, param_list param)
1778 {
1779   struct player *pp = &player_globals.parray[p];
1780   int on, g;
1781
1782   if (pp->simul_info == NULL) {
1783     pprintf(p, "You are not giving a simul.\n");
1784     return COM_OK;
1785   }
1786
1787   if (!pp->simul_info->numBoards) {
1788     pprintf(p, "You are not giving a simul.\n");
1789     return COM_OK;
1790   }
1791
1792   if (pp->simul_info->numBoards > 1) {
1793     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1794     on = pp->simul_info->onBoard;
1795     g = pp->simul_info->boards[on];
1796     if (g >= 0) {
1797       pprintf(game_globals.garray[g].black, "\n");
1798       pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
1799       pprintf_prompt(game_globals.garray[g].black, " is moving away from your board.\n");
1800       player_goto_next_board(p);
1801     }
1802   } else
1803     pprintf(p, "You are only playing one board!\n");
1804   return COM_OK;
1805 }
1806
1807 int com_simprev(int p, param_list param)
1808 {
1809   struct player *pp = &player_globals.parray[p];
1810   int on, g;
1811
1812   if (pp->simul_info == NULL) {
1813     pprintf(p, "You are not giving a simul.\n");
1814     return COM_OK;
1815   }
1816
1817   if (!pp->simul_info->numBoards) {
1818     pprintf(p, "You are not giving a simul.\n");
1819     return COM_OK;
1820   }
1821   if (pp->simul_info->numBoards > 1) {
1822     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1823     on = pp->simul_info->onBoard;
1824     g = pp->simul_info->boards[on];
1825     if (g >= 0) {
1826       pprintf(game_globals.garray[g].black, "\n");
1827       pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
1828       pprintf_prompt(game_globals.garray[g].black, " is moving back to the previous board.\n");
1829     }
1830     player_goto_prev_board(p);
1831   } else
1832     pprintf(p, "You are only playing one board!\n");
1833   return COM_OK;
1834 }
1835
1836 int com_simgames(int p, param_list param)
1837 {
1838   int p1 = p;
1839
1840   if (param[0].type == TYPE_WORD) {
1841     if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
1842       pprintf(p, "No player named %s is logged in.\n", param[0].val.word);
1843       return COM_OK;
1844     }
1845   }
1846   if (p1 == p)
1847     pprintf(p, "You are playing %d simultaneous games.\n",
1848             player_num_active_boards(p1));
1849   else
1850     pprintf(p, "%s is playing %d simultaneous games.\n", player_globals.parray[p1].name,
1851             player_num_active_boards(p1));
1852   return COM_OK;
1853 }
1854
1855 int com_simpass(int p, param_list param)
1856 {
1857   struct player *pp = &player_globals.parray[p];
1858   int g, p1, on;
1859
1860   if (!pIsPlaying(p))
1861     return COM_OK;
1862
1863   g = pp->game;
1864   p1 = game_globals.garray[g].white;
1865
1866   if (player_globals.parray[p1].simul_info == NULL) {
1867     pprintf(p, "You are not participating in a simul.\n");
1868     return COM_OK;
1869   }
1870
1871   if (!player_globals.parray[p1].simul_info->numBoards) {
1872     pprintf(p, "You are not participating in a simul.\n");
1873     return COM_OK;
1874   }
1875   if (p == p1) {
1876     pprintf(p, "You are the simul holder and cannot pass!\n");
1877     return COM_OK;
1878   }
1879   if (player_num_active_boards(p1) == 1) {
1880     pprintf(p, "This is the only game, so passing is futile.\n");
1881     return COM_OK;
1882   }
1883   on = player_globals.parray[p1].simul_info->onBoard;
1884   if (player_globals.parray[p1].simul_info->boards[on] != g) {
1885     pprintf(p, "You cannot pass until the simul holder arrives!\n");
1886     return COM_OK;
1887   }
1888   if (game_globals.garray[g].passes >= MAX_SIMPASS) {
1889     Bell (p);
1890     pprintf(p, "You have reached your maximum of %d pass(es).\n", MAX_SIMPASS);
1891     pprintf(p, "Please move IMMEDIATELY!\n");
1892     pprintf_highlight(p1, "%s", pp->name);
1893     pprintf_prompt(p1, " tried to pass, but is out of passes.\n");
1894     return COM_OK;
1895   }
1896   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1897
1898   game_globals.garray[g].passes++;
1899   pprintf(p, "You have passed and have %d pass(es) left.\n",
1900           (MAX_SIMPASS - game_globals.garray[g].passes));
1901   pprintf_highlight(p1, "%s", pp->name);
1902   pprintf_prompt(p1, " has decided to pass and has %d pass(es) left.\n",
1903                  (MAX_SIMPASS - game_globals.garray[g].passes));
1904   player_goto_next_board(p1);
1905   return COM_OK;
1906 }
1907
1908 int com_simabort(int p, param_list param)
1909 {
1910   struct player *pp = &player_globals.parray[p];
1911
1912   if (pp->simul_info == NULL) {
1913     pprintf(p, "You are not giving a simul.\n");
1914     return COM_OK;
1915   }
1916
1917   if (!pp->simul_info->numBoards) {
1918     pprintf(p, "You are not giving a simul.\n");
1919     return COM_OK;
1920   }
1921   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1922   game_ended(pp->simul_info->boards[pp->simul_info->onBoard],
1923              WHITE, END_ABORT);
1924   return COM_OK;
1925 }
1926
1927 int com_simallabort(int p, param_list param)
1928 {
1929   struct player *pp = &player_globals.parray[p];
1930   int i;
1931
1932   if (pp->simul_info == NULL) {
1933     pprintf(p, "You are not giving a simul.\n");
1934     return COM_OK;
1935   }
1936
1937   if (!pp->simul_info->numBoards) {
1938     pprintf(p, "You are not giving a simul.\n");
1939     return COM_OK;
1940   }
1941
1942   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1943   for (i = 0; i < pp->simul_info->numBoards; i++)
1944     if (pp->simul_info->boards[i] >= 0)
1945       game_ended(pp->simul_info->boards[i],
1946                  WHITE, END_ABORT);
1947
1948   return COM_OK;
1949 }
1950
1951 int com_simadjourn(int p, param_list param)
1952 {
1953   struct player *pp = &player_globals.parray[p];
1954
1955   if (pp->simul_info == NULL) {
1956     pprintf(p, "You are not giving a simul.\n");
1957     return COM_OK;
1958   }
1959
1960   if (!pp->simul_info->numBoards) {
1961     pprintf(p, "You are not giving a simul.\n");
1962     return COM_OK;
1963   }
1964   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1965   game_ended(pp->simul_info->boards[pp->simul_info->onBoard],
1966              WHITE, END_ADJOURN);
1967   return COM_OK;
1968 }
1969
1970 int com_simalladjourn(int p, param_list param)
1971 {
1972   struct player *pp = &player_globals.parray[p];
1973   int i;
1974
1975   if (pp->simul_info == NULL) {
1976     pprintf(p, "You are not giving a simul.\n");
1977     return COM_OK;
1978   }
1979
1980   if (!pp->simul_info->numBoards) {
1981     pprintf(p, "You are not giving a simul.\n");
1982     return COM_OK;
1983   }
1984   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1985   for (i = 0; i < pp->simul_info->numBoards; i++)
1986     if (pp->simul_info->boards[i] >= 0)
1987       game_ended(pp->simul_info->boards[i],
1988                  WHITE, END_ADJOURN);
1989
1990   return COM_OK;
1991 }
1992
1993 int com_moretime(int p, param_list param)
1994 {
1995   struct player *pp = &player_globals.parray[p];
1996   int g, increment;
1997
1998   if ((pp->game >=0) &&((game_globals.garray[pp->game].status == GAME_EXAMINE) ||
1999         (game_globals.garray[pp->game].status == GAME_SETUP))) {
2000     pprintf(p, "You cannot use moretime in an examined game.\n");
2001     return COM_OK;
2002   }
2003   increment = param[0].val.integer;
2004   if (increment <= 0) {
2005     pprintf(p, "Moretime requires an integer value greater than zero.\n");
2006     return COM_OK;
2007   }
2008   if (!pIsPlaying(p))
2009     return COM_OK;
2010  
2011   if (increment > 600) {
2012     pprintf(p, "Moretime has a maximum limit of 600 seconds.\n");
2013     increment = 600;
2014   }
2015   g = pp->game;
2016   if (game_globals.garray[g].white == p) {
2017     game_globals.garray[g].bTime += increment * 10;
2018     game_globals.garray[g].bRealTime += increment * 10 * 100;
2019     pprintf(p, "%d seconds were added to your opponents clock\n",
2020             increment);
2021     pprintf_prompt(pp->opponent,
2022                    "\nYour opponent has added %d seconds to your clock.\n",
2023                    increment);
2024   }
2025   if (game_globals.garray[g].black == p) {
2026     game_globals.garray[g].wTime += increment * 10;;
2027     game_globals.garray[g].wRealTime += increment * 10 * 100;
2028     pprintf(p, "%d seconds were added to your opponents clock\n",
2029             increment);
2030     pprintf_prompt(pp->opponent,
2031                    "\nYour opponent has added %d seconds to your clock.\n",
2032                    increment);
2033   }
2034   return COM_OK;
2035 }
2036