#include <math.h>
#include <time.h>
#include "chess.h"
#include "data.h"
#if defined(UNIX)
# include <unistd.h>
#endif
/* last modified 02/24/14 */
/*
*******************************************************************************
* *
* LearnAdjust() us used to scale the learn value, which can be used to *
* limit the aggressiveness of the learning algorithm. All we do here is *
* divide the learn value passed in by "learning / 10". *
* *
*******************************************************************************
*/
int LearnAdjust(int value) {
if (learning / 10 > 0)
return value / (learning / 10);
else
return 0;
}
/* last modified 02/24/14 */
/*
*******************************************************************************
* *
* LearnBook() is used to update the book database when a game ends for any *
* reason. It uses the global "learn_value" variable and updates the book *
* based on the moves played and the value that was "learned". *
* *
* The global learn_value has two possible sources. If a game ends with a *
* real result (win, lose or draw) then the learrn_value will be set to a *
* number in the interval {-300, 300} depending on the result. If there is *
* no result (the operator exits the program prior to reaching a conclusion *
* (quit, end, ^C) then we will use the values from the first few searches *
* after leaving book to compute a learrn_value (see LearnValue() comments *
* later in this file). *
* *
*******************************************************************************
*/
void LearnBook() {
float book_learn[64], t_learn_value;
int nplies = 0, thisply = 0, i, j, v, cluster;
unsigned char buf32[4];
/*
************************************************************
* *
* If we have not been "out of book" for N moves, all we *
* 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. *
* *
************************************************************
*/
if (!book_file)
return;
if (!learn)
return;
if (Abs(learn_value) != learning)
learn_value = LearnAdjust(learn_value);
learn = 0;
Print(32, "LearnBook() updating book database\n");
/*
************************************************************
* *
* 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 propagate 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 = 0; i < 64; i++)
if (learn_nmoves[i] > 1)
nplies++;
nplies = Max(nplies, 1);
for (i = 0; i < 64; i++) {
if (learn_nmoves[i] > 1)
thisply++;
book_learn[i] = t_learn_value * (float) thisply / (float) nplies;
}
/*
************************************************************
* *
* Now find the appropriate cluster, find the key we were *
* passed, and update the resulting learn value. *
* *
************************************************************
*/
for (i = 0; i < 64 && learn_seekto[i]; i++) {
if (learn_seekto[i] > 0) {
fseek(book_file
, learn_seekto
[i
], SEEK_SET
);
v
= fread(buf32
, 4, 1, book_file
);
if (v <= 0)
perror("Learn() fread error: ");
cluster = BookIn32(buf32);
if (cluster)
BookClusterIn(book_file, cluster, book_buffer);
for (j = 0; j < cluster; j++)
if (!(learn_key[i] ^ book_buffer[j].position))
break;
if (j >= cluster)
return;
if (fabs(book_buffer
[j
].
learn) < 0.0001)
book_buffer[j].learn = book_learn[i];
else
book_buffer[j].learn = (book_buffer[j].learn + book_learn[i]) / 2.0;
fseek(book_file
, learn_seekto
[i
] + 4, SEEK_SET
);
if (cluster)
BookClusterOut(book_file, cluster, book_buffer);
}
}
}
/* last modified 02/24/14 */
/*
*******************************************************************************
* *
* 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 multiplier;
static const float rating_mult_t[11] = { .00625, .0125, .025, .05, .075, .1,
0.15, 0.2, 0.25, 0.3, 0.35
};
static const float rating_mult_ut[11] = { .25, .2, .15, .1, .05, .025, .012,
.006, .003, .001
};
int sd, rd, value;
sd = Max(Min(search_depth - 10, 19), 0);
rd = Max(Min(rating_difference / 200, 5), -5) + 5;
if (trusted_value)
multiplier = rating_mult_t[rd] * sd;
else
multiplier = rating_mult_ut[rd] * sd;
sv = Max(Min(sv, 5 * learning), -5 * learning);
value = (int) (sv * multiplier);
return value;
}
/* last modified 02/24/14 */
/*
*******************************************************************************
* *
* LearnValue() is used to monitor the scores over 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.) *
* *
* This procedure does not directly update the book. Rather, it sets the *
* global learn_value variable to represent the goodness or badness of the *
* position where we left the opening book. This will be used later to *
* update the book in the event the game ends without any sort of actual *
* result. In a normal situation, we will base our learning on the result *
* of the game, win-lose-draw. But it is possible that the game ends before *
* the final result is known. In this case, we will use the score from the *
* learn_value we compute here so that we learn _something_ from playing a *
* game fragment. *
* *
* 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 LearnValue(int search_value, int search_depth) {
int i, interval;
int best_eval = -999999, best_eval_p = 0;
int worst_eval = 999999, worst_eval_p = 0;
int best_after_worst_eval = -999999, worst_after_best_eval = 999999;
/*
************************************************************
* *
* 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. *
* *
************************************************************
*/
if (!book_file)
return;
if (!learn || learn_value != 0)
return;
if (moves_out_of_book <= LEARN_INTERVAL) {
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 data. *
* *
************************************************************
*/
else if (moves_out_of_book == LEARN_INTERVAL + 1) {
if (moves_out_of_book < 1)
return;
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];
/*
************************************************************
* *
* 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, while avoiding the sound *
* gambits that leave us ahead in material for a short *
* while until the score starts to drop as the gambit *
* begins to show its effect. *
* *
************************************************************
*/
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;
}
learn_value =
LearnFunction(learn_value, search_depth,
crafty_rating - opponent_rating, learn_value < 0);
}
}