aboutsummaryrefslogtreecommitdiffstats
path: root/FICS/gameproc.c
diff options
context:
space:
mode:
authorMarkus Uhlin <markus@nifty-networks.net>2023-12-07 21:31:49 +0100
committerMarkus Uhlin <markus@nifty-networks.net>2023-12-07 21:31:49 +0100
commit79b59f9b30fb6a1fdf8c3efb446271f7cb00d434 (patch)
treef6ade4ccbc3af20d825edacfd12b5da8ded8d240 /FICS/gameproc.c
FICS 1.6.2
Diffstat (limited to 'FICS/gameproc.c')
-rw-r--r--FICS/gameproc.c1745
1 files changed, 1745 insertions, 0 deletions
diff --git a/FICS/gameproc.c b/FICS/gameproc.c
new file mode 100644
index 0000000..cf07200
--- /dev/null
+++ b/FICS/gameproc.c
@@ -0,0 +1,1745 @@
+/* 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.
+*/
+
+#include "stdinclude.h"
+
+#include "common.h"
+#include "command.h"
+#include "ficsmain.h"
+#include "config.h"
+#include "playerdb.h"
+#include "gamedb.h"
+#include "gameproc.h"
+#include "obsproc.h"
+#include "movecheck.h"
+#include "utils.h"
+#include "ratings.h"
+#include "rmalloc.h"
+#include "comproc.h"
+#include "matchproc.h"
+#include "eco.h"
+#include "network.h"
+#include "lists.h"
+/* #include "hostinfo.h" */
+
+PUBLIC void game_ended(int g, int winner, int why)
+{
+ char outstr[200];
+ char tmp[200];
+ int p;
+ int gl = garray[g].link;
+ int rate_change = 0;
+ int isDraw = 0;
+ int whiteResult;
+ char winSymbol[10];
+ char EndSymbol[10];
+ char *NameOfWinner, *NameOfLoser;
+ int beingplayed = 0; /* i.e. it wasn't loaded for adjudication */
+
+ beingplayed = (parray[garray[g].black].game == g);
+
+ sprintf(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;
+ strcpy(winSymbol, "1-0");
+ NameOfWinner = parray[garray[g].white].name;
+ NameOfLoser = parray[garray[g].black].name;
+ } else {
+ whiteResult = RESULT_LOSS;
+ strcpy(winSymbol, "0-1");
+ NameOfWinner = parray[garray[g].black].name;
+ NameOfLoser = parray[garray[g].white].name;
+ }
+ switch (why) {
+ case END_CHECKMATE:
+ sprintf(tmp, "%s checkmated} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Mat");
+ rate_change = 1;
+ break;
+ case END_RESIGN:
+ sprintf(tmp, "%s resigns} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Res");
+ rate_change = 1;
+ break;
+ case END_FLAG:
+ sprintf(tmp, "%s forfeits on time} %s\n", NameOfLoser, winSymbol);
+ strcpy(EndSymbol, "Fla");
+ rate_change = 1;
+ break;
+ case END_STALEMATE:
+ sprintf(tmp, "Game drawn by stalemate} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Sta");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_AGREEDDRAW:
+ sprintf(tmp, "Game drawn by mutual agreement} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Agr");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_BOTHFLAG:
+ sprintf(tmp, "Game drawn because both players ran out of time} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Fla");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_REPETITION:
+ sprintf(tmp, "Game drawn by repetition} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Rep");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_50MOVERULE:
+ sprintf(tmp, "Game drawn by the 50 move rule} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "50");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJOURN:
+ if (gl >= 0) {
+ sprintf(tmp, "Bughouse game aborted.} *\n");
+ whiteResult = RESULT_ABORT;
+ } else {
+ sprintf(tmp, "Game adjourned by mutual agreement} *\n");
+ game_save(g);
+ }
+ break;
+ case END_LOSTCONNECTION:
+ sprintf(tmp, "%s lost connection; game ", NameOfWinner);
+ if (parray[garray[g].white].registered && parray[garray[g].black].registered
+ && gl < 0) {
+ sprintf(tmp, "adjourned} *\n");
+ game_save(g);
+ } else
+ sprintf(tmp, "aborted} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_ABORT:
+ sprintf(tmp, "Game aborted by mutual agreement} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_COURTESY:
+ sprintf(tmp, "Game courtesyaborted by %s} *\n", NameOfWinner);
+ whiteResult = RESULT_ABORT;
+ break;
+ case END_COURTESYADJOURN:
+ if (gl >= 0) {
+ sprintf(tmp, "Bughouse game courtesyaborted by %s.} *\n", NameOfWinner);
+ whiteResult = RESULT_ABORT;
+ } else {
+ sprintf(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) */
+ sprintf(tmp, "Neither player has mating material} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "NM ");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_FLAGNOMATERIAL:
+ sprintf(tmp, "%s ran out of time and %s has no material to mate} 1/2-1/2\n",
+ NameOfLoser, NameOfWinner);
+ isDraw = 1;
+ strcpy(EndSymbol, "TM ");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJWIN:
+ sprintf(tmp, "%s wins by adjudication} %s\n", NameOfWinner, winSymbol);
+ strcpy(EndSymbol, "Adj");
+ rate_change = 1;
+ break;
+ case END_ADJDRAW:
+ sprintf(tmp, "Game drawn by adjudication} 1/2-1/2\n");
+ isDraw = 1;
+ strcpy(EndSymbol, "Adj");
+ rate_change = 1;
+ whiteResult = RESULT_DRAW;
+ break;
+ case END_ADJABORT:
+ sprintf(tmp, "Game aborted by adjudication} *\n");
+ whiteResult = RESULT_ABORT;
+ break;
+ default:
+ sprintf(tmp, "Hmm, the game ended and I don't know why} *\n");
+ break;
+ }
+ strcat(outstr, tmp);
+ if (beingplayed) {
+ pprintf_noformat(garray[g].white, outstr);
+ pprintf_noformat(garray[g].black, 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, outstr);
+ pprintf_noformat(garray[gl].black, 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, outstr);
+/* if (parray[p].bell)
+ pprintf (p, "\007"); removed - caused annoyance */
+ pprintf_prompt(p, "");
+ }
+ }
+ if ((garray[g].rated) && (rate_change)) {
+ /* Adjust ratings */
+ rating_update(g);
+#if 0
+ if (parray[garray[g].white].network_player &&
+ parray[garray[g].black].network_player) {
+ if (MailGameResult) { /* Send to ratings server */
+ if (isDraw) {
+ /* hostinfo_mailresults(garray[g].type == TYPE_BLITZ ? "blitz" :
+ garray[g].type == TYPE_WILD ? "wild " : "stand", "
+ parray[garray[g].white].name, parray[garray[g].black].name,
+ "draw"); */
+ } else {
+/* hostinfo_mailresults(garray[g].type == TYPE_BLITZ ? "blitz" : garray[g].type == TYPE_WILD ? "wild " : "stand",
+ parray[garray[g].white].name,
+ parray[garray[g].black].name,
+ (winner == WHITE) ? parray[garray[g].white].name :
+ parray[garray[g].black].name); */
+ }
+ }
+ }
+#endif
+
+ } 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, "");
+ if (garray[g].black != commanding_player)
+ pprintf_prompt(garray[g].black, "");
+ 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
+#ifdef BUGHOUSE_PAWN_REVERT
+ int i;
+
+ for (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,"
+ "\n 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;
+ move_t move;
+ int result;
+ unsigned now;
+ int len; /* loon: for lame promotion hack */
+ int i;
+
+ 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) 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 = (move_t *) rmalloc(sizeof(move_t) * garray[g].examMoveListSize);
+ } else {
+ garray[g].examMoveList = (move_t *) rrealloc(garray[g].examMoveList, sizeof(move_t) * garray[g].examMoveListSize);
+ }
+ }
+ result = execute_move(&garray[g].game_state, &move, 1);
+ move.atTime = now;
+ move.tookTime = 0;
+ MakeFENpos(g, 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);
+ }
+ now = tenth_secs();
+ 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) {
+ garray[g].wLastRealTime = garray[g].wRealTime;
+ garray[g].wTimeWhenMoved = con[parray[p].socket].time;
+ if (((garray[g].wTimeWhenMoved - garray[g].wTimeWhenReceivedMove) < 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) {
+ garray[g].bLastRealTime = garray[g].bRealTime;
+ garray[g].bTimeWhenMoved = con[parray[p].socket].time;
+ if (((garray[g].bTimeWhenMoved - garray[g].bTimeWhenReceivedMove) < 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);
+
+ /* 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; /* remember to conv to
+ tenth secs */
+ } else if (parray[p].side == BLACK) {
+ garray[g].bRealTime += garray[g].bIncrement * 100; /* conv to ms */
+ garray[g].bTime = garray[g].bRealTime / 100; /* remember to conv to
+ tenth secs */
+ }
+ } 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 = (move_t *) rmalloc(sizeof(move_t) * garray[g].moveListSize);
+ } else {
+ garray[g].moveList = (move_t *) rrealloc(garray[g].moveList, sizeof(move_t) * garray[g].moveListSize);
+ }
+ }
+ 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, 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)) {
+ int p1;
+
+ for (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) {
+ int p1;
+
+ for (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) {
+ int p1;
+
+ for (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) {
+ int p1;
+
+ for (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;
+}
+
+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;
+}
+
+char *GetFENpos (int g, int half_move)
+{
+ if (half_move < 0)
+ return garray[g].FENstartPos;
+ else return garray[g].moveList[half_move].FENpos;
+}
+
+int CheckRepetition (int p, int g)
+{
+ int move_num;
+ int flag1 = 1, flag2 = 1;
+ char *pos1 = GetFENpos (g, garray[g].numHalfMoves - 1);
+ char *pos2 = GetFENpos (g, garray[g].numHalfMoves);
+ char *pos;
+
+ if (garray[g].numHalfMoves < 8) /* can't have three repeats any quicker. */
+ return 0;
+
+ for (move_num = garray[g].game_state.lastIrreversable;
+ move_num < garray[g].numHalfMoves - 1; move_num++) {
+ pos = GetFENpos (g, move_num);
+ if (strlen(pos1) == strlen(pos) && !strcmp(pos1, pos))
+ flag1++;
+ if (strlen(pos2) == strlen(pos) && !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 now;
+ int g;
+
+ 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 p1, g, myColor, yourColor, myGTime, yourGTime;
+ int courtesyOK = 1;
+
+ 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 piece;
+ int minor_pieces = 0;
+
+ 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.\nUse abort instead.\n");
+ return COM_OK;
+ }
+ game_update_time(g);
+
+#ifdef TIMESEAL
+
+ {
+ int myTime, yourTime, opp = parray[p].opponent, serverTime;
+
+ if (con[parray[p].socket].timeseal) { /* does 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) {
+
+ /* server time thinks opponent is down, but RealTIme disagrees.
+ * ask client to acknowledge it's alive. */
+
+ 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 one of:
+ * 1. the server agrees opponent has time, whether lagging or not.
+ * 2. opp. 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
+
+ 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;
+}
+#if 0 /* this might as well die now */
+PUBLIC int com_load(int p, param_list param)
+{
+ pprintf(p, "Obsolete command, please use match <opponent>.\n");
+ return COM_OK;
+}
+#endif
+
+PUBLIC int com_takeback(int p, param_list param)
+{
+ int nHalfMoves = 1;
+ int from;
+ int g, i;
+ 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, nHalfMoves);
+ 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)
+{
+ int g = parray[p].game;
+ int tmp, now;
+ int p1;
+ char *strTmp;
+
+ 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);
+ /* Doing the 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 = strdup(garray[g].white_name);
+ strcpy(garray[g].white_name, garray[g].black_name);
+ strcpy(garray[g].black_name, strTmp);
+ 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.\nType \"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 {
+ g = GameNumFromParam(p, &p1, &param[0]);
+ if (g < 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)
+{
+ char *category = NULL;
+ char dname[MAX_FILENAME_SIZE];
+ DIR *dirp;
+#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);
+ sprintf(dname, "%s/%s", board_dir, category);
+ } else {
+ pprintf(p, "Categories Available:\n");
+ sprintf(dname, "%s", board_dir);
+ }
+ dirp = opendir(dname);
+ if (!dirp) {
+ 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)
+{
+ int p1, g, adjourned;
+ int num;
+ char tmp[100];
+
+ 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);
+
+ /* Accepting Simul ! */
+
+ 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;
+ }
+ unobserveAll(p); /* stop observing when match starts */
+ 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);
+
+ sprintf(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, tmp);
+ pprintf(p1, 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;
+ }
+/* loon: this was (p, p1, PEND_SIMUL) but player_add_request needs 4 args...
+if 0 is the incorrect 4th arg, please fix :) */
+
+ g = game_new(); /* Check if an adjourned untimed game */
+ 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;
+ g = parray[p].simul_info.boards[on];
+ 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)
+{
+ int i;
+
+ 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 (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)
+{
+ int i;
+
+ 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 (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;
+ }
+ increment = param[0].val.integer;
+ if (increment <= 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;
+}
+