2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
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.
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.
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.
23 /*Let users know that someone is available (format the message)*/
24 static void getavailmess(int p, char* message)
26 struct player *pp = &player_globals.parray[p];
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.",
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));
41 void getnotavailmess(int p, char* message)
43 struct player *pp = &player_globals.parray[p];
47 AddPlayerLists(p,titles);
48 sprintf (message,"%s%s is no longer available for matches.",
52 void announce_avail(int p)
54 struct player *pp = &player_globals.parray[p];
57 if ((pp->game < 0) && (CheckPFlag(p, PFLAG_OPEN))) {
58 getavailmess (p,avail);
60 for (p1 = 0; p1 < player_globals.p_num; p1++) {
63 if (player_globals.parray[p1].status != PLAYER_PROMPT)
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);
73 void announce_notavail(int p)
75 struct player *pp = &player_globals.parray[p];
79 getnotavailmess (p,avail);
81 for (p1 = 0; p1 < player_globals.p_num; p1++) {
84 if (player_globals.parray[p1].status != PLAYER_PROMPT)
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);
93 void game_ended(int g, int winner, int why)
95 struct game *gg = &game_globals.garray[g];
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];
109 char *NameOfWinner, *NameOfLoser;
110 int beingplayed = 0; /* i.e. it wasn't loaded for adjudication */
113 avail_white[0] = '\0';
114 avail_black[0] = '\0';
115 avail_bugwhite[0] = '\0';
116 avail_bugblack[0] = '\0';
118 beingplayed = (player_globals.parray[gg->black].game == g);
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);
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;
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;
138 sprintf(tmp, "%s checkmated} %s", NameOfLoser, winSymbol);
139 strcpy(EndSymbol, "Mat");
143 sprintf(tmp, "%s bared} %s", NameOfLoser, winSymbol);
144 strcpy(EndSymbol, "Bar");
148 sprintf(tmp, "%s perpetually checking} %s", NameOfLoser, winSymbol);
149 strcpy(EndSymbol, "Per");
153 sprintf(tmp, "%s resigns} %s", NameOfLoser, winSymbol);
154 strcpy(EndSymbol, "Res");
158 sprintf(tmp, "%s forfeits on time} %s", NameOfLoser, winSymbol);
159 strcpy(EndSymbol, "Fla");
163 sprintf(tmp, "Game drawn by stalemate} 1/2-1/2");
165 strcpy(EndSymbol, "Sta");
167 whiteResult = RESULT_DRAW;
170 sprintf(tmp, "Game drawn by mutual agreement} 1/2-1/2");
172 strcpy(EndSymbol, "Agr");
174 whiteResult = RESULT_DRAW;
177 sprintf(tmp, "Game drawn because both players ran out of time} 1/2-1/2");
179 strcpy(EndSymbol, "Fla");
181 whiteResult = RESULT_DRAW;
184 sprintf(tmp, "Game drawn by repetition} 1/2-1/2");
186 strcpy(EndSymbol, "Rep");
188 whiteResult = RESULT_DRAW;
191 sprintf(tmp, "Game drawn by the 50 move rule} 1/2-1/2");
193 strcpy(EndSymbol, "50");
195 whiteResult = RESULT_DRAW;
199 sprintf(tmp, "Bughouse game aborted.} *");
200 whiteResult = RESULT_ABORT;
202 sprintf(tmp, "Game adjourned by mutual agreement} *");
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)
211 sprintf(tmp, "adjourned} *");
214 sprintf(tmp, "aborted} *");
215 whiteResult = RESULT_ABORT;
218 sprintf(tmp, "Game aborted by mutual agreement} *");
219 whiteResult = RESULT_ABORT;
222 sprintf(tmp, "Game courtesyaborted by %s} *", NameOfWinner);
223 whiteResult = RESULT_ABORT;
225 case END_COURTESYADJOURN:
227 sprintf(tmp, "Bughouse game courtesyaborted by %s.} *", NameOfWinner);
228 whiteResult = RESULT_ABORT;
230 sprintf(tmp, "Game courtesyadjourned by %s} *", NameOfWinner);
235 /* Draw by insufficient material (e.g., lone K vs. lone K) */
236 sprintf(tmp, "Neither player has mating material} 1/2-1/2");
238 strcpy(EndSymbol, "NM ");
240 whiteResult = RESULT_DRAW;
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);
246 strcpy(EndSymbol, "TM ");
248 whiteResult = RESULT_DRAW;
251 sprintf(tmp, "%s wins by adjudication} %s", NameOfWinner, winSymbol);
252 strcpy(EndSymbol, "Adj");
256 sprintf(tmp, "Game drawn by adjudication} 1/2-1/2");
258 strcpy(EndSymbol, "Adj");
260 whiteResult = RESULT_DRAW;
263 sprintf(tmp, "Game aborted by adjudication} *");
264 whiteResult = RESULT_ABORT;
267 sprintf(tmp, "Hmm, the game ended and I don't know why} *");
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);
280 strcat(outstr, "\n");
282 if (gg->rated && rate_change && gg->type != TYPE_BUGHOUSE)
283 /* Adjust ratings; bughouse gets done later. */
284 rating_update(g, -1);
288 int avail_printed = 0;
290 pprintf_noformat(gg->white, outstr);
291 pprintf_noformat(gg->black, outstr);
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);
303 if (CheckPFlag(game_globals.garray[gl].black, PFLAG_OPEN)) {
304 getavailmess (game_globals.garray[gl].black, avail_bugblack);
307 if ((gg->rated) && (rate_change)) {
309 rating_update(g, gl);
311 game_ended(gl, CToggle(winner), why);
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);
318 } else { /* Part of an ongoing simul! Let's shrink the array. */
322 if (CheckPFlag(gg->black, PFLAG_OPEN)) {
323 getavailmess (gg->black,avail_black);
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))
331 if (pp->status != PLAYER_PROMPT)
334 if (CheckPFlag(p, PFLAG_GIN) || player_is_observe(p, g)) {
335 pprintf_noformat(p, outstr);
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);
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);
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);
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);
371 if (!(gg->rated && rate_change)) {
372 pprintf(gg->white, "No ratings adjustment done.\n");
373 pprintf(gg->black, "No ratings adjustment done.\n");
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");
383 if (CheckPFlag(gg->black, PFLAG_AUTOMAIL)) {
384 pcommand(gg->black, "mailmoves");
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);
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);
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);
413 static int was_promoted(struct game *g, int f, int r)
415 #define BUGHOUSE_PAWN_REVERT 1
416 #ifdef BUGHOUSE_PAWN_REVERT
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;
432 if (g->moveList[i].fromFile == ALG_DROP)
434 f = g->moveList[i].fromFile;
435 r = g->moveList[i].fromRank;
442 int pIsPlaying (int p)
444 struct player *pp = &player_globals.parray[p];
446 int p1 = pp->opponent;
448 if (g < 0 || game_globals.garray[g].status != GAME_ACTIVE) {
449 pprintf (p, "You are not playing a game.\n");
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);
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;
469 /* add clock increments */
470 static void game_add_increment(struct player *pp, struct game *gg)
472 /* no update on first move */
473 if (gg->game_state.moveNum == 1) return;
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
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
486 if (gg->game_state.onMove == BLACK) {
487 gg->bTime += gg->bIncrement;
489 if (gg->game_state.onMove == WHITE) {
490 gg->wTime += gg->wIncrement;
495 /* updates clocks for a game with timeseal */
496 void timeseal_update_clocks(struct player *pp, struct game *gg)
498 /* no update on first move */
499 if (gg->game_state.moveNum == 1) return;
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;
511 gg->wRealTime -= gg->wTimeWhenMoved - gg->wTimeWhenReceivedMove;
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;
523 gg->bRealTime -= gg->bTimeWhenMoved - gg->bTimeWhenReceivedMove;
529 void process_move(int p, char *command)
531 struct player *pp = &player_globals.parray[p];
533 int g, result, len, i, f;
538 pprintf(p, "You are not playing or examining a game.\n");
541 decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
544 gg = &game_globals.garray[g];
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");
555 if (gg->status != GAME_EXAMINE) {
556 if (!pIsPlaying(p)) return;
558 if (pp->side != gg->game_state.onMove) {
559 pprintf(p, "It is not your move.\n");
562 if (gg->clockStopped) {
563 pprintf(p, "Game clock is paused, use \"unpause\" to resume.\n");
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])) {
573 pp->promote = KNIGHT;
576 pp->promote = BISHOP;
582 pp->promote = CARDINAL;
585 pp->promote = MARSHALL;
597 // Superchess promotions
599 pp->promote = EMPRESS;
602 pp->promote = PRINCESS;
605 pp->promote = CENTAUR;
611 pp->promote = SQUIRREL;
614 pp->promote = MASTODON;
617 pp->promote = LIEUTENANT;
624 pp->promote = DRAGONHORSE;
627 pp->promote = DRAGONKING;
634 pp->promote = NOPIECE;
637 pprintf(p, "Don't understand that move.\n");
644 switch (parse_move(command, &gg->game_state, &move, pp->promote)) {
646 pprintf(p, "Illegal move.\n");
650 pprintf(p, "Ambiguous move.\n");
657 if (gg->status == GAME_EXAMINE) {
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);
663 result = execute_move(&gg->game_state, &move, 1);
666 MakeFENpos(g, move.FENpos);
667 gg->examMoveList[gg->numHalfMoves - 1] = move;
669 if (gg->game_state.onMove == WHITE) {
670 gg->wTime += (gg->lastDecTime - gg->lastMoveTime);
672 gg->bTime += (gg->lastDecTime - gg->lastMoveTime);
675 if (gg->numHalfMoves == 0)
676 gg->timeOfStart = now;
677 gg->lastMoveTime = now;
678 gg->lastDecTime = now;
680 } else { /* real game */
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");
687 if (net_globals.con[pp->socket]->timeseal) { /* does he use timeseal? */
688 timeseal_update_clocks(pp, &game_globals.garray[g]);
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
694 if (pp->side == WHITE) {
695 gg->bTimeWhenReceivedMove = 0;
697 gg->wTimeWhenReceivedMove = 0;
701 game_add_increment(pp, gg);
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);
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
716 partner = g; // pieces stay with current board
717 if(gg->game_state.holdings == -1) victim ^= WHITE|BLACK; // flip color
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
722 update_holding(partner, victim);
726 if (gg->numHalfMoves > 1) {
727 move.tookTime = move.atTime - gg->lastMoveTime;
729 move.tookTime = move.atTime - gg->startTime;
731 gg->lastMoveTime = now;
732 gg->lastDecTime = now;
733 move.wTime = gg->wTime;
734 move.bTime = gg->bTime;
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;
741 move.tookTime = (game_globals.garray[pp->game].bTimeWhenMoved -
742 game_globals.garray[pp->game].bTimeWhenReceivedMove) / 100;
746 if (gg->numHalfMoves <= 2) {
750 MakeFENpos(g, move.FENpos);
751 gg->moveList[gg->numHalfMoves - 1] = move;
756 if (result == MOVE_ILLEGAL) {
757 pprintf(p, "Internal error, illegal move accepted!\n");
759 if ((result == MOVE_OK) && (gg->status == GAME_EXAMINE)) {
762 for (p1 = 0; p1 < player_globals.p_num; p1++) {
763 if (player_globals.parray[p1].status != PLAYER_PROMPT)
765 if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
766 pprintf(p1, "%s moves: %s\n", pp->name, move.algString);
770 if (result == MOVE_CHECKMATE) {
771 if (gg->status == GAME_EXAMINE) {
774 for (p1 = 0; p1 < player_globals.p_num; p1++) {
775 if (player_globals.parray[p1].status != PLAYER_PROMPT)
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");
783 game_ended(g, CToggle(gg->game_state.onMove), END_CHECKMATE);
786 if (result == MOVE_STALEMATE) {
787 if (gg->status == GAME_EXAMINE) {
790 for (p1 = 0; p1 < player_globals.p_num; p1++) {
791 if (player_globals.parray[p1].status != PLAYER_PROMPT)
793 if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
794 pprintf(p1, "Stalemate.\n");
798 game_ended(g, CToggle(gg->game_state.onMove), END_STALEMATE);
801 if (result == MOVE_NOMATERIAL) {
802 if (gg->status == GAME_EXAMINE) {
805 for (p1 = 0; p1 < player_globals.p_num; p1++) {
806 if (player_globals.parray[p1].status != PLAYER_PROMPT)
808 if (player_is_observe(p1, g) || player_globals.parray[p1].game == g) {
809 pprintf(p1, "No mating material.\n");
813 game_ended(g, CToggle(gg->game_state.onMove), END_NOMATERIAL);
816 if (result == MOVE_BARE) {
817 if (gg->status == GAME_EXAMINE) {
820 for (p1 = 0; p1 < player_globals.p_num; p1++) {
821 if (player_globals.parray[p1].status != PLAYER_PROMPT)
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");
829 game_ended(g, gg->game_state.onMove, END_BARE);
834 int com_resign(int p, param_list param)
836 struct player *pp = &player_globals.parray[p];
837 int g, o, oconnected;
839 if (param[0].type == TYPE_NULL) {
844 decline_withdraw_offers(p, -1, -1, DO_DECLINE);
845 game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_RESIGN);
847 } else if (FindPlayer(p, param[0].val.word, &o, &oconnected)) {
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);
856 game_globals.garray[g].white = o;
857 game_globals.garray[g].black = p;
860 game_globals.garray[g].white = p;
861 game_globals.garray[g].black = o;
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);
876 static int Check50MoveRule (int p, int g)
878 int num_reversible = game_globals.garray[g].numHalfMoves;
880 if (game_globals.garray[g].game_state.lastIrreversable >= 0) {
881 num_reversible -= game_globals.garray[g].game_state.lastIrreversable;
883 if (num_reversible > 99) {
884 game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, END_50MOVERULE);
890 static int perp_check(struct game g, int first, int third)
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);
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;
910 static char *GetFENpos (int g, int half_move)
913 return game_globals.garray[g].FENstartPos;
914 else return game_globals.garray[g].moveList[half_move].FENpos;
917 static int CheckRepetition (int p, int g)
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
927 int turn = numPly - 1;
929 if (numPly < 8) /* can't have three repeats any quicker. */
932 if((game_globals.garray[g].white == p) != (numPly&1)) { // claimer has the move
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
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);
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);
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");
958 game_ended(g, (numPly&1) ? BLACK : WHITE, result); // stm wins
961 game_ended(g, (game_globals.garray[g].white == p) ? BLACK : WHITE, result);
967 int com_draw(int p, param_list param)
969 struct player *pp = &player_globals.parray[p];
970 struct pending* pend;
971 int p1, g = pp->game;
973 if (!pIsPlaying(p)) {
976 if (Check50MoveRule (p, g) || CheckRepetition(p, g)) {
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");
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);
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);
1001 int com_pause(int p, param_list param)
1003 struct player *pp = &player_globals.parray[p];
1005 struct pending* pend;
1007 if (!pIsPlaying(p)) {
1011 if (game_globals.garray[g].wTime == 0) {
1012 pprintf(p, "You can't pause untimed games.\n");
1015 if (game_globals.garray[g].clockStopped) {
1016 pprintf(p, "Game is already paused, use \"unpause\" to resume.\n");
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);
1026 game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
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;
1034 pprintf_prompt(pp->opponent, "\n%s accepted pause. Game clock paused.\n",
1036 pprintf(p, "Game clock paused.\n");
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);
1047 int com_unpause(int p, param_list param)
1049 struct player *pp = &player_globals.parray[p];
1052 struct pending* pend;
1054 if (!pIsPlaying(p)) {
1060 if (!game_globals.garray[g].clockStopped) {
1061 pprintf(p, "Game is not paused.\n");
1064 if ((pend = find_pend(pp->opponent, p, PEND_UNPAUSE)) != NULL) {
1065 delete_pending(pend);
1066 game_globals.garray[g].clockStopped = 0;
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;
1073 pprintf(p, "Game clock resumed.\n");
1074 pprintf_prompt(pp->opponent, "\nGame clock resumed.\n");
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);
1085 int com_abort(int p, param_list param)
1087 struct player *pp = &player_globals.parray[p];
1088 struct pending* pend;
1089 int p1, g, myColor, yourColor, myGTime, yourGTime;
1097 if (p == game_globals.garray[g].white) {
1100 myGTime = game_globals.garray[g].wTime;
1101 yourGTime = game_globals.garray[g].bTime;
1105 myGTime = game_globals.garray[g].bTime;
1106 yourGTime = game_globals.garray[g].wTime;
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");
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);
1119 game_update_time(g);
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);
1132 if (net_globals.con[player_globals.parray[p1].socket]->timeseal) { /* opp uses timeseal? */
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. */
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");
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);
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);
1170 static int player_has_mating_material(struct game_state_t *gs, int color)
1174 int minor_pieces = 0;
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)) {
1182 if (iscolor(piece, color))
1189 if (iscolor(piece, color))
1193 return ((minor_pieces > 1) ? 1 : 0);
1196 int com_flag(int p, param_list param)
1198 struct player *pp = &player_globals.parray[p];
1203 if (!pIsPlaying(p)) {
1208 gg = &game_globals.garray[g];
1210 myColor = (p == gg->white ? WHITE : BLACK);
1211 if (gg->type == TYPE_UNTIMED) {
1212 pprintf(p, "You can't flag an untimed game.\n");
1215 if (gg->numHalfMoves < 2) {
1216 pprintf(p, "You cannot flag before both players have moved.\nUse abort instead.\n");
1219 game_update_time(g);
1222 int myTime, yourTime, opp = pp->opponent, serverTime;
1224 if (net_globals.con[pp->socket]->timeseal) { /* does caller use timeseal? */
1225 myTime = (myColor==WHITE?gg->wRealTime:gg->bRealTime);
1227 myTime = (myColor == WHITE?gg->wTime:gg->bTime);
1229 serverTime = (myColor == WHITE?gg->bTime:gg->wTime);
1231 if (net_globals.con[player_globals.parray[opp].socket]->timeseal) { /* opp uses timeseal? */
1232 yourTime = (myColor == WHITE?gg->bRealTime:gg->wRealTime);
1234 yourTime = serverTime;
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);
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. */
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");
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). */
1268 pprintf(p, "Your opponent is not out of time!\n");
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);
1277 game_ended(g, myColor, END_FLAGNOMATERIAL);
1281 int com_adjourn(int p, param_list param)
1283 struct player *pp = &player_globals.parray[p];
1284 struct pending* pend;
1285 int p1, g, myColor, yourColor;
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");
1296 if (game_globals.garray[g].link >= 0) {
1297 pprintf(p, "Bughouse games cannot be adjourned.\n");
1300 myColor = (p == game_globals.garray[g].white ? WHITE : BLACK);
1301 yourColor = (myColor == WHITE ? BLACK : WHITE);
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);
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);
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);
1328 int com_takeback(int p, param_list param)
1330 struct player *pp = &player_globals.parray[p];
1331 int nHalfMoves = 1, g, i, p1, pend_half_moves;
1332 struct pending* from;
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] !=
1342 pprintf(p, "You can only make requests when the simul player is at your board.\n");
1347 if (game_globals.garray[g].link >= 0) {
1348 pprintf(p, "Takeback not implemented for bughouse games yet.\n");
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");
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);
1372 game_globals.garray[g].wTimeWhenReceivedMove = 0;
1373 game_globals.garray[g].bTimeWhenReceivedMove = 0;
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",
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",
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;
1399 if (!game_globals.garray[g].numHalfMoves) {
1400 pprintf(p, "There are no moves in your game.\n");
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);
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",
1411 pprintf(p, "Takeback request sent.\n");
1412 from = add_request(p, pp->opponent, PEND_TAKEBACK);
1413 from->wtime = nHalfMoves;
1419 int com_switch(int p, param_list param)
1421 struct player *pp = &player_globals.parray[p];
1422 int g = pp->game, tmp, now, p1;
1424 struct pending* pend;
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");
1436 if (game_globals.garray[g].link >= 0) {
1437 pprintf(p, "Switch not implemented for bughouse games.\n");
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);
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);
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);
1460 game_globals.garray[g].bTime += (game_globals.garray[g].lastDecTime - game_globals.garray[g].lastMoveTime);
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;
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");
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);
1482 int com_time(int p, param_list param)
1484 struct player *pp = &player_globals.parray[p];
1487 if (param[0].type == TYPE_NULL) {
1492 g = GameNumFromParam(p, &p1, ¶m[0]);
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");
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);
1512 int com_ptime(int p, param_list param)
1514 struct player *pp = &player_globals.parray[p];
1515 int retval, part = pp->partner;
1518 pprintf(p, "You do not have a partner.\n");
1521 retval = pcommand (p, "time %s", player_globals.parray[part].name);
1522 if (retval == COM_OK)
1523 return COM_OK_NOPROMPT;
1528 int com_boards(int p, param_list param)
1530 char *category = NULL;
1531 char dname[MAX_FILENAME_SIZE];
1535 if (param[0].type == TYPE_WORD)
1536 category = param[0].val.word;
1538 pprintf(p, "Boards Available For Category %s:\n", category);
1539 sprintf(dname, "%s/%s", BOARD_DIR, category);
1541 pprintf(p, "Categories Available:\n");
1542 sprintf(dname, "%s", BOARD_DIR);
1544 dirp = opendir(dname);
1546 pprintf(p, "No such category %s, try \"boards\".\n", category);
1550 /* YUK! what a mess, how about printing an ordered directory? - DAV*/
1552 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1553 if (!strcmp(dp->d_name, "."))
1555 if (!strcmp(dp->d_name, ".."))
1557 pprintf(p, "%s\n", dp->d_name);
1563 int com_simmatch(int p, param_list param)
1565 struct player *pp = &player_globals.parray[p];
1566 int p1, g, adjourned;
1569 struct pending* pend;
1571 char* category = NULL;
1572 char fname[MAX_FILENAME_SIZE];
1575 if (game_globals.garray[pp->game].status == GAME_EXAMINE) {
1576 pprintf(p, "You are still examining a game.\n");
1579 if (game_globals.garray[pp->game].status == GAME_SETUP) {
1580 pprintf(p, "You are still setting up a position.\n");
1584 p1 = player_find_part_login(param[0].val.word);
1586 pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
1590 pprintf(p, "You can't simmatch yourself!\n");
1593 if ((pend = find_pend(p1, p, PEND_SIMUL)) != NULL) {
1595 /* Accepting Simul ! */
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);
1604 unobserveAll(p); /* stop observing when match starts */
1609 if (game_read(g, p, p1) >= 0) {
1611 delete_pending(pend);
1614 if (!adjourned) { /* no adjourned game, so begin a new game */
1616 if ((pend->category != NULL) && (pend->board_type != NULL)) {
1617 board = strdup(pend->category);
1618 category = strdup(pend->board_type);
1621 delete_pending(pend);
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");
1628 if (board != NULL) {
1635 if (board != NULL) {
1640 } else { /* resume adjourned game */
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]);
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;
1656 player_globals.parray[p1].game = g;
1657 player_globals.parray[p1].opponent = p;
1658 player_globals.parray[p1].side = BLACK;
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;
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);
1677 pp->simul_info->onBoard = 0;
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");
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");
1691 pprintf(p, "You are already playing a game.\n");
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");
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);
1705 /* loon: checking for some crazy situations we can't allow :) */
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");
1714 pprintf(p, " can't begin a simul while playing a non-simul game.\n");
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;
1722 if (!(game_globals.garray[g].type == TYPE_UNTIMED))
1727 pend = add_request(p, p1, PEND_SIMUL);
1729 if ((param[1].type == TYPE_WORD) && (param[2].type == TYPE_WORD)) {
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);
1736 pend->category = strdup(param[1].val.word);
1737 pend->board_type = strdup(param[2].val.word);
1739 pend->category = NULL;
1740 pend->board_type = NULL;
1744 pprintf_highlight(p1, "%s", pp->name);
1746 pprintf_prompt(p1, " requests to continue an adjourned simul game.\n");
1747 pprintf(p, "Request to resume simul sent. Adjourned game found.\n");
1749 if (pend->category == NULL)
1750 pprintf_prompt(p1, " requests to join a simul match with you.\n");
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");
1759 int com_goboard(int p, param_list param)
1761 struct player *pp = &player_globals.parray[p];
1762 int on, g, p1, gamenum;
1764 if (pp->simul_info == NULL) {
1765 pprintf(p, "You are not giving a simul.\n");
1769 if (!pp->simul_info->numBoards) {
1770 pprintf(p, "You are not giving a simul.\n");
1774 if (param[0].type == TYPE_WORD) {
1776 p1 = player_find_part_login(param[0].val.word);
1778 pprintf(p, "No user named \"%s\" is logged in.\n", param[0].val.word);
1782 pprintf(p, "You can't goboard yourself!\n");
1786 gamenum = player_globals.parray[p1].game;
1788 pprintf (p,"%s is not playing a game.\n", player_globals.parray[p1].login);
1793 gamenum = param[0].val.integer - 1;
1798 on = pp->simul_info->onBoard;
1799 g = pp->simul_info->boards[on];
1801 pprintf(p, "You are already at that board!\n");
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) {
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");
1813 pprintf(p, "You are not playing that game/person.\n");
1815 pprintf(p, "You are only playing one board!\n");
1819 int com_simnext(int p, param_list param)
1821 struct player *pp = &player_globals.parray[p];
1824 if (pp->simul_info == NULL) {
1825 pprintf(p, "You are not giving a simul.\n");
1829 if (!pp->simul_info->numBoards) {
1830 pprintf(p, "You are not giving a simul.\n");
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];
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);
1845 pprintf(p, "You are only playing one board!\n");
1849 int com_simprev(int p, param_list param)
1851 struct player *pp = &player_globals.parray[p];
1854 if (pp->simul_info == NULL) {
1855 pprintf(p, "You are not giving a simul.\n");
1859 if (!pp->simul_info->numBoards) {
1860 pprintf(p, "You are not giving a simul.\n");
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];
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");
1872 player_goto_prev_board(p);
1874 pprintf(p, "You are only playing one board!\n");
1878 int com_simgames(int p, param_list param)
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);
1889 pprintf(p, "You are playing %d simultaneous games.\n",
1890 player_num_active_boards(p1));
1892 pprintf(p, "%s is playing %d simultaneous games.\n", player_globals.parray[p1].name,
1893 player_num_active_boards(p1));
1897 int com_simpass(int p, param_list param)
1899 struct player *pp = &player_globals.parray[p];
1906 p1 = game_globals.garray[g].white;
1908 if (player_globals.parray[p1].simul_info == NULL) {
1909 pprintf(p, "You are not participating in a simul.\n");
1913 if (!player_globals.parray[p1].simul_info->numBoards) {
1914 pprintf(p, "You are not participating in a simul.\n");
1918 pprintf(p, "You are the simul holder and cannot pass!\n");
1921 if (player_num_active_boards(p1) == 1) {
1922 pprintf(p, "This is the only game, so passing is futile.\n");
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");
1930 if (game_globals.garray[g].passes >= MAX_SIMPASS) {
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");
1938 decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
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);
1950 int com_simabort(int p, param_list param)
1952 struct player *pp = &player_globals.parray[p];
1954 if (pp->simul_info == NULL) {
1955 pprintf(p, "You are not giving a simul.\n");
1959 if (!pp->simul_info->numBoards) {
1960 pprintf(p, "You are not giving a simul.\n");
1963 decline_withdraw_offers(p, -1, -PEND_SIMUL,DO_DECLINE);
1964 game_ended(pp->simul_info->boards[pp->simul_info->onBoard],
1969 int com_simallabort(int p, param_list param)
1971 struct player *pp = &player_globals.parray[p];
1974 if (pp->simul_info == NULL) {
1975 pprintf(p, "You are not giving a simul.\n");
1979 if (!pp->simul_info->numBoards) {
1980 pprintf(p, "You are not giving a simul.\n");
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],
1993 int com_simadjourn(int p, param_list param)
1995 struct player *pp = &player_globals.parray[p];
1997 if (pp->simul_info == NULL) {
1998 pprintf(p, "You are not giving a simul.\n");
2002 if (!pp->simul_info->numBoards) {
2003 pprintf(p, "You are not giving a simul.\n");
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);
2012 int com_simalladjourn(int p, param_list param)
2014 struct player *pp = &player_globals.parray[p];
2017 if (pp->simul_info == NULL) {
2018 pprintf(p, "You are not giving a simul.\n");
2022 if (!pp->simul_info->numBoards) {
2023 pprintf(p, "You are not giving a simul.\n");
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);
2035 int com_moretime(int p, param_list param)
2037 struct player *pp = &player_globals.parray[p];
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");
2045 increment = param[0].val.integer;
2046 if (increment <= 0) {
2047 pprintf(p, "Moretime requires an integer value greater than zero.\n");
2053 if (increment > 600) {
2054 pprintf(p, "Moretime has a maximum limit of 600 seconds.\n");
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",
2063 pprintf_prompt(pp->opponent,
2064 "\nYour opponent has added %d seconds to your clock.\n",
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",
2072 pprintf_prompt(pp->opponent,
2073 "\nYour opponent has added %d seconds to your clock.\n",