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