diff options
Diffstat (limited to 'FICS/gameproc.c')
-rw-r--r-- | FICS/gameproc.c | 2164 |
1 files changed, 2164 insertions, 0 deletions
diff --git a/FICS/gameproc.c b/FICS/gameproc.c new file mode 100644 index 0000000..cd6398e --- /dev/null +++ b/FICS/gameproc.c @@ -0,0 +1,2164 @@ +/* gameproc.c + * + */ + +/* + fics - An internet chess server. + Copyright (C) 1993 Richard V. Nash + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +/* Revision history: + name email yy/mm/dd Change + Richard Nash 93/10/22 Created + Dave Herscovici 95/11/26 Split into two files; + Second is 'obsproc.c'. + Markus Uhlin 23/12/16 Fixed compiler warnings + Markus Uhlin 24/01/04 Fixed pprintf_prompt() calls + Markus Uhlin 24/03/30 Refactored and reformatted all + functions. + Markus Uhlin 24/03/30 Size-bounded string handling. + Markus Uhlin 24/04/13 Added usage of msnprintf(), + mstrlcpy() and mstrlcat(). + Markus Uhlin 24/05/05 Added usage of reallocarray(). + Markus Uhlin 25/03/09 Calc string length once +*/ + +#include "stdinclude.h" +#include "common.h" + +#include <err.h> + +#include "command.h" +#include "comproc.h" +#include "config.h" +#include "eco.h" +#include "ficsmain.h" +#include "gamedb.h" +#include "gameproc.h" +#include "lists.h" +#include "matchproc.h" +#include "maxxes-utils.h" +#include "movecheck.h" +#include "network.h" +#include "obsproc.h" +#include "playerdb.h" +#include "ratings.h" +#include "rmalloc.h" +#include "utils.h" + +#if __linux__ +#include <bsd/string.h> +#endif + +PUBLIC void +game_ended(int g, int winner, int why) +{ + char *NameOfWinner, *NameOfLoser; + char EndSymbol[10] = { '\0' }; + char outstr[200] = { '\0' }; + char tmp[200] = { '\0' }; + char winSymbol[10] = { '\0' }; + int beingplayed = 0; // i.e. it wasn't loaded for adjudication + int gl = garray[g].link; + int isDraw = 0; + int p; + int rate_change = 0; + int whiteResult; + + beingplayed = (parray[garray[g].black].game == g); + + msnprintf(outstr, sizeof outstr, "\n{Game %d (%s vs. %s) ", + (g + 1), + parray[garray[g].white].name, + parray[garray[g].black].name); + + garray[g].result = why; + garray[g].winner = winner; + + if (winner == WHITE) { + whiteResult = RESULT_WIN; + mstrlcpy(winSymbol, "1-0", sizeof winSymbol); + NameOfWinner = parray[garray[g].white].name; + NameOfLoser = parray[garray[g].black].name; + } else { + whiteResult = RESULT_LOSS; + mstrlcpy(winSymbol, "0-1", sizeof winSymbol); + NameOfWinner = parray[garray[g].black].name; + NameOfLoser = parray[garray[g].white].name; + } + + switch (why) { + case END_CHECKMATE: + msnprintf(tmp, sizeof tmp, "%s checkmated} %s\n", + NameOfLoser, + winSymbol); + mstrlcpy(EndSymbol, "Mat", sizeof EndSymbol); + rate_change = 1; + break; + case END_RESIGN: + msnprintf(tmp, sizeof tmp, "%s resigns} %s\n", + NameOfLoser, + winSymbol); + mstrlcpy(EndSymbol, "Res", sizeof EndSymbol); + rate_change = 1; + break; + case END_FLAG: + msnprintf(tmp, sizeof tmp, "%s forfeits on time} %s\n", + NameOfLoser, + winSymbol); + mstrlcpy(EndSymbol, "Fla", sizeof EndSymbol); + rate_change = 1; + break; + case END_STALEMATE: + mstrlcpy(tmp, "Game drawn by stalemate} 1/2-1/2\n", sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "Sta", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_AGREEDDRAW: + mstrlcpy(tmp, "Game drawn by mutual agreement} 1/2-1/2\n", + sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "Agr", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_BOTHFLAG: + mstrlcpy(tmp, "Game drawn because both players ran out of " + "time} 1/2-1/2\n", sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "Fla", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_REPETITION: + mstrlcpy(tmp, "Game drawn by repetition} 1/2-1/2\n", sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "Rep", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_50MOVERULE: + mstrlcpy(tmp, "Game drawn by the 50 move rule} 1/2-1/2\n", + sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "50", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_ADJOURN: + if (gl >= 0) { + mstrlcpy(tmp, "Bughouse game aborted.} *\n", + sizeof tmp); + whiteResult = RESULT_ABORT; + } else { + mstrlcpy(tmp, "Game adjourned by mutual agreement} *\n", + sizeof tmp); + game_save(g); + } + break; + case END_LOSTCONNECTION: + msnprintf(tmp, sizeof tmp, "%s lost connection; game ", + NameOfWinner); + + if (parray[garray[g].white].registered && + parray[garray[g].black].registered && + gl < 0) { + mstrlcpy(tmp, "adjourned} *\n", sizeof tmp); + game_save(g); + } else + mstrlcpy(tmp, "aborted} *\n", sizeof tmp); + whiteResult = RESULT_ABORT; + break; + case END_ABORT: + mstrlcpy(tmp, "Game aborted by mutual agreement} *\n", + sizeof tmp); + whiteResult = RESULT_ABORT; + break; + case END_COURTESY: + msnprintf(tmp, sizeof tmp, "Game courtesyaborted by %s} *\n", + NameOfWinner); + whiteResult = RESULT_ABORT; + break; + case END_COURTESYADJOURN: + if (gl >= 0) { + msnprintf(tmp, sizeof tmp, "Bughouse game " + "courtesyaborted by %s.} *\n", + NameOfWinner); + whiteResult = RESULT_ABORT; + } else { + msnprintf(tmp, sizeof tmp, "Game courtesyadjourned by " + "%s} *\n", + NameOfWinner); + game_save(g); + } + break; + case END_NOMATERIAL: + // Draw by insufficient material (e.g., lone K vs. lone K) + mstrlcpy(tmp, "Neither player has mating material} 1/2-1/2\n", + sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "NM ", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_FLAGNOMATERIAL: + msnprintf(tmp, sizeof tmp, "%s ran out of time and %s has no " + "material to mate} 1/2-1/2\n", + NameOfLoser, + NameOfWinner); + isDraw = 1; + mstrlcpy(EndSymbol, "TM ", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_ADJWIN: + msnprintf(tmp, sizeof tmp, "%s wins by adjudication} %s\n", + NameOfWinner, winSymbol); + mstrlcpy(EndSymbol, "Adj", sizeof EndSymbol); + rate_change = 1; + break; + case END_ADJDRAW: + mstrlcpy(tmp, "Game drawn by adjudication} 1/2-1/2\n", + sizeof tmp); + isDraw = 1; + mstrlcpy(EndSymbol, "Adj", sizeof EndSymbol); + rate_change = 1; + whiteResult = RESULT_DRAW; + break; + case END_ADJABORT: + mstrlcpy(tmp, "Game aborted by adjudication} *\n", sizeof tmp); + whiteResult = RESULT_ABORT; + break; + default: + mstrlcpy(tmp, "Hmm, the game ended and I don't know why} *\n", + sizeof tmp); + break; + } + + mstrlcat(outstr, tmp, sizeof outstr); + + if (beingplayed) { + pprintf_noformat(garray[g].white, "%s", outstr); + pprintf_noformat(garray[g].black, "%s", outstr); + + if (parray[garray[g].white].bell) + pprintf(garray[g].white, "\007"); + if (parray[garray[g].black].bell) + pprintf(garray[g].black, "\007"); + + garray[g].link = -1; // IanO: avoids recursion + + if (gl >= 0 && garray[gl].link >= 0) { + pprintf_noformat(garray[gl].white, "%s", outstr); + pprintf_noformat(garray[gl].black, "%s", outstr); + + game_ended(gl, CToggle(winner), why); + } + + for (p = 0; p < p_num; p++) { + if (p == garray[g].white || p == garray[g].black) + continue; + if (parray[p].status != PLAYER_PROMPT) + continue; + if (!parray[p].i_game && !player_is_observe(p, g)) + continue; + + pprintf_noformat(p, "%s", outstr); + pprintf_prompt(p, "%s", ""); + } + } + + if (garray[g].rated && rate_change) { + /* Adjust ratings */ + rating_update(g); + } else { + if (beingplayed) { + pprintf(garray[g].white, "No ratings adjustment done." + "\n"); + pprintf(garray[g].black, "No ratings adjustment done." + "\n"); + } + } + + if (rate_change && gl < 0) + game_write_complete(g, isDraw, EndSymbol); + + /* + * Mail off the moves + */ + if (parray[garray[g].white].automail) + pcommand(garray[g].white, "mailmoves"); + if (parray[garray[g].black].automail) + pcommand(garray[g].black, "mailmoves"); + + parray[garray[g].white].num_white++; + parray[garray[g].white].lastColor = WHITE; + parray[garray[g].black].num_black++; + parray[garray[g].black].lastColor = BLACK; + parray[garray[g].white].last_opponent = garray[g].black; + parray[garray[g].black].last_opponent = garray[g].white; + + if (beingplayed) { + parray[garray[g].white].game = -1; + parray[garray[g].black].game = -1; + parray[garray[g].white].opponent = -1; + parray[garray[g].black].opponent = -1; + + if (garray[g].white != commanding_player) + pprintf_prompt(garray[g].white, "%s", ""); + if (garray[g].black != commanding_player) + pprintf_prompt(garray[g].black, "%s", ""); + + if (parray[garray[g].white].simul_info.numBoards) + player_simul_over(garray[g].white, g, whiteResult); + } + + game_finish(g); +} + +PRIVATE int +was_promoted(game *g, int f, int r) +{ +#define BUGHOUSE_PAWN_REVERT 1 +#if BUGHOUSE_PAWN_REVERT + for (int i = g->numHalfMoves-2; i > 0; i -= 2) { + if (g->moveList[i].toFile == f && + g->moveList[i].toRank == r) { + if (g->moveList[i].piecePromotionTo) + return 1; + if (g->moveList[i].fromFile == ALG_DROP) + return 0; + f = g->moveList[i].fromFile; + r = g->moveList[i].fromRank; + } + } +#endif + return 0; +} + +PUBLIC int +pIsPlaying(int p) +{ + int g = parray[p].game; + int p1 = parray[p].opponent; + + if (g < 0 || garray[g].status == GAME_EXAMINE) { + pprintf(p, "You are not playing a game.\n"); + return 0; + } else if (garray[g].white != p && garray[g].black != p) { + /* + * oh oh; big bad game bug. + */ + + fprintf(stderr, "BUG: Player %s playing game %d according to " + "parray, but not according to garray.\n", + parray[p].name, (g + 1)); + pprintf(p, "Disconnecting you from game number %d.\n", (g + 1)); + parray[p].game = -1; + + if (p1 >= 0 && + parray[p1].game == g && + garray[g].white != p1 && + garray[g].black != p1) { + pprintf(p1, "Disconnecting you from game number %d.\n", + (g + 1)); + parray[p1].game = -1; + } + + return 0; + } else + return 1; +} + +PUBLIC void +process_move(int p, char *command) +{ + int g; + int i; + int len; + int result; + move_t move; + unsigned int now; + + if (parray[p].game < 0) { + pprintf(p, "You are not playing or examining a game.\n"); + return; + } + + player_decline_offers(p, -1, -PEND_SIMUL); + g = parray[p].game; + + if (garray[g].status != GAME_EXAMINE) { + if (!pIsPlaying(p)) // XXX + return; + if (parray[p].side != garray[g].game_state.onMove) { + pprintf(p, "It is not your move.\n"); + return; + } + if (garray[g].clockStopped) { + pprintf(p, "Game clock is paused, use \"unpause\" " + "to resume.\n"); + return; + } + } + + if ((len = strlen(command)) > 1) { + if (command[len - 2] == '=') { + switch (tolower(command[strlen(command) - 1])) { + case 'n': + parray[p].promote = KNIGHT; + break; + case 'b': + parray[p].promote = BISHOP; + break; + case 'r': + parray[p].promote = ROOK; + break; + case 'q': + parray[p].promote = QUEEN; + break; + default: + pprintf(p, "Don't understand that move.\n"); + return; + break; + } + } + } + + switch (parse_move(command, &garray[g].game_state, &move, + parray[p].promote)) { + case MOVE_ILLEGAL: + pprintf(p, "Illegal move.\n"); + return; + break; + case MOVE_AMBIGUOUS: + pprintf(p, "Ambiguous move.\n"); + return; + break; + default: + break; + } + + if (garray[g].status == GAME_EXAMINE) { + garray[g].numHalfMoves++; + + if (garray[g].numHalfMoves > garray[g].examMoveListSize) { + garray[g].examMoveListSize += 20; // Allocate 20 + // moves at a + // time + if (!garray[g].examMoveList) { + garray[g].examMoveList = + reallocarray(NULL, + sizeof(move_t), + garray[g].examMoveListSize); + if (garray[g].examMoveList == NULL) + err(1, "%s: reallocarray", __func__); + else + malloc_count++; + } else { + garray[g].examMoveList = + reallocarray(garray[g].examMoveList, + sizeof(move_t), + garray[g].examMoveListSize); + if (garray[g].examMoveList == NULL) + err(1, "%s: reallocarray", __func__); + } + } + + now = tenth_secs(); + + result = execute_move(&garray[g].game_state, &move, 1); + move.atTime = now; // XXX + move.tookTime = 0; + MakeFENpos(g, (char *)move.FENpos, ARRAY_SIZE(move.FENpos)); + garray[g].examMoveList[garray[g].numHalfMoves - 1] = move; + + /* + * roll back time + */ + + if (garray[g].game_state.onMove == WHITE) { + garray[g].wTime += + (garray[g].lastDecTime - garray[g].lastMoveTime); + } else { + garray[g].bTime += + (garray[g].lastDecTime - garray[g].lastMoveTime); + } + + // XXX: 'now' was assigned here + // <-- + + if (garray[g].numHalfMoves == 0) + garray[g].timeOfStart = now; + + garray[g].lastMoveTime = now; + garray[g].lastDecTime = now; + } else { // real game + i = parray[p].opponent; + + if (parray[i].simul_info.numBoards && + parray[i].simul_info.boards[parray[i].simul_info.onBoard] != + g) { + pprintf(p, "It isn't your turn: wait until the simul " + "giver is at your board.\n"); + return; + } + +#ifdef TIMESEAL + if (con[parray[p].socket].timeseal) { // Does he use timeseal? + if (parray[p].side == WHITE) { + int diff; + + garray[g].wLastRealTime = garray[g].wRealTime; + garray[g].wTimeWhenMoved = + con[parray[p].socket].time; + + diff = (garray[g].wTimeWhenMoved - + garray[g].wTimeWhenReceivedMove); + + if (diff < 0 || + garray[g].wTimeWhenReceivedMove == 0) { + /* + * Might seem weird - but + * could be caused by a person + * moving BEFORE he receives + * the board pos (this is + * possible due to lag) but + * it's safe to say he moved + * in 0 secs. + */ + garray[g].wTimeWhenReceivedMove = + garray[g].wTimeWhenMoved; + } else { + garray[g].wRealTime -= + (garray[g].wTimeWhenMoved - + garray[g].wTimeWhenReceivedMove); + } + } else if (parray[p].side == BLACK) { + int diff; + + garray[g].bLastRealTime = garray[g].bRealTime; + garray[g].bTimeWhenMoved = + con[parray[p].socket].time; + + diff = (garray[g].bTimeWhenMoved - + garray[g].bTimeWhenReceivedMove); + + if (diff < 0 || + garray[g].bTimeWhenReceivedMove == 0) { + /* + * Might seem weird - but + * could be caused by a person + * moving BEFORE he receives + * the board pos (this is + * possible due to lag) but + * it's safe to say he moved + * in 0 secs. + */ + garray[g].bTimeWhenReceivedMove = + garray[g].bTimeWhenMoved; + } else { + garray[g].bRealTime -= + (garray[g].bTimeWhenMoved - + garray[g].bTimeWhenReceivedMove); + } + } + } + + /* + * We need to reset the opp's time for receiving the + * board since the timeseal decoder only alters the + * time if it's 0. Otherwise the time would be changed + * if the player did a refresh which would screw up + * the timings. + */ + if (parray[p].side == WHITE) + garray[g].bTimeWhenReceivedMove = 0; + else + garray[g].wTimeWhenReceivedMove = 0; +#endif + + game_update_time(g); + + /* + * XXX: Maybe add autoflag here in the future? + */ + +#ifdef TIMESEAL + if (con[parray[p].socket].timeseal) { // Does he use timeseal? + if (parray[p].side == WHITE) { + garray[g].wRealTime += + (garray[g].wIncrement * 100); + garray[g].wTime = (garray[g].wRealTime / 100); + } else if (parray[p].side == BLACK) { + garray[g].bRealTime += + (garray[g].bIncrement * 100); + garray[g].bTime = (garray[g].bRealTime / 100); + } + } else { + if (garray[g].game_state.onMove == BLACK) + garray[g].bTime += garray[g].bIncrement; + if (garray[g].game_state.onMove == WHITE) + garray[g].wTime += garray[g].wIncrement; + } +#else + if (garray[g].game_state.onMove == BLACK) + garray[g].bTime += garray[g].bIncrement; + if (garray[g].game_state.onMove == WHITE) + garray[g].wTime += garray[g].wIncrement; +#endif + + /* + * Do the move. + */ + + garray[g].numHalfMoves++; + + if (garray[g].numHalfMoves > garray[g].moveListSize) { + garray[g].moveListSize += 20; // Allocate 20 moves at + // a time + + if (!garray[g].moveList) { + garray[g].moveList = + reallocarray(NULL, + sizeof(move_t), + garray[g].moveListSize); + if (garray[g].moveList == NULL) + err(1, "%s: reallocarray", __func__); + else + malloc_count++; + } else { + garray[g].moveList = + reallocarray(garray[g].moveList, + sizeof(move_t), + garray[g].moveListSize); + if (garray[g].moveList == NULL) + err(1, "%s: reallocarray", __func__); + } + } + + result = execute_move(&garray[g].game_state, &move, 1); + + if (result == MOVE_OK && + garray[g].link >= 0 && + move.pieceCaptured != NOPIECE) { + /* + * Transfer captured piece to partner. + * Check if piece reverts to a pawn. + */ + if (was_promoted(&garray[g], move.toFile, move.toRank)) { + update_holding(garray[g].link, + colorval(move.pieceCaptured) | PAWN); + } else { + update_holding(garray[g].link, + move.pieceCaptured); + } + } + + now = tenth_secs(); + move.atTime = now; + + if (garray[g].numHalfMoves > 1) + move.tookTime = (move.atTime - garray[g].lastMoveTime); + else + move.tookTime = (move.atTime - garray[g].startTime); + + garray[g].lastMoveTime = now; + garray[g].lastDecTime = now; + +#ifdef TIMESEAL + if (con[parray[p].socket].timeseal) { // Does he use timeseal? + if (parray[p].side == WHITE) { + move.tookTime = + ((garray[parray[p].game].wTimeWhenMoved - + garray[parray[p].game].wTimeWhenReceivedMove) / + 100); + } else { + move.tookTime = + ((garray[parray[p].game].bTimeWhenMoved - + garray[parray[p].game].bTimeWhenReceivedMove) / + 100); + } + } +#endif + + MakeFENpos(g, (char *)move.FENpos, ARRAY_SIZE(move.FENpos)); + garray[g].moveList[garray[g].numHalfMoves - 1] = move; + } + + send_boards(g); + + if (result == MOVE_ILLEGAL) + pprintf(p, "Internal error, illegal move accepted!\n"); + if (result == MOVE_OK && garray[g].status == GAME_EXAMINE) { + for (int p1 = 0; p1 < p_num; p1++) { + if (parray[p1].status != PLAYER_PROMPT) + continue; + if (player_is_observe(p1, g) || parray[p1].game == g) { + pprintf_prompt(p1, "%s moves: %s\n", + parray[p].name, move.algString); + } + } + } + + if (result == MOVE_CHECKMATE) { + if (garray[g].status == GAME_EXAMINE) { + for (int p1 = 0; p1 < p_num; p1++) { + if (parray[p1].status != PLAYER_PROMPT) + continue; + if (player_is_observe(p1, g) || + parray[p1].game == g) { + pprintf(p1, "%s has been checkmated.\n", + (CToggle(garray[g].game_state.onMove) + == BLACK ? "White" : "Black")); + } + } + } else { + game_ended(g, CToggle(garray[g].game_state.onMove), + END_CHECKMATE); + } + } + + if (result == MOVE_STALEMATE) { + if (garray[g].status == GAME_EXAMINE) { + for (int p1 = 0; p1 < p_num; p1++) { + if (parray[p1].status != PLAYER_PROMPT) + continue; + if (player_is_observe(p1, g) || + parray[p1].game == g) + pprintf(p1, "Stalemate.\n"); + } + } else { + game_ended(g, CToggle(garray[g].game_state.onMove), + END_STALEMATE); + } + } + + if (result == MOVE_NOMATERIAL) { + if (garray[g].status == GAME_EXAMINE) { + for (int p1 = 0; p1 < p_num; p1++) { + if (parray[p1].status != PLAYER_PROMPT) + continue; + if (player_is_observe(p1, g) || + parray[p1].game == g) + pprintf(p1, "No mating material.\n"); + } + } else { + game_ended(g, CToggle(garray[g].game_state.onMove), + END_NOMATERIAL); + } + } +} + +PUBLIC int +com_resign(int p, param_list param) +{ + int g, o, oconnected; + + if (param[0].type == TYPE_NULL) { + g = parray[p].game; + + if (!pIsPlaying(p)) + return COM_OK; + else { + player_decline_offers(p, -1, -1); + game_ended(g, (garray[g].white == p ? BLACK : WHITE), + END_RESIGN); + } + } else if (FindPlayer(p, param[0].val.word, &o, &oconnected)) { + g = game_new(); + + if (game_read(g, p, o) < 0) { + if (game_read(g, o, p) < 0) { + pprintf(p, "You have no stored game with %s\n", + parray[o].name); + if (!oconnected) + player_remove(o); + return COM_OK; + } else { + garray[g].white = o; + garray[g].black = p; + } + } else { + garray[g].white = p; + garray[g].black = o; + } + + pprintf(p, "You resign your stored game with %s\n", + parray[o].name); + + game_delete(garray[g].white, garray[g].black); + game_ended(g, (garray[g].white == p ? BLACK : WHITE), + END_RESIGN); + + pcommand(p, "message %s I have resigned our stored game " + "\"%s vs. %s.\"", + parray[o].name, + parray[garray[g].white].name, + parray[garray[g].black].name); + + if (!oconnected) + player_remove(o); + } + + return COM_OK; +} + +PRIVATE int +Check50MoveRule(int p, int g) +{ + int num_reversible = garray[g].numHalfMoves; + + if (garray[g].game_state.lastIrreversable >= 0) + num_reversible -= garray[g].game_state.lastIrreversable; + + if (num_reversible > 99) { + game_ended(g, (garray[g].white == p ? BLACK : WHITE), + END_50MOVERULE); + return 1; + } + + return 0; +} + +PRIVATE char * +GetFENpos(int g, int half_move) +{ + if (half_move < 0) + return ((char *)garray[g].FENstartPos); + return ((char *)garray[g].moveList[half_move].FENpos); +} + +PRIVATE int +CheckRepetition(int p, int g) +{ + char *pos1 = GetFENpos(g, garray[g].numHalfMoves - 1); + char *pos2 = GetFENpos(g, garray[g].numHalfMoves); + char *pos; + int flag1 = 1, flag2 = 1; + int move_num; + size_t len[3]; + + if (garray[g].numHalfMoves < 8) // Can't have three repeats any quicker. + return 0; + + len[0] = strlen(pos1); + len[1] = strlen(pos2); + + for (move_num = garray[g].game_state.lastIrreversable; + move_num < garray[g].numHalfMoves - 1; + move_num++) { + pos = GetFENpos(g, move_num); + len[2] = strlen(pos); + + if (len[0] == len[2] && !strcmp(pos1, pos)) + flag1++; + if (len[1] == len[2] && !strcmp(pos2, pos)) + flag2++; + } + + if (flag1 >= 3 || flag2 >= 3) { + if (player_find_pendfrom(p, parray[p].opponent, PEND_DRAW) + >= 0) { + player_remove_request(parray[p].opponent, p, PEND_DRAW); + player_decline_offers(p, -1, -1); + } + + game_ended(g, (garray[g].white == p ? BLACK : WHITE), + END_REPETITION); + return 1; + } else + return 0; +} + +PUBLIC int +com_draw(int p, param_list param) +{ + int p1, g = parray[p].game; + + ASSERT(param[0].type == TYPE_NULL); + + if (!pIsPlaying(p)) + return COM_OK; + if (Check50MoveRule(p, g) || CheckRepetition(p, g)) + return COM_OK; + + p1 = parray[p].opponent; + + if (parray[p1].simul_info.numBoards && + parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) { + pprintf(p, "You can only make requests when the simul player " + "is at your board.\n"); + return COM_OK; + } + + if (player_find_pendfrom(p, parray[p].opponent, PEND_DRAW) >= 0) { + player_remove_request(parray[p].opponent, p, PEND_DRAW); + player_decline_offers(p, -1, -1); + game_ended(g, (garray[g].white == p ? BLACK : WHITE), + END_AGREEDDRAW); + } else { + pprintf(parray[p].opponent, "\n"); + pprintf_highlight(parray[p].opponent, "%s", parray[p].name); + pprintf_prompt(parray[p].opponent, " offers you a draw.\n"); + pprintf(p, "Draw request sent.\n"); + player_add_request(p, parray[p].opponent, PEND_DRAW, 0); + } + + return COM_OK; +} + +PUBLIC int +com_pause(int p, param_list param) +{ + int g; + int now; + + ASSERT(param[0].type == TYPE_NULL); + + if (!pIsPlaying(p)) + return COM_OK; + + g = parray[p].game; + + if (garray[g].wTime == 0) { + pprintf(p, "You can't pause untimed games.\n"); + return COM_OK; + } + + if (garray[g].clockStopped) { + pprintf(p, "Game is already paused, " + "use \"unpause\" to resume.\n"); + return COM_OK; + } + + if (player_find_pendfrom(p, parray[p].opponent, PEND_PAUSE) >= 0) { + player_remove_request(parray[p].opponent, p, PEND_PAUSE); + garray[g].clockStopped = 1; + + // Roll back the time + if (garray[g].game_state.onMove == WHITE) { + garray[g].wTime += (garray[g].lastDecTime - + garray[g].lastMoveTime); + } else { + garray[g].bTime += (garray[g].lastDecTime - + garray[g].lastMoveTime); + } + + now = tenth_secs(); + + if (garray[g].numHalfMoves == 0) + garray[g].timeOfStart = now; + + garray[g].lastMoveTime = now; + garray[g].lastDecTime = now; + + send_boards(g); + + pprintf_prompt(parray[p].opponent, "\n%s accepted pause. " + "Game clock paused.\n", parray[p].name); + pprintf(p, "Game clock paused.\n"); + } else { + pprintf(parray[p].opponent, "\n"); + pprintf_highlight(parray[p].opponent, "%s", parray[p].name); + pprintf_prompt(parray[p].opponent, " requests to pause the " + "game.\n"); + pprintf(p, "Pause request sent.\n"); + player_add_request(p, parray[p].opponent, PEND_PAUSE, 0); + } + + return COM_OK; +} + +PUBLIC int +com_unpause(int p, param_list param) +{ + int g; + int now; + + ASSERT(param[0].type == TYPE_NULL); + + if (!pIsPlaying(p)) + return COM_OK; + + g = parray[p].game; + + if (!garray[g].clockStopped) { + pprintf(p, "Game is not paused.\n"); + return COM_OK; + } + + garray[g].clockStopped = 0; + now = tenth_secs(); + + if (garray[g].numHalfMoves == 0) + garray[g].timeOfStart = now; + + garray[g].lastMoveTime = now; + garray[g].lastDecTime = now; + + send_boards(g); + + pprintf(p, "Game clock resumed.\n"); + pprintf_prompt(parray[p].opponent, "\nGame clock resumed.\n"); + + return COM_OK; +} + +PUBLIC int +com_abort(int p, param_list param) +{ + int courtesyOK = 1; + int p1, g, myColor, yourColor, myGTime, yourGTime; + + ASSERT(param[0].type == TYPE_NULL); + + g = parray[p].game; + + if (!pIsPlaying(p)) + return COM_OK; + + p1 = parray[p].opponent; + + if (p == garray[g].white) { + myColor = WHITE; + yourColor = BLACK; + myGTime = garray[g].wTime; + yourGTime = garray[g].bTime; + } else { + myColor = BLACK; + yourColor = WHITE; + myGTime = garray[g].bTime; + yourGTime = garray[g].wTime; + } + + if (parray[p1].simul_info.numBoards && + parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) { + pprintf(p, "You can only make requests when the simul player " + "is at your board.\n"); + return COM_OK; + } + + if (player_find_pendfrom(p, p1, PEND_ABORT) >= 0) { + player_remove_request(p1, p, PEND_ABORT); + player_decline_offers(p, -1, -1); + game_ended(g, yourColor, END_ABORT); + } else { + game_update_time(g); + +#ifdef TIMESEAL + if (con[parray[p].socket].timeseal && + garray[g].game_state.onMove == myColor && + garray[g].flag_pending == FLAG_ABORT) { + /* + * It's my move, opponent has asked for abort; + * I lagged out, my timeseal prevented + * courtesyabort, and I sent an abort request + * before acknowledging (and processing) my + * opponent's courtesyabort. OK, let's abort + * already :-). + */ + + player_decline_offers(p, -1, -1); + game_ended(g, yourColor, END_ABORT); + } + + if (con[parray[p1].socket].timeseal) { // Opp uses timeseal? + int yourRealTime = (myColor == WHITE ? + garray[g].bRealTime : garray[g].wRealTime); + + if (myGTime > 0 && yourGTime <= 0 && yourRealTime > 0) { + /* + * Override courtesyabort; opponent + * still has time. Check for lag. + */ + courtesyOK = 0; + + if (garray[g].game_state.onMove != myColor && + garray[g].flag_pending != FLAG_CHECKING) { + // Opponent may be lagging; let's ask. + garray[g].flag_pending = FLAG_ABORT; + garray[g].flag_check_time = time(0); + + pprintf(p, "Opponent has timeseal; " + "trying to courtesyabort.\n"); + pprintf(p1, "\n[G]\n"); + + return COM_OK; + } + } + } +#endif + + if (myGTime > 0 && yourGTime <= 0 && courtesyOK) { + /* + * Player wants to abort + opponent is out of + * time = courtesyabort. + */ + + pprintf(p, "Since you have time, and your opponent " + "has none, the game has been aborted."); + pprintf(p1, "Your opponent has aborted the game " + "rather than calling your flag."); + player_decline_offers(p, -1, -1); + game_ended(g, myColor, END_COURTESY); + } else { + pprintf(p1, "\n"); + pprintf_highlight(p1, "%s", parray[p].name); + pprintf(p1, " would like to abort the game; "); + pprintf_prompt(p1, "type \"abort\" to accept.\n"); + pprintf(p, "Abort request sent.\n"); + player_add_request(p, p1, PEND_ABORT, 0); + } + } + + return COM_OK; +} + +PUBLIC int +com_courtesyabort(int p, param_list param) +{ + pprintf(p, "Courtesyabort is obsolete; use \"abort\" instead.\n"); + return COM_OK; +} + +PUBLIC int +com_courtesyadjourn(int p, param_list param) +{ + pprintf(p, "Use \"adjourn\" to courtesyadjourn a game.\n"); + return COM_OK; +} + +PRIVATE int +player_has_mating_material(game_state_t *gs, int color) +{ + int i, j; + int minor_pieces = 0; + int piece; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + piece = gs->board[i][j]; + + switch (piecetype(piece)) { + case BISHOP: + case KNIGHT: + if (iscolor(piece, color)) + minor_pieces++; + break; + case KING: + case NOPIECE: + break; + default: + if (iscolor(piece, color)) + return 1; + } + } + } + + return (minor_pieces > 1 ? 1 : 0); +} + +PUBLIC int +com_flag(int p, param_list param) +{ + int g; + int myColor; + + ASSERT(param[0].type == TYPE_NULL); + + if (!pIsPlaying(p)) + return COM_OK; + + g = parray[p].game; + myColor = (p == garray[g].white ? WHITE : BLACK); + + if (garray[g].type == TYPE_UNTIMED) { + pprintf(p, "You can't flag an untimed game.\n"); + return COM_OK; + } + + if (garray[g].numHalfMoves < 2) { + pprintf(p, "You cannot flag before both players have moved.\n" + "Use abort instead.\n"); + return COM_OK; + } + + game_update_time(g); + +#ifdef TIMESEAL + { + int myTime, yourTime, serverTime; + int opp = parray[p].opponent; + + if (con[parray[p].socket].timeseal) { // Does the caller use + // timeseal? + myTime = (myColor == WHITE ? garray[g].wRealTime : + garray[g].bRealTime); + } else { + myTime = (myColor == WHITE ? garray[g].wTime : + garray[g].bTime); + } + + serverTime = (myColor == WHITE ? garray[g].bTime : + garray[g].wTime); + + if (con[parray[opp].socket].timeseal) { // Opp uses timeseal? + yourTime = (myColor == WHITE ? garray[g].bRealTime : + garray[g].wRealTime); + } else { + yourTime = serverTime; + } + + // The clocks to compare are now in 'myTime' and 'yourTime'. + if (myTime <= 0 && yourTime <= 0) { + player_decline_offers(p, -1, -1); + game_ended(g, myColor, END_BOTHFLAG); + return COM_OK; + } + + if (yourTime > 0) { + /* + * Opponent still has time, but if that's only + * because s/he may be lagging, we should ask + * for an acknowledgement and then try to call + * the flag. + */ + + if (serverTime <= 0 && + garray[g].game_state.onMove != myColor && + garray[g].flag_pending != FLAG_CHECKING) { + garray[g].flag_pending = FLAG_CALLED; + garray[g].flag_check_time = time(0); + + pprintf(p, "Opponent has timeseal; " + "checking if (s)he's lagging.\n"); + pprintf(opp, "\n[G]\n"); + + return COM_OK; + } + + /* + * If we're here, it means: + * 1) The server agrees opponent has time, whether + * lagging or not. + * 2) Opponent has timeseal (if yourTime != serverTime), + * had time left after the last move (yourTime > 0), + * and it's still your move. + * 3) We're currently checking a flag call after having + * receiving acknowledgement from the other timeseal + * (and would have reset 'yourTime' if the flag were + * down). + */ + pprintf(p, "Your opponent is not out of time!\n"); + return COM_OK; + } + } +#else // !defined(TIMESEAL) + if (garray[g].wTime <= 0 && garray[g].bTime <= 0) { + player_decline_offers(p, -1, -1); + game_ended(g, myColor, END_BOTHFLAG); + return COM_OK; + } + + if (myColor == WHITE) { + if (garray[g].bTime > 0) { + pprintf(p, "Your opponent is not out of time!\n"); + return COM_OK; + } + } else { + if (garray[g].wTime > 0) { + pprintf(p, "Your opponent is not out of time!\n"); + return COM_OK; + } + } +#endif + + player_decline_offers(p, -1, -1); + + if (player_has_mating_material(&garray[g].game_state, myColor)) + game_ended(g, myColor, END_FLAG); + else + game_ended(g, myColor, END_FLAGNOMATERIAL); + + return COM_OK; +} + +PUBLIC int +com_adjourn(int p, param_list param) +{ + int p1, g, myColor, yourColor; + + ASSERT(param[0].type == TYPE_NULL); + + if (!pIsPlaying(p)) + return COM_OK; + + p1 = parray[p].opponent; + g = parray[p].game; + + if (!(parray[p].registered && parray[p1].registered)) { + pprintf(p, "Both players must be registered to adjorn a game. " + "Use \"abort\".\n"); + return COM_OK; + } + + if (garray[g].link >= 0) { + pprintf(p, "Bughouse games cannot be adjourned.\n"); + return COM_OK; + } + + myColor = (p == garray[g].white ? WHITE : BLACK); + yourColor = (myColor == WHITE ? BLACK : WHITE); + + if (player_find_pendfrom(p, p1, PEND_ADJOURN) >= 0) { + player_remove_request(p1, p, PEND_ADJOURN); + player_decline_offers(p, -1, -1); + game_ended(parray[p].game, yourColor, END_ADJOURN); + } else { + game_update_time(g); + + if ((myColor == WHITE && + garray[g].wTime > 0 && + garray[g].bTime <= 0) || + (myColor == BLACK && + garray[g].bTime > 0 && + garray[g].wTime <= 0)) { + /* + * Player wants to adjourn + opponent is out + * of time = courtesyadjourn. + */ + + pprintf(p, "Since you have time, and your opponent " + "has none, the game has been adjourned."); + pprintf(p1, "Your opponent has adjourned the game " + "rather than calling your flag."); + player_decline_offers(p, -1, -1); + game_ended(g, myColor, END_COURTESYADJOURN); + } else { + pprintf(p1, "\n"); + pprintf_highlight(p1, "%s", parray[p].name); + pprintf(p1, " would like to adjourn the game; "); + pprintf_prompt(p1, "type \"adjourn\" to accept.\n"); + pprintf(p, "Adjourn request sent.\n"); + player_add_request(p, p1, PEND_ADJOURN, 0); + } + } + + return COM_OK; +} + +PUBLIC int +com_takeback(int p, param_list param) +{ + int from; + int g, i; + int nHalfMoves = 1; + int p1; + + if (!pIsPlaying(p)) + return COM_OK; + + p1 = parray[p].opponent; + + if (parray[p1].simul_info.numBoards && + parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != + parray[p].game) { + pprintf(p, "You can only make requests when the simul player " + "is at your board.\n"); + return COM_OK; + } + + g = parray[p].game; + + if (garray[g].link >= 0) { + pprintf(p, "Takeback not implemented for bughouse games yet." + "\n"); + return COM_OK; + } + + if (param[0].type == TYPE_INT) + nHalfMoves = param[0].val.integer; + + if ((from = player_find_pendfrom(p, parray[p].opponent, PEND_TAKEBACK)) + >= 0) { + player_remove_request(parray[p].opponent, p, PEND_TAKEBACK); + + if (parray[p].p_from_list[from].param1 == nHalfMoves) { + // Doing the takeback + player_decline_offers(p, -1, -PEND_SIMUL); + + for (i = 0; i < nHalfMoves; i++) { + if (backup_move(g, REL_GAME) != MOVE_OK) { + pprintf(garray[g].white, "Can only " + "backup %d moves\n", i); + pprintf(garray[g].black, "Can only " + "backup %d moves\n", i); + break; + } + } + +#ifdef TIMESEAL + garray[g].wTimeWhenReceivedMove = 0; + garray[g].bTimeWhenReceivedMove = 0; +#endif + + send_boards(g); + } else { + if (garray[g].numHalfMoves < nHalfMoves) { + pprintf(p, "There are only %d half moves in " + "your game.\n", garray[g].numHalfMoves); + pprintf_prompt(parray[p].opponent, "\n%s has " + "declined the takeback request.\n", + parray[p].name); + return COM_OK; + } + + pprintf(p, "You disagree on the number of half-moves " + "to takeback.\n"); + pprintf(p, "Alternate takeback request sent.\n"); + pprintf_prompt(parray[p].opponent, "\n%s proposes a " + "different number (%d) of half-move(s).\n", + parray[p].name, + nHalfMoves); + player_add_request(p, parray[p].opponent, PEND_TAKEBACK, + nHalfMoves); + } + } else { + if (garray[g].numHalfMoves < nHalfMoves) { + pprintf(p, "There are only %d half moves in your game." + "\n", garray[g].numHalfMoves); + return COM_OK; + } + + pprintf(parray[p].opponent, "\n"); + pprintf_highlight(parray[p].opponent, "%s", parray[p].name); + pprintf_prompt(parray[p].opponent, " would like to take back " + "%d half move(s).\n", nHalfMoves); + pprintf(p, "Takeback request sent.\n"); + player_add_request(p, parray[p].opponent, PEND_TAKEBACK, + nHalfMoves); + } + + return COM_OK; +} + +PUBLIC int +com_switch(int p, param_list param) +{ + char *strTmp; + int g = parray[p].game; + int p1; + int tmp, now; + + if (!pIsPlaying(p)) + return COM_OK; + + p1 = parray[p].opponent; + + if (parray[p1].simul_info.numBoards && + parray[p1].simul_info.boards[parray[p1].simul_info.onBoard] != g) { + pprintf(p, "You can only make requests when the simul player " + "is at your board.\n"); + return COM_OK; + } + + if (garray[g].link >= 0) { + pprintf(p, "Switch not implemented for bughouse games.\n"); + return COM_OK; + } + + if (player_find_pendfrom(p, parray[p].opponent, PEND_SWITCH) >= 0) { + player_remove_request(parray[p].opponent, p, PEND_SWITCH); + player_decline_offers(p, -1, -PEND_SIMUL); + + tmp = garray[g].white; + garray[g].white = garray[g].black; + garray[g].black = tmp; + parray[p].side = (parray[p].side == WHITE ? BLACK : WHITE); + + strTmp = xstrdup(garray[g].white_name); + mstrlcpy(garray[g].white_name, garray[g].black_name, + sizeof(garray[g].white_name)); + mstrlcpy(garray[g].black_name, strTmp, + sizeof(garray[g].black_name)); + strfree(strTmp); + + parray[parray[p].opponent].side = + (parray[parray[p].opponent].side == WHITE ? BLACK : WHITE); + + // Roll back the time + if (garray[g].game_state.onMove == WHITE) { + garray[g].wTime += (garray[g].lastDecTime - + garray[g].lastMoveTime); + } else { + garray[g].bTime += (garray[g].lastDecTime - + garray[g].lastMoveTime); + } + + now = tenth_secs(); + + if (garray[g].numHalfMoves == 0) + garray[g].timeOfStart = now; + + garray[g].lastMoveTime = now; + garray[g].lastDecTime = now; + + send_boards(g); + return COM_OK; + } + + if (garray[g].rated && garray[g].numHalfMoves > 0) { + pprintf(p, "You cannot switch sides once a rated game is " + "underway.\n"); + return COM_OK; + } + + pprintf(parray[p].opponent, "\n"); + pprintf_highlight(parray[p].opponent, "%s", parray[p].name); + pprintf_prompt(parray[p].opponent, " would like to switch sides.\n" + "Type \"accept\" to switch sides, or \"decline\" to refuse.\n"); + pprintf(p, "Switch request sent.\n"); + player_add_request(p, parray[p].opponent, PEND_SWITCH, 0); + + return COM_OK; +} + +PUBLIC int +com_time(int p, param_list param) +{ + int p1, g; + + if (param[0].type == TYPE_NULL) { + g = parray[p].game; + + if (!pIsPlaying(p)) + return COM_OK; + } else { + if ((g = GameNumFromParam(p, &p1, ¶m[0])) < 0) + return COM_OK; + } + + if (g < 0 || g >= g_num || garray[g].status != GAME_ACTIVE) { + pprintf(p, "There is no such game.\n"); + return COM_OK; + } + + game_update_time(g); + + pprintf(p, "White (%s) : %d mins, %d secs\n", + parray[garray[g].white].name, + garray[g].wTime / 600, + (garray[g].wTime - ((garray[g].wTime / 600) * 600)) / 10); + pprintf(p, "Black (%s) : %d mins, %d secs\n", + parray[garray[g].black].name, + garray[g].bTime / 600, + (garray[g].bTime - ((garray[g].bTime / 600) * 600)) / 10); + + return COM_OK; +} + +PUBLIC int +com_boards(int p, param_list param) +{ + DIR *dirp; + char *category = NULL; + char dname[MAX_FILENAME_SIZE] = { '\0' }; +#ifdef USE_DIRENT + struct dirent *dp; +#else + struct direct *dp; +#endif + + if (param[0].type == TYPE_WORD) + category = param[0].val.word; + + if (category) { + pprintf(p, "Boards Available For Category %s:\n", category); + msnprintf(dname, sizeof dname, "%s/%s", board_dir, category); + } else { + pprintf(p, "Categories Available:\n"); + msnprintf(dname, sizeof dname, "%s", board_dir); + } + + if ((dirp = opendir(dname)) == NULL) { + pprintf(p, "No such category %s, try \"boards\".\n", category); + return COM_OK; + } + + // YUK! What a mess, how about printing an ordered directory? - DAV + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + if (!strcmp(dp->d_name, ".")) + continue; + if (!strcmp(dp->d_name, "..")) + continue; + pprintf(p, "%s\n", dp->d_name); + } + + closedir(dirp); + return COM_OK; +} + +PUBLIC int +com_simmatch(int p, param_list param) +{ + char tmp[100] = { '\0' }; + int num; + int p1, g, adjourned; + + if (parray[p].game >= 0 && garray[parray[p].game].status == + GAME_EXAMINE) { + pprintf(p, "You are still examining a game.\n"); + return COM_OK; + } + + p1 = player_find_part_login(param[0].val.word); + + if (p1 < 0) { + pprintf(p, "No user named \"%s\" is logged in.\n", + param[0].val.word); + return COM_OK; + } + + if (p == p1) { + pprintf(p, "You can't simmatch yourself!\n"); + return COM_OK; + } + + if (player_find_pendfrom(p, p1, PEND_SIMUL) >= 0) { + player_remove_request(p, p1, PEND_MATCH); + player_remove_request(p1, p, PEND_MATCH); + player_remove_request(p, p1, PEND_SIMUL); + player_remove_request(p1, p, PEND_SIMUL); + player_withdraw_offers(p, -1, PEND_SIMUL); + player_decline_offers(p1, -1, PEND_SIMUL); + player_withdraw_offers(p1, -1, PEND_SIMUL); + player_decline_offers(p, -1, PEND_MATCH); + player_withdraw_offers(p, -1, PEND_MATCH); + player_decline_offers(p1, -1, PEND_MATCH); + player_withdraw_offers(p1, -1, PEND_MATCH); + player_decline_offers(p, -1, PEND_PARTNER); + player_withdraw_offers(p, -1, PEND_PARTNER); + player_decline_offers(p1, -1, PEND_PARTNER); + player_withdraw_offers(p1, -1, PEND_PARTNER); + + if (parray[p].simul_info.numBoards >= MAX_SIMUL) { + pprintf(p, "You are already playing the maximum of %d " + "boards.\n", MAX_SIMUL); + pprintf(p1, "Simul request removed, boards filled.\n"); + return COM_OK; + } + + // stop observing when match starts + unobserveAll(p); + unobserveAll(p1); + + g = game_new(); + adjourned = 0; + + if (game_read(g, p, p1) >= 0) + adjourned = 1; + + if (!adjourned) { // no adjourned game - so begin a new game. + game_remove(g); + + if (create_new_match(p, p1, 0, 0, 0, 0, 0, "standard", + "standard", 1)) { + pprintf(p, "There was a problem creating the " + "new match.\n"); + pprintf_prompt(p1, "There was a problem " + "creating the new match.\n"); + return COM_OK; + } + } else { // resume adjourned game + game_delete(p, p1); + + msnprintf(tmp, sizeof tmp, "{Game %d (%s vs. %s) " + "Continuing %s %s simul.}\n", + (g + 1), + parray[p].name, + parray[p1].name, + rstr[garray[g].rated], + bstr[garray[g].type]); + + pprintf(p, "%s", tmp); + pprintf(p1, "%s", tmp); + + garray[g].white = p; + garray[g].black = p1; + garray[g].status = GAME_ACTIVE; + garray[g].startTime = tenth_secs(); + garray[g].lastMoveTime = garray[g].startTime; + garray[g].lastDecTime = garray[g].startTime; + parray[p].game = g; + parray[p].opponent = p1; + parray[p].side = WHITE; + parray[p1].game = g; + parray[p1].opponent = p; + parray[p1].side = BLACK; + + send_boards(g); + } + + num = parray[p].simul_info.numBoards; + + parray[p].simul_info.results[num] = -1; + parray[p].simul_info.boards[num] = parray[p].game; + parray[p].simul_info.numBoards++; + + if (parray[p].simul_info.numBoards > 1 && + parray[p].simul_info.onBoard >= 0) + player_goto_board(p, parray[p].simul_info.onBoard); + else + parray[p].simul_info.onBoard = 0; + return COM_OK; + } + + if (player_find_pendfrom(p, -1, PEND_SIMUL) >= 0) { + 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"); + return COM_OK; + } + + if (parray[p].simul_info.numBoards) { + 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"); + return COM_OK; + } + + if (parray[p].game >= 0) { + pprintf(p, "You are already playing a game.\n"); + return COM_OK; + } + + if (!parray[p1].sopen) { + pprintf_highlight(p, "%s", parray[p1].name); + pprintf(p, " is not open to receiving simul requests.\n"); + return COM_OK; + } + + if (parray[p1].simul_info.numBoards >= MAX_SIMUL) { + pprintf_highlight(p, "%s", parray[p1].name); + pprintf(p, " is already playing the maximum of %d boards.\n", + MAX_SIMUL); + return COM_OK; + } + + // loon: checking for some crazy situations we can't allow :) + if (parray[p1].game >= 0 && parray[p1].simul_info.numBoards == 0) { + pprintf_highlight(p, "%s", parray[p1].name); + + if (parray[garray[parray[p1].game].white].simul_info.numBoards) { + pprintf(p, " is playing in "); + pprintf_highlight(p, "%s", + parray[parray[p1].opponent].name); + pprintf(p, "'s simul, and can't accept.\n"); + } else { + pprintf(p, " can't begin a simul while playing a " + "non-simul game.\n"); + } + + return COM_OK; + } + + g = game_new(); + adjourned = ((game_read(g, p, p1) < 0 && game_read(g, p1, p) < 0) + ? 0 : 1); + + if (adjourned) { + if (!(garray[g].type == TYPE_UNTIMED)) + adjourned = 0; + } + + game_remove(g); + + if (player_add_request(p, p1, PEND_SIMUL, 0)) { + pprintf(p, "Maximum number of pending actions reached. " + "Your request was not sent.\nTry again later.\n"); + return COM_OK; + } else { + + pprintf(p1, "\n"); + pprintf_highlight(p1, "%s", parray[p].name); + + if (adjourned) { + pprintf_prompt(p1, " requests to continue an " + "adjourned simul game.\n"); + pprintf(p, "Request to resume simul sent. " + "Adjourned game found.\n"); + } else { + pprintf_prompt(p1, " requests to join a simul match " + "with you.\n"); + pprintf(p, "Simul match request sent.\n"); + } + } + + return COM_OK; +} + +PUBLIC int +com_goboard(int p, param_list param) +{ + int on, g, p1; + + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + p1 = player_find_part_login(param[0].val.word); + + if (p1 < 0) { + pprintf(p, "No user named \"%s\" is logged in.\n", + param[0].val.word); + return COM_OK; + } + + if (p == p1) { + pprintf(p, "You can't goboard yourself!\n"); + return COM_OK; + } + + on = parray[p].simul_info.onBoard; + + if ((g = parray[p].simul_info.boards[on]) < 0) { + pprintf(p, "Internal error! Unexpected negative value!\n"); + return COM_OK; + } + + if (p1 == garray[g].black) { + pprintf(p, "You are already at that board!\n"); + return COM_OK; + } + + if (parray[p].simul_info.numBoards > 1) { + player_decline_offers(p, -1, -PEND_SIMUL); + + if (player_goto_simulgame_bynum(p, parray[p1].game) != -1) { + if (g >= 0) { + pprintf(garray[g].black, "\n"); + pprintf_highlight(garray[g].black, "%s", + parray[p].name); + pprintf_prompt(garray[g].black, " has moved " + "away from your board.\n"); + } + } + } else + pprintf(p, "You are only playing one board!\n"); + return COM_OK; +} + +PUBLIC int +com_gonum(int p, param_list param) +{ + int on, g, gamenum; + + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + on = parray[p].simul_info.onBoard; + g = parray[p].simul_info.boards[on]; + gamenum = param[0].val.integer - 1; + + if (gamenum < 0) + gamenum = 0; + + if (on == gamenum) { + pprintf(p, "You are already at that board!\n"); + return COM_OK; + } + + if (parray[p].simul_info.numBoards > 1) { + player_decline_offers(p, -1, -PEND_SIMUL); + + if (player_goto_simulgame_bynum(p, gamenum) != -1) { + if (g >= 0) { + pprintf(garray[g].black, "\n"); + pprintf_highlight(garray[g].black, "%s", + parray[p].name); + pprintf_prompt(garray[g].black, " has moved " + "away from your board.\n"); + } + } + } else + pprintf(p, "You are only playing one board!\n"); + return COM_OK; +} + +PUBLIC int +com_simnext(int p, param_list param) +{ + int on, g; + + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + if (parray[p].simul_info.numBoards > 1) { + player_decline_offers(p, -1, -PEND_SIMUL); + on = parray[p].simul_info.onBoard; + g = parray[p].simul_info.boards[on]; + + if (g >= 0) { + pprintf(garray[g].black, "\n"); + pprintf_highlight(garray[g].black, "%s", + parray[p].name); + pprintf_prompt(garray[g].black, " is moving away from " + "your board.\n"); + player_goto_next_board(p); + } + } else + pprintf(p, "You are only playing one board!\n"); + return COM_OK; +} + +PUBLIC int +com_simprev(int p, param_list param) +{ + int on, g; + + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + if (parray[p].simul_info.numBoards > 1) { + player_decline_offers(p, -1, -PEND_SIMUL); + on = parray[p].simul_info.onBoard; + g = parray[p].simul_info.boards[on]; + + if (g >= 0) { + pprintf(garray[g].black, "\n"); + pprintf_highlight(garray[g].black, "%s", + parray[p].name); + pprintf_prompt(garray[g].black, " is moving back to " + "the previous board.\n"); + } + + player_goto_prev_board(p); + } else + pprintf(p, "You are only playing one board!\n"); + return COM_OK; +} + +PUBLIC int +com_simgames(int p, param_list param) +{ + int p1 = p; + + if (param[0].type == TYPE_WORD) { + if ((p1 = player_find_part_login(param[0].val.word)) < 0) { + pprintf(p, "No player named %s is logged in.\n", + param[0].val.word); + return COM_OK; + } + } + + if (p1 == p) { + pprintf(p, "You are playing %d simultaneous games.\n", + player_num_active_boards(p1)); + } else { + pprintf(p, "%s is playing %d simultaneous games.\n", + parray[p1].name, player_num_active_boards(p1)); + } + + return COM_OK; +} + +PUBLIC int +com_simpass(int p, param_list param) +{ + int g, p1, on; + + if (!pIsPlaying(p)) + return COM_OK; + + g = parray[p].game; + p1 = garray[g].white; + + if (!parray[p1].simul_info.numBoards) { + pprintf(p, "You are not participating in a simul.\n"); + return COM_OK; + } + + if (p == p1) { + pprintf(p, "You are the simul holder and cannot pass!\n"); + return COM_OK; + } + + if (player_num_active_boards(p1) == 1) { + pprintf(p, "This is the only game, so passing is futile.\n"); + return COM_OK; + } + + on = parray[p1].simul_info.onBoard; + + if (parray[p1].simul_info.boards[on] != g) { + pprintf(p, "You cannot pass until the simul holder arrives!\n"); + return COM_OK; + } + + if (garray[g].passes >= MAX_SIMPASS) { + if (parray[p].bell) + pprintf(p, "\a"); + pprintf(p, "You have reached your maximum of %d pass(es).\n", + MAX_SIMPASS); + pprintf(p, "Please move IMMEDIATELY!\n"); + pprintf_highlight(p1, "%s", parray[p].name); + pprintf_prompt(p1, " tried to pass, but is out of passes.\n"); + return COM_OK; + } + + player_decline_offers(p, -1, -PEND_SIMUL); + garray[g].passes++; + + pprintf(p, "You have passed and have %d pass(es) left.\n", + (MAX_SIMPASS - garray[g].passes)); + pprintf_highlight(p1, "%s", parray[p].name); + pprintf_prompt(p1, " has decided to pass and has %d pass(es) left.\n", + (MAX_SIMPASS - garray[g].passes)); + player_goto_next_board(p1); + + return COM_OK; +} + +PUBLIC int +com_simabort(int p, param_list param) +{ + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + player_decline_offers(p, -1, -PEND_SIMUL); + game_ended(parray[p].simul_info.boards[parray[p].simul_info.onBoard], + WHITE, END_ABORT); + + return COM_OK; +} + +PUBLIC int +com_simallabort(int p, param_list param) +{ + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + player_decline_offers(p, -1, -PEND_SIMUL); + + for (int i = 0; i < parray[p].simul_info.numBoards; i++) { + if (parray[p].simul_info.boards[i] >= 0) { + game_ended(parray[p].simul_info.boards[i], WHITE, + END_ABORT); + } + } + + return COM_OK; +} + +PUBLIC int +com_simadjourn(int p, param_list param) +{ + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + player_decline_offers(p, -1, -PEND_SIMUL); + game_ended(parray[p].simul_info.boards[parray[p].simul_info.onBoard], + WHITE, END_ADJOURN); + + return COM_OK; +} + +PUBLIC int +com_simalladjourn(int p, param_list param) +{ + if (!parray[p].simul_info.numBoards) { + pprintf(p, "You are not giving a simul.\n"); + return COM_OK; + } + + player_decline_offers(p, -1, -PEND_SIMUL); + + for (int i = 0; i < parray[p].simul_info.numBoards; i++) { + if (parray[p].simul_info.boards[i] >= 0) { + game_ended(parray[p].simul_info.boards[i], WHITE, + END_ADJOURN); + } + } + + return COM_OK; +} + +PUBLIC int +com_moretime(int p, param_list param) +{ + int g, increment; + + ASSERT(param[0].type == TYPE_INT); + + if (parray[p].game >= 0 && garray[parray[p].game].status == + GAME_EXAMINE) { + pprintf(p, "You cannot use moretime in an examined game.\n"); + return COM_OK; + } + + if ((increment = param[0].val.integer) <= 0) { + pprintf(p, "Moretime requires an integer value greater than " + "zero.\n"); + return COM_OK; + } + + if (!pIsPlaying(p)) + return COM_OK; + + if (increment > 600) { + pprintf(p, "Moretime has a maximum limit of 600 seconds.\n"); + increment = 600; + } + + g = parray[p].game; + + if (garray[g].white == p) { + garray[g].bTime += increment * 10; +#ifdef TIMESEAL + garray[g].bRealTime += increment * 10 * 100; +#endif + pprintf(p, "%d seconds were added to your opponents clock\n", + increment); + pprintf_prompt(parray[p].opponent, "\nYour opponent has " + "added %d seconds to your clock.\n", increment); + } + + if (garray[g].black == p) { + garray[g].wTime += increment * 10;; +#ifdef TIMESEAL + garray[g].wRealTime += increment * 10 * 100; +#endif + pprintf(p, "%d seconds were added to your opponents clock\n", + increment); + pprintf_prompt(parray[p].opponent, "\nYour opponent has " + "added %d seconds to your clock.\n", increment); + } + + return COM_OK; +} |