2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 static int check_kings(struct game_state_t *gs);
24 static int get_empty_slot(void);
25 static int ReadGameAttrs_common(FILE *fp, int g, int version);
26 static long OldestHistGame(char *login);
27 static void RemoveHistGame(char *file, int maxlines);
28 static void write_g_out(int g, char *file, int maxlines, int isDraw, char *EndSymbol, char *name, time_t *now);
29 static int game_zero(int g);
31 const char *TypeStrings[NUM_GAMETYPES] = {"untimed", "blitz", "standard",
32 "nonstandard", "wild", "lightning",
33 "bughouse", "gothic", "knightmate",
36 /* this method is awful! how about allocation as we need it and freeing
38 static int get_empty_slot(void)
42 for (i = 0; i < game_globals.g_num; i++) {
43 if (game_globals.garray[i].status == GAME_EMPTY)
47 game_globals.garray = (struct game *)realloc(game_globals.garray, sizeof(struct game) * game_globals.g_num);
48 /* yeah great, bet this causes lag! - DAV*/
49 /* I have serious doubt of the truth to the above client- bugg */
50 game_globals.garray[game_globals.g_num - 1].status = GAME_EMPTY;
51 return game_globals.g_num - 1;
56 int new = get_empty_slot();
61 static int game_zero(int g)
63 ZERO_STRUCT(game_globals.garray[g]);
65 game_globals.garray[g].white = -1;
66 game_globals.garray[g].black = -1;
68 game_globals.garray[g].status = GAME_NEW;
69 game_globals.garray[g].link = -1;
70 game_globals.garray[g].result = END_NOTENDED;
71 game_globals.garray[g].type = TYPE_UNTIMED;
72 game_globals.garray[g].game_state.gameNum = g;
73 game_globals.garray[g].wInitTime = 300; /* 5 minutes */
74 game_globals.garray[g].wIncrement = 0;
75 game_globals.garray[g].bInitTime = 300; /* 5 minutes */
76 game_globals.garray[g].bIncrement = 0;
77 game_globals.garray[g].flag_pending = FLAG_NONE;
78 strcpy(game_globals.garray[g].FENstartPos,INITIAL_FEN);
84 FREE(game_globals.garray[g].moveList);
85 FREE(game_globals.garray[g].examMoveList);
86 game_globals.garray[g].moveList = NULL;
87 game_globals.garray[g].examMoveList = NULL;
88 game_globals.garray[g].moveListSize = 0;
89 game_globals.garray[g].examMoveListSize = 0;
93 int game_remove(int g)
95 /* Should remove game from players observation list */
98 game_globals.garray[g].status = GAME_EMPTY;
102 /* old moves not stored now - uses smoves */
103 int game_finish(int g)
105 player_game_ended(g); /* Alert playerdb that game ended */
110 void MakeFENpos (int g, char *FEN)
112 strcpy(FEN, boardToFEN(g));
115 static char *game_time_str(int wt, int winc, int bt, int binc)
117 static char tstr[50];
119 if ((!wt) && (!winc)) { /* Untimed */
123 if ((wt == bt) && (winc == binc)) {
124 sprintf(tstr, " %d %d", wt, winc);
126 sprintf(tstr, " %d %d : %d %d", wt, winc, bt, binc);
131 const char *bstr[] = {"untimed", "blitz", "standard", "non-standard", "wild", "lightning", "Bughouse", "Gothic", "Knightmate", "Capablanca"};
133 const char *rstr[] = {"unrated", "rated"};
135 char *game_str(int rated, int wt, int winc, int bt, int binc,
136 char *cat, char *board)
138 static char tstr[200];
140 if (cat && cat[0] && board && board[0] &&
141 (strcmp(cat, "standard") || strcmp(board, "standard"))) {
142 sprintf(tstr, "%s %s%s Loaded from %s/%s",
144 bstr[game_isblitz(wt / 60, winc, bt / 60, binc, cat, board)],
145 game_time_str(wt / 60, winc, bt / 60, binc),
148 sprintf(tstr, "%s %s%s",
150 bstr[game_isblitz(wt / 60, winc, bt / 60, binc, cat, board)],
151 game_time_str(wt / 60, winc, bt / 60, binc));
156 int game_isblitz(int wt, int winc, int bt, int binc,
157 char *cat, char *board)
162 if (!strcmp(cat, "bughouse"))
163 return TYPE_BUGHOUSE;
164 if (!strcmp(cat, "gothic"))
166 if (!strcmp(cat, "knightmate"))
167 return TYPE_KNIGHTMATE;
168 if (!strcmp(cat, "capablanca"))
169 return TYPE_CAPABLANCA;
170 if (board && board[0] && strcmp(board, "0")) {
171 if (!strcmp(cat, "wild"))
173 if (strcmp(cat, "standard") || strcmp(board, "standard"))
174 return TYPE_NONSTANDARD;
179 if ((wt == 0) || (bt == 0))
180 /* nonsense if one is timed and one is not */
183 if ((wt != bt) || (winc != binc))
184 return TYPE_NONSTANDARD;
185 total = wt * 60 + winc * 40;
186 if (total < 180) /* 3 minute */
188 if (total >= 900) /* 15 minutes */
194 void send_board_to(int g, int p)
196 struct player *pp = &player_globals.parray[p];
201 /* since we know g and p, figure out our relationship to this game */
204 if ((game_globals.garray[g].status == GAME_EXAMINE) || (game_globals.garray[g].status == GAME_SETUP)) {
213 relation = ((side == game_globals.garray[g].game_state.onMove) ? 1 : -1);
219 if (CheckPFlag(p, PFLAG_FLIP)) { /* flip board? */
226 b = board_to_string(game_globals.garray[g].white_name,
227 game_globals.garray[g].black_name,
228 game_globals.garray[g].wTime,
229 game_globals.garray[g].bTime,
230 &game_globals.garray[g].game_state,
231 (game_globals.garray[g].status == GAME_EXAMINE || game_globals.garray[g].status == GAME_SETUP) ?
232 game_globals.garray[g].examMoveList : game_globals.garray[g].moveList,
237 if (pp->game == g && net_globals.con[pp->socket]->timeseal) {
238 pprintf_noformat(p, "\n%s\n[G]\n", b);
240 pprintf_noformat(p, "\n%s", b);
243 if (p != command_globals.commanding_player) {
248 void send_boards(int g)
251 struct simul_info_t *simInfo = player_globals.parray[game_globals.garray[g].white].simul_info;
252 int which_board = -1;
254 /* Begin code added 3/28/96 - Figbert */
256 if ((game_globals.garray[g].status != GAME_SETUP) &&
257 (check_kings(&game_globals.garray[g].game_state))) {
258 d_printf( "Game has invalid number of kings. Aborting...\n");
260 d_printf("files = %d, ranks = %d\n", game_globals.garray[g].game_state.files, game_globals.garray[g].game_state.ranks);
261 for(r=0; r<game_globals.garray[g].game_state.ranks; r++) {
262 for(f=0; f<game_globals.garray[g].game_state.files; f++)
263 d_printf("b[%d][%d]=%d\n",f,r,game_globals.garray[g].game_state.board[f][r]);
268 for (p = 0; p < player_globals.p_num; p++) {
269 struct player *pp = &player_globals.parray[p];
270 if (pp->status == PLAYER_EMPTY)
276 if (p1 >= 0 && player_globals.parray[p1].game == g) {
277 pprintf (p1, "Disconnecting you from game number %d.\n", g+1);
278 player_globals.parray[p1].game = -1;
281 pprintf (p, "Disconnecting you from game number %d.\n", g+1);
288 /* End code added 3/28/96 - Figbert */
291 which_board = simInfo->boards[simInfo->onBoard];
293 if ((simInfo == NULL) || (which_board == g)) {
294 for (p = 0; p < player_globals.p_num; p++) {
295 struct player *pp = &player_globals.parray[p];
296 if (pp->status == PLAYER_EMPTY)
298 if (player_is_observe(p, g) || (pp->game == g))
304 void game_update_time(int g)
307 unsigned now, timesince;
313 gg = &game_globals.garray[g];
315 /* no update on first move */
316 if (gg->game_state.moveNum == 1)
318 if (gg->clockStopped)
320 if (gg->type == TYPE_UNTIMED)
323 timesince = now - gg->lastDecTime;
324 if (gg->game_state.onMove == WHITE) {
325 gg->wTime -= timesince;
327 gg->bTime -= timesince;
329 gg->lastDecTime = now;
333 static void game_update_times(void)
337 for (g = 0; g < game_globals.g_num; g++) {
338 if (game_globals.garray[g].status != GAME_ACTIVE)
340 if (game_globals.garray[g].clockStopped)
347 char *EndString(int g, int personal)
349 static char endstr[200];
350 char *blackguy, *whiteguy;
351 static char blackstr[] = "Black";
352 static char whitestr[] = "White";
354 blackguy = (personal ? game_globals.garray[g].black_name : blackstr);
355 whiteguy = (personal ? game_globals.garray[g].white_name : whitestr);
357 switch (game_globals.garray[g].result) {
359 sprintf(endstr, "%s checkmated",
360 game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
363 sprintf(endstr, "%s bared",
364 game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
367 sprintf(endstr, "%s perpetually checking",
368 game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
371 sprintf(endstr, "%s resigned",
372 game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
375 sprintf(endstr, "%s ran out of time",
376 game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
379 sprintf(endstr, "Game drawn by mutual agreement");
382 sprintf(endstr, "Game drawn because both players ran out of time");
385 sprintf(endstr, "Game drawn by repetition");
388 sprintf(endstr, "Draw by the 50 move rule");
391 sprintf(endstr, "Game adjourned by mutual agreement");
393 case END_LOSTCONNECTION:
394 sprintf(endstr, "%s lost connection, game adjourned",
395 game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
398 sprintf(endstr, "Game aborted by mutual agreement");
401 sprintf(endstr, "Stalemate.");
404 sprintf(endstr, "Still in progress");
407 sprintf(endstr, "Game courtesyaborted by %s",
408 game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
410 case END_COURTESYADJOURN:
411 sprintf(endstr, "Game courtesyadjourned by %s",
412 game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
415 sprintf(endstr, "Game drawn because neither player has mating material");
417 case END_FLAGNOMATERIAL:
418 sprintf(endstr, "%s ran out of time and %s has no material to mate",
419 game_globals.garray[g].winner == WHITE ? blackguy : whiteguy,
420 game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
423 sprintf(endstr, "Game drawn by adjudication");
426 sprintf(endstr, "%s wins by adjudication",
427 game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
430 sprintf(endstr, "Game aborted by adjudication");
433 sprintf(endstr, "???????");
440 const char *EndSym(int g)
442 static const char *symbols[] = {"1-0", "0-1", "1/2-1/2", "*"};
444 switch (game_globals.garray[g].result) {
451 return ((game_globals.garray[g].winner == WHITE) ? symbols[0] : symbols[1]);
459 case END_FLAGNOMATERIAL:
470 /* This should be enough to hold any game up to at least 8000 moves
471 * If we overwrite this, the server will crash :-).
473 /* 8000? who you trying to kid? this is awful - enough for 600 halfs :) -DAV*/
474 #define GAME_STRING_LEN 19000
475 static char gameString[GAME_STRING_LEN];
476 char *movesToString(int g, int pgn)
482 struct move_t *moves;
484 if (game_globals.garray[g].status == GAME_EXAMINE || game_globals.garray[g].status == GAME_SETUP)
485 moves = game_globals.garray[g].examMoveList;
486 else moves = game_globals.garray[g].moveList;
488 wr = game_globals.garray[g].white_rating;
489 br = game_globals.garray[g].black_rating;
492 curTime = untenths(game_globals.garray[g].timeOfStart);
496 "\n[Event \"%s %s %s game\"]\n"
497 "[Site \"%s, %s\"]\n",
498 config_get_tmp("SERVER_NAME"),
499 rstr[game_globals.garray[g].rated], /*bstr[game_globals.garray[g].type],*/
500 game_globals.garray[g].variant, // [HGM] allow more variation in game_types
501 config_get_tmp("SERVER_NAME"),
502 config_get_tmp("SERVER_LOCATION"));
503 strftime(tmp, sizeof(tmp),
504 "[Date \"%Y.%m.%d\"]\n"
505 "[Time \"%H:%M:%S\"]\n",
506 localtime((time_t *) &curTime));
507 strcat(gameString, tmp);
512 "[WhiteElo \"%d\"]\n"
513 "[BlackElo \"%d\"]\n",
514 game_globals.garray[g].white_name, game_globals.garray[g].black_name, wr, br);
515 strcat(gameString, tmp);
516 if(game_globals.garray[g].game_state.variant[0]) { // [HGM] variant: print variant tag
518 "[Variant \"%s\"]\n",
519 game_globals.garray[g].game_state.variant);
520 strcat(gameString, tmp);
522 if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the FEN
526 game_globals.garray[g].FENstartPos);
527 strcat(gameString, tmp);
530 "[TimeControl \"%d+%d\"]\n"
532 "[Result \"%s\"]\n\n",
533 game_globals.garray[g].wInitTime / 10, game_globals.garray[g].wIncrement / 10, EndSym(g));
534 strcat(gameString, tmp);
537 for (i = 0; i < game_globals.garray[g].numHalfMoves; i++) {
538 if (moves[i].color == WHITE) {
539 if ((col += sprintf(tmp, "%d. ", (i+1) / 2 + 1)) > 70) {
540 strcat(gameString, "\n");
543 strcat(gameString, tmp);
545 strcat (tmp, "1. ... ");
548 if ((col += sprintf(tmp, "%s ", moves[i].algString)) > 70) {
549 strcat(gameString, "\n");
552 strcat(gameString, tmp);
553 if(moves[i].depth > 0) { // [HGM] computer game, add {score/depth} comment
554 if ((col += sprintf(tmp, "{%s%.2f/%d} ", moves[i].score > 0 ? "+" : "",
555 moves[i].score, moves[i].depth)) > 70) {
556 strcat(gameString, "\n");
559 strcat(gameString, tmp);
562 strcat(gameString, "\n");
566 sprintf(gameString, "\n%s ", game_globals.garray[g].white_name);
568 sprintf(tmp, "(%d) ", wr);
570 sprintf(tmp, "(UNR) ");
572 strcat(gameString, tmp);
573 sprintf(tmp, "vs. %s ", game_globals.garray[g].black_name);
574 strcat(gameString, tmp);
576 sprintf(tmp, "(%d) ", br);
578 sprintf(tmp, "(UNR) ");
580 strcat(gameString, tmp);
581 strcat(gameString, "--- ");
582 strcat(gameString, strltime(&curTime));
583 if (game_globals.garray[g].rated) {
584 strcat(gameString, "\nRated ");
586 strcat(gameString, "\nUnrated ");
588 // strcat (gameString, TypeStrings[game_globals.garray[g].type]);
589 strcat (gameString, game_globals.garray[g].variant);
590 strcat(gameString, " match, initial time: ");
591 if ((game_globals.garray[g].bInitTime != game_globals.garray[g].wInitTime) || (game_globals.garray[g].wIncrement != game_globals.garray[g].bIncrement)) { /* different starting times */
592 sprintf(tmp, "%d minutes, increment: %d seconds AND %d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10, game_globals.garray[g].bInitTime / 600, game_globals.garray[g].bIncrement / 10);
594 sprintf(tmp, "%d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10);
596 strcat(gameString, tmp);
598 if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the initial position board
599 char *q; struct game_state_t initial_gs; struct move_t ml[600]; int r, f;
601 initial_gs.gameNum = g;
602 initial_gs.wkrmoved = 0; // [HGM] for some reason calling reset_board_vars() does not work here
603 initial_gs.wqrmoved = 0; // so I just duplicated the code and pasted it here...
604 initial_gs.wkmoved = 0;
605 initial_gs.bkrmoved = 0;
606 initial_gs.bqrmoved = 0;
607 initial_gs.bkmoved = 0;
608 initial_gs.onMove = WHITE;
609 initial_gs.lastIrreversable = -1;
610 initial_gs.files = game_globals.garray[g].game_state.files;
611 initial_gs.ranks = game_globals.garray[g].game_state.ranks;
612 initial_gs.holdings = game_globals.garray[g].game_state.holdings;
613 strcpy(initial_gs.variant, game_globals.garray[g].game_state.variant);
614 for (f = 0; f < 2; f++) {
615 for (r = 0; r < initial_gs.files; r++)
616 initial_gs.ep_possible[f][r] = 0;
617 for (r = PAWN; r <= PIECES-1; r++)
618 initial_gs.holding[f][r-PAWN] = 0;
620 FEN_to_board(game_globals.garray[g].FENstartPos ,&initial_gs);
622 kludgeFlag = 1; // [HGM] setup: this is not thread safe. Must it be???
624 game_globals.garray[g].white_name,
625 game_globals.garray[g].black_name,
626 game_globals.garray[g].wTime,
627 game_globals.garray[g].bTime,
634 strcat(gameString, q);
635 strcat(gameString, "\n");
637 sprintf(tmp, "Move %-19s%-19s\n", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
638 strcat(gameString, tmp);
639 strcat(gameString, "---- ---------------- ----------------\n");
641 for (i = 0; i < game_globals.garray[g].numHalfMoves; i += 2) {
642 if (i==0 && (moves[i].color == BLACK)) {
643 sprintf(tmp, "%3d. %-16s %-16s\n", (i+1)/2 + 1, "...",
644 move_and_time(&moves[i]));
646 } else if (i + 1 < game_globals.garray[g].numHalfMoves) {
647 sprintf(tmp, "%3d. %-16s ", (i+1)/2 + 1, move_and_time(&moves[i]));
648 strcat(gameString, tmp);
649 sprintf(tmp, "%-16s\n", move_and_time(&moves[i+1]));
651 sprintf(tmp, "%3d. %-16s\n", (i+1)/2 + 1, move_and_time(&moves[i]));
653 strcat(gameString, tmp);
654 if (strlen(gameString) > GAME_STRING_LEN - 100) { /* Bug out if getting
655 close to filling this
661 strcat(gameString, " ");
664 sprintf(tmp, "{%s} %s\n", EndString(g, 0), EndSym(g));
665 strcat(gameString, tmp);
670 void game_disconnect(int g, int p)
672 game_ended(g, (game_globals.garray[g].white == p) ? WHITE : BLACK, END_LOSTCONNECTION);
675 int CharToPiece(char c, char *variant)
679 if(!strcmp(variant, "shogi")) {
682 return W_HONORABLEHORSE;
684 return B_HONORABLEHORSE;
698 } else if(!strcmp(variant, "xiangqi")) {
713 } else if(!strcmp(variant, "super")) {
732 } else if(!strcmp(variant, "spartan")) {
781 if(!strcmp(variant, "seirawan")) return W_ELEPHANT;
784 if(!strcmp(variant, "seirawan")) return B_ELEPHANT;
799 char PieceToChar(int piece)
802 case W_PAWN:return 'P';
806 case W_HONORABLEHORSE:
809 case B_HONORABLEHORSE:
920 write a new style generic game file
922 static void WriteGameFile_v100(FILE * fp, int g)
924 struct game gg = game_globals.garray[g];
927 /* zero any elements we don't want to save */
928 memset(&gg, 0, offsetof(struct game, not_saved_marker));
929 gg.game_state.gameNum = 0;
931 /* marshall it into a string */
932 s = marshall_game(&gg);
934 d_printf("Unable to marshall game structure!\n");
939 fprintf(fp, "v 100\n%s\n", s);
945 read a game file using the new generic and extensible format
947 static int ReadGameAttrs_v100(FILE *fp, int g)
950 struct game *gg = &game_globals.garray[g];
953 s = fd_load(fileno(fp), NULL);
955 d_printf("Error reading game file!\n");
959 /* skip first line */
960 s2 = strchr(s, '\n');
962 /* remember the game state for later */
965 /* the marshaller doesn't save zero elements, but some elements don't
966 default to zero. Make sure they get the right value */
967 memset(&gg->not_saved_marker, 0,
968 sizeof(struct game) - offsetof(struct game, not_saved_marker));
969 gg->game_state.gameNum = g;
971 if (!s2 || unmarshall_game(gg, s2) != 0) {
972 d_printf("Error unmarshalling game data!\n");
978 /* when examining we are not supposed to restore the game
979 state, so put it back here */
980 if (g1.status == GAME_EXAMINE || g1.status == GAME_SETUP) {
981 gg->game_state = g1.game_state;
983 gg->status = g1.status;
985 /* cope with continuing a game with timeseal that was started without it */
986 gg->wRealTime = gg->wTime * 100;
987 gg->bRealTime = gg->bTime * 100;
993 static int ReadGameAttrs_common(FILE * fp, int g,int version)
995 if (version == 9 || version == 100) {
996 return ReadGameAttrs_v100(fp, g);
999 return ReadGameAttrs_old(fp, g, version);
1002 int ReadGameAttrs_exam(FILE * fp, int g)
1005 char line[MAX_GLINE_SIZE];
1007 fgets(line, MAX_GLINE_SIZE - 1, fp);
1009 if (line[0] == 'v') {
1010 sscanf(line, "%*c %d", &version);
1015 return ReadGameAttrs_common(fp,g,version);
1021 int ReadGameAttrs(FILE * fp, int g)
1024 char line[MAX_GLINE_SIZE];
1026 fgets(line, MAX_GLINE_SIZE - 1, fp);
1028 if (line[0] == 'v') {
1029 sscanf(line, "%*c %d", &version);
1031 return ReadGameAttrs_common(fp,g,version);
1034 int game_read(int g, int wp, int bp)
1039 game_globals.garray[g].white = wp;
1040 game_globals.garray[g].black = bp;
1041 /* game_globals.garray[g].old_white = -1;
1042 game_globals.garray[g].old_black = -1;
1044 game_globals.garray[g].moveListSize = 0;
1045 game_globals.garray[g].game_state.gameNum = g;
1046 strcpy(game_globals.garray[g].white_name, player_globals.parray[wp].name);
1047 strcpy(game_globals.garray[g].black_name, player_globals.parray[bp].name);
1048 if (game_globals.garray[g].type == TYPE_BLITZ) {
1049 game_globals.garray[g].white_rating = player_globals.parray[wp].b_stats.rating;
1050 game_globals.garray[g].black_rating = player_globals.parray[bp].b_stats.rating;
1051 } else if (game_globals.garray[g].type == TYPE_WILD ||
1052 game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1053 game_globals.garray[g].type == TYPE_CAPABLANCA ||
1054 game_globals.garray[g].type == TYPE_GOTHIC) {
1055 game_globals.garray[g].white_rating = player_globals.parray[wp].w_stats.rating;
1056 game_globals.garray[g].black_rating = player_globals.parray[bp].w_stats.rating;
1057 } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1058 game_globals.garray[g].white_rating = player_globals.parray[wp].l_stats.rating;
1059 game_globals.garray[g].black_rating = player_globals.parray[bp].l_stats.rating;
1060 } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1061 game_globals.garray[g].white_rating = player_globals.parray[wp].bug_stats.rating;
1062 game_globals.garray[g].black_rating = player_globals.parray[bp].bug_stats.rating;
1064 game_globals.garray[g].white_rating = player_globals.parray[wp].s_stats.rating;
1065 game_globals.garray[g].black_rating = player_globals.parray[bp].s_stats.rating;
1067 fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1068 player_globals.parray[wp].login, player_globals.parray[bp].login);
1072 for (piece=PAWN; piece < KING; piece++) {
1073 game_globals.garray[g].game_state.holding[0][piece-PAWN]
1074 = game_globals.garray[g].game_state.holding[1][piece-PAWN] = 0;
1076 if (ReadGameAttrs(fp, g) < 0) {
1082 if (game_globals.garray[g].result == END_ADJOURN
1083 || game_globals.garray[g].result == END_COURTESYADJOURN)
1084 game_globals.garray[g].result = END_NOTENDED;
1085 game_globals.garray[g].status = GAME_ACTIVE;
1086 game_globals.garray[g].startTime = tenth_secs();
1087 game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1088 game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1090 /* cope with continuing a game with timeseal that was started without it */
1091 game_globals.garray[g].wRealTime = game_globals.garray[g].wTime * 100;
1092 game_globals.garray[g].bRealTime = game_globals.garray[g].bTime * 100;
1094 /* Need to do notification and pending cleanup */
1098 int game_delete(int wp, int bp)
1100 char fname[MAX_FILENAME_SIZE];
1101 char lname[MAX_FILENAME_SIZE];
1103 sprintf(fname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1104 player_globals.parray[wp].login, player_globals.parray[bp].login);
1105 sprintf(lname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[bp].login[0],
1106 player_globals.parray[wp].login, player_globals.parray[bp].login);
1112 int game_save(int g)
1115 struct player *wp, *bp;
1116 struct game *gg = &game_globals.garray[g];
1119 wp = &player_globals.parray[gg->white];
1120 bp = &player_globals.parray[gg->black];
1121 asprintf(&fname, "%s/%c/%s-%s", ADJOURNED_DIR, wp->login[0],
1122 wp->login, bp->login);
1123 fp = fopen_p("%s", "w", fname);
1125 d_printf("CHESSD: Problem opening adjourn file %s-%s for write\n",
1126 wp->login, bp->login);
1130 WriteGameFile_v100(fp, g);
1132 /* Create link for easier stored game finding */
1133 if (bp->login[0] != wp->login[0]) {
1135 asprintf(&lname, "%s/%c/%s-%s", ADJOURNED_DIR, bp->login[0],
1136 wp->login, bp->login);
1144 static long OldestHistGame(char *login)
1149 fp = fopen_p("%s/player_data/%c/%s.%s", "r", STATS_DIR,
1150 login[0], login, STATS_GAMES);
1152 fp = fopen_p("%s/player_data/%c/.rem.%s.%s", "r", STATS_DIR,
1153 login[0], login, STATS_GAMES);
1157 fscanf(fp, "%*d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
1163 static void RemoveHistGame(char *file, int maxlines)
1166 char GameFile[MAX_FILENAME_SIZE];
1167 char Opponent[MAX_LOGIN_NAME];
1168 char line[MAX_LINE_SIZE];
1172 fp = fopen_p("%s", "r", file);
1176 fgets(line, MAX_LINE_SIZE - 1, fp);
1177 sscanf(line, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1182 fgets(line, MAX_LINE_SIZE - 1, fp);
1189 if (count > maxlines) {
1190 truncate_file(file, maxlines);
1192 oppWhen = OldestHistGame(Opponent);
1193 if (oppWhen > When || oppWhen <= 0L) {
1194 sprintf(GameFile, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1200 void RemHist(char *who)
1203 char Opp[MAX_LOGIN_NAME];
1206 fp = fopen_p("%s/player_data/%c/%s.%s", "r",
1208 who[0], who, STATS_GAMES);
1213 fscanf(fp, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1216 oppWhen = OldestHistGame(Opp);
1217 if (oppWhen > When || oppWhen <= 0L) {
1219 asprintf(&fName, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1227 static void write_g_out(int g, char *file, int maxlines, int isDraw,
1228 char *EndSymbol, char *name, time_t *now)
1240 wp = game_globals.garray[g].white;
1241 bp = game_globals.garray[g].black;
1243 if (game_globals.garray[g].private) {
1248 if (game_globals.garray[g].type == TYPE_BLITZ) {
1249 wr = player_globals.parray[wp].b_stats.rating;
1250 br = player_globals.parray[bp].b_stats.rating;
1252 } else if (game_globals.garray[g].type == TYPE_WILD ||
1253 game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1254 game_globals.garray[g].type == TYPE_CAPABLANCA ||
1255 game_globals.garray[g].type == TYPE_GOTHIC) {
1256 wr = player_globals.parray[wp].w_stats.rating;
1257 br = player_globals.parray[bp].w_stats.rating;
1259 } else if (game_globals.garray[g].type == TYPE_STAND) {
1260 wr = player_globals.parray[wp].s_stats.rating;
1261 br = player_globals.parray[bp].s_stats.rating;
1263 } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1264 wr = player_globals.parray[wp].l_stats.rating;
1265 br = player_globals.parray[bp].l_stats.rating;
1267 } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1268 wr = player_globals.parray[wp].bug_stats.rating;
1269 br = player_globals.parray[bp].bug_stats.rating;
1274 if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1279 if (game_globals.garray[g].rated) {
1286 fp = fopen_s(file, "r");
1289 fgets(tmp, 1024, fp);
1290 sscanf(ptmp, "%d", &count);
1293 count = (count + 1) % 100;
1295 fp = fopen_s(file, "a");
1301 /* Counter Result MyRating MyColor OppRating OppName [pbr 2 12 2 12] ECO End Date */
1302 if (name == player_globals.parray[wp].name) {
1305 else if (game_globals.garray[g].winner == WHITE)
1310 fprintf(fp, "%d %c %d W %d %s %s %d %d %d %d %s %s %ld\n",
1311 count, cResult, wr, br, player_globals.parray[bp].name, type,
1312 game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1313 game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1320 else if (game_globals.garray[g].winner == BLACK)
1325 fprintf(fp, "%d %c %d B %d %s %s %d %d %d %d %s %s %ld\n",
1326 count, cResult, br, wr, player_globals.parray[wp].name, type,
1327 game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1328 game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1335 RemoveHistGame(file, maxlines);
1338 /* Test if entry is present - 1 yes 0 no */
1339 /* returns -1 if an error */
1341 char get_journalgame_type(int p,char* fname,char slot)
1344 struct player *pp = &player_globals.parray[p];
1348 FILE* fp = fopen_s(fname,"r");
1350 d_printf("Corrupt journal file! %s\n",fname);
1351 pprintf (p, "The journal file is corrupt! See an admin.\n");
1356 if (fscanf(fp, "%c %*s %*d %*s %*d %s %*d %*d %*s %*s %*s\n",
1357 &cur_slot,type) != 2) {
1358 d_printf( "CHESSD: Error in journal info format for player %s.\n",
1360 pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1363 if (cur_slot == slot) {
1376 /* Returns 1 if successful */
1378 int removejournalitem(int p, char slot,FILE* fp,char* fname, int* empty)
1386 fp_new = fopen_s(fname, "w");
1388 d_printf("Can't write to journal %s!\n",fname);
1389 pprintf (p, "Was unable to write to the file! See an admin.\n");
1392 j = (struct journal*) malloc(sizeof(struct journal));
1394 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1405 d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1406 pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1411 if (slot != j->slot) {
1413 fprintf(fp_new, "%c %s %d %s %d %s %d %d %s %s %s\n",
1432 /* Find from_spot in journal list - return 0 if corrupted */
1433 int journal_get_info(int p, char from_spot,struct journal* j, char *fname)
1437 fp = fopen_s(fname, "r");
1439 d_printf("Corrupt journal file! %s\n",fname);
1440 pprintf (p, "The journal file is corrupt! See an admin.\n");
1444 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1455 d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1456 pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1460 if (tolower(j->slot) == from_spot) {
1469 void addjournalitem(int p,struct journal* j_add, char* fname)
1471 struct journal* j_cur;
1473 char fname2[MAX_FILENAME_SIZE];
1477 strcpy (fname2,fname);
1478 strcat (fname2,".w");
1479 fp2 = fopen_s(fname2, "w");
1481 d_printf( "CHESSD: Problem opening file %s for write\n", fname);
1482 pprintf (p, "Couldn't update journal! Report this to an admin.\n");
1485 fp = fopen_s(fname, "r");
1486 if (!fp) { /* Empty? */
1487 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1488 j_add->slot, j_add->WhiteName, j_add->WhiteRating,
1489 j_add->BlackName, j_add->BlackRating,
1490 j_add->type, j_add->t, j_add->i, j_add->eco, j_add->ending,
1493 rename (fname2, fname);
1496 j_cur = (struct journal*) malloc(sizeof(struct journal));
1498 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1501 &j_cur->WhiteRating,
1503 &j_cur->BlackRating,
1505 &j_cur->t, &j_cur->i,
1508 j_cur->result) != 11) {
1509 d_printf( "CHESSD: Error in journal info format - aborting. %s\n", fname);
1515 if ((j_cur->slot >= j_add->slot) && (!have_output)) {
1517 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1531 if (j_cur->slot != j_add->slot) {
1533 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1547 if (!have_output) { /* Haven't written yet */
1548 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1563 rename(fname2, fname);
1568 int pjournal(int p, int p1, char *fname)
1573 fp = fopen_s(fname, "r");
1575 pprintf(p, "Sorry, no journal information available.\n");
1579 j = (struct journal*) malloc(sizeof(struct journal));
1580 pprintf(p, "Journal for %s:\n", player_globals.parray[p1].name);
1581 pprintf(p, " White Rating Black Rating Type ECO End Result\n");
1583 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1594 d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1599 j->WhiteName[13] = '\0'; /* only first 13 chars in name */
1600 j->BlackName[13] = '\0';
1601 pprintf(p, "%c: %-13s %4d %-13s %4d [%3s%3d%4d] %s %3s %-7s\n",
1602 j->slot, j->WhiteName, j->WhiteRating,
1603 j->BlackName, j->BlackRating,
1604 j->type, j->t / 600, j->i / 10, j->eco, j->ending,
1612 int pgames(int p, int p1, char *fname)
1616 int MyRating, OppRating;
1618 char OppName[MAX_LOGIN_NAME + 1];
1626 fp = fopen_s(fname, "r");
1628 pprintf(p, "Sorry, no game information available.\n");
1631 pprintf(p, "History for %s:\n", player_globals.parray[p1].name);
1632 pprintf(p, " Opponent Type ECO End Date\n");
1634 if (fscanf(fp, "%d %s %d %s %d %s %s %d %d %d %d %s %s %ld\n",
1646 (long *) &t) != 14) {
1647 d_printf( "CHESSD: Error in games info format. %s\n", fname);
1651 OppName[13] = '\0'; /* only first 13 chars in name */
1652 pprintf(p, "%2d: %s %4d %s %4d %-13s [%3s%3d%4d] %s %3s %s",
1653 count, result, MyRating, MyColor,
1655 type, wt / 600, wi / 10, eco, ending,
1662 void game_write_complete(int g, int isDraw, char *EndSymbol)
1664 char fname[MAX_FILENAME_SIZE];
1665 int wp = game_globals.garray[g].white, bp = game_globals.garray[g].black;
1666 time_t now = time(NULL);
1675 sprintf(fname, "%s/%ld/%ld", HISTORY_DIR, (long) now % 100, (long) now);
1676 fp = fopen_s(fname, "r");
1677 } while (fp); /* terminates when the file doesn't exist */
1679 fp = fopen_s(fname, "w");
1682 WriteGameFile_v100(fp, g);
1685 d_printf( "Trouble writing history file %s", fname);
1688 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1689 player_globals.parray[wp].login[0], player_globals.parray[wp].login, STATS_GAMES);
1690 write_g_out(g, fname, 40, isDraw, EndSymbol, player_globals.parray[wp].name, &now);
1691 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1692 player_globals.parray[bp].login[0], player_globals.parray[bp].login, STATS_GAMES);
1693 write_g_out(g, fname, 40, isDraw, EndSymbol, player_globals.parray[bp].name, &now);
1697 else if (game_globals.garray[g].winner == WHITE)
1701 sprintf(fname,"%s/player_data/%c/%s.gstats", STATS_DIR, player_globals.parray[game_globals.garray[g].white].login[0], player_globals.parray[game_globals.garray[g].white].login);
1702 if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1703 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].black].name,Result,game_globals.garray[g].rated);
1706 else if (game_globals.garray[g].winner == BLACK)
1710 sprintf(fname,"%s/player_data/%c/%s.gstats", STATS_DIR, player_globals.parray[game_globals.garray[g].black].login[0], player_globals.parray[game_globals.garray[g].black].login);
1711 if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1712 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].white].name,Result,game_globals.garray[g].rated);
1715 int game_count(void)
1719 for (g = 0; g < game_globals.g_num; g++) {
1720 if ((game_globals.garray[g].status == GAME_ACTIVE) || (game_globals.garray[g].status == GAME_EXAMINE)
1721 || (game_globals.garray[g].status == GAME_SETUP))
1724 if (count > command_globals.game_high)
1725 command_globals.game_high = count;
1729 static int check_kings(struct game_state_t *gs)
1731 /* Function written 3/28/96 by Figbert to check for 1 king each side only! */
1732 int blackking = 0, whiteking = 0;
1736 for (f = 0; f < gs->files; f++) {
1737 for (r = 0; r < gs->ranks; r++) {
1738 if (gs->board[f][r] == B_KING) blackking++;
1739 if (gs->board[f][r] == W_KING) whiteking++;
1743 if ((blackking == 1 || blackking == 2 && !strcmp(gs->variant, "spartan")) && whiteking == 1) return 0; /* Perfect! */