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