X-Git-Url: http://winboard.nl/cgi-bin?p=xboard.git;a=blobdiff_plain;f=book.c;h=9828dd24e3d09decce7d4e5e45b1914489de2b94;hp=1eb0d6824d4e71486a96518a62d78a5602f559c7;hb=HEAD;hpb=4e062d14429ed3a3a251c971690bade4c8cba946 diff --git a/book.c b/book.c index 1eb0d68..9828dd2 100644 --- a/book.c +++ b/book.c @@ -2,7 +2,7 @@ * book.c -- code for probing Polyglot opening books * * This code was first released in the public domain by Michel Van den Bergh. - * The array Random64 is taken from the Polyglot source code. + * The array Random64 is taken from the Polyglot source code. * I am pretty sure that a table of random numbers is never protected * by copyright. * @@ -28,6 +28,8 @@ * ------------------------------------------------------------------------ */ +#include "config.h" + #include #include #include @@ -35,6 +37,7 @@ #include #include "common.h" +#include "frontend.h" #include "backend.h" #include "moves.h" #include "gettext.h" @@ -64,7 +67,7 @@ typedef unsigned short uint16; typedef unsigned int uint32; typedef struct { - uint64 key; + uint64 key; uint16 move; uint16 weight; uint16 learnPoints; @@ -75,7 +78,7 @@ entry_t entry_none = { 0, 0, 0, 0 }; -char *promote_pieces=" nbrqac="; +char *promote_pieces=" nbrqac=+"; uint64 Random64[781] = { U64(0x9D39247E33776D41), U64(0x2AF7398005AAA5C7), U64(0x44DB015024623547), U64(0x9C15F73E62A76AE2), @@ -310,8 +313,9 @@ hash (int moveNr) ChessSquare p = boards[moveNr][r][f]; if(f == BOARD_LEFT-1 || f == BOARD_RGHT) continue; // between board and holdings if(p != EmptySquare){ - int j = (int)p; + int j = (int)p, promoted = 0; j -= (j >= (int)BlackPawn) ? (int)BlackPawn :(int)WhitePawn; + if(j >= WhitePBishop && j != WhiteKing) promoted++, j -= WhiteTokin; if(j > (int)WhiteQueen) j++; // make space for King if(j > (int) WhiteKing) j = (int)WhiteQueen + 1; p_enc = 2*j + ((int)p < (int)BlackPawn); @@ -325,7 +329,8 @@ hash (int moveNr) pieceGroup = p_enc / 12; p_enc = p_enc % 12; Zobrist = RandomPiece[64*p_enc + (squareNr & 63)]; - switch(pieceGroup) { + if(pieceGroup & 4) Zobrist *= 987654321; + switch(pieceGroup & 3) { case 1: // pieces 5-10 (FEACWM) Zobrist = (Zobrist << 16) ^ (Zobrist >> 48); break; @@ -336,7 +341,9 @@ hash (int moveNr) Zobrist = (Zobrist << 48) ^ (Zobrist >> 16); break; } - if(squareNr >= 64) Zobrist = (Zobrist << 8) ^ (Zobrist >> 56); + if(promoted) Zobrist ^= 123456789*RandomPiece[squareNr & 63]; + if(squareNr & 64) Zobrist = (Zobrist << 8) ^ (Zobrist >> 56); + if(squareNr & 128) Zobrist = (Zobrist << 4) ^ (Zobrist >> 60); // holdings have separate (additive) key, to encode presence of multiple pieces on same square if(f == BOARD_LEFT-2) holdingsKey += Zobrist * boards[moveNr][r][f+1]; else if(f == BOARD_RGHT+1) holdingsKey += Zobrist * boards[moveNr][r][f-1]; else @@ -357,7 +364,7 @@ hash (int moveNr) f = boards[moveNr][EP_STATUS]; if(f >= 0 && f < 8){ if(!WhiteOnMove(moveNr)){ - // the test for neighboring Pawns might not be needed, + // the test for neighboring Pawns might not be needed, // as epStatus already kept track of it, but better safe than sorry. if((f>0 && boards[moveNr][3][f-1]==BlackPawn)|| (f<7 && boards[moveNr][3][f+1]==BlackPawn)){ @@ -379,12 +386,42 @@ hash (int moveNr) #define MOVE_BUF 100 +// fs routines read from memory buffer if no file specified + +static unsigned char *memBuf, *memPtr; +static int bufSize; +Boolean mcMode; + +int +fsseek (FILE *f, int n, int mode) +{ + if(f) return fseek(f, n, mode); + if(mode == SEEK_SET) memPtr = memBuf + n; else + if(mode == SEEK_END) memPtr = memBuf + 16*bufSize + n; + return memPtr < memBuf || memPtr > memBuf + 16*bufSize; +} + +int +fstell (FILE *f) +{ + if(f) return ftell(f); + return memPtr - memBuf; +} + +int +fsgetc (FILE *f) +{ + if(f) return fgetc(f); + if(memPtr >= memBuf + 16*bufSize) return EOF; + return *memPtr++ ; +} + int int_from_file (FILE *f, int l, uint64 *r) { int i,c; for(i=0;ikey=r; @@ -420,14 +458,14 @@ int find_key (FILE *f, uint64 key, entry_t *entry) { int first, last, middle; - entry_t first_entry=entry_none, last_entry,middle_entry; + entry_t last_entry,middle_entry; first=-1; - if(fseek(f,-16,SEEK_END)){ + if(fsseek(f,-16,SEEK_END)){ *entry=entry_none; entry->key=key+1; //hack return -1; } - last=ftell(f)/16; + last=fstell(f)/16; entry_from_file(f,&last_entry); while(1){ if(last-first==1){ @@ -435,55 +473,67 @@ find_key (FILE *f, uint64 key, entry_t *entry) return last; } middle=(first+last)/2; - fseek(f,16*middle,SEEK_SET); + fsseek(f,16*middle,SEEK_SET); entry_from_file(f,&middle_entry); if(key<=middle_entry.key){ last=middle; last_entry=middle_entry; }else{ first=middle; - first_entry=middle_entry; } } } +static int xStep[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; +static int yStep[] = { 1, 1, 0,-1,-1,-1, 0, 1 }; + void -move_to_string (char move_s[6], uint16 move) +move_to_string (char move_s[20], uint16 move) { int f,fr,ff,t,tr,tf,p; int width = BOARD_RGHT - BOARD_LEFT, size; // allow for alternative board formats size = width * BOARD_HEIGHT; + p = move / (size*size); + move = move % (size*size); f = move / size; fr = f / width; ff = f % width; t = move % size; tr = t / width; tf = t % width; - p = move / (size*size); - move_s[0] = ff + 'a'; - move_s[1] = fr + '1' - (BOARD_HEIGHT > 9); - move_s[2] = tf + 'a'; - move_s[3] = tr + '1' - (BOARD_HEIGHT > 9); + snprintf(move_s, 9, "%c%d%c%d", ff + 'a', fr + 1 - (BOARD_HEIGHT == 10), tf + 'a', tr + 1 - (BOARD_HEIGHT == 10)); + + if(IS_SHOGI(gameInfo.variant) && p) { + if(p == 2) p = 10; // Lion moves, for boards so big that 10 is out of range + else if(p != 7) p = 8; // use '+' for all others that do not explicitly defer + } // kludge: encode drops as special promotion code - if(gameInfo.holdingsSize && p == 8) { + if(gameInfo.holdingsSize && p == 9) { move_s[0] = f + '@'; // from square encodes piece type move_s[1] = '@'; // drop symbol p = 0; + } else if(p == 10) { // decode Lion move + int i = t & 7, j = t >> 3 & 7; + tf = ff + xStep[i] + xStep[j]; tr = fr + yStep[i] + yStep[j]; // calculate true to-square + snprintf(move_s, 20, "%c%d%c%d,%c%d%c%d", ff + 'a', fr + 1 - (BOARD_HEIGHT == 10), + ff + xStep[i] + 'a', fr + yStep[i] + 1 - (BOARD_HEIGHT == 10), + ff + xStep[i] + 'a', fr + yStep[i] + 1 - (BOARD_HEIGHT == 10), + tf + 'a', tr + 1 - (BOARD_HEIGHT == 10) ); + p = 0; } // add promotion piece, if any if(p){ - move_s[4] = promote_pieces[p]; - move_s[5] = '\0'; - }else{ - move_s[4] = '\0'; + int len = strlen(move_s); + move_s[len] = promote_pieces[p]; + move_s[len+1] = '\0'; } if(gameInfo.variant != VariantNormal) return; - // correct FRC-style castlings in variant normal. + // correct FRC-style castlings in variant normal. // [HGM] This is buggy code! e1h1 could very well be a normal R or Q move. if(!strcmp(move_s,"e1h1")){ safeStrCpy(move_s,"e1g1", 6); @@ -497,29 +547,14 @@ move_to_string (char move_s[6], uint16 move) } int -GetBookMoves (int moveNr, char *book, entry_t entries[]) +GetBookMoves (FILE *f, int moveNr, entry_t entries[], int max) { // retrieve all entries for given position from book in 'entries', return number. - static FILE *f = NULL; - static char curBook[MSG_SIZ]; entry_t entry; int offset; uint64 key; int count; int ret; - if(book == NULL || moveNr >= 2*appData.bookDepth) return -1; -// if(gameInfo.variant != VariantNormal) return -1; // Zobrist scheme only works for normal Chess, so far - if(!f || strcmp(book, curBook)){ // keep book file open until book changed - strncpy(curBook, book, MSG_SIZ); - if(f) fclose(f); - f = fopen(book,"rb"); - } - if(!f){ - DisplayError(_("Polyglot book not valid"), 0); - appData.usePolyglotBook = FALSE; - return -1; - } - key = hash(moveNr); if(appData.debugMode) fprintf(debugFP, "book key = %08x%08x\n", (unsigned int)(key>>32), (unsigned int)key); @@ -529,7 +564,7 @@ GetBookMoves (int moveNr, char *book, entry_t entries[]) } entries[0] = entry; count=1; - fseek(f, 16*(offset+1), SEEK_SET); + fsseek(f, 16*(offset+1), SEEK_SET); while(1){ ret=entry_from_file(f, &entry); if(ret){ @@ -538,22 +573,130 @@ GetBookMoves (int moveNr, char *book, entry_t entries[]) if(entry.key != key){ break; } - if(count == MOVE_BUF) break; + if(count == max) break; entries[count++] = entry; } return count; } +static int dirty; + +int +ReadFromBookFile (int moveNr, char *book, entry_t entries[]) +{ // retrieve all entries for given position from book in 'entries', return number. + static FILE *f = NULL; + static char curBook[MSG_SIZ]; + + if(book == NULL) return -1; + if(dirty) { if(f) fclose(f); dirty = 0; f = NULL; } + if(!f || strcmp(book, curBook)){ // keep book file open until book changed + strncpy(curBook, book, MSG_SIZ); + if(f) fclose(f); + f = fopen(book,"rb"); + } + if(!f){ + DisplayError(_("Polyglot book not valid"), 0); + appData.usePolyglotBook = FALSE; + return -1; + } + + return GetBookMoves(f, moveNr, entries, MOVE_BUF); +} + +// next three made into subroutines to facilitate future changes in storage scheme (e.g. 2 x 3 bytes) + +static int +wins(entry_t *e) +{ + return e->learnPoints; +} + +static int +losses(entry_t *e) +{ + return e->learnCount; +} + +static void +CountMove (entry_t *e, int result) +{ + switch(result) { + case 0: e->learnCount ++; break; + case 1: e->learnCount ++; // count draw as win + loss + case 2: e->learnPoints ++; break; + } +} + +#define MERGESIZE 2048 +#define HASHSIZE 1024*1024*4 + +entry_t *memBook, *hashTab, *mergeBuf; +int bookSize=1, mergeSize=1, mask = HASHSIZE-1; + +void +InitMemBook () +{ + static int initDone = FALSE; + if(initDone) return; + memBook = (entry_t *) calloc(1024*1024, sizeof(entry_t)); + hashTab = (entry_t *) calloc(HASHSIZE, sizeof(entry_t)); + mergeBuf = (entry_t *) calloc(MERGESIZE+5, sizeof(entry_t)); + memBook[0].key = -1LL; + mergeBuf[0].key = -1LL; + initDone = TRUE; +} + char * -ProbeBook (int moveNr, char *book) +MCprobe (int moveNr) { + int count, count2, games, i, choice=0; + entry_t entries[MOVE_BUF]; + float nominal[MOVE_BUF], tot, deficit, max, min; + static char move_s[6]; + + InitMemBook(); + memBuf = (unsigned char*) memBook; bufSize = bookSize; // in MC mode book resides in memory + count = GetBookMoves(NULL, moveNr, entries, MOVE_BUF); + if(count < 0) count = 0; // don't care about miss yet + memBuf = (unsigned char*) mergeBuf; bufSize = mergeSize; // there could be moves still waiting to be merged + count2 = count + GetBookMoves(NULL, moveNr, entries+count, MOVE_BUF - count); + if(appData.debugMode) fprintf(debugFP, "MC probe: %d/%d (%d+%d)\n", count, count2,bookSize,mergeSize); + if(!count2) return NULL; + tot = games = 0; + for(i=0; i max) max = deficit, choice = i; + if(deficit < min) min = deficit; + } // note that a single move will never be underplayed + if(max - min > 0.5*sqrt(nominal[choice])) { // if one of the listed moves is significantly under-played, play it now. + move_to_string(move_s, entries[choice].move); + if(appData.debugMode) fprintf(debugFP, "book move field = %d\n", entries[choice].move); + return move_s; + } + return NULL; // otherwise fake book miss to force engine think, hoping for hitherto unplayed move. +} + +char +*ProbeBook (int moveNr, char *book) +{ // entry_t entries[MOVE_BUF]; int count; int i, j; static char move_s[6]; int total_weight; - if((count = GetBookMoves(moveNr, book, entries)) <= 0) return NULL; // no book, or no hit + if(moveNr >= 2*appData.bookDepth) return NULL; + if(mcMode) return MCprobe(moveNr); + + if((count = ReadFromBookFile(moveNr, book, entries)) <= 0) return NULL; // no book, or no hit if(appData.bookStrength != 50) { // transform weights double power = 0, maxWeight = 0.0; @@ -587,36 +730,65 @@ extern char yy_textstr[]; entry_t lastEntries[MOVE_BUF]; char * -MovesToText (int count, entry_t *entries) +MovesToText(int count, entry_t *entries) { int i, totalWeight = 0; - char algMove[6]; - char *p = (char*) malloc(30*count+1); + char algMove[12]; + char *p = (char*) malloc(40*count+1); for(i=0; i= 4) { + CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), i1-ONE+'0', c1-AAA, i2-ONE+'0', c2-AAA, c3, algMove); + } buf[0] = NULLCHAR; if(entries[i].learnCount || entries[i].learnPoints) snprintf(buf, MSG_SIZ, " {%d/%d}", entries[i].learnPoints, entries[i].learnCount); - snprintf(p+strlen(p), 30, "%5.1f%% %5d %s%s\n", 100*entries[i].weight/(totalWeight+0.001), + snprintf(p+strlen(p), 40, "%5.1f%% %5d %s%s\n", 100*entries[i].weight/(totalWeight+0.001), entries[i].weight, algMove, buf); //lastEntries[i] = entries[i]; } return p; } +static int +CoordsToMove (int fromX, int fromY, int toX, int toY, char promoChar) +{ + int i, width = BOARD_RGHT - BOARD_LEFT; + int to = toX - BOARD_LEFT + toY * width; + int from = fromX - BOARD_LEFT + fromY * width; + for(i=0; promote_pieces[i]; i++) if(promote_pieces[i] == promoChar) break; + if(!promote_pieces[i]) i = 0; + else if(i == 9 && gameInfo.variant == VariantChu) i = 1; // on 12x12 only 3 promotion codes available, so use 1 to indicate promotion + if(fromY == DROP_RANK) i = 9, from = ToUpper(PieceToChar(fromX)) - '@'; + if(killX >= 0) { // multi-leg move + int dx = killX - fromX, dy = killY - fromY; + for(i=0; i<8; i++) if(dx == xStep[i] && dy == yStep[i]) { + int j; + dx = toX - killX; dy = toY - killY; + for(j=0; j<8; j++) if(dx == xStep[j] && dy == yStep[j]) { + // special encoding in to-square, with promoType = 2. Assumes board >= 64 squares! + return i + 8*j + (2 * width * BOARD_HEIGHT + from) * width * BOARD_HEIGHT; + } + } + i = 0; // if not a valid Lion move, ignore kill-square and promoChar + } + return to + (i * width * BOARD_HEIGHT + from) * width * BOARD_HEIGHT; +} + int TextToMoves (char *text, int moveNum, entry_t *entries) { int i, w, count=0; - uint64 hashKey = hash(moveNum); - int fromX, fromY, toX, toY, to, from; + uint64 hashKey = hash(moveNum); + int fromX, fromY, toX, toY; ChessMove moveType; char promoChar, valid; float dummy; - int width = BOARD_RGHT - BOARD_LEFT; entries[0].key = hashKey; // make sure key is returned even if no moves while((i=sscanf(text, "%f%%%d", &dummy, &w))==2 || (i=sscanf(text, "%d", &w))==1) { @@ -624,19 +796,20 @@ TextToMoves (char *text, int moveNum, entry_t *entries) if(w == 1) text = strstr(text, "1 ") + 2; // skip weight that could be recognized as move number one valid = ParseOneMove(text, moveNum, &moveType, &fromX, &fromY, &toX, &toY, &promoChar); text = strstr(text, yy_textstr) + strlen(yy_textstr); // skip what we parsed - if(!valid || moveType != NormalMove) continue; + if(!valid || moveType != NormalMove && moveType != WhiteDrop && moveType != BlackDrop + && moveType != FirstLeg + && moveType != WhitePromotion && moveType != BlackPromotion + && moveType != WhiteCapturesEnPassant && moveType != BlackCapturesEnPassant + && moveType != WhiteKingSideCastle && moveType != BlackKingSideCastle + && moveType != WhiteQueenSideCastle && moveType != BlackQueenSideCastle + && moveType != WhiteNonPromotion && moveType != BlackNonPromotion) continue; if(*text == ' ' && sscanf(text+1, "{%hd/%hd}", &entries[count].learnPoints, &entries[count].learnCount) == 2) { text = strchr(text+1, '}') + 1; } else { entries[count].learnPoints = 0; entries[count].learnCount = 0; } - to = toX + toY * width; - from = fromX + fromY * width; - for(i=0; promote_pieces[i]; i++) if(promote_pieces[i] == promoChar) break; - if(!promote_pieces[i]) i = 0; - if(fromY == DROP_RANK) i = 8, from = ToUpper(PieceToChar(fromX)) - '@'; - entries[count].move = to + (i * width * BOARD_HEIGHT + from) * width * BOARD_HEIGHT; + entries[count].move = CoordsToMove(fromX, fromY, toX, toY, promoChar); killX = killY = -1; entries[count].key = hashKey; entries[count].weight = w; count++; @@ -654,16 +827,17 @@ DisplayBook (int moveNr) int count; char *p; if(!bookUp) return FALSE; - count = currentCount = GetBookMoves(moveNr, appData.polyglotBook, entries); + count = currentCount = ReadFromBookFile(moveNr, appData.polyglotBook, entries); if(count < 0) return FALSE; p = MovesToText(count, entries); EditTagsPopUp(p, NULL); free(p); + addToBookFlag = FALSE; return TRUE; } void -EditBookEvent () +EditBookEvent() { bookUp = TRUE; bookUp = DisplayBook(currentMove); @@ -707,23 +881,175 @@ SaveToBook (char *text) if(count != currentCount) { readpos = 16*(offset + currentCount); writepos = 16*(offset + count); - fseek(f, readpos, SEEK_SET); + fsseek(f, readpos, SEEK_SET); readpos += len1 = fread(buf1, 1, 4096 - 16*currentCount, f); // salvage some entries immediately behind change } - fseek(f, 16*(offset), SEEK_SET); + fsseek(f, 16*(offset), SEEK_SET); for(i=0; i writepos) { - fseek(f, readpos, SEEK_SET); + fsseek(f, readpos, SEEK_SET); readpos += len1 = fread(buf1, 1, 4096, f); } else len1 = 0; // wrote already past old EOF - fseek(f, writepos, SEEK_SET); + fsseek(f, writepos, SEEK_SET); fwrite(buf2, 1, len2, f); writepos += len2; } while(len1); } + dirty = 1; fclose(f); } +void +NewEntry (entry_t *e, uint64 key, int move, int result) +{ + e->key = key; + e->move = move; + e->learnPoints = 0; + e->learnCount = 0; + CountMove(e, result); +} + +void +Merge () +{ + int i; + + if(appData.debugMode) fprintf(debugFP, "book merge %d moves (old size %d)\n", mergeSize, bookSize); + + bookSize += --mergeSize; + for(i=bookSize-1; mergeSize; i--) { + while(mergeSize && (i < mergeSize || mergeBuf[mergeSize-1].key >= memBook[i-mergeSize].key)) + memBook[i--] = mergeBuf[--mergeSize]; + if(i < 0) break; + memBook[i] = memBook[i-mergeSize]; + } + if(mergeSize) DisplayFatalError("merge error", 0, 0); // impossible + mergeSize = 1; + mergeBuf[0].key = -1LL; +} + +void +AddToBook (int moveNr, int result) +{ + entry_t entry; + int offset, start, move; + uint64 key; + int i, j, fromY, toY; + char fromX, toX, promo; +extern char moveList[][MOVE_LEN]; + + if(!moveList[moveNr][0] || moveList[moveNr][0] == '\n') return; // could be terminal position + + if(appData.debugMode) fprintf(debugFP, "add move %d to book %s", moveNr, moveList[moveNr]); + + // calculate key and book representation of move + key = hash(moveNr); + if(moveList[moveNr][1] == '@') { + sscanf(moveList[moveNr], "%c@%c%d", &promo, &toX, &toY); + fromX = CharToPiece(WhiteOnMove(moveNr) ? ToUpper(promo) : ToLower(promo)); + fromY = DROP_RANK; promo = NULLCHAR; + } else sscanf(moveList[moveNr], "%c%d%c%d%c", &fromX, &fromY, &toX, &toY, &promo), fromX -= AAA, fromY -= ONE - '0'; + move = CoordsToMove(fromX, fromY, toX-AAA, toY-ONE+'0', promo); + + // if move already in book, just add count + memBuf = (unsigned char*) memBook; bufSize = bookSize; // in MC mode book resides in memory + offset = find_key(NULL, key, &entry); + while(memBook[offset].key == key) { + if(memBook[offset].move == move) { + CountMove(memBook+offset, result); return; + } else offset++; + } + // move did not occur in the main book + memBuf = (unsigned char*) mergeBuf; bufSize = mergeSize; // it could be amongst moves still waiting to be merged + start = offset = find_key(NULL, key, &entry); + while(mergeBuf[offset].key == key) { + if(mergeBuf[offset].move == move) { + if(appData.debugMode) fprintf(debugFP, "found in book merge buf @ %d\n", offset); + CountMove(mergeBuf+offset, result); return; + } else offset++; + } + if(start != offset) { // position was in mergeBuf, but move is new + if(appData.debugMode) fprintf(debugFP, "add in book merge buf @ %d\n", offset); + for(i=mergeSize++; i>offset; i--) mergeBuf[i] = mergeBuf[i-1]; // make room + NewEntry(mergeBuf+offset, key, move, result); + return; + } + // position was not in mergeBuf; look in hash table + i = (key & mask); offset = -1; + while(hashTab[i].key) { // search in hash table (necessary because sought item could be re-hashed) + if(hashTab[i].key == 1 && offset < 0) offset = i; // remember first invalidated entry we pass + if(!((hashTab[i].key - key) & ~1)) { // hit + if(hashTab[i].move == move) { + CountMove(hashTab+i, result); + for(j=mergeSize++; j>start; j--) mergeBuf[j] = mergeBuf[j-1]; + } else { + // position already in hash now occurs with different move; move both moves to mergeBuf + for(j=mergeSize+1; j>start+1; j--) mergeBuf[j] = mergeBuf[j-2]; + NewEntry(mergeBuf+start+1, key, move, result); mergeSize += 2; + } + hashTab[i].key = 1; // kludge to invalidate hash entry + mergeBuf[start] = hashTab[i]; mergeBuf[start].key = key; + if(mergeSize >= MERGESIZE) Merge(); + return; + } + i = i+1 & mask; // wrap! + } + // position did not yet occur in hash table. Put it there + if(offset < 0) offset = i; + NewEntry(hashTab+offset, key, move, result); + if(appData.debugMode) + fprintf(debugFP, "book hash @ %d (%d-%d)\n", offset, hashTab[offset].learnPoints, hashTab[offset].learnCount); +} + +void +AddGameToBook (int always) +{ + int i, result; + + if(!mcMode && !always) return; + + InitMemBook(); + switch(gameInfo.result) { + case GameIsDrawn: result = 1; break; + case WhiteWins: result = 2; break; + case BlackWins: result = 0; break; + default: return; // don't treat games with unknown result + } + + if(appData.debugMode) fprintf(debugFP, "add game to book (%d-%d)\n", backwardMostMove, forwardMostMove); + for(i=backwardMostMove; i text && start[-1] != ' ' && start[-1] != '\t') start--; + while(*end && *++end != ' ' && *end != '\n') + ; *end = NULLCHAR; // find clicked word + if(start != end) TypeInDoneEvent(start); // fake it was typed in move type-in +} + +void +FlushBook () +{ + FILE *f; + int i; + + InitMemBook(); + Merge(); // flush merge buffer to memBook + + if(f = fopen(appData.polyglotBook, "wb")) { + for(i=0; i