Fix crash after recursive aliasing
[capablanca.git] / lasker-2.2.3 / src / matchproc.c
1 /*
2    This program is free software; you can redistribute it and/or modify
3    it under the terms of the GNU General Public License as published by
4    the Free Software Foundation; either version 2 of the License, or
5    (at your option) any later version.
6    
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11    
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17 /**
18   matchproc.c
19   Feb 9 1996 - rewritten - DAV
20
21 */
22
23 #include "includes.h"
24
25 const char *colorstr[] = {"", "[black] ", "[white] "};
26 const char *adjustr[] = {"", " (adjourned)"};
27
28 static void prepare_match(int g,int wt,int bt,int winc,int binc,int wp, int bp,int rated)
29 {
30
31   wt = wt * 60;                 /* To Seconds */
32   bt = bt * 60;
33   game_globals.garray[g].white = wp;
34   game_globals.garray[g].black = bp;
35   strcpy(game_globals.garray[g].white_name, player_globals.parray[wp].name);
36   strcpy(game_globals.garray[g].black_name, player_globals.parray[bp].name);
37   game_globals.garray[g].status = GAME_ACTIVE;
38   if ((game_globals.garray[g].type == TYPE_UNTIMED) || (game_globals.garray[g].type == TYPE_NONSTANDARD))
39     game_globals.garray[g].rated = 0;
40   else
41     game_globals.garray[g].rated = rated;
42   game_globals.garray[g].private = BoolCheckPFlag(wp, PFLAG_PRIVATE)
43                       || BoolCheckPFlag(bp, PFLAG_PRIVATE);
44   game_globals.garray[g].white = wp;
45   if (game_globals.garray[g].type == TYPE_BLITZ) {
46     game_globals.garray[g].white_rating = player_globals.parray[wp].b_stats.rating;
47     game_globals.garray[g].black_rating = player_globals.parray[bp].b_stats.rating;
48   } else if (game_globals.garray[g].type == TYPE_WILD) {
49     game_globals.garray[g].white_rating = player_globals.parray[wp].w_stats.rating;
50     game_globals.garray[g].black_rating = player_globals.parray[bp].w_stats.rating;
51   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
52     game_globals.garray[g].white_rating = player_globals.parray[wp].l_stats.rating;
53     game_globals.garray[g].black_rating = player_globals.parray[bp].l_stats.rating;
54   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
55     game_globals.garray[g].white_rating = player_globals.parray[wp].bug_stats.rating;
56     game_globals.garray[g].black_rating = player_globals.parray[bp].bug_stats.rating;
57   } else {
58     game_globals.garray[g].white_rating = player_globals.parray[wp].s_stats.rating;
59     game_globals.garray[g].black_rating = player_globals.parray[bp].s_stats.rating;
60   }
61
62   game_globals.garray[g].game_state.gameNum = g;
63
64   game_globals.garray[g].wTime = wt * 10;
65   game_globals.garray[g].wInitTime = wt * 10;
66   game_globals.garray[g].wIncrement = winc * 10;
67   game_globals.garray[g].bTime = bt * 10;
68
69   if (game_globals.garray[g].type != TYPE_UNTIMED) {
70     if (wt == 0)
71         game_globals.garray[g].wTime = 100;
72     if (bt == 0)
73         game_globals.garray[g].bTime = 100;
74   } /* 0 x games start with 10 seconds */
75
76   game_globals.garray[g].wRealTime = game_globals.garray[g].wTime * 100;
77   game_globals.garray[g].bRealTime = game_globals.garray[g].bTime * 100;
78   game_globals.garray[g].wTimeWhenReceivedMove = 0;
79   game_globals.garray[g].bTimeWhenReceivedMove = 0;
80
81   game_globals.garray[g].bInitTime = bt * 10;
82   game_globals.garray[g].bIncrement = binc * 10;
83
84   if (game_globals.garray[g].game_state.onMove == BLACK) {   /* Start with black */
85     game_globals.garray[g].numHalfMoves = 1;
86     game_globals.garray[g].moveListSize = 1;
87     game_globals.garray[g].moveList = (struct move_t *) malloc(sizeof(struct move_t));
88     game_globals.garray[g].moveList[0].fromFile = -1;
89     game_globals.garray[g].moveList[0].fromRank = -1;
90     game_globals.garray[g].moveList[0].toFile = -1;
91     game_globals.garray[g].moveList[0].toRank = -1;
92     game_globals.garray[g].moveList[0].color = WHITE;
93     strcpy(game_globals.garray[g].moveList[0].moveString, "NONE");
94     strcpy(game_globals.garray[g].moveList[0].algString, "NONE");
95   } else {
96     game_globals.garray[g].numHalfMoves = 0;
97     game_globals.garray[g].moveListSize = 0;
98     game_globals.garray[g].moveList = NULL;
99   }
100
101   game_globals.garray[g].timeOfStart = tenth_secs();
102   game_globals.garray[g].startTime = tenth_secs();
103   game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
104   game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
105   game_globals.garray[g].clockStopped = 0;
106
107 }
108
109 static void pick_colors(int* wp,int* bp,int white,int wt,int bt,int winc,
110                                                                 int binc)
111
112 {
113  int reverse = 0;
114
115   if (white == 0) {
116     reverse = 1; /* did challenger ask for black? */
117
118   } else if (white == -1) { /* unknown */
119
120     if ((wt == bt) && (winc == binc)) { /* if diff times challenger is white */
121
122       if (CheckPFlag(*wp, PFLAG_LASTBLACK)==CheckPFlag(*bp, PFLAG_LASTBLACK)) {
123         if ((player_globals.parray[*wp].num_white - player_globals.parray[*wp].num_black) >
124           (player_globals.parray[*bp].num_white - player_globals.parray[*bp].num_black))
125           reverse = 1; /* whose played the most extra whites gets black */
126
127       } else if (!CheckPFlag(*wp, PFLAG_LASTBLACK))
128         reverse = 1;
129
130     } else
131       reverse = 1;              
132   }
133   if (reverse) {
134     int tmp = *wp;
135     *wp = *bp;
136     *bp = tmp;
137   }
138 }
139
140 static void output_match_messages(int wp,int bp,int g, char* mess)
141 {
142
143   int printed;
144   int avail_printed = 0;
145   char notavail_white[200];
146   char notavail_black[200];
147   int p;
148   char *outStr;
149
150   notavail_white[0] = '\0';
151   notavail_black[0] = '\0';
152
153   asprintf(&outStr,"\nCreating: %s (%d) %s (%d) %s %s %d %d\n",
154            player_globals.parray[wp].name,
155            game_globals.garray[g].white_rating,
156            player_globals.parray[bp].name,
157            game_globals.garray[g].black_rating,
158            rstr[game_globals.garray[g].rated],
159            //bstr[game_globals.garray[g].type],
160            game_globals.garray[g].variant,
161            game_globals.garray[g].wInitTime/600, 
162            game_globals.garray[g].wIncrement/10);
163   pprintf(wp, "%s", outStr);
164   pprintf(bp, "%s", outStr);
165   free(outStr);
166
167   asprintf(&outStr, "\n{Game %d (%s vs. %s) %s %s %s match.}\n",
168           g + 1, player_globals.parray[wp].name,
169           player_globals.parray[bp].name,
170           mess,
171           rstr[game_globals.garray[g].rated],
172           //bstr[game_globals.garray[g].type]);
173           game_globals.garray[g].variant);
174   pprintf(wp, "%s", outStr);
175   pprintf(bp, "%s", outStr);
176
177   if (!(player_num_active_boards(wp)) && CheckPFlag(wp, PFLAG_OPEN))
178 /* open may be 0 if a simul */
179     getnotavailmess(wp,notavail_white);
180   if (CheckPFlag(bp, PFLAG_OPEN))
181     getnotavailmess(bp,notavail_black);
182   for (p = 0; p < player_globals.p_num; p++) {
183     struct player *pp = &player_globals.parray[p];
184     int gnw, gnb;
185     printed = 0;
186
187     if ((p == wp) || (p == bp))
188       continue;
189     if (pp->status != PLAYER_PROMPT)
190       continue;
191     if (CheckPFlag(p, PFLAG_GIN)) {
192       pprintf(p, "%s", outStr);
193       printed = 1;
194     }
195     gnw = in_list(p, L_GNOTIFY, player_globals.parray[wp].login);
196     gnb = in_list(p, L_GNOTIFY, player_globals.parray[bp].login);
197     if (gnw || gnb) {
198       pprintf(p, "\nGame notification: ");
199
200       if (gnw)
201         pprintf_highlight(p, player_globals.parray[wp].name);
202       else
203         pprintf(p, player_globals.parray[wp].name);
204
205       pprintf(p, " (%s) vs. ",
206               ratstr(GetRating(&player_globals.parray[wp], game_globals.garray[g].type)));
207
208       if (gnb)
209         pprintf_highlight(p, player_globals.parray[bp].name);
210       else
211         pprintf(p, player_globals.parray[bp].name);
212       pprintf(p, " (%s) %s %s %d %d: Game %d\n",
213                      ratstr(GetRating(&player_globals.parray[bp], game_globals.garray[g].type)),
214                      rstr[game_globals.garray[g].rated], bstr[game_globals.garray[g].type],
215                      game_globals.garray[g].wInitTime/600, game_globals.garray[g].wIncrement/10, g+1);
216
217       printed = 1;
218     }
219
220     if (CheckPFlag(p, PFLAG_AVAIL) && CheckPFlag(p, PFLAG_OPEN) && (pp->game < 0) &&
221        (CheckPFlag(wp, PFLAG_OPEN) || CheckPFlag(bp, PFLAG_OPEN))) {
222
223       if (((player_globals.parray[wp].b_stats.rating <= pp->availmax) &&
224          (player_globals.parray[wp].b_stats.rating >= pp->availmin)) ||
225          (!pp->availmax)) {
226          pprintf (p,"\n%s",notavail_white);
227          avail_printed = 1;
228       }
229
230       if (((player_globals.parray[bp].b_stats.rating <= pp->availmax) &&
231          (player_globals.parray[bp].b_stats.rating >= pp->availmin)) ||
232          (!pp->availmax)) {
233       pprintf (p,"\n%s",notavail_black);
234       avail_printed = 1;
235       }
236
237       if (avail_printed) {
238         printed = 1;
239         avail_printed = 0;
240         pprintf (p,"\n");
241       } /* was black or white open originally (for simuls) */
242     }
243
244     if (printed)
245       send_prompt (p);
246   }
247
248   free(outStr);
249 }
250
251 int create_new_match(int g, int white_player, int black_player,
252                              int wt, int winc, int bt, int binc,
253                              int rated, char *category, char *board,
254                              int white, int simul)
255 {
256
257   remove_request(white_player, black_player, -PEND_PARTNER);
258
259   pick_colors(&white_player,&black_player,white,wt,bt,winc,binc);
260
261   decline_withdraw_offers(white_player,-1, PEND_MATCH,DO_WITHDRAW | DO_DECLINE);
262   decline_withdraw_offers(black_player,-1, PEND_MATCH,DO_WITHDRAW | DO_DECLINE);
263   decline_withdraw_offers(black_player,-1, PEND_SIMUL,DO_WITHDRAW | DO_DECLINE);
264   if (simul) {
265     decline_withdraw_offers(white_player, -1, PEND_SIMUL, DO_WITHDRAW);
266   } else {
267     decline_withdraw_offers(white_player, -1, PEND_SIMUL,
268                             DO_WITHDRAW | DO_DECLINE);
269   }
270   game_globals.garray[g].FENstartPos[0] = 0; // [HGM] new shuffle
271   if (board_init(g,&game_globals.garray[g].game_state, category, board)) {
272     pprintf(white_player, "PROBLEM LOADING BOARD. Game Aborted.\n");
273     pprintf(black_player, "PROBLEM LOADING BOARD. Game Aborted.\n");
274     d_printf( "CHESSD: PROBLEM LOADING BOARD %s %s. Game Aborted.\n",
275             category, board);
276     game_remove(g);
277     return COM_FAILED;
278   }
279
280   if (simul)
281     game_globals.garray[g].type = TYPE_UNTIMED;
282   else
283     game_globals.garray[g].type = game_isblitz(wt, winc, bt, binc, category, board);
284
285   if(category && category[0]) { // [HGM] set variant string from directory for games loaded from file
286     if(!strcmp(category, "wild") && board)
287         sprintf(game_globals.garray[g].variant, "%s/%s", category, board);
288     else
289         strcpy(game_globals.garray[g].variant, category);
290   }
291   else
292     strcpy(game_globals.garray[g].variant, bstr[game_globals.garray[g].type]);
293
294   prepare_match(g,wt,bt,winc,binc,white_player,black_player,rated);
295
296   output_match_messages(white_player,black_player,g, "Creating");
297
298   player_globals.parray[white_player].game = g;
299   player_globals.parray[white_player].opponent = black_player;
300   player_globals.parray[white_player].side = WHITE;
301   player_globals.parray[white_player].promote = QUEEN;
302   player_globals.parray[black_player].game = g;
303   player_globals.parray[black_player].opponent = white_player;
304   player_globals.parray[black_player].side = BLACK;
305   player_globals.parray[black_player].promote = QUEEN;
306   send_boards(g);
307   gics_globals.userstat.games++;
308
309   /* obey any 'follow' lists */
310   follow_start(white_player,black_player);
311
312   return COM_OK;
313 }
314  
315 int accept_match(struct pending *pend, int p, int p1)
316 {
317   struct player *pp = &player_globals.parray[p];
318   int wt, winc, bt, binc, rated, white;
319   char category[50], board[50];
320   char tmp[100];
321   int bh = 0, partner = 0, pp1 = 0, g1, g2;
322  
323   unobserveAll(p);              /* stop observing when match starts */
324   unobserveAll(p1);
325
326   wt = pend->wtime;
327   winc = pend->winc;
328   bt = pend->btime;
329   binc = pend->binc;
330   rated = pend->rated;
331   strcpy (category, pend->category);
332   strcpy (board, pend->board_type);
333   white = (pend->seek_color == -1) ? -1 : 1 - pend->seek_color;
334
335   pprintf(p, "You accept the challenge of %s.\n", player_globals.parray[p1].name);
336   pprintf_prompt(p1, "\n%s accepts your challenge.\n", pp->name);
337
338   if(!pend->status)
339     delete_pending(pend);
340
341   withdraw_seeks(p);
342   withdraw_seeks(p1);
343
344   pend_join_match (p,p1);
345
346   if (game_isblitz(wt, winc, bt, binc, category, board) == TYPE_BUGHOUSE) {
347     bh = 1;
348
349     if ((partner = pp->partner) >= 0 &&
350         (pp1 = player_globals.parray[p1].partner) >= 0) {
351       unobserveAll(partner);         /* stop observing when match starts */
352       unobserveAll(pp1);
353
354       pprintf(partner, "\nYour partner accepts the challenge of %s.\n", player_globals.parray[p1].name);
355       pprintf(pp1, "\n%s accepts your partner's challenge.\n", pp->name);
356
357       pend_join_match (partner,pp1);
358     } else {
359       return COM_OK;
360     }
361   }
362
363   g1 = game_new(); /* create a game structure */
364   if (g1 < 0) {
365     sprintf(tmp, "There was a problem creating the new match.\n");
366     pprintf(p, tmp);
367     pprintf_prompt(p1, tmp);
368   }
369
370   if (game_read(g1, p1, p) >= 0) {
371           int swap;
372           swap = p;
373           p = p1;
374           p1 = swap;
375           pp = &player_globals.parray[p];
376   } else if (game_read(g1, p, p1) < 0) { /* so no adjourned game */ 
377     if (create_new_match(g1,p, p1, wt, winc, bt, binc, rated, category, board, white,0) == COM_FAILED)
378       return COM_OK;
379
380  /* create first game */
381
382     if (bh) { /* do bughouse creation */
383
384       white = (pp->side == WHITE ? 0 : 1);
385       g2 = game_new();
386       if ((g2 < 0) || (create_new_match(g2,partner, pp1, wt, winc, bt, binc, rated, category, board,white,0) == COM_FAILED)) {
387          sprintf(tmp, "There was a problem creating the new match.\n");
388          pprintf_prompt(partner, tmp);
389          pprintf_prompt(pp1, tmp);
390          sprintf(tmp, "There was a problem creating your partner's match.\n");
391          game_remove(g1); /* abort first game */
392          return COM_OK;
393        }
394
395       game_globals.garray[g1].link = g2; /* link the games */
396       game_globals.garray[g2].link = g1;
397
398       sprintf(tmp, "\nYour partner is playing game %d (%s vs. %s).\n",
399               g2 + 1, game_globals.garray[g2].white_name, game_globals.garray[g2].black_name);
400       pprintf(p, tmp);
401       pprintf_prompt(p1, tmp);
402
403       sprintf(tmp, "\nYour partner is playing game %d (%s vs. %s).\n",
404               g1 + 1, game_globals.garray[g1].white_name, game_globals.garray[g1].black_name);
405       pprintf_prompt(partner, tmp);
406       pprintf_prompt(pp1, tmp);
407     } 
408     return COM_OK;
409   }
410
411        /* resume adjourned game */
412
413   game_delete(p, p1); /* remove the game from disk */
414
415   sprintf(tmp, "{Game %d (%s vs. %s) Continuing %s %s match.}\n",
416         g1+1, pp->name, player_globals.parray[p1].name,
417         rstr[game_globals.garray[g1].rated], bstr[game_globals.garray[g1].type]);
418   pprintf(p, tmp);
419   pprintf(p1, tmp);
420   output_match_messages(p, p1, g1, "Continuing");
421
422   game_globals.garray[g1].white = p;
423   game_globals.garray[g1].black = p1;
424   game_globals.garray[g1].status = GAME_ACTIVE;
425   game_globals.garray[g1].result = END_NOTENDED;
426   game_globals.garray[g1].startTime = tenth_secs();
427   game_globals.garray[g1].lastMoveTime = game_globals.garray[g1].startTime;
428   game_globals.garray[g1].lastDecTime = game_globals.garray[g1].startTime;
429   pp->game = g1;
430   pp->opponent = p1;
431   pp->side = WHITE;
432   player_globals.parray[p1].game = g1;
433   player_globals.parray[p1].opponent = p;
434   player_globals.parray[p1].side = BLACK;
435
436   send_boards(g1);
437
438   /* obey any 'follow' lists */
439   follow_start(p,p1);
440
441   return COM_OK;
442 }
443
444 /* board and category are initially empty strings */
445 int parse_match_string(int p, int* wt,int* bt,int* winc,int* binc,
446                                 int* white,int* rated,char* category,
447                                 char* board, char* mstring)
448 {
449   int numba;
450   int confused = 0;
451   char parsebuf[100];
452   int bughouse = 0;
453
454   while (!confused && (sscanf(mstring, " %99s", parsebuf) == 1)) {
455     mstring = eatword(eatwhite(mstring));
456
457     if ((category[0] != '\0') && (board[0] == '\0') && !bughouse)
458       strcpy(board, parsebuf);
459     else if (isdigit(*parsebuf)) {
460       if ((numba = atoi(parsebuf)) < 0) {
461         pprintf(p, "You can't specify negative time controls.\n");
462         return 0;
463       } else if (numba > 999) {
464         pprintf(p, "You can't specify time or inc above 999.\n");
465         return 0;
466       } else if (*wt == -1) {
467         *wt = numba;
468       } else if (*winc == -1) {
469         *winc = numba;
470       } else if (*bt == -1) {
471         *bt = numba;
472       } else if (*binc == -1) {
473         *binc = numba;
474       } else
475         confused = 1;
476
477     } else if ((!strcmp("rated", parsebuf)) || (!strcmp("r",parsebuf))) {
478       if (*rated == -1)
479         *rated = 1;
480       else
481         confused = 1;
482     } else if ((!strcmp("unrated", parsebuf)) || (!strcmp("u",parsebuf))) {
483       if (*rated == -1)
484         *rated = 0;
485       else
486         confused = 1;
487     } else if (!strcmp("white", parsebuf) || !strcmp ("w", parsebuf)) {
488       if (*white == -1)
489         *white = 1;
490       else
491         confused = 1;
492     } else if (!strcmp("black", parsebuf) || !strcmp ("b", parsebuf)) {
493       if (*white == -1)
494         *white = 0;
495       else
496         confused = 1;
497
498     } else if (category[0] == '\0') {
499       if ((parsebuf[0] == 'w') && (isdigit(*(parsebuf + 1)))) {
500         strcpy (category, "wild");
501         strcpy (board, parsebuf+1);
502       } else {
503         strcpy(category,parsebuf);
504         if (!strncmp("bughouse",category, strlen(category))
505             && strlen(category) >= 3 || !strcmp("bh", category)) {
506           strcpy(category, "bughouse");
507           bughouse = 1;
508         }
509         // [HGM] allow some shortcuts for variant names
510         if(!strcmp("bh", category)) {
511           strcpy(category, "bughouse");
512         } else
513         if(!strcmp("zh", category)) {
514           strcpy(category, "crazyhouse");
515         } else
516         if(!strcmp("fr", category)) {
517           strcpy(category, "fischerandom");
518         } else
519         if(!strcmp("sj", category)) {
520           strcpy(category, "shatranj");
521         } else
522         if(!strcmp("gc", category)) {
523           strcpy(category, "gothic");
524         } else
525         if(!strcmp("ca", category)) {
526           strcpy(category, "capablanca");
527         } else
528         if(!strcmp("cr", category)) {
529           strcpy(category, "caparandom");
530         } else
531         if(!strcmp("su", category)) {
532           strcpy(category, "super");
533         } else
534         if(!strcmp("sc", category)) {
535           strcpy(category, "seirawan");
536         } else
537         if(!strcmp("sg", category)) {
538           strcpy(category, "shogi");
539         } else
540         if(!strcmp("km", category)) {
541           strcpy(category, "knightmate");
542         } else
543         if(!strcmp("gr", category)) {
544           strcpy(category, "great");
545         } else
546         if(!strcmp("sp", category)) {
547           strcpy(category, "spartan");
548         } else
549         if(!strcmp("xq", category)) {
550           strcpy(category, "xiangqi");
551         }
552       }
553     } else
554       confused = 1;
555   }
556
557   if (confused) {
558     pprintf(p, "Can't interpret %s in match command.\n", parsebuf);
559     return 0;
560   }
561   if(category && (!board || !board[0]))
562         strcpy(board, "0"); // [HGM] variants: always provide default board
563   return 1;
564 }
565
566 static void print_match_rating_info(int p,int p1,int type,int rated)
567 {
568   if (rated) {
569     int win, draw, loss;
570     double newsterr;
571
572     rating_sterr_delta(p1, p, type, time(0), RESULT_WIN, &win, &newsterr);
573     rating_sterr_delta(p1, p, type, time(0), RESULT_DRAW, &draw, &newsterr);
574     rating_sterr_delta(p1, p, type, time(0), RESULT_LOSS, &loss, &newsterr);
575     pprintf(p1, "Your %s rating will change:  Win: %s%d,  Draw: %s%d,  Loss: %s%d\n",
576             bstr[type],
577             (win >= 0) ? "+" : "", win,
578             (draw >= 0) ? "+" : "", draw,
579             (loss >= 0) ? "+" : "", loss);
580     pprintf(p1, "Your new RD will be %5.1f\n", newsterr);
581
582     rating_sterr_delta(p, p1, type, time(0), RESULT_WIN, &win, &newsterr);
583     rating_sterr_delta(p, p1, type, time(0), RESULT_DRAW, &draw, &newsterr);
584     rating_sterr_delta(p, p1, type, time(0), RESULT_LOSS, &loss, &newsterr);
585     pprintf(p, "Your %s rating will change:  Win: %s%d,  Draw: %s%d,  Loss: %s%d\n",
586             bstr[type],
587             (win >= 0) ? "+" : "", win,
588             (draw >= 0) ? "+" : "", draw,
589             (loss >= 0) ? "+" : "", loss);
590     pprintf(p, "Your new RD will be %5.1f\n", newsterr);
591   }
592 }
593
594 static void check_lists_match(int p,int p1)
595 {
596   struct player *pp = &player_globals.parray[p];
597   if (in_list(p, L_COMPUTER, pp->name)) {
598     pprintf(p1, "--** %s is a ", pp->name);
599     pprintf_highlight(p1, "computer");
600     pprintf(p1, " **--\n");
601   }
602   if (in_list(p, L_COMPUTER, player_globals.parray[p1].name)) {
603     pprintf(p, "--** %s is a ", player_globals.parray[p1].name);
604     pprintf_highlight(p, "computer");
605     pprintf(p, " **--\n");
606   }
607   if (in_list(p, L_ABUSER, pp->name)) {
608     pprintf(p1, "--** %s is in the ", pp->name);
609     pprintf_highlight(p1, "abuser");
610     pprintf(p1, " list **--\n");
611   }
612   if (in_list(p, L_ABUSER, player_globals.parray[p1].name)) {
613     pprintf(p, "--** %s is in the ", player_globals.parray[p1].name);
614     pprintf_highlight(p, "abuser");
615     pprintf(p, " list **--\n");
616   }
617 }
618
619 int com_match(int p, param_list param)
620 {
621   struct player *pp = &player_globals.parray[p];
622   int adjourned;                /* adjourned game? */
623   int g;                        /* more adjourned game junk */
624   int p1;
625   int bh = 0, partner = 0, pp1 = 0;
626   struct pending* pendfrom;
627   struct pending* pendto;
628   struct pending* pend;
629   int wt = -1;                  /* white start time */
630   int winc = -1;                /* white increment */
631   int bt = -1;                  /* black start time */
632   int binc = -1;                /* black increment */
633   int rated = -1;               /* 1 = rated, 0 = unrated */
634   int white = -1;               /* 1 = want white, 0 = want black */
635   char category[100], board[100];
636   textlist *clauses = NULL;
637   int type = 0;
638
639   category[0] ='\0';
640   board[0] ='\0';
641
642   if ((pp->game >= 0) && ((game_globals.garray[pp->game].status == GAME_EXAMINE)
643 || (game_globals.garray[pp->game].status == GAME_SETUP))) {
644
645     pprintf(p, "You can't challenge while you are examining a game.\n");
646     return COM_OK;
647   }
648
649   if (pp->game >= 0) {
650     pprintf(p, "You can't challenge while you are playing a game.\n");
651     return COM_OK;
652   }
653
654   stolower(param[0].val.word);
655   p1 = player_find_part_login(param[0].val.word);
656   if (p1 < 0) {
657     pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
658     return COM_OK;
659   }
660
661   if (p1 == p) {                /* Allowing to match yourself to enter
662                                    analysis mode */
663     ExamineScratch (p, param, 0);
664     return COM_OK;
665   }
666
667   if (!CheckPFlag(p1, PFLAG_OPEN)) {
668     pprintf(p, "Player \"%s\" is not open to match requests.\n", player_globals.parray[p1].name);
669     return COM_OK;
670   }
671
672   if (player_globals.parray[p1].game >= 0) {
673     pprintf(p, "Player \"%s\" is involved in another game.\n", player_globals.parray[p1].name);    return COM_OK;
674   }
675
676   if (CheckPFlag(p, PFLAG_TOURNEY) && !CheckPFlag(p1, PFLAG_TOURNEY)) {
677     pprintf(p, "You may only match players with their tournament variable set.\n");
678     return COM_OK;
679   }
680
681   if (!CheckPFlag(p, PFLAG_TOURNEY) && CheckPFlag(p1, PFLAG_TOURNEY)) {
682     pprintf(p, "%s is in a tournament, and cannot accept other challenges.\n", player_globals.parray[p1].name);
683     return COM_OK;
684   }
685
686   if (!CheckPFlag(p, PFLAG_OPEN)) {
687     PFlagON(p, PFLAG_OPEN);
688     pprintf(p, "Setting you open for matches.\n");
689   }
690
691
692 /* look for an adjourned game between p and p1 */
693   g = game_new();
694   adjourned = (game_read(g, p, p1) >= 0) || (game_read(g, p1, p) >= 0);
695   if (adjourned) {
696     char *q;
697     type = game_globals.garray[g].type;
698     wt = game_globals.garray[g].wInitTime / 600;
699     bt = game_globals.garray[g].bInitTime / 600;
700     winc = game_globals.garray[g].wIncrement / 10;
701     binc = game_globals.garray[g].bIncrement / 10;
702     rated = game_globals.garray[g].rated;
703     strcpy(category, game_globals.garray[g].variant);
704     if(q = strchr(category, '/')) {
705       *q = 0; strcpy(board, q+1);
706     } else strcpy(board, "0");
707   }
708   game_remove(g);
709
710   pendto = find_pend(p, p1, PEND_MATCH);
711
712   pendfrom = find_pend(p1, p, PEND_MATCH);
713  
714   if (!adjourned) {
715     if (in_list(p1, L_NOPLAY, pp->login)) {
716       pprintf(p, "You are on %s's noplay list.\n", player_globals.parray[p1].name);
717       return COM_OK;
718     }
719     if (player_censored(p1, p)) {
720       pprintf(p, "Player \"%s\" is censoring you.\n", player_globals.parray[p1].name);
721       return COM_OK;
722     }
723     if (player_censored(p, p1)) {
724       pprintf(p, "You are censoring \"%s\".\n", player_globals.parray[p1].name);
725       return COM_OK;
726     }
727
728     if (param[1].type != TYPE_NULL) {
729       if (!parse_match_string(p, &wt,&bt,&winc,&binc,&white,&rated,category,
730                                                   board,param[1].val.string))
731       return COM_OK; /* couldn't parse */
732     }
733
734     if (rated == -1)
735       rated = BoolCheckPFlag(p, PFLAG_RATED);
736     if (!CheckPFlag(p, PFLAG_REG) || !CheckPFlag(p1, PFLAG_REG))
737       rated = 0;
738
739     if (winc == -1)
740       winc = (wt == -1) ? pp->d_inc : 0;  /* match 5 == match 5 0 */
741
742     if (wt == -1)
743       wt = pp->d_time;
744
745     if (bt == -1)
746       bt = wt;
747
748     if (binc == -1)
749       binc = winc;
750
751     if (category[0]) {
752       if (!board[0] && strcmp(category,"bughouse")) {
753         pprintf(p, "You must specify a board and a category.\n");
754         return COM_OK;
755
756       } else if (board[0]) { /* not bughouse */
757         char fname[MAX_FILENAME_SIZE];
758
759         sprintf(fname, "%s/%s/%s", BOARD_DIR, category, board);
760         if (!file_exists(fname)) {
761           pprintf(p, "No such category/board: %s/%s\n", category, board);
762           return COM_OK;
763         }
764       }
765     }
766     type = game_isblitz(wt, winc, bt, binc, category, board);
767
768     if (!strcmp(category, "bughouse")) {
769       type = TYPE_BUGHOUSE;
770       if (rated && pp->partner >= 0 && player_globals.parray[p1].partner >= 0) {
771         if (!CheckPFlag(pp->partner, PFLAG_REG)
772               || !CheckPFlag(player_globals.parray[p1].partner, PFLAG_REG))
773           rated = 0;
774       }
775     }
776     if (rated && (type == TYPE_NONSTANDARD)) {
777       pprintf(p, "Game is non-standard - reverting to unrated\n");
778       rated = 0;
779     }
780     if (rated && (type == TYPE_UNTIMED)) {
781       pprintf(p, "Game is untimed - reverting to unrated\n");
782       rated = 0;
783     }
784     if ((pendfrom == NULL) && !CheckPFlag(p1, PFLAG_ROPEN)
785         && (rated != BoolCheckPFlag(p1, PFLAG_RATED))) {
786       pprintf(p, "%s only wants to play %s games.\n", player_globals.parray[p1].name,
787               rstr[!rated]);
788       pprintf_highlight(p1, "Ignoring");
789       pprintf(p1, " %srated match request from %s.\n",
790               (rated ? "" : "un"), pp->name);
791       return COM_OK;
792     }
793
794     /* Now check formula. */
795     if (!adjourned
796         && !GameMatchesFormula(p,p1, wt,winc,bt,binc, rated, type, &clauses)) {
797       pprintf(p, "Match request does not fit formula for %s:\n",
798               player_globals.parray[p1].name);
799       pprintf(p, "%s's formula: %s\n", player_globals.parray[p1].name, player_globals.parray[p1].formula);
800       ShowClauses (p, p1, clauses);
801       ClearTextList(clauses);
802       pprintf_highlight(p1, "Ignoring");
803       pprintf_prompt(p1, " (formula): %s (%d) %s (%d) %s.\n",
804                      pp->name,
805                      GetRating(&player_globals.parray[p], type),
806                      player_globals.parray[p1].name,
807                      GetRating(&player_globals.parray[p1], type),
808             game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
809       return COM_OK;
810     }
811
812     if (type == TYPE_BUGHOUSE) {
813       bh = 1;
814       partner = pp->partner;
815       pp1 = player_globals.parray[p1].partner;
816
817       if (pp < 0) {
818         pprintf(p, "You have no partner for bughouse.\n");
819         return COM_OK;
820       }
821       if (pp1 < 0) {
822         pprintf(p, "Your opponent has no partner for bughouse.\n");
823         return COM_OK;
824       }
825       if (partner == pp1) { /* should be an impossible case - DAV */
826         pprintf(p, "You and your opponent both chose the same partner!\n");
827         return COM_OK;
828       }
829       if (partner == p1 || pp1 == p) {
830         pprintf(p, "You and your opponent can't choose each other as partners!\n");
831         return COM_OK;
832       }
833       if (player_globals.parray[partner].partner != p) { /* another impossible case - DAV */
834         pprintf(p, "Your partner hasn't chosen you as his partner!\n");
835         return COM_OK;
836       }
837       if (player_globals.parray[pp1].partner != p1) { /* another impossible case - DAV */
838         pprintf(p, "Your opponent's partner hasn't chosen your opponent as his partner!\n");
839         return COM_OK;
840       }
841       if (!CheckPFlag(partner, PFLAG_OPEN) || player_globals.parray[partner].game >= 0) {
842         pprintf(p, "Your partner isn't open to play right now.\n");
843         return COM_OK;
844       }
845       if (!CheckPFlag(pp1, PFLAG_OPEN) || player_globals.parray[pp1].game >= 0) {
846         pprintf(p, "Your opponent's partner isn't open to play right now.\n");
847         return COM_OK;
848       }
849
850       /* Bypass NOPLAY lists, censored lists, ratedness, privacy, and formula for now */
851       /*  Active challenger/ee will determine these. */
852     }
853     /* Ok match offer will be made */
854
855   }                             /* adjourned games shouldn't have to worry
856                                    about that junk? */
857                                 /* keep incase of adjourned bughouse in future*/
858
859   if (pendto != NULL) {
860     pprintf(p, "Updating offer already made to \"%s\".\n", player_globals.parray[p1].name);
861   }
862
863   if (pendfrom != NULL) {
864     if (pendto != NULL) {
865       pprintf(p, "Pending list error!.\n");
866       d_printf( "CHESSD: This shouldn't happen. You can't have a match pending from and to the same person.\n");
867       return COM_OK;
868     }
869
870     if (adjourned || ((wt == pendfrom->btime) &&
871                       (winc == pendfrom->binc) &&
872                       (bt == pendfrom->wtime) &&
873                       (binc == pendfrom->winc) &&
874                       (rated == pendfrom->rated) &&
875                       ((white == -1) || (white + pendfrom->seek_color == 1)) &&
876                (!strcmp(category, pendfrom->category)) &&
877                  (!strcmp(board, pendfrom->board_type)))) {
878       /* Identical match, should accept! */
879       accept_match(pendfrom,p, p1);
880       return COM_OK;
881
882     } else {
883       delete_pending(pendfrom);
884     }
885   }
886
887   if (pendto == NULL)
888     pend = add_pending(p,p1,PEND_MATCH);
889   else
890     pend = pendto; 
891
892   pend->wtime = wt;
893   pend->winc = winc;
894   pend->btime = bt;
895   pend->binc = binc;
896   pend->rated = rated;
897   pend->seek_color = white;
898   pend->game_type = type;
899   pend->category = strdup(category);
900   pend->board_type = strdup (board);
901
902   if (pendfrom != NULL) {
903     pprintf(p, "Declining offer from %s and offering new match parameters.\n", player_globals.parray[p1].name);
904     pprintf(p1, "\n%s declines your match offer a match with these parameters:", pp->name);
905   }
906
907   if (pendto != NULL) {
908     pprintf(p, "Updating match request to: ");
909     pprintf(p1, "\n%s updates the match request.\n", pp->name);
910   } else {
911     pprintf(p, "Issuing: ");
912     pprintf(p1, "\n");
913   }
914
915   pprintf(p, "%s (%s) %s", pp->name,
916           ratstrii(GetRating(&player_globals.parray[p], type), p),
917           colorstr[white + 1]);
918   pprintf_highlight(p, "%s", player_globals.parray[p1].name);
919   pprintf(p, " (%s) %s%s.\n",
920           ratstrii(GetRating(&player_globals.parray[p1], type), p1),
921           game_str(rated, wt * 60, winc, bt * 60, binc, category, board),
922           adjustr[adjourned]);
923   pprintf(p1, "Challenge: ");
924   pprintf_highlight(p1, "%s", pp->name);
925   pprintf(p1, " (%s) %s",
926           ratstrii(GetRating(&player_globals.parray[p], type), p),
927           colorstr[white + 1]);
928   pprintf(p1, "%s (%s) %s%s.\n", player_globals.parray[p1].name,
929           ratstrii(GetRating(&player_globals.parray[p1], type), p1),
930           game_str(rated, wt * 60, winc, bt * 60, binc, category, board),
931           adjustr[adjourned]);
932   Bell (p1);
933
934   if (bh) {
935
936     pprintf(partner, "\nYour bughouse partner issuing %s (%s) %s",
937             pp->name, ratstrii(GetRating(&player_globals.parray[p], type), p),
938             colorstr[white + 1]);
939     pprintf_highlight(partner, "%s", player_globals.parray[p1].name);
940     pprintf(partner, " (%s) %s.\n",
941             ratstrii(GetRating(&player_globals.parray[p1], type), p1),
942             game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
943     pprintf(partner, "Your game would be ");
944     pprintf_highlight(partner, "%s", player_globals.parray[pp1].name);
945     pprintf_prompt(partner, " (%s) %s%s (%s) %s.\n",
946           ratstrii(GetRating(&player_globals.parray[pp1], type), pp1),
947           colorstr[white + 1], player_globals.parray[partner].name,
948           ratstrii(GetRating(&player_globals.parray[partner], type), partner),
949           game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
950     Bell (partner);
951
952     pprintf(pp1, "\nYour bughouse partner was challenged ");
953     pprintf_highlight(pp1, "%s", pp->name);
954     pprintf(pp1, " (%s) %s", ratstrii(GetRating(&player_globals.parray[p], type), p),
955                              colorstr[white + 1]);
956     pprintf(pp1, "%s (%s) %s.\n", player_globals.parray[p1].name,
957             ratstrii(GetRating(&player_globals.parray[p1], type), p1),
958             game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
959     pprintf(pp1, "Your game would be %s (%s) %s", player_globals.parray[pp1].name,
960           ratstrii(GetRating(&player_globals.parray[pp1], type), pp1),
961           colorstr[white + 1]);
962     pprintf_highlight(pp1, "%s", player_globals.parray[partner].name);
963     pprintf_prompt(pp1, " (%s) %s.\n",
964           ratstrii(GetRating(&player_globals.parray[partner], type), partner),
965           game_str(rated, wt * 60, winc, bt * 60, binc, category, board));
966     Bell(pp1);
967   }
968
969   check_lists_match (p,p1);
970
971   print_match_rating_info (p,p1,type,rated);
972
973   pprintf_prompt(p1, "You can \"accept\" or \"decline\", or propose different parameters.\n");
974
975   return COM_OK;
976 }
977
978
979 /*
980   rmatch is used by mamer to start matches in tournaments
981 */
982 int com_rmatch(int p, param_list param)
983 {
984         struct player *pp = &player_globals.parray[p];
985         int p1, p2;
986
987         if (!in_list(p, L_TD, pp->name)) {
988                 pprintf(p, "Only TD programs are allowed to use this command.\n");
989                 return COM_OK;
990         }
991
992         if ((p1 = player_find_bylogin(param[0].val.word)) < 0 || 
993             !CheckPFlag(p1, PFLAG_REG)) {
994                 /* can't rmatch this user ... */
995                 return COM_OK;
996         }
997
998         if ((p2 = player_find_bylogin(param[1].val.word)) < 0 || 
999             !CheckPFlag(p2, PFLAG_REG)) {
1000                 /* can't rmatch this user ... */
1001                 return COM_OK;
1002         }
1003
1004         return pcommand(p1, "match %s %s", param[1].val.word, param[2].val.string);
1005 }