/* matchproc.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 hersco dhersco@stmarys-ca.edu 95/07/24 Created Markus Uhlin 23/12/14 Fixed compiler warnings Markus Uhlin 24/03/29 Refactored and reformatted all functions. Markus Uhlin 24/03/29 Size-bounded string handling. Markus Uhlin 24/03/29 Fixed potentially insecure format strings. */ #include "stdinclude.h" #include "common.h" #include #include #include "board.h" #include "command.h" #include "comproc.h" #include "eco.h" #include "formula.h" #include "gamedb.h" #include "lists.h" #include "matchproc.h" #include "network.h" #include "obsproc.h" #include "playerdb.h" #include "ratings.h" #include "rmalloc.h" #include "talkproc.h" #include "utils.h" #if __linux__ #include #endif struct print_bh_context { int pp; int pp1; int rated; int type; int white; char *board; char *category; int binc; int bt; int winc; int wt; }; PUBLIC int create_new_match(int white_player, int black_player, int wt, int winc, int bt, int binc, int rated, char *category, char *board, int white) { char outStr[1024] = { '\0' }; int g, p; int reverse = 0; if ((g = game_new()) < 0) return COM_FAILED; if (white == 0) { reverse = 1; } else if (white == -1) { if (wt == bt && winc == binc) { if (parray[white_player].lastColor == parray[black_player].lastColor) { int diff1, diff2; diff1 = (parray[white_player].num_white - parray[white_player].num_black); diff2 = (parray[black_player].num_white - parray[black_player].num_black); if (diff1 > diff2) reverse = 1; } else if (parray[white_player].lastColor == WHITE) reverse = 1; } else reverse = 1; // Challenger is always white in // unbalanced match } if (reverse) { int tmp = white_player; white_player = black_player; black_player = tmp; } player_remove_request(white_player, black_player, PEND_MATCH); player_remove_request(black_player, white_player, PEND_MATCH); player_remove_request(white_player, black_player, PEND_SIMUL); player_remove_request(black_player, white_player, PEND_SIMUL); player_decline_offers(white_player, -1, PEND_MATCH); player_withdraw_offers(white_player, -1, PEND_MATCH); player_decline_offers(black_player, -1, PEND_MATCH); player_withdraw_offers(black_player, -1, PEND_MATCH); player_withdraw_offers(white_player, -1, PEND_SIMUL); player_withdraw_offers(black_player, -1, PEND_SIMUL); wt = (wt * 60); // To Seconds bt = (bt * 60); garray[g].white = white_player; garray[g].black = black_player; strlcpy(garray[g].white_name, parray[white_player].name, sizeof(garray[g].white_name)); strlcpy(garray[g].black_name, parray[black_player].name, sizeof(garray[g].black_name)); garray[g].status = GAME_ACTIVE; garray[g].type = game_isblitz(wt / 60, winc, bt / 60, binc, category, board); if (garray[g].type == TYPE_UNTIMED || garray[g].type == TYPE_NONSTANDARD) garray[g].rated = 0; else garray[g].rated = rated; garray[g].private = (parray[white_player].private || parray[black_player].private); garray[g].white = white_player; if (garray[g].type == TYPE_BLITZ) { garray[g].white_rating = parray[white_player].b_stats.rating; garray[g].black_rating = parray[black_player].b_stats.rating; } else if (garray[g].type == TYPE_WILD) { garray[g].white_rating = parray[white_player].w_stats.rating; garray[g].black_rating = parray[black_player].w_stats.rating; } else if (garray[g].type == TYPE_LIGHT) { garray[g].white_rating = parray[white_player].l_stats.rating; garray[g].black_rating = parray[black_player].l_stats.rating; } else if (garray[g].type == TYPE_BUGHOUSE) { garray[g].white_rating = parray[white_player].bug_stats.rating; garray[g].black_rating = parray[black_player].bug_stats.rating; } else { garray[g].white_rating = parray[white_player].s_stats.rating; garray[g].black_rating = parray[black_player].s_stats.rating; } if (board_init(&garray[g].game_state, category, board)) { pprintf(white_player, "PROBLEM LOADING BOARD. Game Aborted.\n"); pprintf(black_player, "PROBLEM LOADING BOARD. Game Aborted.\n"); fprintf(stderr, "FICS: PROBLEM LOADING BOARD %s %s. " "Game Aborted.\n", category, board); } garray[g].game_state.gameNum = g; garray[g].wTime = (wt * 10); garray[g].wInitTime = (wt * 10); garray[g].wIncrement = (winc * 10); garray[g].bTime = (bt * 10); if (garray[g].type != TYPE_UNTIMED) { if (wt == 0) garray[g].wTime = 100; if (bt == 0) garray[g].bTime = 100; } // 0 x games start with 10 seconds #ifdef TIMESEAL garray[g].wRealTime = garray[g].wTime * 100; garray[g].bRealTime = garray[g].bTime * 100; garray[g].wTimeWhenReceivedMove = 0; garray[g].bTimeWhenReceivedMove = 0; #endif garray[g].bInitTime = bt * 10; garray[g].bIncrement = binc * 10; if (garray[g].game_state.onMove == BLACK) { // Start with black garray[g].numHalfMoves = 1; garray[g].moveListSize = 1; garray[g].moveList = rmalloc(sizeof(move_t)); garray[g].moveList[0].fromFile = -1; garray[g].moveList[0].fromRank = -1; garray[g].moveList[0].toFile = -1; garray[g].moveList[0].toRank = -1; garray[g].moveList[0].color = WHITE; strlcpy(garray[g].moveList[0].moveString, "NONE", sizeof(garray[g].moveList[0].moveString)); strlcpy(garray[g].moveList[0].algString, "NONE", sizeof(garray[g].moveList[0].algString)); } else { garray[g].numHalfMoves = 0; garray[g].moveListSize = 0; garray[g].moveList = NULL; } garray[g].timeOfStart = tenth_secs(); garray[g].startTime = tenth_secs(); garray[g].lastMoveTime = garray[g].startTime; garray[g].lastDecTime = garray[g].startTime; garray[g].clockStopped = 0; snprintf(outStr, sizeof outStr, "\n{Game %d (%s vs. %s) " "Creating %s %s match.}\n", (g + 1), parray[white_player].name, parray[black_player].name, rstr[garray[g].rated], bstr[garray[g].type]); pprintf(white_player, "%s", outStr); pprintf(black_player, "%s", outStr); for (p = 0; p < p_num; p++) { int gnw, gnb; if ((p == white_player) || (p == black_player)) continue; if (parray[p].status != PLAYER_PROMPT) continue; if (parray[p].i_game) pprintf_prompt(p, "%s", outStr); gnw = in_list(p, L_GNOTIFY, parray[white_player].login); gnb = in_list(p, L_GNOTIFY, parray[black_player].login); if (gnw || gnb) { pprintf(p, "Game notification: "); if (gnw) { pprintf_highlight(p, "%s", parray[white_player].name); } else { pprintf(p, "%s", parray[white_player].name); } pprintf(p, " (%s) vs. ", ratstr(GetRating(&parray[white_player], garray[g].type))); if (gnb) { pprintf_highlight(p, "%s", parray[black_player].name); } else { pprintf(p, "%s", parray[black_player].name); } pprintf_prompt(p, " (%s) %s %s %d %d\n", ratstr(GetRating(&parray[black_player], garray[g].type)), rstr[garray[g].rated], bstr[garray[g].type], (garray[g].wInitTime / 600), (garray[g].wIncrement / 10)); } } parray[white_player].game = g; parray[white_player].opponent = black_player; parray[white_player].side = WHITE; parray[white_player].promote = QUEEN; parray[black_player].game = g; parray[black_player].opponent = white_player; parray[black_player].side = BLACK; parray[black_player].promote = QUEEN; send_boards(g); MakeFENpos(g, (char *)garray[g].FENstartPos); return COM_OK; } PRIVATE int accept_match(int p, int p1) { char board[50] = { '\0' }; char category[50] = { '\0' }; char tmp[100] = { '\0' }; int bh = 0, pp, pp1; int g, adjourned, foo, which; int wt, winc, bt, binc, rated, white; pending *pend; // stop observing when match starts unobserveAll(p); unobserveAll(p1); which = player_find_pendfrom(p, p1, PEND_MATCH); pend = &parray[p].p_from_list[which]; wt = pend->param1; winc = pend->param2; bt = pend->param3; binc = pend->param4; rated = pend->param5; strlcpy(category, pend->char1, sizeof category); strlcpy(board, pend->char2, sizeof board); white = (pend->param6 == -1 ? -1 : (1 - pend->param6)); pprintf(p, "You accept the challenge of %s.\n", parray[p1].name); pprintf(p1, "\n%s accepts your challenge.\n", parray[p].name); player_remove_request(p, p1, -1); player_remove_request(p1, p, -1); while ((which = player_find_pendto(p, -1, -1)) != -1) { foo = parray[p].p_to_list[which].whoto; pprintf_prompt(foo, "\n%s, who was challenging you, " "has joined a match with %s.\n", parray[p].name, parray[p1].name); pprintf(p, "Challenge to %s withdrawn.\n", parray[foo].name); player_remove_request(p, foo, -1); } while ((which = player_find_pendto(p1, -1, -1)) != -1) { foo = parray[p1].p_to_list[which].whoto; pprintf_prompt(foo, "\n%s, who was challenging you, " "has joined a match with %s.\n", parray[p1].name, parray[p].name); pprintf(p1, "Challenge to %s withdrawn.\n", parray[foo].name); player_remove_request(p1, foo, -1); } while ((which = player_find_pendfrom(p, -1, -1)) != -1) { foo = parray[p].p_from_list[which].whofrom; pprintf_prompt(foo, "\n%s, whom you were challenging, " "has joined a match with %s.\n", parray[p].name, parray[p1].name); pprintf(p, "Challenge from %s removed.\n", parray[foo].name); player_remove_request(foo, p, -1); } while ((which = player_find_pendfrom(p1, -1, -1)) != -1) { foo = parray[p1].p_from_list[which].whofrom; pprintf_prompt(foo, "\n%s, whom you were challenging, " "has joined a match with %s.\n", parray[p1].name, parray[p].name); pprintf(p1, "Challenge from %s removed.\n", parray[foo].name); player_remove_request(foo, p1, -1); } if (game_isblitz(wt, winc, bt, binc, category, board) == TYPE_WILD && strcmp(board, "bughouse") == 0) { bh = 1; if ((pp = parray[p].partner) >= 0 && (pp1 = parray[p1].partner) >= 0) { // stop observing when match starts unobserveAll(pp); unobserveAll(pp1); pprintf(pp, "\nYour partner accepts the challenge of " "%s.\n", parray[p1].name); pprintf(pp1, "\nYour partner %s's challenge was " "accepted.\n", parray[p].name); while ((which = player_find_pendto(pp, -1, -1)) != -1) { foo = parray[pp].p_to_list[which].whoto; pprintf_prompt(foo, "\n%s, who was challenging " "you, has joined a match with %s.\n", parray[pp].name, parray[pp1].name); pprintf(pp, "Challenge to %s withdrawn.\n", parray[foo].name); player_remove_request(pp, foo, -1); } while ((which = player_find_pendto(pp1, -1, -1)) != -1) { foo = parray[pp1].p_to_list[which].whoto; pprintf_prompt(foo, "\n%s, who was challenging " "you, has joined a match with %s.\n", parray[pp1].name, parray[pp].name); pprintf(pp1, "Challenge to %s withdrawn.\n", parray[foo].name); player_remove_request(pp1, foo, -1); } while ((which = player_find_pendfrom(pp, -1, -1)) != -1) { foo = parray[pp].p_from_list[which].whofrom; pprintf_prompt(foo, "\n%s, whom you were " "challenging, has joined a match with %s." "\n", parray[pp].name, parray[pp1].name); pprintf(pp, "Challenge from %s removed.\n", parray[foo].name); player_remove_request(foo, pp, -1); } while ((which = player_find_pendfrom(pp1, -1, -1)) != -1) { foo = parray[pp1].p_from_list[which].whofrom; pprintf_prompt(foo, "\n%s, whom you were " "challenging, has joined a match with %s." "\n", parray[pp1].name, parray[pp].name); pprintf(pp1, "Challenge from %s removed.\n", parray[foo].name); player_remove_request(foo, pp1, -1); } } else { return COM_OK; } } g = game_new(); adjourned = 0; if (game_read(g, p, p1) >= 0) { adjourned = 1; } else if (game_read(g, p1, p) >= 0) { int swap; adjourned = 1; swap = p; p = p1; p1 = swap; } if (!adjourned) { // No adjourned game, so begin a new game. game_remove(g); if (create_new_match(p, p1, wt, winc, bt, binc, rated, category, board, white) != COM_OK) { snprintf(tmp, sizeof tmp, "There was a problem " "creating the new match.\n"); pprintf(p, "%s", tmp); pprintf_prompt(p1, "%s", tmp); } else if (bh) { white = (parray[p].side == WHITE ? 0 : 1); if (create_new_match(pp, pp1, wt, winc, bt, binc, rated, category, board, white) != COM_OK) { snprintf(tmp, sizeof tmp, "There was a problem " "creating the new match.\n"); // XXX pprintf_prompt(pp, "%s", tmp); pprintf_prompt(pp1, "%s", tmp); snprintf(tmp, sizeof tmp, "There was a problem " "creating your partner's match.\n"); pprintf(p, "%s", tmp); pprintf_prompt(p1, "%s", tmp); // IanO: abort_game()? } else { int g1 = parray[p].game; int g2 = parray[pp].game; garray[g1].link = g2; garray[g2].link = g1; snprintf(tmp, sizeof tmp, "\nYour partner is " "playing game %d (%s vs. %s).\n", (g2 + 1), garray[g2].white_name, garray[g2].black_name); pprintf(p, "%s", tmp); pprintf_prompt(p1, "%s", tmp); snprintf(tmp, sizeof tmp, "\nYour partner is " "playing game %d (%s vs. %s).\n", (g1 + 1), garray[g1].white_name, garray[g1].black_name); pprintf_prompt(pp, "%s", tmp); pprintf_prompt(pp1, "%s", tmp); } } } else { // resume adjourned game game_delete(p, p1); snprintf(tmp, sizeof tmp, "{Game %d (%s vs. %s) Continuing " "%s %s match.}\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].result = END_NOTENDED; 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; #ifdef TIMESEAL garray[g].wRealTime = garray[g].wTime * 100; garray[g].bRealTime = garray[g].bTime * 100; garray[g].wTimeWhenReceivedMove = 0; garray[g].bTimeWhenReceivedMove = 0; #endif send_boards(g); } return COM_OK; } PRIVATE void print_bughouse(int p, int p1, struct print_bh_context *ctx, char *colorstr[]) { const int pp = ctx->pp; const int pp1 = ctx->pp1; // pp pprintf(pp, "\nYour bughouse partner issuing %s (%s) %s", parray[p].name, ratstrii(GetRating(&parray[p], ctx->type), parray[p].registered), colorstr[ctx->white + 1]); pprintf_highlight(pp, "%s", parray[p1].name); pprintf(pp, " (%s) %s.\n", ratstrii(GetRating(&parray[p1], ctx->type), parray[p1].registered), game_str(ctx->rated, ctx->wt * 60, ctx->winc, ctx->bt * 60, ctx->binc, ctx->category, ctx->board)); pprintf(pp, "Your game would be "); pprintf_highlight(pp, "%s", parray[pp1].name); pprintf_prompt(pp, " (%s) %s%s (%s) %s.\n", ratstrii(GetRating(&parray[pp1], ctx->type), parray[pp1].registered), colorstr[ctx->white + 1], parray[pp].name, ratstrii(GetRating(&parray[pp], ctx->type), parray[pp].registered), game_str(ctx->rated, ctx->wt * 60, ctx->winc, ctx->bt * 60, ctx->binc, ctx->category, ctx->board)); if (parray[pp].bell == 1) pprintf_noformat(pp, "\007"); // pp1 pprintf(pp1, "\nYour bughouse partner was challenged "); pprintf_highlight(pp1, "%s", parray[p].name); pprintf(pp1, " (%s) %s", ratstrii(GetRating(&parray[p], ctx->type), parray[p].registered), colorstr[ctx->white + 1]); pprintf(pp1, "%s (%s) %s.\n", parray[p1].name, ratstrii(GetRating(&parray[p1], ctx->type), parray[p1].registered), game_str(ctx->rated, ctx->wt * 60, ctx->winc, ctx->bt * 60, ctx->binc, ctx->category, ctx->board)); pprintf(pp1, "Your game would be %s (%s) %s", parray[pp1].name, ratstrii(GetRating(&parray[pp1], ctx->type), parray[pp1].registered), colorstr[ctx->white + 1]); pprintf_highlight(pp1, "%s", parray[pp].name); pprintf_prompt(pp1, " (%s) %s.\n", ratstrii(GetRating(&parray[pp], ctx->type), parray[pp].registered), game_str(ctx->rated, ctx->wt * 60, ctx->winc, ctx->bt * 60, ctx->binc, ctx->category, ctx->board)); if (parray[pp1].bell == 1) pprintf_noformat(pp1, "\007"); } PUBLIC int com_match(int p, param_list param) { char board[100] = { '\0' }; char category[100] = { '\0' }; char parsebuf[100] = { '\0' }; char *adjustr[] = { "", " (adjourned)" }; char *colorstr[] = { "", "[black] ", "[white] " }; char *val; int adjourned; // adjourned game? int bh = 0, pp, pp1; int binc = -1; // black increment int bt = -1; // black start time int confused = 0; int g; // more adjourned game junk int p1; int pendfrom, pendto; int ppend, p1pend; int rated = -1; // 1 = rated, 0 = unrated int type; int white = -1; // 1 = want white, 0 = want black int winc = -1; // white increment int wt = -1; // white start time textlist *clauses = NULL; if (parray[p].game >= 0 && garray[parray[p].game].status == GAME_EXAMINE) { pprintf(p, "You can't challenge while you are examining a game." "\n"); return COM_OK; } if (parray[p].game >= 0) { pprintf(p, "You can't challenge while you are playing a game." "\n"); return COM_OK; } stolower(param[0].val.word); 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 (p1 == p) { // Allowing to match yourself to enter analysis mode ExamineScratch(p, param); return COM_OK; } if (parray[p].open == 0) { parray[p].open = 1; pprintf(p, "Setting you open for matches.\n"); } if (!parray[p1].open) { pprintf(p, "Player \"%s\" is not open to match requests.\n", parray[p1].name); return COM_OK; } if (parray[p1].game >= 0) { pprintf(p, "Player \"%s\" is involved in another game.\n", parray[p1].name); return COM_OK; } // Look for an adjourned game between p and p1 g = game_new(); adjourned = (game_read(g, p, p1) >= 0 || game_read(g, p1, p) >= 0); if (adjourned) { type = garray[g].type; wt = garray[g].wInitTime / 600; bt = garray[g].bInitTime / 600; winc = garray[g].wIncrement / 10; binc = garray[g].bIncrement / 10; rated = garray[g].rated; } game_remove(g); pendto = player_find_pendto(p, p1, PEND_MATCH); pendfrom = player_find_pendfrom(p, p1, PEND_MATCH); category[0] = '\0'; board[0] = '\0'; if (!adjourned) { if (in_list(p1, L_NOPLAY, parray[p].login)) { pprintf(p, "You are on %s's noplay list.\n", parray[p1].name); return COM_OK; } if (player_censored(p1, p)) { pprintf(p, "Player \"%s\" is censoring you.\n", parray[p1].name); return COM_OK; } if (player_censored(p, p1)) { pprintf(p, "You are censoring \"%s\".\n", parray[p1].name); return COM_OK; } if (param[1].type != TYPE_NULL) { int numba; // temp for atoi() val = param[1].val.string; while (!confused && sscanf(val, " %99s", parsebuf) == 1) { val = eatword(eatwhite(val)); if (category[0] != '\0' && board[0] == '\0') { strlcpy(board, parsebuf, sizeof board); } else if (isdigit(*parsebuf)) { if ((numba = atoi(parsebuf)) < 0) { pprintf(p, "You can't specify " "negative time controls." "\n"); return COM_OK; } else if (numba > 1000) { pprintf(p, "You can't specify " "time or inc above 1000." "\n"); return COM_OK; } else if (wt == -1) { wt = numba; } else if (winc == -1) { winc = numba; } else if (bt == -1) { bt = numba; } else if (binc == -1) { binc = numba; } else { confused = 1; } } else if (strstr("rated", parsebuf) != NULL) { if (rated == -1) rated = 1; else confused = 1; } else if (strstr("unrated", parsebuf) != NULL) { if (rated == -1) rated = 0; else confused = 1; } else if (strstr("white", parsebuf) != NULL) { if (white == -1) white = 1; else confused = 1; } else if (strstr("black", parsebuf) != NULL) { if (white == -1) white = 0; else confused = 1; } else if (category[0] == '\0') { strlcpy(category, parsebuf, sizeof category); } else { confused = 1; } } /* while */ if (confused) { pprintf(p, "Can't interpret %s in match " "command.\n", parsebuf); return COM_OK; } } rated = (rated == -1 ? parray[p].rated : rated) && parray[p1].registered && parray[p].registered; if (winc == -1) winc = (wt == -1 ? parray[p].d_inc : 0); if (wt == -1) wt = parray[p].d_time; if (bt == -1) bt = wt; if (binc == -1) binc = winc; if (!strcmp(category, "bughouse")) { // save mentioning wild strlcpy(board, "bughouse", sizeof board); strlcpy(category, "wild", sizeof category); } if (category[0] && !board[0]) { pprintf(p, "You must specify a board and a category." "\n"); return COM_OK; } if (category[0]) { char fname[MAX_FILENAME_SIZE] = { '\0' }; (void) snprintf(fname, sizeof fname, "%s/%s/%s", board_dir, category, board); if (!file_exists(fname)) { pprintf(p, "No such category/board: %s/%s\n", category, board); return COM_OK; } } if (pendfrom < 0 && parray[p1].ropen == 0 && rated != parray[p1].rated) { pprintf(p, "%s only wants to play %s games.\n", parray[p1].name, rstr[parray[p1].rated]); pprintf_highlight(p1, "Ignoring"); pprintf(p1, " %srated match request from %s.\n", (parray[p1].rated ? "un" : ""), parray[p].name); return COM_OK; } type = game_isblitz(wt, winc, bt, binc, category, board); if (rated && type == TYPE_WILD && !strcmp(board, "bughouse")) { pprintf(p, "Game is bughouse - " "reverting to unrated\n"); rated = 0; // will need to kill wild and make // TYPE_BUGHOUSE } if (rated && type == TYPE_NONSTANDARD) { pprintf(p, "Game is non-standard - " "reverting to unrated\n"); rated = 0; } if (rated && type == TYPE_UNTIMED) { pprintf(p, "Game is untimed - " "reverting to unrated\n"); rated = 0; } // Now check formula. if ((pendfrom < 0 || param[1].type != TYPE_NULL) && !GameMatchesFormula(p, p1, wt, winc, bt, binc, rated, type, &clauses)) { pprintf(p, "Match request does not fit formula for " "%s:\n", parray[p1].name); pprintf(p, "%s's formula: %s\n", parray[p1].name, parray[p1].formula); ShowClauses(p, p1, clauses); ClearTextList(clauses); pprintf_highlight(p1, "Ignoring"); pprintf_prompt(p1, " (formula): %s (%d) %s (%d) %s.\n", parray[p].name, GetRating(&parray[p], type), parray[p1].name, GetRating(&parray[p1], type), game_str(rated, wt * 60, winc, bt * 60, binc, category, board)); return COM_OK; } if (type == TYPE_WILD && strcmp(board, "bughouse") == 0) { bh = 1; pp = parray[p].partner; pp1 = parray[p1].partner; if (pp < 0) { pprintf(p, "You have no partner for bughouse." "\n"); return COM_OK; } if (pp1 < 0) { pprintf(p, "Your opponent has no partner for " "bughouse.\n"); return COM_OK; } if (pp == pp1) { pprintf(p, "You and your opponent both chose " "the same partner!\n"); return COM_OK; } if (pp == p1 || pp1 == p) { pprintf(p, "You and your opponent can't choose " "each other as partners!\n"); return COM_OK; } if (parray[pp].partner != p) { pprintf(p, "Your partner hasn't chosen you as " "his partner!\n"); return COM_OK; } if (parray[pp1].partner != p1) { pprintf(p, "Your opponent's partner hasn't " "chosen your opponent as his partner!\n"); return COM_OK; } if (!parray[pp].open || parray[pp].game >= 0) { pprintf(p, "Your partner isn't open to play " "right now.\n"); return COM_OK; } if (!parray[pp1].open || parray[pp1].game >= 0) { pprintf(p, "Your opponent's partner isn't " "open to play right now.\n"); return COM_OK; } /* * Bypass NOPLAY lists, censored lists, * ratedness, privacy, and formula for * now. Active challenger/ee will determine * these. */ } // Ok match offer will be made } // !adjourned if (pendto >= 0) { pprintf(p, "Updating offer already made to \"%s\".\n", parray[p1].name); } if (pendfrom >= 0) { if (pendto >= 0) { pprintf(p, "Internal error\n"); fprintf(stderr, "FICS: This shouldn't happen. " "You can't have a match pending from and to the " "same person.\n"); return COM_OK; } if (adjourned || (wt == parray[p].p_from_list[pendfrom].param1 && winc == parray[p].p_from_list[pendfrom].param2 && bt == parray[p].p_from_list[pendfrom].param3 && binc == parray[p].p_from_list[pendfrom].param4 && rated == parray[p].p_from_list[pendfrom].param5 && (white == -1 || white + parray[p].p_from_list[pendfrom].param6 == 1) && !strcmp(category, parray[p].p_from_list[pendfrom].char1) && !strcmp(board, parray[p].p_from_list[pendfrom].char2))) { // Identical match - should accept! accept_match(p, p1); return COM_OK; } else { player_remove_pendfrom(p, p1, PEND_MATCH); player_remove_pendto(p1, p, PEND_MATCH); } } if (pendto < 0) { if ((ppend = player_new_pendto(p)) < 0) { pprintf(p, "Sorry you can't have any more pending " "matches.\n"); return COM_OK; } if ((p1pend = player_new_pendfrom(p1)) < 0) { pprintf(p, "Sorry %s can't have any more pending " "matches.\n", parray[p1].name); parray[p].num_to = parray[p].num_to - 1; return COM_OK; } } else { ppend = pendto; p1pend = player_find_pendfrom(p1, p, PEND_MATCH); } parray[p].p_to_list[ppend].param1 = wt; parray[p].p_to_list[ppend].param2 = winc; parray[p].p_to_list[ppend].param3 = bt; parray[p].p_to_list[ppend].param4 = binc; parray[p].p_to_list[ppend].param5 = rated; parray[p].p_to_list[ppend].param6 = white; strlcpy(parray[p].p_to_list[ppend].char1, category, sizeof(parray[p].p_to_list[ppend].char1)); strlcpy(parray[p].p_to_list[ppend].char2, board, sizeof(parray[p].p_to_list[ppend].char2)); parray[p].p_to_list[ppend].type = PEND_MATCH; parray[p].p_to_list[ppend].whoto = p1; parray[p].p_to_list[ppend].whofrom = p; parray[p1].p_from_list[p1pend].param1 = wt; parray[p1].p_from_list[p1pend].param2 = winc; parray[p1].p_from_list[p1pend].param3 = bt; parray[p1].p_from_list[p1pend].param4 = binc; parray[p1].p_from_list[p1pend].param5 = rated; parray[p1].p_from_list[p1pend].param6 = white; strlcpy(parray[p1].p_from_list[p1pend].char1, category, sizeof(parray[p1].p_from_list[p1pend].char1)); strlcpy(parray[p1].p_from_list[p1pend].char2, board, sizeof(parray[p1].p_from_list[p1pend].char2)); parray[p1].p_from_list[p1pend].type = PEND_MATCH; parray[p1].p_from_list[p1pend].whoto = p1; parray[p1].p_from_list[p1pend].whofrom = p; if (pendfrom >= 0) { pprintf(p, "Declining offer from %s and offering new match " "parameters.\n", parray[p1].name); pprintf(p1, "\n%s declines your match offer a match with these " "parameters:", parray[p].name); } if (pendto >= 0) { pprintf(p, "Updating match request to: "); pprintf(p1, "\n%s updates the match request.\n", parray[p].name); } else { pprintf(p, "Issuing: "); pprintf(p1, "\n"); // XXX: 'parray[p].name' } pprintf(p, "%s (%s) %s", parray[p].name, ratstrii(GetRating(&parray[p], type), parray[p].registered), colorstr[white + 1]); pprintf_highlight(p, "%s", parray[p1].name); pprintf(p, " (%s) %s%s.\n", ratstrii(GetRating(&parray[p1], type), parray[p1].registered), game_str(rated, wt * 60, winc, bt * 60, binc, category, board), adjustr[adjourned]); pprintf(p1, "Challenge: "); pprintf_highlight(p1, "%s", parray[p].name); pprintf(p1, " (%s) %s", ratstrii(GetRating(&parray[p], type), parray[p].registered), colorstr[white + 1]); pprintf(p1, "%s (%s) %s%s.\n", parray[p1].name, ratstrii(GetRating(&parray[p1], type), parray[p1].registered), game_str(rated, wt * 60, winc, bt * 60, binc, category, board), adjustr[adjourned]); if (parray[p1].bell == 1) pprintf_noformat(p1, "\007"); if (bh) { struct print_bh_context ctx = { .pp = pp, .pp1 = pp1, .rated = rated, .type = type, .white = white, .board = &board[0], .category = &category[0], .binc = binc, .bt = bt, .winc = winc, .wt = wt, }; print_bughouse(p, p1, &ctx, colorstr); } if (in_list(p, L_COMPUTER, parray[p].name)) { pprintf(p1, "--** %s is a ", parray[p].name); pprintf_highlight(p1, "computer"); pprintf(p1, " **--\n"); } if (in_list(p, L_COMPUTER, parray[p1].name)) { pprintf(p, "--** %s is a ", parray[p1].name); pprintf_highlight(p, "computer"); pprintf(p, " **--\n"); } if (in_list(p, L_ABUSER, parray[p].name)) { pprintf(p1, "--** %s is in the ", parray[p].name); pprintf_highlight(p1, "abuser"); pprintf(p1, " list **--\n"); } if (in_list(p, L_ABUSER, parray[p1].name)) { pprintf(p, "--** %s is in the ", parray[p1].name); pprintf_highlight(p, "abuser"); pprintf(p, " list **--\n"); } if (rated) { double newsterr; int win, draw, loss; rating_sterr_delta(p1, p, type, time(NULL), RESULT_WIN, &win, &newsterr); rating_sterr_delta(p1, p, type, time(NULL), RESULT_DRAW, &draw, &newsterr); rating_sterr_delta(p1, p, type, time(NULL), RESULT_LOSS, &loss, &newsterr); pprintf(p1, "Your %s rating will change: " "Win: %s%d, Draw: %s%d, Loss: %s%d\n", bstr[type], (win >= 0 ? "+" : ""), win, (draw >= 0 ? "+" : ""), draw, (loss >= 0 ? "+" : ""), loss); pprintf(p1, "Your new RD will be %5.1f\n", newsterr); rating_sterr_delta(p, p1, type, time(NULL), RESULT_WIN, &win, &newsterr); rating_sterr_delta(p, p1, type, time(NULL), RESULT_DRAW, &draw, &newsterr); rating_sterr_delta(p, p1, type, time(NULL), RESULT_LOSS, &loss, &newsterr); pprintf(p, "Your %s rating will change: " "Win: %s%d, Draw: %s%d, Loss: %s%d\n", bstr[type], (win >= 0 ? "+" : ""), win, (draw >= 0 ? "+" : ""), draw, (loss >= 0 ? "+" : ""), loss); pprintf(p, "Your new RD will be %5.1f\n", newsterr); } pprintf_prompt(p1, "You can \"accept\" or \"decline\", or propose " "different parameters.\n"); return COM_OK; } PUBLIC int com_accept(int p, param_list param) { int acceptNum = -1; int from; int p1; int type = -1; if (parray[p].num_from == 0) { pprintf(p, "You have no offers to accept.\n"); return COM_OK; } if (param[0].type == TYPE_NULL) { if (parray[p].num_from != 1) { pprintf(p, "You have more than one offer to accept.\n" "Use \"pending\" to see them and \"accept n\" to " "choose which one.\n"); return COM_OK; } acceptNum = 0; } else if (param[0].type == TYPE_INT) { if (param[0].val.integer < 1 || param[0].val.integer > parray[p].num_from) { pprintf(p, "Out of range. Use \"pending\" to see " "the list of offers.\n"); return COM_OK; } acceptNum = (param[0].val.integer - 1); } else if (param[0].type == TYPE_WORD) { if (!strcmp(param[0].val.word, "draw")) { type = PEND_DRAW; } else if (!strcmp(param[0].val.word, "pause")) { type = PEND_PAUSE; } else if (!strcmp(param[0].val.word, "adjourn")) { type = PEND_ADJOURN; } else if (!strcmp(param[0].val.word, "abort")) { type = PEND_ABORT; } else if (!strcmp(param[0].val.word, "takeback")) { type = PEND_TAKEBACK; } else if (!strcmp(param[0].val.word, "simmatch")) { type = PEND_SIMUL; } else if (!strcmp(param[0].val.word, "switch")) { type = PEND_SWITCH; } else if (!strcmp(param[0].val.word, "partner")) { type = PEND_PARTNER; } if (type >= 0) { acceptNum = player_find_pendfrom(p, -1, type); if (acceptNum < 0) { pprintf(p, "There are no pending %s offers.\n", param[0].val.word); return COM_OK; } } else { // Word must be a name 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 ((acceptNum = player_find_pendfrom(p, p1, -1)) < 0) { pprintf(p, "There are no pending offers from " "%s.\n", parray[p1].name); return COM_OK; } } } from = parray[p].p_from_list[acceptNum].whofrom; switch (parray[p].p_from_list[acceptNum].type) { case PEND_MATCH: accept_match(p, from); return COM_OK; case PEND_DRAW: pcommand(p, "draw"); break; case PEND_PAUSE: pcommand(p, "pause"); break; case PEND_ABORT: pcommand(p, "abort"); break; case PEND_TAKEBACK: pcommand(p, "takeback %d", parray[p].p_from_list[acceptNum].param1); break; case PEND_SIMUL: pcommand(p, "simmatch %s", parray[from].name); break; case PEND_SWITCH: pcommand(p, "switch"); break; case PEND_ADJOURN: pcommand(p, "adjourn"); break; case PEND_PARTNER: pcommand(p, "partner %s", parray[from].name); break; } return COM_OK_NOPROMPT; } PRIVATE int WordToOffer(int p, char *Word, int *type, int *p1) { /* * Convert draw adjourn match takeback abort pause simmatch * switch partner or to offer type. */ if (!strcmp(Word, "match")) { *type = PEND_MATCH; } else if (!strcmp(Word, "draw")) { *type = PEND_DRAW; } else if (!strcmp(Word, "pause")) { *type = PEND_PAUSE; } else if (!strcmp(Word, "abort")) { *type = PEND_ABORT; } else if (!strcmp(Word, "takeback")) { *type = PEND_TAKEBACK; } else if (!strcmp(Word, "adjourn")) { *type = PEND_ADJOURN; } else if (!strcmp(Word, "switch")) { *type = PEND_SWITCH; } else if (!strcmp(Word, "simul")) { *type = PEND_SIMUL; } else if (!strcmp(Word, "partner")) { *type = PEND_PARTNER; } else if (!strcmp(Word, "all")) { ; } else { if ((*p1 = player_find_part_login(Word)) < 0) { pprintf(p, "No user named \"%s\" is logged in.\n", Word); return 0; } } return 1; } PUBLIC int com_decline(int p, param_list param) { int count; int declineNum; int p1 = -1; int type = -1; if (parray[p].num_from == 0) { pprintf(p, "You have no pending offers from other players.\n"); return COM_OK; } if (param[0].type == TYPE_NULL) { if (parray[p].num_from == 1) { p1 = parray[p].p_from_list[0].whofrom; type = parray[p].p_from_list[0].type; } else { pprintf(p, "You have more than one pending offer. " "Please specify which one\nyou wish to decline.\n" "'Pending' will give you the list.\n"); return COM_OK; } } else { if (param[0].type == TYPE_WORD) { if (!WordToOffer(p, param[0].val.word, &type, &p1)) return COM_OK; } else { // Must be an integer declineNum = param[0].val.integer - 1; if (declineNum >= parray[p].num_from || declineNum < 0) { pprintf(p, "Invalid offer number. Must be " "between 1 and %d.\n", parray[p].num_from); return COM_OK; } p1 = parray[p].p_from_list[declineNum].whofrom; type = parray[p].p_from_list[declineNum].type; } } if ((count = player_decline_offers(p, p1, type)) != 1) pprintf(p, "%d offers declined\n", count); return COM_OK; } PUBLIC int com_withdraw(int p, param_list param) { int count; int p1 = -1; int type = -1; int withdrawNum; if (parray[p].num_to == 0) { pprintf(p, "You have no pending offers to other players.\n"); return COM_OK; } if (param[0].type == TYPE_NULL) { if (parray[p].num_to == 1) { p1 = parray[p].p_to_list[0].whoto; type = parray[p].p_to_list[0].type; } else { pprintf(p, "You have more than one pending offer. " "Please specify which one\nyou wish to withdraw.\n" "'Pending' will give you the list.\n"); return COM_OK; } } else { if (param[0].type == TYPE_WORD) { if (!WordToOffer(p, param[0].val.word, &type, &p1)) return COM_OK; } else { // Must be an integer withdrawNum = param[0].val.integer - 1; if (withdrawNum >= parray[p].num_to || withdrawNum < 0) { pprintf(p, "Invalid offer number. Must be " "between 1 and %d.\n", parray[p].num_to); return COM_OK; } p1 = parray[p].p_to_list[withdrawNum].whoto; type = parray[p].p_to_list[withdrawNum].type; } } if ((count = player_withdraw_offers(p, p1, type)) != 1) pprintf(p, "%d offers withdrawn\n", count); return COM_OK; } PUBLIC int com_pending(int p, param_list param) { int i; if (!parray[p].num_to) { pprintf(p, "There are no offers pending TO other players.\n"); } else { pprintf(p, "Offers TO other players:\n"); for (i = 0; i < parray[p].num_to; i++) { pprintf(p, " "); player_pend_print(p, &parray[p].p_to_list[i]); } } if (!parray[p].num_from) { pprintf(p, "\nThere are no offers pending FROM other players." "\n"); } else { pprintf(p, "\nOffers FROM other players:\n"); for (i = 0; i < parray[p].num_from; i++) { pprintf(p, " %d: ", i + 1); player_pend_print(p, &parray[p].p_from_list[i]); } pprintf(p, "\nIf you wish to accept any of these offers type " "'accept n'\nor just 'accept' if there is only one offer." "\n"); } return COM_OK; }