/* Ordo is program for calculating ratings of engine or chess players Copyright 2013 Miguel A. Ballicora This file is part of Ordo. Ordo 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 3 of the License, or (at your option) any later version. Ordo 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. You should have received a copy of the GNU General Public License along with Ordo. If not, see <http://www.gnu.org/licenses/>. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <assert.h> #include "mystr.h" #include "pgnget.h" #include "boolean.h" #include "ordolim.h" #include "mytypes.h" #include "mymem.h" #include "namehash.h" #if 0 static void hashstat(void); #endif /* | | ORDO DEFINITIONS | \*--------------------------------------------------------------*/ #define PGNSTRSIZE 1024 struct pgn_result { int wtag_present; int btag_present; int result_present; int result; char wtag[PGNSTRSIZE]; char btag[PGNSTRSIZE]; }; static struct DATA * structdata_init (void); static void structdata_done (struct DATA *d); /*------------------------------------------------------------------------*/ static bool_t addplayer (struct DATA *d, const char *s, player_t *i); static void report_error (long int n); static int res2int (const char *s); static bool_t fpgnscan (FILE *fpgn, bool_t quiet, struct DATA *d); static bool_t is_complete (struct pgn_result *p); static void pgn_result_reset (struct pgn_result *p); static bool_t pgn_result_collect (struct pgn_result *p, struct DATA *d); static void syn_preload (bool_t quiet, const char *synfile_name, struct DATA *d); /* | | \*--------------------------------------------------------------*/ static struct DATA * structdata_init (void) { struct DATA *d = NULL; struct GAMEBLOCK *p = NULL; struct NAMEBLOCK *t = NULL; bool_t ok = TRUE; ok = ok && NULL != (d = memnew (sizeof(struct DATA))); if (ok) { d->labels_head.buf = NULL; d->labels_head.nxt = NULL; d->labels_head.idx = 0; d->curr = &d->labels_head; d->n_players = 0; d->n_games = 0; d->gb_filled = 0;; d->gb_idx = 0; d->gb_allocated = 0; d->nm_filled = 0;; d->nm_idx = 0; d->nm_allocated = 0; ok = ok && NULL != (p = memnew (sizeof(struct GAMEBLOCK))); if (ok) d->gb_allocated++; d->gb[0] = p; ok = ok && NULL != (t = memnew (sizeof(struct NAMEBLOCK))); if (ok) d->nm_allocated++; d->nm[0] = t; if (!ok) structdata_done(d); } return ok? d: NULL; } static void structdata_done (struct DATA *d) { size_t n; namenode_t *p; namenode_t *q; d->n_players = 0; d->n_games = 0; // n = d->gb_allocated; while (n-->0) { memrel(d->gb[n]); d->gb[n] = NULL; } d->gb_filled = 0;; d->gb_idx = 0; d->gb_allocated = 0; // n = d->nm_allocated; while (n-->0) { memrel(d->nm[n]); d->nm[n] = NULL; } d->nm_filled = 0;; d->nm_idx = 0; d->nm_allocated = 0; // p = d->labels_head.nxt; while (p) { if (p->buf) {memrel(p->buf); p->buf = NULL; p->idx = 0;} q = p->nxt; memrel(p); p = q; } p = &d->labels_head; if (p->buf) {memrel(p->buf); p->buf = NULL; p->idx = 0;} } /* | | \*--------------------------------------------------------------*/ #include "strlist.h" struct DATA * database_init_frompgn (strlist_t *sl, const char *synfile_name, bool_t quiet) { struct DATA *pDAB = NULL; FILE *fpgn; bool_t ok = FALSE; const char *pgn; ok = NULL != (pDAB = structdata_init ()); if (NULL != synfile_name) // not provided syn_preload (quiet, synfile_name, pDAB); strlist_rwnd(sl); pgn = strlist_next(sl); while (ok && pgn) { if (!quiet) printf ("\nFile: %s\n",pgn); if (NULL != (fpgn = fopen (pgn, "r"))) { ok = fpgnscan (fpgn, quiet, pDAB); fclose(fpgn); } else { ok = FALSE; } if (ok) pgn = strlist_next(sl); } return ok? pDAB: NULL; #if 0 hashstat(); #endif } void database_done (struct DATA *p) { structdata_done (p); return; } const char * database_getname(const struct DATA *d, player_t i) { size_t j = (size_t)i / MAXNAMESxBLOCK; size_t k = (size_t)i % MAXNAMESxBLOCK; return d->nm[j]->p[k]; } #include "mytypes.h" void database_transform(const struct DATA *db, struct GAMES *g, struct PLAYERS *p, struct GAMESTATS *gs) { enum maxresults {MAXRESTYPE = 8}; player_t j; player_t topn; gamesnum_t gamestat[MAXRESTYPE] = {0,0,0,0,0,0,0,0}; assert(db && p && g && gs); assert(p->name && p->flagged && p->present_in_games && p->prefed && p->priored && p->performance_type); p->n = db->n_players; g->n = db->n_games; topn = db->n_players; for (j = 0; j < topn; j++) { p->name[j] = database_getname(db,j); p->flagged[j] = FALSE; p->present_in_games[j] = FALSE; p->prefed [j] = FALSE; p->priored[j] = FALSE; p->performance_type[j] = PERF_NORMAL; } { player_t wp, bp; size_t blk_filled = db->gb_filled; size_t blk; size_t idx_last = db->gb_idx; size_t idx; gamesnum_t i = 0; for (blk = 0; blk < blk_filled; blk++) { for (idx = 0; idx < MAXGAMESxBLOCK; idx++) { g->ga[i].whiteplayer = wp = db->gb[blk]->white[idx]; g->ga[i].blackplayer = bp = db->gb[blk]->black[idx]; g->ga[i].score = db->gb[blk]->score[idx]; if (g->ga[i].score < MAXRESTYPE) gamestat[g->ga[i].score]++; if (g->ga[i].score < DISCARD) { p->present_in_games[wp] = TRUE; p->present_in_games[bp] = TRUE; } i++; } } blk = blk_filled; for (idx = 0; idx < idx_last; idx++) { g->ga[i].whiteplayer = wp = db->gb[blk]->white[idx]; g->ga[i].blackplayer = bp = db->gb[blk]->black[idx]; g->ga[i].score = db->gb[blk]->score[idx]; if (g->ga[i].score < MAXRESTYPE) gamestat[g->ga[i].score]++; if (g->ga[i].score < DISCARD) { p->present_in_games[wp] = TRUE; p->present_in_games[bp] = TRUE; } i++; } if (i != db->n_games) { fprintf (stderr, "Error, games not loaded propely\n"); exit(EXIT_FAILURE); } } gs->white_wins = gamestat[WHITE_WIN]; gs->draws = gamestat[RESULT_DRAW]; gs->black_wins = gamestat[BLACK_WIN]; gs->noresult = gamestat[DISCARD] + gamestat[IGNORED|WHITE_WIN] + gamestat[IGNORED|RESULT_DRAW] + gamestat[IGNORED|BLACK_WIN] ; assert ((long)g->n == (gs->white_wins + gs->draws + gs->black_wins + gs->noresult)); return; } void database_ignore_draws (struct DATA *db) { size_t blk_filled = db->gb_filled; size_t idx_last = db->gb_idx; size_t blk; size_t idx; blk_filled = db->gb_filled; idx_last = db->gb_idx; for (blk = 0; blk < blk_filled; blk++) { for (idx = 0; idx < MAXGAMESxBLOCK; idx++) { if (db->gb[blk]->score[idx] == RESULT_DRAW) db->gb[blk]->score[idx] |= IGNORED; } } blk = blk_filled; for (idx = 0; idx < idx_last; idx++) { if (db->gb[blk]->score[idx] == RESULT_DRAW) db->gb[blk]->score[idx] |= IGNORED; } return; } #include "bitarray.h" void database_include_only (struct DATA *db, bitarray_t *pba) { player_t wp, bp; size_t blk; size_t idx; size_t blk_filled = db->gb_filled; size_t idx_last = db->gb_idx; for (blk = 0; blk < blk_filled; blk++) { for (idx = 0; idx < MAXGAMESxBLOCK; idx++) { wp = db->gb[blk]->white[idx]; bp = db->gb[blk]->black[idx]; if (!ba_ison(pba, wp) || !ba_ison(pba, bp)) db->gb[blk]->score[idx] |= IGNORED; } } blk = blk_filled; for (idx = 0; idx < idx_last; idx++) { wp = db->gb[blk]->white[idx]; bp = db->gb[blk]->black[idx]; if (!ba_ison(pba, wp) || !ba_ison(pba, bp)) db->gb[blk]->score[idx] |= IGNORED; } return; } /*--------------------------------------------------------------*\ | | \**/ static const char * addname (struct DATA *d, const char *s) { char *b; char *nameptr; char *bf = NULL; namenode_t *nd; size_t sz = strlen(s) + 1; bool_t ok; assert (d->curr != NULL); ok = d->curr->buf != NULL; if (!ok) { assert (d->curr->nxt == NULL); assert (d->curr->idx == 0); ok = NULL != (bf = memnew (LABELBUFFERSIZE)); if (ok) { bf[0] = '\0'; d->curr->buf = bf; d->curr->nxt = NULL; d->curr->idx = 0; } } if (!ok) return NULL; ok = ok && (LABELBUFFERSIZE > d->curr->idx + sz); if (!ok) { ok = NULL != (nd = memnew (sizeof(namenode_t))); if (ok) { ok = NULL != (bf = memnew (LABELBUFFERSIZE)); if (ok) bf[0] = '\0'; else memrel(nd); } if (ok) { nd->nxt = NULL; nd->buf = bf; nd->idx = 0; d->curr->nxt = nd; d->curr = nd; ok = LABELBUFFERSIZE > d->curr->idx + sz; } } if (ok) { size_t i; nameptr = b = &d->curr->buf[d->curr->idx]; for (i = 0; i < sz; i++) {*b++ = *s++;} d->curr->idx += sz; } else { nameptr = NULL; } return nameptr; } static bool_t addplayer (struct DATA *d, const char *s, player_t *idx) { const char *nameptr; bool_t success = NULL != (nameptr = addname(d,s)); if (success) { *idx = d->n_players++; d->nm[d->nm_filled]->p[d->nm_idx++] = nameptr; if (d->nm_idx == MAXNAMESxBLOCK) { // hit new block struct NAMEBLOCK *nm; d->nm_idx = 0; d->nm_filled++; success = NULL != (nm = memnew (sizeof(struct NAMEBLOCK))); if (success) { d->nm_allocated++; } d->nm[d->nm_filled] = nm; } } return success; } static void report_error (long int n) { fprintf(stderr, "\nParsing error in line: %ld\n", n+1); } #include "csv.h" static char *skipblanks(char *p) {while (isspace(*p)) p++; return p;} static bool_t syn_apply_pair (struct DATA *d, const char *m, const char *s) { #define NOPLAYER -1 bool_t ok = TRUE; player_t plyr_0 = NOPLAYER; // to silence warnings player_t plyr_i = NOPLAYER; // to silence warnings const char *tagstr; uint32_t taghsh; tagstr = m; taghsh = namehash(tagstr); if (ok && !name_ispresent (d, tagstr, taghsh, &plyr_0)) { ok = addplayer (d, tagstr, &plyr_0) && name_register(taghsh,plyr_0,plyr_0); } tagstr = s; taghsh = namehash(tagstr); if (ok && !name_ispresent (d, tagstr, taghsh, &plyr_i)) { ok = addplayer (d, tagstr, &plyr_i) && name_register(taghsh,plyr_i,plyr_0); } return ok; } static bool_t csvline_syn_apply (csv_line_t *pcsvln, struct DATA *d /*, bool_t quiet*/) { bool_t ok = TRUE; int i; for (i = 1; ok && i < pcsvln->n; i++) { // if (!quiet) printf ("[%s] synonym of [%s]\n",pcsvln->s[0],pcsvln->s[i]); ok = ok && syn_apply_pair (d, pcsvln->s[0], pcsvln->s[i]); } return ok; } static void syn_preload (bool_t quietmode, const char *fsyns_name, struct DATA *d) { FILE *fsyns; char myline[MAXSIZE_CSVLINE]; char *p; bool_t success; bool_t line_success = TRUE; bool_t file_success = TRUE; assert(NULL != fsyns_name); if (NULL == fsyns_name) { fprintf (stderr, "Error, synonym file not provided, absent, or corrupted\n"); exit(EXIT_FAILURE); } if (NULL != (fsyns = fopen (fsyns_name, "r"))) { csv_line_t csvln; line_success = TRUE; file_success = TRUE; while ( line_success && file_success && NULL != fgets(myline, MAXSIZE_CSVLINE, fsyns)) { success = FALSE; p = myline; p = skipblanks(p); if (*p == '\0') continue; if (csv_line_init(&csvln, myline)) { if (csvln.n >= 1) { success = csvline_syn_apply (&csvln, d /*, quietmode*/); } csv_line_done(&csvln); } else { fprintf (stderr,"Failure to input synonym file\n"); exit(EXIT_FAILURE); } file_success = success; } fclose(fsyns); } else { file_success = FALSE; } if (!file_success) { fprintf (stderr, "Errors in file \"%s\"\n",fsyns_name); exit(EXIT_FAILURE); } else { if (!quietmode) printf ("Synonyms uploaded succesfully\n"); } if (!line_success) { fprintf (stderr, "Errors in file \"%s\" (not matching names)\n",fsyns_name); exit(EXIT_FAILURE); } return; } //---- for name preload static bool_t name2player (const struct DATA *d, const char *namestr, player_t *plyr) { player_t p = 0; // to silence warning uint32_t hsh = namehash(namestr); if (name_ispresent (d, namestr, hsh, &p)) { *plyr = p; return TRUE; } return FALSE; } static bool_t do_tick (const struct DATA *d, const char *namestr, bitarray_t *pba) { player_t p = 0; // to silence warnings bool_t ok = name2player (d, namestr, &p); if (ok) ba_put (pba, p); return ok; } static void warning(bool_t do_warning, const char *filename, const char *myline, size_t linenumber) { if (!do_warning) return; fprintf(stderr, "WARNING (not matching name): file \"%s\", line %ld = %s\n", filename, (long)linenumber, myline); } static bool_t isblankline(const char *s) { while (isspace(*s)) s++; return *s == '\0'; } void namelist_to_bitarray (bool_t quietmode, bool_t do_warning, const char *finp_name, const struct DATA *d, bitarray_t *pba) { FILE *finp; char myline[MAXSIZE_CSVLINE]; size_t linenumber = 0; bool_t line_success = TRUE; bool_t file_success = TRUE; assert (pba); assert (d); ba_clear (pba); if (NULL == finp_name) { return; } if (NULL != (finp = fopen (finp_name, "r"))) { csv_line_t csvln; line_success = TRUE; while ( line_success && NULL != fgets(myline, MAXSIZE_CSVLINE, finp)) { linenumber++; if (isblankline(myline)) continue; if (TRUE == (line_success = csv_line_init(&csvln, myline))) { if (!(csvln.n == 1 && do_tick (d, csvln.s[0], pba))) { warning(do_warning, finp_name, myline, linenumber); } csv_line_done(&csvln); } } fclose(finp); } else { file_success = FALSE; } if (!file_success) { fprintf (stderr, "Errors in file \"%s\"\n",finp_name); exit(EXIT_FAILURE); } else if (!line_success) { fprintf (stderr, "Errors in file \"%s\", line %ld (line parsing problem or lack of memory)\n",finp_name, (long)linenumber); exit(EXIT_FAILURE); } if (!quietmode) printf ("Names uploaded succesfully\n"); return; } //-------------------------------------- static void pgn_result_reset (struct pgn_result *p) { p->wtag_present = FALSE; p->btag_present = FALSE; p->result_present = FALSE; p->wtag[0] = '\0'; p->btag[0] = '\0'; p->result = 0; } static bool_t pgn_result_collect (struct pgn_result *p, struct DATA *d) { #define NOPLAYER -1 player_t i; player_t j; bool_t ok = TRUE; player_t plyr = NOPLAYER; // to silence warnings const char *tagstr; uint32_t taghsh; tagstr = p->wtag; taghsh = namehash(tagstr); if (ok && !name_ispresent (d, tagstr, taghsh, &plyr)) { ok = addplayer (d, tagstr, &plyr) && name_register(taghsh,plyr,plyr); } i = plyr; tagstr = p->btag; taghsh = namehash(tagstr); if (ok && !name_ispresent (d, tagstr, taghsh, &plyr)) { ok = addplayer (d, tagstr, &plyr) && name_register(taghsh,plyr,plyr); } j = plyr; ok = ok && (uint64_t)d->n_games < ((uint64_t)MAXGAMESxBLOCK*(uint64_t)MAXBLOCKS); assert (i != NOPLAYER && j != NOPLAYER); if (ok) { struct GAMEBLOCK *g; size_t idx = d->gb_idx; size_t blk = d->gb_filled; d->gb[blk]->white [idx] = i; d->gb[blk]->black [idx] = j; d->gb[blk]->score [idx] = p->result; d->n_games++; d->gb_idx++; if (d->gb_idx == MAXGAMESxBLOCK) { // hit new block d->gb_idx = 0; d->gb_filled++; blk = d->gb_filled; if (NULL == (g = memnew (sizeof(struct GAMEBLOCK) * (size_t)1))) { d->gb[blk] = NULL; ok = FALSE; // failed } else { d->gb[blk] = g; d->gb_allocated++; ok = TRUE; } } } return ok; } static bool_t is_complete (struct pgn_result *p) { return p->wtag_present && p->btag_present && p->result_present; } static void parsing_error(long line_counter) { report_error (line_counter); fprintf(stderr, "Parsing problems\n"); exit(EXIT_FAILURE); } static bool_t fpgnscan (FILE *fpgn, bool_t quiet, struct DATA *d) { #define MAX_MYLINE 40000 char myline[MAX_MYLINE]; const char *whitesep = "[White \""; const char *whiteend = "\"]"; const char *blacksep = "[Black \""; const char *blackend = "\"]"; const char *resulsep = "[Result \""; const char *resulend = "\"]"; size_t whitesep_len, blacksep_len, resulsep_len; char *x, *y; struct pgn_result result; long int line_counter = 0; long int game_counter = 0; int games_x_dot = 2000; if (NULL == fpgn) return FALSE; if (!quiet) printf("Loading data (%d games x dot): \n\n",games_x_dot); fflush(stdout); pgn_result_reset (&result); whitesep_len = strlen(whitesep); blacksep_len = strlen(blacksep); resulsep_len = strlen(resulsep); while (NULL != fgets(myline, MAX_MYLINE, fpgn)) { line_counter++; if (NULL != (x = strstr (myline, whitesep))) { x += whitesep_len; if (NULL != (y = strstr (myline, whiteend))) { *y = '\0'; strcpy (result.wtag, x); result.wtag_present = TRUE; } else { parsing_error(line_counter); } } if (NULL != (x = strstr (myline, blacksep))) { x += blacksep_len; if (NULL != (y = strstr (myline, blackend))) { *y = '\0'; strcpy (result.btag, x); result.btag_present = TRUE; } else { parsing_error(line_counter); } } if (NULL != (x = strstr (myline, resulsep))) { x += resulsep_len; if (NULL != (y = strstr (myline, resulend))) { *y = '\0'; result.result = res2int (x); result.result_present = TRUE; } else { parsing_error(line_counter); } } if (is_complete (&result)) { if (!pgn_result_collect (&result, d)) { fprintf (stderr, "\nCould not collect more games: Limits reached\n"); exit(EXIT_FAILURE); } pgn_result_reset (&result); game_counter++; if (!quiet) { if ((game_counter%games_x_dot)==0) { printf ("."); fflush(stdout); } if ((game_counter%100000)==0) { printf ("| %4ldk\n", game_counter/1000); fflush(stdout); } } } } /* while */ if (!quiet) printf("|\n\n"); fflush(stdout); return TRUE; } static int res2int (const char *s) { if (!strcmp(s, "1-0")) { return WHITE_WIN; } else if (!strcmp(s, "0-1")) { return BLACK_WIN; } else if (!strcmp(s, "1/2-1/2")) { return RESULT_DRAW; } else if (!strcmp(s, "=-=")) { return RESULT_DRAW; } else if (!strcmp(s, "*")) { return DISCARD; } else { fprintf(stderr, "PGN reading problems in Result tag: %s\n",s); exit(EXIT_FAILURE); return DISCARD; } } /************************************************************************/