home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 57
/
Amiga_Dream_57.iso
/
Amiga
/
Jeux
/
Reflexion
/
Crafty-15.19.lha
/
crafty-15.19
/
src
/
learn.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-09-13
|
48KB
|
1,176 lines
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include "chess.h"
#include "data.h"
#if defined(UNIX)
# include <unistd.h>
#endif
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnBook() is used to accumulate the evaluations for the first N moves *
* out of book. after these moves have been played, the evaluations are then *
* used to decide whether the last book move played was a reasonable choice *
* or not. (N is set by the #define LEARN_INTERVAL definition.) *
* *
* there are three cases to be handled. (1) if the evaluation is bad right *
* out of book, or it drops enough to be considered a bad line, then the book *
* move will have its "learn" value reduced to discourage playing this move *
* again. (2) if the evaluation is even after N moves, then the learn *
* value will be increased, but by a relatively modest amount, so that a few *
* even results will offset one bad result. (3) if the evaluation is very *
* good after N moves, the learn value will be increased by a large amount *
* so that this move will be favored the next time the game is played. *
* *
********************************************************************************
*/
void LearnBook(TREE *tree, int wtm, int search_value, int search_depth, int lv,
int force) {
/*
----------------------------------------------------------
| |
| if we have not been "out of book" for N moves, all |
| we need to do is take the search evaluation for the |
| search just completed and tuck it away in the book |
| learning array (book_learn_eval[]) for use later. |
| |
----------------------------------------------------------
*/
int t_wtm=wtm;
if (!book_file) return;
if (!(learning&book_learning)) return;
if (moves_out_of_book <= LEARN_INTERVAL && !force) {
if (moves_out_of_book) {
book_learn_eval[moves_out_of_book-1]=search_value;
book_learn_depth[moves_out_of_book-1]=search_depth;
}
}
/*
----------------------------------------------------------
| |
| check the evaluations we've seen so far. if they are |
| within reason (+/- 1/3 of a pawn or so) we simply keep |
| playing and leave the book alone. if the eval is much |
| better or worse, we need to update the learning count. |
| |
----------------------------------------------------------
*/
else if (moves_out_of_book == LEARN_INTERVAL+1 || force) {
int move, i, j, learn_value, read;
int secs, interval, last_book_move=-1;
float temp_value;
char cmd[32], buff[80], *nextc;
int best_eval=-999999, best_eval_p=0;
int worst_eval=999999, worst_eval_p=0, save_wtm;
int best_after_worst_eval=-999999, worst_after_best_eval=999999;
struct tm *timestruct;
int n_book_moves[512];
float book_learn[512], t_learn_value;
if (moves_out_of_book < 1) return;
Print(128,"LearnBook() executed\n");
learning&=~book_learning;
interval=Min(LEARN_INTERVAL,moves_out_of_book);
if (interval < 2) return;
for (i=0;i<interval;i++) {
if (book_learn_eval[i] > best_eval) {
best_eval=book_learn_eval[i];
best_eval_p=i;
}
if (book_learn_eval[i] < worst_eval) {
worst_eval=book_learn_eval[i];
worst_eval_p=i;
}
}
if (best_eval_p < interval-1) {
for (i=best_eval_p;i<interval;i++)
if (book_learn_eval[i] < worst_after_best_eval)
worst_after_best_eval=book_learn_eval[i];
}
else worst_after_best_eval=book_learn_eval[interval-1];
if (worst_eval_p < interval-1) {
for (i=worst_eval_p;i<interval;i++)
if (book_learn_eval[i] > best_after_worst_eval)
best_after_worst_eval=book_learn_eval[i];
}
else best_after_worst_eval=book_learn_eval[interval-1];
#if defined(DEBUG)
Print(128,"Learning analysis ...\n");
Print(128,"worst=%d best=%d baw=%d wab=%d\n",
worst_eval, best_eval, best_after_worst_eval, worst_after_best_eval);
for (i=0;i<interval;i++)
Print(128,"%d(%d) ",book_learn_eval[i],book_learn_depth[i]);
Print(128,"\n");
#endif
/*
----------------------------------------------------------
| |
| we now have the best eval for the first N moves out |
| of book, the worst eval for the first N moves out of |
| book, and the worst eval that follows the best eval. |
| this will be used to recognize the following cases of |
| results that follow a book move: |
| |
----------------------------------------------------------
*/
/*
----------------------------------------------------------
| |
| (1) the best score is very good, and it doesn't drop |
| after following the game further. this case detects |
| those moves in book that are "good" and should be |
| played whenever possible. |
| |
----------------------------------------------------------
*/
if (best_eval == best_after_worst_eval) {
learn_value=best_eval;
for (i=0;i<interval;i++)
if (learn_value == book_learn_eval[i])
search_depth=Max(search_depth,book_learn_depth[i]);
}
/*
----------------------------------------------------------
| |
| (2) the worst score is bad, and doesn't improve any |
| after the worst point, indicating that the book move |
| chosen was "bad" and should be avoided in the future. |
| |
----------------------------------------------------------
*/
else if (worst_eval == worst_after_best_eval) {
learn_value=worst_eval;
for (i=0;i<interval;i++)
if (learn_value == book_learn_eval[i])
search_depth=Max(search_depth,book_learn_depth[i]);
}
/*
----------------------------------------------------------
| |
| (3) things seem even out of book and remain that way |
| for N moves. we will just average the 10 scores and |
| use that as an approximation. |
| |
----------------------------------------------------------
*/
else {
learn_value=0;
search_depth=0;
for (i=0;i<interval;i++) {
learn_value+=book_learn_eval[i];
search_depth+=book_learn_depth[i];
}
learn_value/=interval;
search_depth/=interval;
}
if (lv) learn_value=search_value;
if (lv == 0)
learn_value=LearnFunction(learn_value,search_depth,
crafty_rating-opponent_rating,
learn_value<0);
else learn_value=search_value;
/*
----------------------------------------------------------
| |
| first, we are going to find every book move in the |
| game, and note how many alternatives there were at |
| every book move. |
| |
----------------------------------------------------------
*/
save_wtm=wtm;
InitializeChessBoard(&tree->position[0]);
for (i=0;i<512;i++) n_book_moves[i]=0;
wtm=1;
for (i=0;i<512;i++) {
int *mv, cluster, key, test;
BITBOARD common, temp_hash_key;
n_book_moves[i]=0;
fseek(history_file,i*10,SEEK_SET);
strcpy(cmd,"");
read=fscanf(history_file,"%s",cmd);
if (read != 1) break;
if (strcmp(cmd,"pass")) {
move=InputMove(tree,cmd,0,wtm,1,0);
if (!move) break;
tree->position[1]=tree->position[0];
tree->last[1]=GenerateCaptures(tree, 1, wtm, tree->last[0]);
tree->last[1]=GenerateNonCaptures(tree, 1, wtm, tree->last[1]);
test=HashKey>>49;
fseek(book_file,test*sizeof(int),SEEK_SET);
fread(&key,sizeof(int),1,book_file);
if (key > 0) {
fseek(book_file,key,SEEK_SET);
fread(&cluster,sizeof(int),1,book_file);
fread(book_buffer,sizeof(BOOK_POSITION),cluster,book_file);
}
else cluster=0;
for (mv=tree->last[0];mv<tree->last[1];mv++) {
common=And(HashKey,mask_16);
MakeMove(tree, 1,*mv,wtm);
temp_hash_key=Xor(HashKey,wtm_random[wtm]);
temp_hash_key=Or(And(temp_hash_key,Compl(mask_16)),common);
for (j=0;j<cluster;j++)
if (!Xor(temp_hash_key,book_buffer[j].position) &&
book_buffer[j].learn > (float) LEARN_COUNTER_BAD/100.0) {
n_book_moves[i]++;
last_book_move=i;
}
UnMakeMove(tree, 1,*mv,wtm);
}
if (move) MakeMoveRoot(tree, move,wtm);
}
wtm=ChangeSide(wtm);
}
/*
----------------------------------------------------------
| |
| now we build a vector of book learning results. we |
| give every book move below the last point where there |
| were alternatives 100% of the learned score. We give |
| the book move played at that point 100% of the learned |
| score as well. then we divide the learned score by |
| the number of alternatives, and propogate this score |
| back until there was another alternative, where we do |
| this again and again until we reach the top of the |
| book tree. |
----------------------------------------------------------
*/
t_learn_value=((float) learn_value)/100.0;
for (i=511;i>=0;i--) {
book_learn[i]=t_learn_value;
if (n_book_moves[i] > 1) t_learn_value/=n_book_moves[i];
}
/*
----------------------------------------------------------
| |
| finally, we run thru the book file and update each |
| book move learned value based on the computation we |
| calculated above. |
| |
----------------------------------------------------------
*/
InitializeChessBoard(&tree->position[0]);
wtm=1;
for (i=0;i<512;i++) {
strcpy(cmd,"");
fseek(history_file,i*10,SEEK_SET);
strcpy(cmd,"");
read=fscanf(history_file,"%s",cmd);
if (read != 1) break;
if (strcmp(cmd,"pass")) {
move=InputMove(tree,cmd,0,wtm,1,0);
if (!move) break;
tree->position[1]=tree->position[0];
/*
----------------------------------------------------------
| |
| now call LearnBookUpdate() to find this position in |
| the book database and update the learn stuff. |
| |
----------------------------------------------------------
*/
temp_value=book_learn[i];
if (wtm != crafty_is_white) temp_value*=-1;
LearnBookUpdate(tree, wtm, move, temp_value);
MakeMoveRoot(tree, move,wtm);
}
wtm=ChangeSide(wtm);
}
/*
----------------------------------------------------------
| |
| now update the "book.lrn" file so that this can be |
| shared with other crafty users or else saved in case. |
| the book must be re-built. |
| |
----------------------------------------------------------
*/
fprintf(book_lrn_file,"[White \"%s\"]\n",pgn_white);
fprintf(book_lrn_file,"[Black \"%s\"]\n",pgn_black);
secs=time(0);
timestruct=localtime((time_t*) &secs);
fprintf(book_lrn_file,"[Date \"%4d.%02d.%02d\"]\n",timestruct->tm_year+1900,
timestruct->tm_mon+1,timestruct->tm_mday);
nextc=buff;
for (i=0;i<=last_book_move;i++) {
fseek(history_file,i*10,SEEK_SET);
strcpy(cmd,"");
read=fscanf(history_file,"%s",cmd);
if (read!=1) break;
if (strchr(cmd,' ')) *strchr(cmd,' ')=0;
sprintf(nextc," %s",cmd);
nextc=buff+strlen(buff);
if (nextc-buff > 60) {
fprintf(book_lrn_file,"%s\n",buff);
nextc=buff;
strcpy(buff,"");
}
}
fprintf(book_lrn_file,"%s {%d %d %d}\n",
buff, (t_wtm) ? learn_value:-learn_value, search_depth,
crafty_rating-opponent_rating);
fflush(book_lrn_file);
/*
----------------------------------------------------------
| |
| done. now restore the game back to where it was |
| before we started all this nonsense. :) |
| |
----------------------------------------------------------
*/
RestoreGame();
}
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnBookUpdate() is called to find the current position in the book and *
* update the learn counter. if it is supposed to mark a move as not to be *
* played, and after marking such a move there are no more left at this point *
* in the database, it returns (0) which will force LearnBook() to back up *
* two plies and update that position as well, since no more choices at the *
* current position doesn't really do much for us... *
* *
********************************************************************************
*/
void LearnBookUpdate(TREE *tree, int wtm, int move, float learn_value) {
int cluster, test, move_index, key;
BITBOARD temp_hash_key, common;
/*
----------------------------------------------------------
| |
| first find the appropriate cluster, make the move we |
| were passed, and find the resulting position in the |
| database. |
| |
----------------------------------------------------------
*/
test=HashKey>>49;
if (book_file) {
fseek(book_file,test*sizeof(int),SEEK_SET);
fread(&key,sizeof(int),1,book_file);
if (key > 0) {
fseek(book_file,key,SEEK_SET);
fread(&cluster,sizeof(int),1,book_file);
fread(book_buffer,sizeof(BOOK_POSITION),cluster,book_file);
common=And(HashKey,mask_16);
MakeMove(tree, 1,move,wtm);
temp_hash_key=Xor(HashKey,wtm_random[wtm]);
temp_hash_key=Or(And(temp_hash_key,Compl(mask_16)),common);
for (move_index=0;move_index<cluster;move_index++)
if (!Xor(temp_hash_key,book_buffer[move_index].position)) break;
UnMakeMove(tree, 1,move,wtm);
if (move_index >= cluster) return;
if (book_buffer[move_index].learn == 0.0)
book_buffer[move_index].learn=learn_value;
else
book_buffer[move_index].learn=
(book_buffer[move_index].learn+learn_value)/2.0;
fseek(book_file,key+sizeof(int),SEEK_SET);
fwrite(book_buffer,sizeof(BOOK_POSITION),cluster,book_file);
fflush(book_file);
}
}
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnFunction() is called to compute the adjustment value added to the *
* learn counter in the opening book. it takes three pieces of information *
* into consideration to do this: the search value, the search depth that *
* produced this value, and the rating difference (Crafty-opponent) so that *
* + numbers means Crafty is expected to win, - numbers mean Crafty is ex- *
* pected to lose. *
* *
********************************************************************************
*/
int LearnFunction(int sv, int search_depth, int rating_difference,
int trusted_value) {
float rating_multiplier_trusted[11] = {.00625, .0125, .025, .05, .075, .1,
0.15, 0.2, 0.25, 0.3, 0.35};
float rating_multiplier_untrusted[11] = {.25, .2, .15, .1, .05, .025, .012,
.006, .003, .001};
float multiplier;
int sd, rd;
sd=Max(Min(search_depth,19),0);
rd=Max(Min(rating_difference/200,5),-5)+5;
if (trusted_value)
multiplier=rating_multiplier_trusted[rd]*sd;
else
multiplier=rating_multiplier_untrusted[rd]*sd;
sv=Max(Min(sv,600),-600);
return(sv*multiplier);
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnImport() is used to read in a learn data file (*.lrn) and apply *
* it to either book.bin (book.lrn file) or position.bin (position.lrn file). *
* this allows users to create a new book.bin at any time, adding more games *
* as needed, without losing all of the "learned" openings in the database. *
* *
* the second intent is to allow users to "share" *.lrn files, and to allow me *
* to keep several of them on the ftp machine, so that anyone can use those *
* file(s) and have their version of Crafty (or any other program that wants *
* to participate in this) "learn" what other crafty's have already found out *
* about which openings and positions are good and bad. *
* *
* the basic idea is to (a) stuff each book opening line into the game history *
* for LearnBook(), then set things up so that LearnBook() can be called and *
* it will behave just as though this book line was just "learned". if the *
* file is a position.lrn type of file (which is recognized by finding a *
* "setboard" command in the file as well as the word "position" in the first *
* eight bytes of the file, then the positions and scores are read in and *
* added to the position.bin file. *
* *
********************************************************************************
*/
void LearnImport(TREE *tree, int nargs, char **args) {
FILE *learn_in;
char text[128];
int eof;
/*
----------------------------------------------------------
| |
| first, get the name of the file that contains the |
| learned book lines. |
| |
----------------------------------------------------------
*/
display_options&=4095-128;
learn_in=fopen(*args,"r");
if (learn_in == NULL) {
Print(4095,"unable to open %s for input\n", *args);
return;
}
eof=fscanf(learn_in,"%s",text);
fclose(learn_in);
if (eof == 0) return;
if (!strcmp(text,"position")) LearnImportPosition(tree,nargs,args);
else LearnImportBook(tree,nargs,args);
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnImportBook() is used to import book learning and save it in the *
* book.bin file (see LearnBook for details.) *
* *
********************************************************************************
*/
#if defined(MACOS)
int index[32768];
#endif
void LearnImportBook(TREE *tree, int nargs, char **args) {
FILE *learn_in;
char nextc, text[128], *eof;
int wtm, learn_value, depth, rating_difference, move=0, i, added_lines=0;
/*
----------------------------------------------------------
| |
| if the <clear> option was given, first we cycle thru |
| the entire book and clear every learned value. |
| |
----------------------------------------------------------
*/
learn_in = fopen(args[0],"r");
if (nargs>1 && !strcmp(args[1],"clear")) {
#if defined(MACOS)
int i, j, cluster;
#else
int index[32768], i, j, cluster;
#endif
fclose(book_lrn_file);
#if defined(MACOS)
sprintf(text,":%s:book.lrn",book_path);
#else
sprintf(text,"%s/book.lrn",book_path);
#endif
book_lrn_file=fopen(text,"w");
fseek(book_file,0,SEEK_SET);
fread(index,sizeof(int),32768,book_file);
for (i=0;i<32768;i++)
if (index[i] > 0) {
fseek(book_file,index[i],SEEK_SET);
fread(&cluster,sizeof(int),1,book_file);
fread(book_buffer,sizeof(BOOK_POSITION),cluster,book_file);
for (j=0;j<cluster;j++) book_buffer[j].learn=0.0;
fseek(book_file,index[i]+sizeof(int),SEEK_SET);
fwrite(book_buffer,sizeof(BOOK_POSITION),cluster,book_file);
}
}
/*
----------------------------------------------------------
| |
| outer loop loops thru the games (opening lines) one by |
| one, while the inner loop stuffs the game history file |
| with moves that were played. the series of moves in a |
| line is terminated by the {x y z} data values. |
| |
----------------------------------------------------------
*/
while (1) {
if (added_lines%10==0) {
printf(".");
fflush(stdout);
}
if ((added_lines+1)%600 == 0) printf(" (%d)\n",added_lines+1);
InitializeChessBoard(&tree->position[0]);
wtm=0;
move_number=0;
for (i=0;i<100;i++) {
fseek(history_file,i*10,SEEK_SET);
fprintf(history_file," \n");
}
for (i=0;i<3;i++) {
eof=fgets(text,80,learn_in);
if (eof) {
char *delim;
delim=strchr(text,'\n');
if (delim) *delim=0;
delim=strchr(text,'\r');
if (delim) *delim=' ';
}
else break;
if (strchr(text,'[')) do {
char *bracket1, *bracket2;
char value[32];
bracket1=strchr(text,'\"');
bracket2=strchr(bracket1+1,'\"');
if (bracket1 == 0 || bracket2 == 0) break;
*bracket2=0;
strcpy(value,bracket1+1);
if (bracket2 == 0) break;
if (strstr(text,"White")) strcpy(pgn_white,value);
if (strstr(text,"Black")) strcpy(pgn_black,value);
} while(0);
}
if (eof == 0) break;
do {
wtm=ChangeSide(wtm);
if (wtm) move_number++;
do {
nextc=fgetc(learn_in);
} while(nextc == ' ' || nextc == '\n');
if (nextc == '{') break;
ungetc(nextc,learn_in);
move=ReadChessMove(tree,learn_in,wtm,1);
if (move < 0) break;
strcpy(text,OutputMove(tree,move,0,wtm));
fseek(history_file,((move_number-1)*2+1-wtm)*10,SEEK_SET);
fprintf(history_file,"%9s\n",text);
moves_out_of_book=0;
MakeMoveRoot(tree, move,wtm);
} while (1);
if (move < 0) break;
fscanf(learn_in,"%d %d %d}\n",&learn_value, &depth, &rating_difference);
moves_out_of_book=LEARN_INTERVAL+1;
move_number+=LEARN_INTERVAL+1-wtm;
for (i=0;i<LEARN_INTERVAL;i++) book_learn_eval[i]=learn_value;
crafty_rating=rating_difference;
opponent_rating=0;
learning|=book_learning;
if (abs(learn_value) != 99900)
LearnBook(tree, wtm, (wtm) ? learn_value : -learn_value, depth, 1, 1);
else
LearnResult(tree, learn_value < 0);
added_lines++;
}
move_number=1;
Print(4095,"\nadded %d learned book lines to book.bin\n",added_lines);
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnImportPosition() is used to import positions and save them in the *
* position.bin file. (see LearnPosition for details.) *
* *
********************************************************************************
*/
void LearnImportPosition(TREE *tree, int nargs, char **args) {
BITBOARD word1, word2;
int positions, nextp, secs;
struct tm *timestruct;
char xlate[15]={'q','r','b',0,'k','n','p',0,'P','N','K',0,'B','R','Q'};
char empty[9]={' ','1','2','3','4','5','6','7','8'};
int i, rank, file, nempty, value, move, depth, added_positions=0;
char *eof, text[80];
FILE *learn_in;
/*
----------------------------------------------------------
| |
| open the input file and skip the "position" signature, |
| since we know it's a position.lrn file because we are |
| *here*. |
| |
----------------------------------------------------------
*/
learn_in = fopen(args[0],"r");
eof=fgets(text,80,learn_in);
if (eof) {
char *delim;
delim=strchr(text,'\n');
if (delim) *delim=0;
delim=strchr(text,'\r');
if (delim) *delim=' ';
}
if (nargs>1 && !strcmp(args[1],"clear")) {
fclose(position_file);
#if defined(MACOS)
sprintf(text,":%s:position.lrn",book_path);
#else
sprintf(text,"%s/position.lrn",book_path);
#endif
position_lrn_file=fopen(text,"w");
if (!position_lrn_file) {
#if defined(MACOS)
printf("unable to open position learning file [:%s:position.lrn].\n",
book_path);
#else
printf("unable to open position learning file [%s/position.lrn].\n",
book_path);
#endif
return;
}
fprintf(position_lrn_file,"position\n");
#if defined(MACOS)
sprintf(text,":%s:position.bin",book_path);
#else
sprintf(text,"%s/position.bin",book_path);
#endif
position_file=fopen(text,"wb+");
if (position_file) {
i=0;
fseek(position_file,0,SEEK_SET);
fwrite(&i,sizeof(int),1,position_file);
i--;
fwrite(&i,sizeof(int),1,position_file);
}
else {
#if defined(MACOS)
printf("unable to open position learning file [:%s:position.bin].\n",
book_path);
#else
printf("unable to open position learning file [%s/position.bin].\n",
book_path);
#endif
return;
}
}
/*
----------------------------------------------------------
| |
| loop through the file, reading in 5 records at a time, |
| the White, Black, Date PGN tags, the setboard FEN, and |
| the search value/depth to store. |
| |
----------------------------------------------------------
*/
while (1) {
for (i=0;i<3;i++) {
eof=fgets(text,80,learn_in);
if (eof) {
char *delim;
delim=strchr(text,'\n');
if (delim) *delim=0;
delim=strchr(text,'\r');
if (delim) *delim=' ';
}
else break;
if (strchr(text,'[')) do {
char *bracket1, *bracket2;
char value[32];
bracket1=strchr(text,'\"');
bracket2=strchr(bracket1+1,'\"');
if (bracket1 == 0 || bracket2 == 0) break;
*bracket2=0;
strcpy(value,bracket1+1);
if (bracket2 == 0) break;
if (strstr(text,"White")) strcpy(pgn_white,value);
if (strstr(text,"Black")) strcpy(pgn_black,value);
} while(0);
}
if (eof == 0) break;
eof=fgets(text,80,learn_in);
if (eof) {
char *delim;
delim=strchr(text,'\n');
if (delim) *delim=0;
delim=strchr(text,'\r');
if (delim) *delim=' ';
}
nargs=ReadParse(text,args," ;\n");
if (strcmp(args[0],"setboard"))
Print(4095,"ERROR. missing setboard command in file.\n");
SetBoard(nargs-1,args+1,0);
eof=fgets(text,80,learn_in);
if (eof) {
char *delim;
delim=strchr(text,'\n');
if (delim) *delim=0;
delim=strchr(text,'\r');
if (delim) *delim=' ';
}
else break;
nargs=ReadParse(text+1,args," ,;{}\n");
value=atoi(args[0]);
move=atoi(args[1]);
depth=atoi(args[2]);
/*
----------------------------------------------------------
| |
| now "fill in the blank" and build a table entry from |
| current search information. |
| |
----------------------------------------------------------
*/
if (abs(value) < MATE-100)
word1=Shiftl((BITBOARD) (value+65536),43);
else if (value > 0)
word1=Shiftl((BITBOARD) (value+65536),43);
else
word1=Shiftl((BITBOARD) (value+65536),43);
word1=Or(word1,Shiftl((BITBOARD) wtm,63));
word1=Or(word1,Shiftl((BITBOARD) move,16));
word1=Or(word1,(BITBOARD) depth);
word2=HashKey;
fseek(position_file,0,SEEK_SET);
fread(&positions,sizeof(int),1,position_file);
fread(&nextp,sizeof(int),1,position_file);
if (positions < 65536) positions++;
fseek(position_file,0,SEEK_SET);
fwrite(&positions,sizeof(int),1,position_file);
nextp++;
if (nextp == 65536) nextp=0;
fwrite(&nextp,sizeof(int),1,position_file);
fseek(position_file,2*(nextp-1)*sizeof(BITBOARD)+2*sizeof(int),SEEK_SET);
fwrite(&word1,sizeof(BITBOARD),1,position_file);
fwrite(&word2,sizeof(BITBOARD),1,position_file);
added_positions++;
/*
----------------------------------------------------------
| |
| now update the position.lrn file so that the position |
| is saved in a form that can be imported later in other |
| versions of crafty on different machines. |
| |
----------------------------------------------------------
*/
fprintf(position_lrn_file,"[Black \"%s\"]\n",pgn_white);
fprintf(position_lrn_file,"[White \"%s\"]\n",pgn_black);
secs=time(0);
timestruct=localtime((time_t*) &secs);
fprintf(position_lrn_file,"[Date \"%4d.%02d.%02d\"]\n",timestruct->tm_year+1900,
timestruct->tm_mon+1,timestruct->tm_mday);
fprintf(position_lrn_file,"setboard ");
for (rank=RANK8;rank>=RANK1;rank--) {
nempty=0;
for (file=FILEA;file<=FILEH;file++) {
if (PieceOnSquare((rank<<3)+file)) {
if (nempty) {
fprintf(position_lrn_file,"%c",empty[nempty]);
nempty=0;
}
fprintf(position_lrn_file,"%c",xlate[PieceOnSquare((rank<<3)+file)+7]);
}
else nempty++;
}
fprintf(position_lrn_file,"/");
}
fprintf(position_lrn_file," %c ",(wtm)?'w':'b');
if (WhiteCastle(0) & 1) fprintf(position_lrn_file,"K");
if (WhiteCastle(0) & 2) fprintf(position_lrn_file,"Q");
if (BlackCastle(0) & 1) fprintf(position_lrn_file,"k");
if (BlackCastle(0) & 2) fprintf(position_lrn_file,"q");
if (EnPassant(0)) fprintf(position_lrn_file," %c%c",File(EnPassant(0))+'a',
Rank(EnPassant(0))+((wtm)?-1:+1)+'1');
fprintf(position_lrn_file,"\n{%d %d %d}\n",value,move,depth);
}
Print(4095,"added %d new positions to position.bin\n",added_positions);
Print(4095," %d total positions in position.bin\n",positions);
fflush(position_file);
fflush(position_lrn_file);
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnPosition() is the driver for the second phase of Crafty's learning *
* code. this procedure takes the result of selected (or all) searches that *
* are done during a game and stores them in a permanent hash table that is *
* kept on disk. before a new search begins, the values in this permanent *
* file are copied to the active transposition table, so that the values will *
* be accessible a few plies earlier than in the game where the positions *
* were learned. *
* *
* bits name SL description *
* 1 wtm 63 used to control which transposition table gets this *
* position. *
* 19 value 43 unsigned integer value of this position + 65536. *
* this might be a good score or search bound. *
* 21 move 16 best move from the current position, according to the *
* search at the time this position was stored. *
* *
* 16 draft 0 the depth of the search below this position, which is *
* used to see if we can use this entry at the current *
* position. note that this is in units of 1/60th of a *
* ply. *
* 64 key 0 complete 64bit hash key. *
* *
* the file will, by default, hold 65536 learned positions. the first word *
* indicates how many positions are actually stored in the file, while the *
* second word points to the overwrite point. once the file reaches the max *
* size, this overwrite point will wrap to the beginning so that the file will *
* always contain the most recent 64K positions. *
* *
********************************************************************************
*/
void LearnPosition(TREE *tree, int wtm, int last_value, int value) {
BITBOARD word1, word2;
int positions, nextp, secs;
struct tm *timestruct;
char xlate[15]={'q','r','b',0,'k','n','p',0,'P','N','K',0,'B','R','Q'};
char empty[9]={' ','1','2','3','4','5','6','7','8'};
int rank, file, nempty;
/*
----------------------------------------------------------
| |
| is there anything to learn? if we are already behind |
| a significant amount, losing more is not going to help |
| learning. otherwise if the score drops by 1/3 of a |
| pawn, remember the position. if we are way out of the |
| book, learning won't help either, as the position will |
| not likely show up again. |
| |
----------------------------------------------------------
*/
if ((!position_lrn_file) || (!position_file)) return;
if (last_value < -2*PAWN_VALUE) return;
if (last_value < value+PAWN_VALUE/3) return;
if (moves_out_of_book > 10) return;
/*
----------------------------------------------------------
| |
| now "fill in the blank" and build a table entry from |
| current search information. |
| |
----------------------------------------------------------
*/
Print(4095,"learning position, wtm=%d value=%d\n",wtm,value);
word1=Shiftl((BITBOARD) (value+65536),43);
word1=Or(word1,Shiftl((BITBOARD) wtm,63));
word1=Or(word1,Shiftl((BITBOARD) tree->pv[0].path[1],16));
word1=Or(word1,(BITBOARD) (tree->pv[0].path_iteration_depth*INCREMENT_PLY-INCREMENT_PLY));
word2=HashKey;
fseek(position_file,0,SEEK_SET);
fread(&positions,sizeof(int),1,position_file);
fread(&nextp,sizeof(int),1,position_file);
if (positions < 65536) positions++;
fseek(position_file,0,SEEK_SET);
fwrite(&positions,sizeof(int),1,position_file);
nextp++;
if (nextp == 65536) nextp=0;
fwrite(&nextp,sizeof(int),1,position_file);
fseek(position_file,2*nextp*sizeof(BITBOARD)+2*sizeof(int),SEEK_SET);
fwrite(&word1,sizeof(BITBOARD),1,position_file);
fwrite(&word2,sizeof(BITBOARD),1,position_file);
fflush(position_file);
/*
----------------------------------------------------------
| |
| now update the position.lrn file so that the position |
| is saved in a form that can be imported later in other |
| versions of crafty on different machines. |
| |
----------------------------------------------------------
*/
fprintf(position_lrn_file,"[Black \"%s\"]\n",pgn_white);
fprintf(position_lrn_file,"[White \"%s\"]\n",pgn_black);
secs=time(0);
timestruct=localtime((time_t*) &secs);
fprintf(position_lrn_file,"[Date \"%4d.%02d.%02d\"]\n",timestruct->tm_year+1900,
timestruct->tm_mon+1,timestruct->tm_mday);
fprintf(position_lrn_file,"setboard ");
for (rank=RANK8;rank>=RANK1;rank--) {
nempty=0;
for (file=FILEA;file<=FILEH;file++) {
if (PieceOnSquare((rank<<3)+file)) {
if (nempty) {
fprintf(position_lrn_file,"%c",empty[nempty]);
nempty=0;
}
fprintf(position_lrn_file,"%c",xlate[PieceOnSquare((rank<<3)+file)+7]);
}
else nempty++;
}
fprintf(position_lrn_file,"/");
}
fprintf(position_lrn_file," %c ",(wtm)?'w':'b');
if (WhiteCastle(0) & 1) fprintf(position_lrn_file,"K");
if (WhiteCastle(0) & 2) fprintf(position_lrn_file,"Q");
if (BlackCastle(0) & 1) fprintf(position_lrn_file,"k");
if (BlackCastle(0) & 2) fprintf(position_lrn_file,"q");
if (EnPassant(0)) fprintf(position_lrn_file," %c%c",File(EnPassant(0))+'a',
Rank(EnPassant(0))+'1');
fprintf(position_lrn_file,"\n{%d %d %d}\n",value,
tree->pv[0].path[1],
tree->pv[0].path_iteration_depth*INCREMENT_PLY);
fflush(position_lrn_file);
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* simply read from the learn.bin file, and stuffed into the correct table. *
* *
********************************************************************************
*/
void LearnPositionLoad(TREE *tree) {
register BITBOARD temp_hash_key;
BITBOARD word1, word2, worda, wordb;
register HASH_ENTRY *htable;
int n, positions;
/*
----------------------------------------------------------
| |
| If position learning file not accessible: exit. also, |
| if the time/move is very short, skip this. |
| |
----------------------------------------------------------
*/
if (!position_file) return;
if (time_limit < 100) return;
/*
----------------------------------------------------------
| |
| first, find out how many learned positions are in the |
| file and set up to start reading/stuffing them. |
| |
----------------------------------------------------------
*/
if (moves_out_of_book >= 10) return;
fseek(position_file,0,SEEK_SET);
fread(&positions,sizeof(int),1,position_file);
fseek(position_file,2*sizeof(int),SEEK_SET);
/*
----------------------------------------------------------
| |
| first, find out how many learned positions are in the |
| file and set up to start reading/stuffing them. |
| |
----------------------------------------------------------
*/
for (n=0;n<positions;n++) {
fread(&word1,sizeof(BITBOARD),1,position_file);
fread(&word2,sizeof(BITBOARD),1,position_file);
temp_hash_key=word2;
htable=((Shiftr(word1,63)) ? trans_ref_wa : trans_ref_ba)+
(((int) temp_hash_key)&hash_maska);
temp_hash_key=temp_hash_key>>16;
wordb=Shiftr(word2,16);
wordb=Or(wordb,Shiftl(And(word1,mask_112),48));
worda=Shiftl((BITBOARD) (Shiftr(word1,43) & 01777777),21);
worda=Or(worda,Shiftl((BITBOARD) 3,59));
htable->word1=worda;
htable->word2=wordb;
}
}
/* last modified 03/11/98 */
/*
********************************************************************************
* *
* LearnResult() is used to learn from the result of a game that was played *
* to mate or resignation. Crafty will use the result (if it lost the game) *
* to mark the book move made at the last position where it had more than one *
* book move, so that that particular move won't be played again, just as if *
* the learned-value was extremely bad. *
* *
********************************************************************************
*/
void LearnResult(TREE *tree, int wtm) {
int move, i, j;
int secs, last_choice;
char cmd[32], buff[80], *nextc;
struct tm *timestruct;
int n_book_moves[512];
int t_wtm=wtm;
/*
----------------------------------------------------------
| |
| first, we are going to find every book move in the |
| game, and note how many alternatives there were at |
| every book move. |
| |
----------------------------------------------------------
*/
if (!book_file) return;
if (!(learning & result_learning)) return;
Print(128,"LearnResult() executed\n");
learning&=~result_learning;
InitializeChessBoard(&tree->position[0]);
for (i=0;i<512;i++) n_book_moves[i]=0;
wtm=1;
for (i=0;i<512;i++) {
int *mv, cluster, key, test;
BITBOARD common, temp_hash_key;
n_book_moves[i]=0;
strcpy(cmd,"");
fseek(history_file,i*10,SEEK_SET);
fscanf(history_file,"%s",cmd);
if (strcmp(cmd,"pass")) {
move=InputMove(tree,cmd,0,wtm,1,0);
if (!move) break;
tree->position[1]=tree->position[0];
tree->last[1]=GenerateCaptures(tree, 1, wtm, tree->last[0]);
tree->last[1]=GenerateNonCaptures(tree, 1, wtm, tree->last[1]);
test=HashKey>>49;
fseek(book_file,test*sizeof(int),SEEK_SET);
fread(&key,sizeof(int),1,book_file);
if (key > 0) {
fseek(book_file,key,SEEK_SET);
fread(&cluster,sizeof(int),1,book_file);
fread(book_buffer,sizeof(BOOK_POSITION),cluster,book_file);
}
else cluster=0;
for (mv=tree->last[0];mv<tree->last[1];mv++) {
common=And(HashKey,mask_16);
MakeMove(tree, 1,*mv,wtm);
temp_hash_key=Xor(HashKey,wtm_random[wtm]);
temp_hash_key=Or(And(temp_hash_key,Compl(mask_16)),common);
for (j=0;j<cluster;j++)
if (!Xor(temp_hash_key,book_buffer[j].position) &&
book_buffer[j].learn > (float) LEARN_COUNTER_BAD/100.0)
n_book_moves[i]++;
UnMakeMove(tree, 1,*mv,wtm);
}
if (move) MakeMoveRoot(tree, move,wtm);
}
wtm=ChangeSide(wtm);
}
/*
----------------------------------------------------------
| |
| now, find the last book move where there was more than |
| one alternative to choose from. |
| |
----------------------------------------------------------
*/
if (!t_wtm) {
for (last_choice=511;last_choice>=0;last_choice-=2)
if (n_book_moves[last_choice] > 1) break;
}
else {
for (last_choice=510;last_choice>=0;last_choice-=2)
if (n_book_moves[last_choice] > 1) break;
}
if (last_choice < 0) return;
/*
----------------------------------------------------------
| |
| finally, we run thru the book file and update each |
| book move learned value based on the computation we |
| calculated above. |
| |
----------------------------------------------------------
*/
InitializeChessBoard(&tree->position[0]);
wtm=1;
for (i=0;i<512;i++) {
strcpy(cmd,"");
fseek(history_file,i*10,SEEK_SET);
strcpy(cmd,"");
fscanf(history_file,"%s",cmd);
if (strcmp(cmd,"pass")) {
move=InputMove(tree,cmd,0,wtm,1,0);
if (!move) break;
tree->position[1]=tree->position[0];
if (i == last_choice) LearnBookUpdate(tree, wtm, move, -999.0);
MakeMoveRoot(tree, move,wtm);
}
wtm=ChangeSide(wtm);
}
/*
----------------------------------------------------------
| |
| now update the "book.lrn" file so that this can be |
| shared with other crafty users or else saved in case. |
| the book must be re-built. |
| |
----------------------------------------------------------
*/
fprintf(book_lrn_file,"[White \"%s\"]\n",pgn_white);
fprintf(book_lrn_file,"[Black \"%s\"]\n",pgn_black);
secs=time(0);
timestruct=localtime((time_t*) &secs);
fprintf(book_lrn_file,"[Date \"%4d.%02d.%02d\"]\n",timestruct->tm_year+1900,
timestruct->tm_mon+1,timestruct->tm_mday);
nextc=buff;
for (i=0;i<=last_choice;i++) {
fseek(history_file,i*10,SEEK_SET);
fscanf(history_file,"%s",cmd);
if (strchr(cmd,' ')) *strchr(cmd,' ')=0;
sprintf(nextc," %s",cmd);
nextc=buff+strlen(buff);
if (nextc-buff > 60) {
fprintf(book_lrn_file,"%s\n",buff);
nextc=buff;
strcpy(buff,"");
}
}
fprintf(book_lrn_file,"%s {%d %d %d}\n",
buff, (t_wtm) ? -99900:99900, 10, 0);
fflush(book_lrn_file);
/*
----------------------------------------------------------
| |
| done. now restore the game back to where it was |
| before we started all this nonsense. :) |
| |
----------------------------------------------------------
*/
RestoreGame();
}