a0dba3fe1f2aae1d89b8f5fd97e02ab9bcdff586
[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         return 1;
418       if (g->moveList[i].fromFile == ALG_DROP)
419         return 0;
420       f = g->moveList[i].fromFile;
421       r = g->moveList[i].fromRank;
422     }
423   }
424 #endif
425   return 0;
426 }
427
428 int pIsPlaying (int p)
429 {
430         struct player *pp = &player_globals.parray[p];
431         int g = pp->game;
432         int p1 = pp->opponent;
433         
434         if (g < 0 || game_globals.garray[g].status != GAME_ACTIVE) {
435                 pprintf (p, "You are not playing a game.\n");
436                 return 0;
437         } 
438
439         if (game_globals.garray[g].white != p && game_globals.garray[g].black != p) {
440                 /* oh oh; big bad game bug. */
441                 d_printf("BUG:  Player %s playing game %d according to player_globals.parray,"
442                          "\n      but not according to game_globals.garray.\n", pp->name, g+1);
443                 pprintf (p, "Disconnecting you from game number %d.\n", g+1);
444                 pp->game = -1;
445                 if (p1 >= 0 && player_globals.parray[p1].game == g
446                     && game_globals.garray[g].white != p1 && game_globals.garray[g].black != p1) {
447                         pprintf (p1, "Disconnecting you from game number %d.\n", g+1);
448                         player_globals.parray[p1].game = -1;
449                 }
450                 return 0;
451         }
452         return 1;
453 }
454
455 /* add clock increments */
456 static void game_add_increment(struct player *pp, struct game *gg)
457 {
458         /* no update on first move */
459         if (gg->game_state.moveNum == 1) return;
460
461         if (net_globals.con[pp->socket]->timeseal) {    /* does he use timeseal? */
462                 if (pp->side == WHITE) {
463                         gg->wRealTime += gg->wIncrement * 100;
464                         gg->wTime = gg->wRealTime / 100;        /* remember to conv to
465                                                                                                    tenth secs */
466                 } else if (pp->side == BLACK) {
467                         gg->bRealTime += gg->bIncrement * 100;  /* conv to ms */
468                         gg->bTime = gg->bRealTime / 100;        /* remember to conv to
469                                                                                                    tenth secs */
470                 }
471         } else {
472                 if (gg->game_state.onMove == BLACK) {
473                         gg->bTime += gg->bIncrement;
474                 }
475                 if (gg->game_state.onMove == WHITE) {
476                         gg->wTime += gg->wIncrement;
477                 }
478         }
479 }
480
481 /* updates clocks for a game with timeseal */
482 void timeseal_update_clocks(struct player *pp, struct game *gg)
483 {
484         /* no update on first move */
485         if (gg->game_state.moveNum == 1) return;
486
487         if (pp->side == WHITE) {
488                 gg->wLastRealTime = gg->wRealTime;
489                 gg->wTimeWhenMoved = net_globals.con[pp->socket]->time;
490                 if (((gg->wTimeWhenMoved - gg->wTimeWhenReceivedMove) < 0) ||
491                     (gg->wTimeWhenReceivedMove == 0)) {
492                         /* might seem weird - but could be caused by a person moving BEFORE
493                            he receives the board pos (this is possible due to lag) but it's
494                            safe to say he moved in 0 secs :-) */
495                         gg->wTimeWhenReceivedMove = gg->wTimeWhenMoved;
496                 } else {
497                         gg->wRealTime -= gg->wTimeWhenMoved - gg->wTimeWhenReceivedMove;
498                 }
499         } else if (pp->side == BLACK) {
500                 gg->bLastRealTime = gg->bRealTime;
501                 gg->bTimeWhenMoved = net_globals.con[pp->socket]->time;
502                 if (((gg->bTimeWhenMoved - gg->bTimeWhenReceivedMove) < 0) ||
503                     (gg->bTimeWhenReceivedMove == 0)) {
504                         /* might seem weird - but could be caused by a person moving BEFORE
505                            he receives the board pos (this is possible due to lag) but it's
506                            safe to say he moved in 0 secs :-) */
507                         gg->bTimeWhenReceivedMove = gg->bTimeWhenMoved;
508                 } else {
509                         gg->bRealTime -= gg->bTimeWhenMoved - gg->bTimeWhenReceivedMove;
510                 }
511         }
512 }
513
514
515 void process_move(int p, char *command)
516 {
517   struct player *pp = &player_globals.parray[p];
518   struct game *gg;
519   int g, result, len, i;
520   struct move_t move;
521   unsigned now = 0;
522
523   if (pp->game < 0) {
524     pprintf(p, "You are not playing or examining a game.\n");
525     return;
526   }
527   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
528
529   g = pp->game;
530   gg = &game_globals.garray[g];
531
532   if (gg->status == GAME_SETUP) {
533     if (!attempt_drop(p,g,command)) {
534       pprintf(p, "You are still setting up the position.\n");
535       pprintf(p, "Type: 'setup done' when you are finished editing.\n");
536     } else
537     send_board_to(g, p); 
538     return;
539   }
540
541   if (gg->status != GAME_EXAMINE) {
542     if (!pIsPlaying(p)) return;
543
544     if (pp->side != gg->game_state.onMove) {
545       pprintf(p, "It is not your move.\n");
546       return;
547     }
548     if (gg->clockStopped) {
549       pprintf(p, "Game clock is paused, use \"unpause\" to resume.\n");
550       return;
551     }
552   }
553   if ((len = strlen(command)) > 1) {
554     if (command[len - 2] == '=') {
555 printf("promo '%s'\n", command);
556       switch (tolower(command[strlen(command) - 1])) {
557       case 'n':
558         pp->promote = KNIGHT;
559         break;
560       case 'b':
561         pp->promote = BISHOP;
562         break;
563       case 'r':
564         pp->promote = ROOK;
565         break;
566       case 'a':
567         pp->promote = CARDINAL;
568         break;
569       case 'c':
570         pp->promote = MARSHALL;
571         break;
572       case 'm':
573         pp->promote = MAN;
574         break;
575       case 'q':
576         pp->promote = QUEEN;
577         break;
578       // courier promotion
579       case 'f':
580         pp->promote = FERZ2;
581         break;
582       // Superchess promotions
583       case 'e':
584         pp->promote = EMPRESS;
585         break;
586       case 's':
587         pp->promote = PRINCESS;
588         break;
589       case 'v':
590         pp->promote = CENTAUR;
591         break;
592       case 'w':
593         pp->promote = WOODY;
594         break;
595       case 'o':
596         pp->promote = SQUIRREL;
597         break;
598       case 'g':
599         pp->promote = MASTODON;
600         break;
601       default:
602         pprintf(p, "Don't understand that move.\n");
603         return;
604         break;
605       }
606     }
607   }
608
609   switch (parse_move(command, &gg->game_state, &move, pp->promote)) {
610   case MOVE_ILLEGAL:
611     pprintf(p, "Illegal move.\n");
612     return;
613     break;
614   case MOVE_AMBIGUOUS:
615     pprintf(p, "Ambiguous move.\n");
616     return;
617     break;
618   default:
619     break;
620   }
621
622   if (gg->status == GAME_EXAMINE) {
623     gg->numHalfMoves++;
624     if (gg->numHalfMoves > gg->examMoveListSize) {
625       gg->examMoveListSize += 20;       /* Allocate 20 moves at a time */
626       gg->examMoveList = (struct move_t *) realloc(gg->examMoveList, sizeof(struct move_t) * gg->examMoveListSize);
627     }
628     result = execute_move(&gg->game_state, &move, 1);
629     move.atTime = now;
630     move.tookTime = 0;
631     MakeFENpos(g, move.FENpos);
632     gg->examMoveList[gg->numHalfMoves - 1] = move;
633     /* roll back time */
634     if (gg->game_state.onMove == WHITE) {
635       gg->wTime += (gg->lastDecTime - gg->lastMoveTime);
636     } else {
637       gg->bTime += (gg->lastDecTime - gg->lastMoveTime);
638     }
639     now = tenth_secs();
640     if (gg->numHalfMoves == 0)
641       gg->timeOfStart = now;
642     gg->lastMoveTime = now;
643     gg->lastDecTime = now;
644
645   } else {                      /* real game */
646     i = pp->opponent;
647     if ((player_globals.parray[i].simul_info != NULL) && (player_globals.parray[i].simul_info->numBoards &&
648          (player_globals.parray[i].simul_info->boards[player_globals.parray[i].simul_info->onBoard] != g))) {
649       pprintf(p, "It isn't your turn: wait until the simul giver is at your board.\n");
650       return;
651     }
652     if (net_globals.con[pp->socket]->timeseal) {        /* does he use timeseal? */
653             timeseal_update_clocks(pp, &game_globals.garray[g]);
654     }
655     /* we need to reset the opp's time for receiving the board since the
656        timeseal decoder only alters the time if it's 0 Otherwise the time
657        would be changed if the player did a refresh which would screw up
658        the timings */
659     if (pp->side == WHITE) {
660       gg->bTimeWhenReceivedMove = 0;
661     } else {
662       gg->wTimeWhenReceivedMove = 0;
663     }
664
665     game_update_time(g);
666     game_add_increment(pp, gg);
667
668     /* Do the move */
669     gg->numHalfMoves++;
670     if (gg->numHalfMoves > gg->moveListSize) {
671       gg->moveListSize += 20;   /* Allocate 20 moves at a time */
672       gg->moveList = (struct move_t *) realloc(gg->moveList, sizeof(struct move_t) * gg->moveListSize);
673     }
674     result = execute_move(&gg->game_state, &move, 1);
675     if (result == MOVE_OK && (gg->link >= 0 || gg->game_state.holdings) && move.pieceCaptured != NOPIECE) {
676       /* transfer captured piece to partner */
677       /* check if piece reverts to a pawn */
678       int victim = move.pieceCaptured, partner = gg->link;
679       // [HGM] zh: if not Bughouse, the game_state.holdings field decides what happens
680       if(gg->link < 0) { 
681         partner = g; // pieces stay with current board
682         if(gg->game_state.holdings == -1) victim ^= WHITE|BLACK; // flip color
683       } 
684       if (was_promoted(&game_globals.garray[g], move.toFile, move.toRank))
685         update_holding(partner, colorval(victim) | PAWN); // [HGM] todo: for Shogi we would have to demote differently!
686       else
687         update_holding(partner, victim);
688     }
689     now = tenth_secs();
690     move.atTime = now;
691     if (gg->numHalfMoves > 1) {
692       move.tookTime = move.atTime - gg->lastMoveTime;
693     } else {
694       move.tookTime = move.atTime - gg->startTime;
695     }
696     gg->lastMoveTime = now;
697     gg->lastDecTime = now;
698     move.wTime = gg->wTime;
699     move.bTime = gg->bTime;
700
701     if (net_globals.con[pp->socket]->timeseal) {        /* does he use timeseal? */
702       if (pp->side == WHITE) {
703         move.tookTime = (game_globals.garray[pp->game].wTimeWhenMoved -
704                          game_globals.garray[pp->game].wTimeWhenReceivedMove) / 100;
705       } else {
706         move.tookTime = (game_globals.garray[pp->game].bTimeWhenMoved -
707                          game_globals.garray[pp->game].bTimeWhenReceivedMove) / 100;
708       }
709     }
710
711     if (gg->numHalfMoves <= 2) {
712             move.tookTime = 0;
713     }
714
715     MakeFENpos(g, move.FENpos);
716     gg->moveList[gg->numHalfMoves - 1] = move;
717   }
718
719   send_boards(g);
720
721   if (result == MOVE_ILLEGAL) {
722     pprintf(p, "Internal error, illegal move accepted!\n");
723   }
724   if ((result == MOVE_OK) && (gg->status == GAME_EXAMINE)) {
725     int p1;
726
727     for (p1 = 0; p1 < player_globals.p_num; p1++) {
728       if (player_globals.parray[p1].status != PLAYER_PROMPT)
729         continue;
730       if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
731         pprintf(p1, "%s moves: %s\n", pp->name, move.algString);
732       }
733     }
734   }
735   if (result == MOVE_CHECKMATE) {
736     if (gg->status == GAME_EXAMINE) {
737       int p1;
738
739       for (p1 = 0; p1 < player_globals.p_num; p1++) {
740         if (player_globals.parray[p1].status != PLAYER_PROMPT)
741           continue;
742         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
743           pprintf(p1, "%s has been checkmated.\n",
744                   (CToggle(gg->game_state.onMove) == BLACK) ? "White" : "Black");
745         }
746       }
747     } else {
748       game_ended(g, CToggle(gg->game_state.onMove), END_CHECKMATE);
749     }
750   }
751   if (result == MOVE_STALEMATE) {
752     if (gg->status == GAME_EXAMINE) {
753       int p1;
754
755       for (p1 = 0; p1 < player_globals.p_num; p1++) {
756         if (player_globals.parray[p1].status != PLAYER_PROMPT)
757           continue;
758         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
759           pprintf(p1, "Stalemate.\n");
760         }
761       }
762     } else {
763       game_ended(g, CToggle(gg->game_state.onMove), END_STALEMATE);
764     }
765   }
766   if (result == MOVE_NOMATERIAL) {
767     if (gg->status == GAME_EXAMINE) {
768       int p1;
769
770       for (p1 = 0; p1 < player_globals.p_num; p1++) {
771         if (player_globals.parray[p1].status != PLAYER_PROMPT)
772           continue;
773         if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
774           pprintf(p1, "No mating material.\n");
775         }
776       }
777     } else {
778       game_ended(g, CToggle(gg->game_state.onMove), END_NOMATERIAL);
779     }
780   }
781   if (result == MOVE_BARE) {
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, "%s bared.\n",
790                   (gg->game_state.onMove == BLACK) ? "White" : "Black");
791         }
792       }
793     } else {
794       game_ended(g, gg->game_state.onMove, END_BARE);
795     }
796   }
797 }
798
799 int com_resign(int p, param_list param)
800 {
801   struct player *pp = &player_globals.parray[p];
802   int g, o, oconnected;
803
804   if (param[0].type == TYPE_NULL) {
805     g = pp->game;
806     if (!pIsPlaying(p))
807       return COM_OK;
808     else {
809       decline_withdraw_offers(p, -1, -1, DO_DECLINE);
810       game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
811     }
812   } else if (FindPlayer(p, param[0].val.word, &o, &oconnected)) {
813     g = game_new();
814     if (game_read(g, p, o) < 0) {
815       if (game_read(g, o, p) < 0) {
816         pprintf(p, "You have no stored game with %s\n", player_globals.parray[o].name);
817         if (!oconnected)
818           player_remove(o);
819         return COM_OK;
820       } else {
821         game_globals.garray[g].white = o;
822         game_globals.garray[g].black = p;
823       }
824     } else {
825       game_globals.garray[g].white = p;
826       game_globals.garray[g].black = o;
827     }
828     pprintf(p, "You resign your stored game with %s\n", player_globals.parray[o].name);
829     pcommand(p, "message %s I have resigned our stored game \"%s vs. %s.\"",
830              player_globals.parray[o].name,
831              player_globals.parray[game_globals.garray[g].white].name,
832              player_globals.parray[game_globals.garray[g].black].name);
833     game_delete(game_globals.garray[g].white, game_globals.garray[g].black);
834     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
835     if (!oconnected)
836       player_remove(o);
837   }
838   return COM_OK;
839 }
840
841 static int Check50MoveRule (int p, int g)
842 {
843   int num_reversible = game_globals.garray[g].numHalfMoves;
844
845   if (game_globals.garray[g].game_state.lastIrreversable >= 0) {
846     num_reversible -= game_globals.garray[g].game_state.lastIrreversable;
847   }
848   if (num_reversible > 99) {
849     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_50MOVERULE);
850     return 1;
851   }
852   return 0;
853 }
854
855 static char *GetFENpos (int g, int half_move)
856 {
857   if (half_move < 0)
858     return game_globals.garray[g].FENstartPos;
859   else return game_globals.garray[g].moveList[half_move].FENpos;
860 }
861
862 static int CheckRepetition (int p, int g)
863 {
864   struct player *pp = &player_globals.parray[p];
865   struct pending* pend;
866   int move_num;
867   int flag1 = 1, flag2 = 1;
868   char *pos1 = GetFENpos (g, game_globals.garray[g].numHalfMoves - 1);
869   char *pos2 = GetFENpos (g, game_globals.garray[g].numHalfMoves);
870   char *pos;
871
872   if (game_globals.garray[g].numHalfMoves < 8)  /* can't have three repeats any quicker. */
873     return 0;
874
875   for (move_num = game_globals.garray[g].game_state.lastIrreversable;
876        move_num < game_globals.garray[g].numHalfMoves - 1; move_num++) {
877     pos = GetFENpos (g, move_num);
878     if (strlen(pos1) == strlen(pos) && !strcmp(pos1, pos))
879       flag1++;
880     if (strlen(pos2) == strlen(pos) && !strcmp(pos2, pos))
881       flag2++;
882   }
883   if (flag1 >= 3 || flag2 >= 3) {
884     if ((pend = find_pend(pp->opponent, p, PEND_DRAW)) != NULL) {
885       delete_pending(pend);
886       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
887     }
888     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_REPETITION);
889     return 1;
890   }
891   else return 0;
892 }
893
894 int com_draw(int p, param_list param)
895 {
896   struct player *pp = &player_globals.parray[p];
897   struct pending* pend;
898   int p1, g = pp->game;
899
900   if (!pIsPlaying(p)) {
901     return COM_OK;
902   }
903   if (Check50MoveRule (p, g) || CheckRepetition(p, g)) {
904     return COM_OK;
905   }
906   p1 = pp->opponent;
907
908   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards &&
909         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] != g)) {
910     pprintf(p, "You can only make requests when the simul player is at your board.\n");
911     return COM_OK;
912   }
913
914   if ((pend = (find_pend(pp->opponent, p, PEND_DRAW))) != NULL) {
915     delete_pending(pend);
916     decline_withdraw_offers(p, -1, -1,DO_DECLINE);
917     game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_AGREEDDRAW);
918   } else {
919     pprintf(pp->opponent, "\n");
920     pprintf_highlight(pp->opponent, "%s", pp->name);
921     pprintf_prompt(pp->opponent, " offers you a draw.\n");
922     pprintf(p, "Draw request sent.\n");
923     add_request(p, pp->opponent, PEND_DRAW);
924   }
925   return COM_OK;
926 }
927
928 int com_pause(int p, param_list param)
929 {
930   struct player *pp = &player_globals.parray[p];
931   int g, now;
932   struct pending* pend;
933
934   if (!pIsPlaying(p)) {
935     return COM_OK;
936   }
937   g = pp->game;
938   if (game_globals.garray[g].wTime == 0) {
939     pprintf(p, "You can't pause untimed games.\n");
940     return COM_OK;
941   }
942   if (game_globals.garray[g].clockStopped) {
943     pprintf(p, "Game is already paused, use \"unpause\" to resume.\n");
944     return COM_OK;
945   }
946   if ((pend = find_pend(pp->opponent, p, PEND_PAUSE)) != NULL) {
947     delete_pending(pend);
948     game_globals.garray[g].clockStopped = 1;
949     /* Roll back the time */
950     if (game_globals.garray[g].game_state.onMove == WHITE) {
951       game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
952     } else {
953       game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
954     }
955     now = tenth_secs();
956     if (game_globals.garray[g].numHalfMoves == 0)
957       game_globals.garray[g].timeOfStart = now;
958     game_globals.garray[g].lastMoveTime = now;
959     game_globals.garray[g].lastDecTime = now;
960     send_boards(g);
961     pprintf_prompt(pp->opponent, "\n%s accepted pause. Game clock paused.\n",
962                    pp->name);
963     pprintf(p, "Game clock paused.\n");
964   } else {
965     pprintf(pp->opponent, "\n");
966     pprintf_highlight(pp->opponent, "%s", pp->name);
967     pprintf_prompt(pp->opponent, " requests to pause the game.\n");
968     pprintf(p, "Pause request sent.\n");
969     add_request(p, pp->opponent, PEND_PAUSE);
970   }
971   return COM_OK;
972 }
973
974 int com_unpause(int p, param_list param)
975 {
976   struct player *pp = &player_globals.parray[p];
977   int g;
978   int now;
979   struct pending* pend;
980
981   if (!pIsPlaying(p)) {
982     return COM_OK;
983   }
984
985   g = pp->game;
986
987   if (!game_globals.garray[g].clockStopped) {
988     pprintf(p, "Game is not paused.\n");
989     return COM_OK;
990   }
991   if ((pend = find_pend(pp->opponent, p, PEND_UNPAUSE)) != NULL) {
992     delete_pending(pend);
993     game_globals.garray[g].clockStopped = 0;
994     now = tenth_secs();
995     if (game_globals.garray[g].numHalfMoves == 0)
996       game_globals.garray[g].timeOfStart = now;
997     game_globals.garray[g].lastMoveTime = now;
998     game_globals.garray[g].lastDecTime = now;
999     send_boards(g);
1000     pprintf(p, "Game clock resumed.\n");
1001     pprintf_prompt(pp->opponent, "\nGame clock resumed.\n");
1002   } else {
1003     pprintf(pp->opponent, "\n");
1004     pprintf_highlight(pp->opponent, "%s", pp->name);
1005     pprintf_prompt(pp->opponent, " requests to unpause the game.\n");
1006     pprintf(p, "Unpause request sent.\n");
1007     add_request(p, pp->opponent, PEND_UNPAUSE);
1008   }
1009   return COM_OK;
1010 }
1011
1012 int com_abort(int p, param_list param)
1013 {
1014   struct player *pp = &player_globals.parray[p];
1015   struct pending* pend;
1016   int p1, g, myColor, yourColor, myGTime, yourGTime;
1017   int courtesyOK = 1;
1018
1019   g = pp->game;
1020   if (!pIsPlaying(p))
1021     return COM_OK;
1022
1023   p1 = pp->opponent;
1024   if (p == game_globals.garray[g].white) {
1025     myColor = WHITE;
1026     yourColor = BLACK;
1027     myGTime = game_globals.garray[g].wTime;
1028     yourGTime = game_globals.garray[g].bTime;
1029   } else {
1030     myColor = BLACK;
1031     yourColor = WHITE;
1032     myGTime = game_globals.garray[g].bTime;
1033     yourGTime = game_globals.garray[g].wTime;
1034   }
1035   if ((player_globals.parray[p1].simul_info != NULL) && 
1036      (player_globals.parray[p1].simul_info->numBoards &&
1037         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] != g)) {
1038     pprintf(p, "You can only make requests when the simul player is at your board.\n");
1039     return COM_OK;
1040   }
1041   if ((pend = find_pend(p1, p, PEND_ABORT)) != NULL) {
1042     delete_pending(pend);
1043     decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1044     game_ended(g, yourColor, END_ABORT);
1045   } else {
1046     game_update_time(g);
1047
1048     if (net_globals.con[pp->socket]->timeseal
1049         && game_globals.garray[g].game_state.onMove == myColor
1050         && game_globals.garray[g].flag_pending == FLAG_ABORT) {
1051       /* It's my move, opponent has asked for abort; I lagged out,
1052          my timeseal prevented courtesyabort, and I am sending an abort
1053          request before acknowledging (and processing) my opponent's
1054          courtesyabort.  OK, let's abort already :-). */
1055       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1056       game_ended(g, yourColor, END_ABORT);
1057     }
1058
1059     if (net_globals.con[player_globals.parray[p1].socket]->timeseal) {  /* opp uses timeseal? */
1060
1061       int yourRealTime = (myColor == WHITE  ?  game_globals.garray[g].bRealTime
1062                                             :  game_globals.garray[g].wRealTime);
1063       if (myGTime > 0 && yourGTime <= 0 && yourRealTime > 0) {
1064         /* Override courtesyabort; opponent still has time.  Check for lag. */
1065         courtesyOK = 0;
1066
1067         if (game_globals.garray[g].game_state.onMove != myColor
1068             && game_globals.garray[g].flag_pending != FLAG_CHECKING) {
1069           /* Opponent may be lagging; let's ask. */
1070           game_globals.garray[g].flag_pending = FLAG_ABORT;
1071           game_globals.garray[g].flag_check_time = time(0);
1072           pprintf(p, "Opponent has timeseal; trying to courtesyabort.\n");
1073           pprintf(p1, "\n[G]\n");
1074           return COM_OK;
1075         }
1076       }
1077     }
1078
1079     if (myGTime > 0 && yourGTime <= 0 && courtesyOK) {
1080       /* player wants to abort + opponent is out of time = courtesyabort */
1081       pprintf(p, "Since you have time, and your opponent has none, the game has been aborted.");
1082       pprintf(p1, "Your opponent has aborted the game rather than calling your flag.");
1083       decline_withdraw_offers(p, -1, -1, DO_DECLINE);
1084       game_ended(g, myColor, END_COURTESY);
1085     } else {
1086       pprintf(p1, "\n");
1087       pprintf_highlight(p1, "%s", pp->name);
1088       pprintf(p1, " would like to abort the game; ");
1089       pprintf_prompt(p1, "type \"abort\" to accept.\n");
1090       pprintf(p, "Abort request sent.\n");
1091       add_request(p, p1, PEND_ABORT);
1092     }
1093   }
1094   return COM_OK;
1095 }
1096
1097 static int player_has_mating_material(struct game_state_t *gs, int color)
1098 {
1099   int i, j;
1100   int piece;
1101   int minor_pieces = 0;
1102
1103   for (i = 0; i < gs->files; i++)
1104     for (j = 0; j < gs->ranks; j++) {
1105       piece = gs->board[i][j];
1106       switch (piecetype(piece)) {
1107       case BISHOP:
1108       case KNIGHT:
1109         if (iscolor(piece, color))
1110           minor_pieces++;
1111         break;
1112       case KING:
1113       case NOPIECE:
1114         break;
1115       default:
1116         if (iscolor(piece, color))
1117           return 1;
1118       }
1119     }
1120   return ((minor_pieces > 1) ? 1 : 0);
1121 }
1122
1123 int com_flag(int p, param_list param)
1124 {
1125         struct player *pp = &player_globals.parray[p];
1126         struct game *gg;
1127         int g;
1128         int myColor;
1129
1130         if (!pIsPlaying(p)) {
1131                 return COM_OK;
1132         }
1133         g = pp->game;
1134
1135         gg = &game_globals.garray[g];
1136
1137         myColor = (p == gg->white ? WHITE : BLACK);
1138         if (gg->type == TYPE_UNTIMED) {
1139                 pprintf(p, "You can't flag an untimed game.\n");
1140                 return COM_OK;
1141         }
1142         if (gg->numHalfMoves < 2) {
1143                 pprintf(p, "You cannot flag before both players have moved.\nUse abort instead.\n");
1144                 return COM_OK;
1145         }
1146         game_update_time(g);
1147         
1148         {
1149                 int myTime, yourTime, opp = pp->opponent, serverTime;
1150                 
1151                 if (net_globals.con[pp->socket]->timeseal) {    /* does caller use timeseal? */
1152                         myTime = (myColor==WHITE?gg->wRealTime:gg->bRealTime);
1153                 } else {
1154                         myTime = (myColor == WHITE?gg->wTime:gg->bTime);
1155                 }
1156                 serverTime = (myColor == WHITE?gg->bTime:gg->wTime);
1157                 
1158                 if (net_globals.con[player_globals.parray[opp].socket]->timeseal) {     /* opp uses timeseal? */
1159                         yourTime = (myColor == WHITE?gg->bRealTime:gg->wRealTime);
1160                 } else {
1161                         yourTime = serverTime;
1162                 }
1163
1164                 /* the clocks to compare are now in myTime and yourTime */
1165                 if ((myTime <= 0) && (yourTime <= 0)) {
1166                         decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1167                         game_ended(g, myColor, END_BOTHFLAG);
1168                         return COM_OK;
1169                 }
1170
1171                 if (yourTime > 0) {
1172                         /* Opponent still has time, but if that's only because s/he
1173                          * may be lagging, we should ask for an acknowledgement and then
1174                          * try to call the flag. */
1175                         
1176                         if (serverTime <= 0 && gg->game_state.onMove != myColor
1177                             && gg->flag_pending != FLAG_CHECKING) {                             
1178                                 /* server time thinks opponent is down, but RealTIme disagrees.
1179                                  * ask client to acknowledge it's alive. */                             
1180                                 gg->flag_pending = FLAG_CALLED;
1181                                 gg->flag_check_time = time(0);
1182                                 pprintf(p, "Opponent has timeseal; checking if (s)he's lagging.\n");
1183                                 pprintf (opp, "\n[G]\n");
1184                                 return COM_OK;
1185                         }
1186                         
1187                         /* if we're here, it means one of:
1188                          * 1. the server agrees opponent has time, whether lagging or not.
1189                          * 2. opp. has timeseal (if yourTime != serverTime), had time left
1190                          *    after the last move (yourTime > 0), and it's still your move.
1191                          * 3. we're currently checking a flag call after having receiving
1192                          *    acknowledgement from the other timeseal (and would have reset
1193                          *    yourTime if the flag were down). */
1194                         
1195                         pprintf(p, "Your opponent is not out of time!\n");
1196                         return COM_OK;
1197                 }
1198         }
1199         
1200         decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1201         if (player_has_mating_material(&gg->game_state, myColor))
1202                 game_ended(g, myColor, END_FLAG);
1203         else
1204                 game_ended(g, myColor, END_FLAGNOMATERIAL);
1205         return COM_OK;
1206 }
1207
1208 int com_adjourn(int p, param_list param)
1209 {
1210   struct player *pp = &player_globals.parray[p];
1211   struct pending* pend;
1212   int p1, g, myColor, yourColor;
1213
1214   if (!pIsPlaying(p))
1215     return COM_OK;
1216
1217   p1 = pp->opponent;
1218   g = pp->game;
1219   if (!CheckPFlag(p, PFLAG_REG) || !CheckPFlag(p, PFLAG_REG)) {
1220     pprintf(p, "Both players must be registered to adjourn a game.  Use \"abort\".\n");
1221     return COM_OK;
1222   }
1223   if (game_globals.garray[g].link >= 0) {
1224     pprintf(p, "Bughouse games cannot be adjourned.\n");
1225     return COM_OK;
1226   }
1227   myColor = (p == game_globals.garray[g].white ? WHITE : BLACK);
1228   yourColor = (myColor == WHITE ? BLACK : WHITE);
1229
1230   if ((pend = find_pend(p1, p, PEND_ADJOURN)) != NULL) {
1231     delete_pending(pend);
1232     decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1233     game_ended(pp->game, yourColor, END_ADJOURN);
1234   } else {
1235     game_update_time(g);
1236     if (((myColor == WHITE) && (game_globals.garray[g].wTime > 0) && (game_globals.garray[g].bTime <= 0))
1237         || ((myColor == BLACK) && (game_globals.garray[g].bTime > 0) && (game_globals.garray[g].wTime <= 0))) {
1238 /* player wants to adjourn + opponent is out of time = courtesyadjourn */
1239       pprintf(p, "Since you have time, and your opponent has none, the game has been adjourned.");
1240       pprintf(p1, "Your opponent has adjourned the game rather than calling your flag.");
1241       decline_withdraw_offers(p, -1, -1,DO_DECLINE);
1242       game_ended(g, myColor, END_COURTESYADJOURN);
1243     } else {
1244       pprintf(p1, "\n");
1245       pprintf_highlight(p1, "%s", pp->name);
1246       pprintf(p1, " would like to adjourn the game; ");
1247       pprintf_prompt(p1, "type \"adjourn\" to accept.\n");
1248       pprintf(p, "Adjourn request sent.\n");
1249       add_request(p, p1, PEND_ADJOURN);
1250     }
1251   }
1252   return COM_OK;
1253 }
1254
1255 int com_takeback(int p, param_list param)
1256 {
1257   struct player *pp = &player_globals.parray[p];
1258   int nHalfMoves = 1, g, i, p1, pend_half_moves;
1259   struct pending* from;
1260
1261   if (!pIsPlaying(p))
1262     return COM_OK;
1263
1264   p1 = pp->opponent;
1265   if ((player_globals.parray[p1].simul_info != NULL) && 
1266      (player_globals.parray[p1].simul_info->numBoards &&
1267         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] !=
1268         pp->game)) {
1269     pprintf(p, "You can only make requests when the simul player is at your board.\n");
1270     return COM_OK;
1271   }
1272
1273   g = pp->game;
1274   if (game_globals.garray[g].link >= 0) {
1275     pprintf(p, "Takeback not implemented for bughouse games yet.\n");
1276     return COM_OK;
1277   }
1278   if (param[0].type == TYPE_INT) {
1279     nHalfMoves = param[0].val.integer;
1280     if (nHalfMoves <= 0) {
1281       pprintf (p,"You can't takeback less than 1 move.\n");
1282       return COM_OK;
1283     }
1284   }
1285   if ((from = find_pend(pp->opponent, p, PEND_TAKEBACK)) != NULL) {
1286     pend_half_moves = from->wtime;
1287     delete_pending(from);
1288     if (pend_half_moves == nHalfMoves) {
1289       /* Doing the takeback */
1290       decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1291       for (i = 0; i < nHalfMoves; i++) {
1292         if (backup_move(g, REL_GAME) != MOVE_OK) {
1293           pprintf(game_globals.garray[g].white, "Can only backup %d moves\n", i);
1294           pprintf(game_globals.garray[g].black, "Can only backup %d moves\n", i);
1295           break;
1296         }
1297       }
1298
1299       game_globals.garray[g].wTimeWhenReceivedMove = 0;
1300       game_globals.garray[g].bTimeWhenReceivedMove = 0;
1301
1302       send_boards(g);
1303     } else {
1304
1305       if (!game_globals.garray[g].numHalfMoves) {
1306         pprintf(p, "There are no moves in your game.\n");
1307         pprintf_prompt(pp->opponent, "\n%s has declined the takeback request.\n", 
1308                        pp->name);
1309         return COM_OK;
1310       }
1311  
1312       if (game_globals.garray[g].numHalfMoves < nHalfMoves) {
1313         pprintf(p, "There are only %d half moves in your game.\n", game_globals.garray[g].numHalfMoves);
1314         pprintf_prompt(pp->opponent, "\n%s has declined the takeback request.\n", 
1315                        pp->name);
1316         return COM_OK;
1317       }
1318       pprintf(p, "You disagree on the number of half-moves to takeback.\n");
1319       pprintf(p, "Alternate takeback request sent.\n");
1320       pprintf_prompt(pp->opponent, "\n%s proposes a different number (%d) of half-move(s).\n", pp->name, nHalfMoves);
1321       from = add_request(p, pp->opponent, PEND_TAKEBACK);
1322       from->wtime = nHalfMoves;
1323     }
1324   } else {
1325
1326     if (!game_globals.garray[g].numHalfMoves) {
1327       pprintf(p, "There are no moves in your game.\n");
1328       return COM_OK;
1329     }
1330     if (game_globals.garray[g].numHalfMoves < nHalfMoves) {
1331       pprintf(p, "There are only %d half moves in your game.\n", game_globals.garray[g].numHalfMoves);
1332       return COM_OK;
1333     }
1334     pprintf(pp->opponent, "\n");
1335     pprintf_highlight(pp->opponent, "%s", pp->name);
1336     pprintf_prompt(pp->opponent, " would like to take back %d half move(s).\n",
1337            nHalfMoves);
1338     pprintf(p, "Takeback request sent.\n");
1339     from = add_request(p, pp->opponent, PEND_TAKEBACK);
1340     from->wtime = nHalfMoves;
1341   }
1342   return COM_OK;
1343 }
1344
1345
1346 int com_switch(int p, param_list param)
1347 {
1348   struct player *pp = &player_globals.parray[p];
1349   int g = pp->game, tmp, now, p1;
1350   char *strTmp;
1351   struct pending* pend;
1352
1353   if (!pIsPlaying(p))
1354     return COM_OK;
1355
1356   p1 = pp->opponent;
1357   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards &&
1358         player_globals.parray[p1].simul_info->boards[player_globals.parray[p1].simul_info->onBoard] != g)) {
1359     pprintf(p, "You can only make requests when the simul player is at your board.\n");
1360     return COM_OK;
1361   }
1362
1363   if (game_globals.garray[g].link >= 0) {
1364     pprintf(p, "Switch not implemented for bughouse games.\n");
1365     return COM_OK;
1366   }
1367   if ((pend = find_pend(pp->opponent, p, PEND_SWITCH)) != NULL) {
1368     delete_pending(pend);
1369     /* Doing the switch */
1370     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1371
1372     tmp = game_globals.garray[g].white;
1373     game_globals.garray[g].white = game_globals.garray[g].black;
1374     game_globals.garray[g].black = tmp;
1375     pp->side = (pp->side == WHITE) ? BLACK : WHITE;
1376     strTmp = strdup(game_globals.garray[g].white_name);
1377     strcpy(game_globals.garray[g].white_name, game_globals.garray[g].black_name);
1378     strcpy(game_globals.garray[g].black_name, strTmp);
1379     free(strTmp);
1380
1381     player_globals.parray[pp->opponent].side =
1382       (player_globals.parray[pp->opponent].side == WHITE) ? BLACK : WHITE;
1383     /* Roll back the time */
1384     if (game_globals.garray[g].game_state.onMove == WHITE) {
1385       game_globals.garray[g].wTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1386     } else {
1387       game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
1388     }
1389     now = tenth_secs();
1390     if (game_globals.garray[g].numHalfMoves == 0)
1391       game_globals.garray[g].timeOfStart = now;
1392     game_globals.garray[g].lastMoveTime = now;
1393     game_globals.garray[g].lastDecTime = now;
1394     send_boards(g);
1395     return COM_OK;
1396   }
1397   if (game_globals.garray[g].rated && game_globals.garray[g].numHalfMoves > 0) {
1398     pprintf(p, "You cannot switch sides once a rated game is underway.\n");
1399     return COM_OK;
1400   }
1401   pprintf(pp->opponent, "\n");
1402   pprintf_highlight(pp->opponent, "%s", pp->name);
1403   pprintf_prompt(pp->opponent, " would like to switch sides.\nType \"accept\" to switch sides, or \"decline\" to refuse.\n");
1404   pprintf(p, "Switch request sent.\n");
1405   add_request(p, pp->opponent, PEND_SWITCH);
1406   return COM_OK;
1407 }
1408
1409 int com_time(int p, param_list param)
1410 {
1411   struct player *pp = &player_globals.parray[p];
1412   int p1, g;
1413
1414   if (param[0].type == TYPE_NULL) {
1415     g = pp->game;
1416     if (!pIsPlaying(p))
1417       return COM_OK;
1418   } else {
1419     g = GameNumFromParam(p, &p1, &param[0]);
1420     if (g < 0)
1421       return COM_OK;
1422   }
1423   if ((g < 0) || (g >= game_globals.g_num) || (game_globals.garray[g].status != GAME_ACTIVE)) {
1424     pprintf(p, "There is no such game.\n");
1425     return COM_OK;
1426   }
1427   game_update_time(g);
1428   pprintf(p, "White (%s) : %d mins, %d secs\n",
1429           player_globals.parray[game_globals.garray[g].white].name,
1430           game_globals.garray[g].wTime / 600,
1431           (game_globals.garray[g].wTime - ((game_globals.garray[g].wTime / 600) * 600)) / 10);
1432   pprintf(p, "Black (%s) : %d mins, %d secs\n",
1433           player_globals.parray[game_globals.garray[g].black].name,
1434           game_globals.garray[g].bTime / 600,
1435           (game_globals.garray[g].bTime - ((game_globals.garray[g].bTime / 600) * 600)) / 10);
1436   return COM_OK;
1437 }
1438
1439 int com_ptime(int p, param_list param)
1440 {
1441   struct player *pp = &player_globals.parray[p];
1442   int retval, part = pp->partner;
1443
1444   if (part < 0) {
1445     pprintf(p, "You do not have a partner.\n");
1446     return COM_OK;
1447   }
1448   retval = pcommand (p, "time %s", player_globals.parray[part].name);
1449   if (retval == COM_OK)
1450     return COM_OK_NOPROMPT;
1451   else
1452     return retval;
1453 }
1454
1455 int com_boards(int p, param_list param)
1456 {
1457   char *category = NULL;
1458   char dname[MAX_FILENAME_SIZE];
1459   DIR *dirp;
1460   struct dirent *dp;
1461
1462   if (param[0].type == TYPE_WORD)
1463     category = param[0].val.word;
1464   if (category) {
1465     pprintf(p, "Boards Available For Category %s:\n", category);
1466     sprintf(dname, "%s/%s", BOARD_DIR, category);
1467   } else {
1468     pprintf(p, "Categories Available:\n");
1469     sprintf(dname, "%s", BOARD_DIR);
1470   }
1471   dirp = opendir(dname);
1472   if (!dirp) {
1473     pprintf(p, "No such category %s, try \"boards\".\n", category);
1474     return COM_OK;
1475   }
1476
1477 /* YUK! what a mess, how about printing an ordered directory? - DAV*/
1478
1479   for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1480     if (!strcmp(dp->d_name, "."))
1481       continue;
1482     if (!strcmp(dp->d_name, ".."))
1483       continue;
1484     pprintf(p, "%s\n", dp->d_name);
1485   }
1486   closedir(dirp);
1487   return COM_OK;
1488 }
1489
1490 int com_simmatch(int p, param_list param)
1491 {
1492   struct player *pp = &player_globals.parray[p];
1493   int p1, g, adjourned;
1494   int num;
1495   char tmp[100];
1496   struct pending* pend;
1497   char* board = NULL;
1498   char* category = NULL;
1499   char fname[MAX_FILENAME_SIZE];
1500
1501   if (pp->game >=0) {
1502     if (game_globals.garray[pp->game].status == GAME_EXAMINE) {
1503       pprintf(p, "You are still examining a game.\n");
1504       return COM_OK;
1505     }
1506     if (game_globals.garray[pp->game].status == GAME_SETUP) {
1507       pprintf(p, "You are still setting up a position.\n");
1508       return COM_OK;
1509     }
1510   } 
1511   p1 = player_find_part_login(param[0].val.word);
1512   if (p1 < 0) {
1513     pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
1514     return COM_OK;
1515   }
1516   if (p == p1) {
1517     pprintf(p, "You can't simmatch yourself!\n");
1518     return COM_OK;
1519   }
1520   if ((pend = find_pend(p1, p, PEND_SIMUL)) != NULL) {
1521
1522     /* Accepting Simul ! */
1523
1524     if ((pp->simul_info != NULL) && 
1525        (pp->simul_info->numBoards >= MAX_SIMUL)) {
1526       pprintf(p, "You are already playing the maximum of %d boards.\n", MAX_SIMUL);
1527       pprintf(p1, "Simul request removed, boards filled.\n");
1528       delete_pending(pend);
1529       return COM_OK;
1530     }
1531     unobserveAll(p);            /* stop observing when match starts */
1532     unobserveAll(p1);
1533
1534     g = game_new();
1535     adjourned = 0;
1536     if (game_read(g, p, p1) >= 0) {
1537       adjourned = 1;
1538       delete_pending(pend);
1539     }
1540
1541     if (!adjourned) {           /* no adjourned game, so begin a new game */
1542
1543       if ((pend->category != NULL) && (pend->board_type != NULL)) {
1544         board = strdup(pend->category);
1545         category = strdup(pend->board_type);
1546       } 
1547
1548       delete_pending(pend);
1549
1550       if (create_new_match(g,p, p1, 0, 0, 0, 0, 0, ((board == NULL) ? "\0" : board), ((category == NULL) ? "\0" : category), 1,1) == COM_FAILED) {
1551         pprintf(p, "There was a problem creating the new match.\n");
1552         pprintf_prompt(p1, "There was a problem creating the new match.\n");
1553         game_remove(g);
1554
1555         if (board != NULL) {
1556           free (board);
1557           free (category);
1558         }
1559         return COM_OK;
1560       }
1561
1562       if (board != NULL) {
1563         free (board);
1564         free (category);
1565       }
1566
1567     } else {                    /* resume adjourned game */
1568       game_delete(p, p1);
1569
1570       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]);
1571       pprintf(p, tmp);
1572       pprintf(p1, tmp);
1573
1574       game_globals.garray[g].white = p;
1575       game_globals.garray[g].black = p1;
1576       game_globals.garray[g].status = GAME_ACTIVE;
1577       game_globals.garray[g].startTime = tenth_secs();
1578       game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1579       game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1580       pp->game = g;
1581       pp->opponent = p1;
1582       pp->side = WHITE;
1583       player_globals.parray[p1].game = g;
1584       player_globals.parray[p1].opponent = p;
1585       player_globals.parray[p1].side = BLACK;
1586       send_boards(g);
1587     }
1588
1589     if (pp->simul_info == NULL) {
1590       pp->simul_info = (struct simul_info_t *) malloc(sizeof(struct simul_info_t));
1591       pp->simul_info->numBoards = 0;
1592       pp->simul_info->onBoard = 0;
1593       pp->simul_info->num_wins = pp->simul_info->num_draws
1594         = pp->simul_info->num_losses = 0;
1595     }
1596     num = pp->simul_info->numBoards;
1597     /*    pp->simul_info->results[num] = -1; */
1598     pp->simul_info->boards[num] = pp->game;
1599     pp->simul_info->numBoards++;
1600     if (pp->simul_info->numBoards > 1 &&
1601         pp->simul_info->onBoard >= 0)
1602       player_goto_board(p, pp->simul_info->onBoard);
1603     else
1604       pp->simul_info->onBoard = 0;
1605     return COM_OK;
1606   }
1607   if (find_pend(-1, p, PEND_SIMUL) != NULL) {
1608     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");
1609     return COM_OK;
1610   }
1611   if (pp->simul_info != NULL) {
1612     if (pp->simul_info->numBoards) {
1613       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");
1614       return COM_OK;
1615     }
1616   }
1617   if (pp->game >=0) {
1618     pprintf(p, "You are already playing a game.\n");
1619     return COM_OK;
1620   }
1621   if (!CheckPFlag(p1, PFLAG_SIMOPEN)) {
1622     pprintf_highlight(p, "%s", player_globals.parray[p1].name);
1623     pprintf(p, " is not open to receiving simul requests.\n");
1624     return COM_OK;
1625   }
1626   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].simul_info->numBoards >= MAX_SIMUL)) {
1627     pprintf_highlight(p, "%s", player_globals.parray[p1].name);
1628     pprintf(p, " is already playing the maximum of %d boards.\n", MAX_SIMUL);
1629     return COM_OK;
1630   }
1631
1632 /* loon: checking for some crazy situations we can't allow :) */
1633
1634   if ((player_globals.parray[p1].simul_info != NULL) && (player_globals.parray[p1].game >=0) && (player_globals.parray[p1].simul_info->numBoards == 0)) {
1635     pprintf_highlight(p, "%s", player_globals.parray[p1].name);
1636     if (player_globals.parray[game_globals.garray[player_globals.parray[p1].game].white].simul_info->numBoards) {
1637       pprintf(p, " is playing in ");
1638       pprintf_highlight(p, "%s", player_globals.parray[player_globals.parray[p1].opponent].name);
1639       pprintf(p, "'s simul, and can't accept.\n");
1640     } else {
1641       pprintf(p, " can't begin a simul while playing a non-simul game.\n");
1642     }
1643     return COM_OK;
1644   }
1645
1646   g = game_new();               /* Check if an adjourned untimed game */
1647   adjourned = ((game_read(g, p, p1) < 0) && (game_read(g, p1, p) < 0)) ? 0 : 1;
1648   if (adjourned) {
1649     if (!(game_globals.garray[g].type == TYPE_UNTIMED))
1650       adjourned = 0;
1651   }
1652   game_remove(g);
1653
1654   pend = add_request(p, p1, PEND_SIMUL);
1655
1656   if ((param[1].type == TYPE_WORD) && (param[2].type == TYPE_WORD)) {
1657
1658     sprintf(fname, "%s/%s/%s", BOARD_DIR, param[1].val.word , param[2].val.word);
1659     if (!file_exists(fname)) {
1660       pprintf(p, "No such category/board: %s/%s\n", param[1].val.word , param[2].val.word);
1661       return COM_OK;
1662     }
1663     pend->category = strdup(param[1].val.word);
1664     pend->board_type = strdup(param[2].val.word);
1665   } else {
1666     pend->category = NULL;
1667     pend->board_type = NULL;
1668   }
1669  
1670   pprintf(p1, "\n");
1671   pprintf_highlight(p1, "%s", pp->name);
1672   if (adjourned) {
1673     pprintf_prompt(p1, " requests to continue an adjourned simul game.\n");
1674     pprintf(p, "Request to resume simul sent. Adjourned game found.\n");
1675   } else {
1676     if (pend->category == NULL)
1677       pprintf_prompt(p1, " requests to join a simul match with you.\n");
1678     else
1679       pprintf_prompt(p1, " requests to join a %s %s simul match with you.\n",
1680                 pend->category,pend->board_type);
1681     pprintf(p, "Simul match request sent.\n");
1682   }
1683   return COM_OK;
1684 }
1685
1686 int com_goboard(int p, param_list param)
1687 {
1688   struct player *pp = &player_globals.parray[p];
1689   int on, g, p1, gamenum;
1690
1691   if (pp->simul_info == NULL) {
1692     pprintf(p, "You are not giving a simul.\n");
1693     return COM_OK;
1694   }
1695
1696   if (!pp->simul_info->numBoards) {
1697     pprintf(p, "You are not giving a simul.\n");
1698     return COM_OK;
1699   }
1700
1701   if (param[0].type == TYPE_WORD) {
1702
1703     p1 = player_find_part_login(param[0].val.word);
1704     if (p1 < 0) {
1705       pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
1706       return COM_OK;
1707     }
1708     if (p == p1) {
1709       pprintf(p, "You can't goboard yourself!\n");
1710       return COM_OK;
1711     }
1712
1713     gamenum = player_globals.parray[p1].game;
1714     if (gamenum < 0) {
1715       pprintf (p,"%s is not playing a game.\n", player_globals.parray[p1].login);
1716       return COM_OK;
1717     }
1718
1719   } else { 
1720     gamenum = param[0].val.integer - 1;
1721     if (gamenum < 0)
1722       gamenum = 0;
1723   }
1724
1725   on = pp->simul_info->onBoard;
1726   g = pp->simul_info->boards[on];
1727   if (gamenum == g) {
1728     pprintf(p, "You are already at that board!\n");
1729     return COM_OK;
1730   }
1731   if (pp->simul_info->numBoards > 1) {
1732     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1733     if (player_goto_simulgame_bynum(p, gamenum) !=-1) {
1734       if (g >= 0) {
1735         pprintf(game_globals.garray[g].black, "\n");
1736         pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
1737         pprintf_prompt(game_globals.garray[g].black, " has moved away from your board.\n");
1738       }
1739     } else
1740     pprintf(p, "You are not playing that game/person.\n");
1741   } else
1742     pprintf(p, "You are only playing one board!\n");
1743   return COM_OK;
1744 }
1745
1746 int com_simnext(int p, param_list param)
1747 {
1748   struct player *pp = &player_globals.parray[p];
1749   int on, g;
1750
1751   if (pp->simul_info == NULL) {
1752     pprintf(p, "You are not giving a simul.\n");
1753     return COM_OK;
1754   }
1755
1756   if (!pp->simul_info->numBoards) {
1757     pprintf(p, "You are not giving a simul.\n");
1758     return COM_OK;
1759   }
1760
1761   if (pp->simul_info->numBoards > 1) {
1762     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1763     on = pp->simul_info->onBoard;
1764     g = pp->simul_info->boards[on];
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, " is moving away from your board.\n");
1769       player_goto_next_board(p);
1770     }
1771   } else
1772     pprintf(p, "You are only playing one board!\n");
1773   return COM_OK;
1774 }
1775
1776 int com_simprev(int p, param_list param)
1777 {
1778   struct player *pp = &player_globals.parray[p];
1779   int on, g;
1780
1781   if (pp->simul_info == NULL) {
1782     pprintf(p, "You are not giving a simul.\n");
1783     return COM_OK;
1784   }
1785
1786   if (!pp->simul_info->numBoards) {
1787     pprintf(p, "You are not giving a simul.\n");
1788     return COM_OK;
1789   }
1790   if (pp->simul_info->numBoards > 1) {
1791     decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1792     on = pp->simul_info->onBoard;
1793     g = pp->simul_info->boards[on];
1794     if (g >= 0) {
1795       pprintf(game_globals.garray[g].black, "\n");
1796       pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
1797       pprintf_prompt(game_globals.garray[g].black, " is moving back to the previous board.\n");
1798     }
1799     player_goto_prev_board(p);
1800   } else
1801     pprintf(p, "You are only playing one board!\n");
1802   return COM_OK;
1803 }
1804
1805 int com_simgames(int p, param_list param)
1806 {
1807   int p1 = p;
1808
1809   if (param[0].type == TYPE_WORD) {
1810     if ((p1 = player_find_part_login(param[0].val.word)) < 0) {
1811       pprintf(p, "No player named %s is logged in.\n", param[0].val.word);
1812       return COM_OK;
1813     }
1814   }
1815   if (p1 == p)
1816     pprintf(p, "You are playing %d simultaneous games.\n",
1817             player_num_active_boards(p1));
1818   else
1819     pprintf(p, "%s is playing %d simultaneous games.\n", player_globals.parray[p1].name,
1820             player_num_active_boards(p1));
1821   return COM_OK;
1822 }
1823
1824 int com_simpass(int p, param_list param)
1825 {
1826   struct player *pp = &player_globals.parray[p];
1827   int g, p1, on;
1828
1829   if (!pIsPlaying(p))
1830     return COM_OK;
1831
1832   g = pp->game;
1833   p1 = game_globals.garray[g].white;
1834
1835   if (player_globals.parray[p1].simul_info == NULL) {
1836     pprintf(p, "You are not participating in a simul.\n");
1837     return COM_OK;
1838   }
1839
1840   if (!player_globals.parray[p1].simul_info->numBoards) {
1841     pprintf(p, "You are not participating in a simul.\n");
1842     return COM_OK;
1843   }
1844   if (p == p1) {
1845     pprintf(p, "You are the simul holder and cannot pass!\n");
1846     return COM_OK;
1847   }
1848   if (player_num_active_boards(p1) == 1) {
1849     pprintf(p, "This is the only game, so passing is futile.\n");
1850     return COM_OK;
1851   }
1852   on = player_globals.parray[p1].simul_info->onBoard;
1853   if (player_globals.parray[p1].simul_info->boards[on] != g) {
1854     pprintf(p, "You cannot pass until the simul holder arrives!\n");
1855     return COM_OK;
1856   }
1857   if (game_globals.garray[g].passes >= MAX_SIMPASS) {
1858     Bell (p);
1859     pprintf(p, "You have reached your maximum of %d pass(es).\n", MAX_SIMPASS);
1860     pprintf(p, "Please move IMMEDIATELY!\n");
1861     pprintf_highlight(p1, "%s", pp->name);
1862     pprintf_prompt(p1, " tried to pass, but is out of passes.\n");
1863     return COM_OK;
1864   }
1865   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1866
1867   game_globals.garray[g].passes++;
1868   pprintf(p, "You have passed and have %d pass(es) left.\n",
1869           (MAX_SIMPASS - game_globals.garray[g].passes));
1870   pprintf_highlight(p1, "%s", pp->name);
1871   pprintf_prompt(p1, " has decided to pass and has %d pass(es) left.\n",
1872                  (MAX_SIMPASS - game_globals.garray[g].passes));
1873   player_goto_next_board(p1);
1874   return COM_OK;
1875 }
1876
1877 int com_simabort(int p, param_list param)
1878 {
1879   struct player *pp = &player_globals.parray[p];
1880
1881   if (pp->simul_info == NULL) {
1882     pprintf(p, "You are not giving a simul.\n");
1883     return COM_OK;
1884   }
1885
1886   if (!pp->simul_info->numBoards) {
1887     pprintf(p, "You are not giving a simul.\n");
1888     return COM_OK;
1889   }
1890   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1891   game_ended(pp->simul_info->boards[pp->simul_info->onBoard],
1892              WHITE, END_ABORT);
1893   return COM_OK;
1894 }
1895
1896 int com_simallabort(int p, param_list param)
1897 {
1898   struct player *pp = &player_globals.parray[p];
1899   int i;
1900
1901   if (pp->simul_info == NULL) {
1902     pprintf(p, "You are not giving a simul.\n");
1903     return COM_OK;
1904   }
1905
1906   if (!pp->simul_info->numBoards) {
1907     pprintf(p, "You are not giving a simul.\n");
1908     return COM_OK;
1909   }
1910
1911   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1912   for (i = 0; i < pp->simul_info->numBoards; i++)
1913     if (pp->simul_info->boards[i] >= 0)
1914       game_ended(pp->simul_info->boards[i],
1915                  WHITE, END_ABORT);
1916
1917   return COM_OK;
1918 }
1919
1920 int com_simadjourn(int p, param_list param)
1921 {
1922   struct player *pp = &player_globals.parray[p];
1923
1924   if (pp->simul_info == NULL) {
1925     pprintf(p, "You are not giving a simul.\n");
1926     return COM_OK;
1927   }
1928
1929   if (!pp->simul_info->numBoards) {
1930     pprintf(p, "You are not giving a simul.\n");
1931     return COM_OK;
1932   }
1933   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1934   game_ended(pp->simul_info->boards[pp->simul_info->onBoard],
1935              WHITE, END_ADJOURN);
1936   return COM_OK;
1937 }
1938
1939 int com_simalladjourn(int p, param_list param)
1940 {
1941   struct player *pp = &player_globals.parray[p];
1942   int i;
1943
1944   if (pp->simul_info == NULL) {
1945     pprintf(p, "You are not giving a simul.\n");
1946     return COM_OK;
1947   }
1948
1949   if (!pp->simul_info->numBoards) {
1950     pprintf(p, "You are not giving a simul.\n");
1951     return COM_OK;
1952   }
1953   decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1954   for (i = 0; i < pp->simul_info->numBoards; i++)
1955     if (pp->simul_info->boards[i] >= 0)
1956       game_ended(pp->simul_info->boards[i],
1957                  WHITE, END_ADJOURN);
1958
1959   return COM_OK;
1960 }
1961
1962 int com_moretime(int p, param_list param)
1963 {
1964   struct player *pp = &player_globals.parray[p];
1965   int g, increment;
1966
1967   if ((pp->game >=0) &&((game_globals.garray[pp->game].status == GAME_EXAMINE) ||
1968         (game_globals.garray[pp->game].status == GAME_SETUP))) {
1969     pprintf(p, "You cannot use moretime in an examined game.\n");
1970     return COM_OK;
1971   }
1972   increment = param[0].val.integer;
1973   if (increment <= 0) {
1974     pprintf(p, "Moretime requires an integer value greater than zero.\n");
1975     return COM_OK;
1976   }
1977   if (!pIsPlaying(p))
1978     return COM_OK;
1979  
1980   if (increment > 600) {
1981     pprintf(p, "Moretime has a maximum limit of 600 seconds.\n");
1982     increment = 600;
1983   }
1984   g = pp->game;
1985   if (game_globals.garray[g].white == p) {
1986     game_globals.garray[g].bTime += increment * 10;
1987     game_globals.garray[g].bRealTime += increment * 10 * 100;
1988     pprintf(p, "%d seconds were added to your opponents clock\n",
1989             increment);
1990     pprintf_prompt(pp->opponent,
1991                    "\nYour opponent has added %d seconds to your clock.\n",
1992                    increment);
1993   }
1994   if (game_globals.garray[g].black == p) {
1995     game_globals.garray[g].wTime += increment * 10;;
1996     game_globals.garray[g].wRealTime += increment * 10 * 100;
1997     pprintf(p, "%d seconds were added to your opponents clock\n",
1998             increment);
1999     pprintf_prompt(pp->opponent,
2000                    "\nYour opponent has added %d seconds to your clock.\n",
2001                    increment);
2002   }
2003   return COM_OK;
2004 }
2005