crafty-23.4/0000775000175000017500000000000011466036157012327 5ustar oliverolivercrafty-23.4/make.c0000644000175000017500000002634311466036157013416 0ustar oliveroliver#include "chess.h" #include "data.h" /* last modified 11/05/10 */ /* ******************************************************************************* * * * MakeMove() is responsible for updating the position database whenever a * * piece is moved. It performs the following operations: (1) update the * * board structure itself by moving the piece and removing any captured * * piece. (2) update the hash keys. (3) update material counts. (4) update* * castling status. (5) update number of moves since last reversible move. * * * * There are some special-cases handled here, such as en passant captures * * where the enemy pawn is not on the square, castling which moves * * both the king and rook, and then rook moves/captures which give up the * * castling right to that side when the rook is moved. * * * * note: wtm = 1 if white is to move, 0 otherwise. btm is the opposite and * * is 1 if it is not white to move, 0 otherwise. * * * ******************************************************************************* */ void MakeMove(TREE * RESTRICT tree, int ply, int move, int wtm) { BITBOARD bit_move; int piece, from, to, captured, promote, btm = Flip(wtm); int cpiece; #if defined(DEBUG) int i; #endif /* ************************************************************ * * * First, some basic information is updated for all moves * * before we do the piece-specific stuff. We need to * * save the current position and both hash signatures, * * and add the current position to the repetition-list * * for the side on move, before the move is actually made * * on the board. We also update the 50 move rule * * counter, which will be reset if a capture or pawn move * * is made here. * * * * If the en passant flag was set the previous ply, we * * have already used it to generate moves at this ply, * * and we need to clear it before continuing. If it is * * set, we also need to update the hash signature since * * the EP opportunity no longer exists after making any * * move at this ply (one ply deeper than when a pawn was * * advanced two squares). * * * ************************************************************ */ #if defined(DEBUG) ValidatePosition(tree, ply, move, "MakeMove(1)"); #endif tree->position[ply + 1] = tree->position[ply]; tree->save_hash_key[ply] = HashKey; tree->save_pawn_hash_key[ply] = PawnHashKey; if (EnPassant(ply + 1)) { HashEP(EnPassant(ply + 1)); EnPassant(ply + 1) = 0; } Rule50Moves(ply + 1)++; /* ************************************************************ * * * Now do the things that are common to all pieces, such * * as updating the bitboards and hash signature. * * * ************************************************************ */ piece = Piece(move); from = From(move); to = To(move); captured = Captured(move); promote = Promote(move); bit_move = SetMask(from) | SetMask(to); cpiece = PcOnSq(to); ClearSet(bit_move, Pieces(wtm, piece)); ClearSet(bit_move, Occupied(wtm)); Hash(wtm, piece, from); Hash(wtm, piece, to); PcOnSq(from) = 0; PcOnSq(to) = pieces[wtm][piece]; /* ************************************************************ * * * Now do the piece-specific things by jumping to the * * appropriate routine. * * * ************************************************************ */ switch (piece) { case pawn: HashP(wtm, from); HashP(wtm, to); Rule50Moves(ply + 1) = 0; if (captured == 1 && !cpiece) { Clear(to + epsq[wtm], Pawns(btm)); Clear(to + epsq[wtm], Occupied(btm)); Hash(btm, pawn, to + epsq[wtm]); HashP(btm, to + epsq[wtm]); PcOnSq(to + epsq[wtm]) = 0; Material -= PieceValues(btm, pawn); TotalPieces(btm, pawn)--; TotalAllPieces--; captured = 0; } if (promote) { TotalPieces(wtm, pawn)--; Material -= PieceValues(wtm, pawn); Clear(to, Pawns(wtm)); Hash(wtm, pawn, to); HashP(wtm, to); Hash(wtm, promote, to); PcOnSq(to) = pieces[wtm][promote]; TotalPieces(wtm, occupied) += p_vals[promote]; TotalPieces(wtm, promote)++; Material += PieceValues(wtm, promote); Set(to, Pieces(wtm, promote)); switch (promote) { case knight: tree->pos.minors[wtm]++; break; case bishop: tree->pos.minors[wtm]++; break; case rook: tree->pos.majors[wtm]++; break; case queen: tree->pos.majors[wtm] += 2; break; } } else if ((Abs(to - from) == 16) && (mask_eptest[to] & Pawns(btm))) { EnPassant(ply + 1) = to + epsq[wtm]; HashEP(to + epsq[wtm]); } break; case knight: break; case bishop: break; case rook: if (Castle(ply + 1, wtm) > 0) { if ((from == rook_A[wtm]) && (Castle(ply + 1, wtm) & 2)) { Castle(ply + 1, wtm) &= 1; HashCastle(1, wtm); } else if ((from == rook_H[wtm]) && (Castle(ply + 1, wtm) & 1)) { Castle(ply + 1, wtm) &= 2; HashCastle(0, wtm); } } break; case queen: break; case king: KingSQ(wtm) = to; if (Castle(ply + 1, wtm) > 0) { if (Castle(ply + 1, wtm) & 2) HashCastle(1, wtm); if (Castle(ply + 1, wtm) & 1) HashCastle(0, wtm); if (Abs(to - from) == 2) { Castle(ply + 1, wtm) = -ply; piece = rook; if (to == rook_G[wtm]) { from = rook_H[wtm]; to = rook_F[wtm]; } else { from = rook_A[wtm]; to = rook_D[wtm]; } bit_move = SetMask(from) | SetMask(to); ClearSet(bit_move, Rooks(wtm)); ClearSet(bit_move, Occupied(wtm)); Hash(wtm, rook, from); Hash(wtm, rook, to); PcOnSq(from) = 0; PcOnSq(to) = pieces[wtm][rook]; } else Castle(ply + 1, wtm) = 0; } break; } /* ************************************************************ * * * If this is a capture move, we also have to update the * * information that must change when a piece is removed * * from the board. * * * ************************************************************ */ if (captured) { Rule50Moves(ply + 1) = 0; TotalAllPieces--; if (promote) piece = promote; Hash(btm, captured, to); Clear(to, Pieces(btm, captured)); Clear(to, Occupied(btm)); Material -= PieceValues(btm, captured); TotalPieces(btm, captured)--; if (captured != pawn) TotalPieces(btm, occupied) -= p_vals[captured]; switch (captured) { case pawn: HashP(btm, to); break; case knight: tree->pos.minors[btm]--; break; case bishop: tree->pos.minors[btm]--; break; case rook: if (Castle(ply + 1, btm) > 0) { if ((to == rook_A[btm]) && (Castle(ply + 1, btm) & 2)) { Castle(ply + 1, btm) &= 1; HashCastle(1, btm); } else if ((to == rook_H[btm]) && (Castle(ply + 1, btm) & 1)) { Castle(ply + 1, btm) &= 2; HashCastle(0, btm); } } tree->pos.majors[btm]--; break; case queen: tree->pos.majors[btm] -= 2; break; case king: #if defined(DEBUG) Print(128, "captured a king (Make)\n"); for (i = 1; i <= ply; i++) Print(128, "ply=%2d, piece=%2d,from=%2d,to=%2d,captured=%2d\n", i, Piece(tree->curmv[i]), From(tree->curmv[i]), To(tree->curmv[i]), Captured(tree->curmv[i])); Print(128, "ply=%2d, piece=%2d,from=%2d,to=%2d,captured=%2d\n", i, piece, from, to, captured); if (log_file) DisplayChessBoard(log_file, tree->pos); #endif break; } } #if defined(DEBUG) ValidatePosition(tree, ply + 1, move, "MakeMove(2)"); #endif return; } /* last modified 11/05/10 */ /* ******************************************************************************* * * * MakeMoveRoot() is used to make a move at the root of the game tree, * * before any searching is done. It uses MakeMove() to execute the move, * * but then copies the resulting position back to position[0], the actual * * board position. It handles the special-case of the draw-by-repetition * * rule by clearing the repetition list when a non-reversible move is made, * * since no repetitions are possible once such a move is played. * * * ******************************************************************************* */ void MakeMoveRoot(TREE * RESTRICT tree, int move, int wtm) { int side; /* ************************************************************ * * * First, make the move and replace position[0] with the * * new position. * * * ************************************************************ */ tree->rep_list[wtm][Repetition(wtm)++] = HashKey; MakeMove(tree, 0, move, wtm); /* ************************************************************ * * * Now, if this is a non-reversible move, reset the * * repetition list pointer to start the count over. * * * * One odd action is to note if the castle status is * * currently negative, which indicates that that side * * castled during the previous search. We simply set the * * castle status for that side to zero and we are done. * * * ************************************************************ */ for (side = black; side <= white; side++) { Castle(1, side) = Max(0, Castle(1, side)); if (Rule50Moves(1) == 0) Repetition(side) = 0; } tree->position[0] = tree->position[1]; } crafty-23.4/learn.c0000644000175000017500000003317711466036157013605 0ustar oliveroliver#include #include #include "chess.h" #include "data.h" #if defined(UNIX) # include #endif /* last modified 02/26/09 */ /* ******************************************************************************* * * * 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() { int nplies = 0, thisply = 0; unsigned char buf32[4]; int i, j, cluster; float book_learn[64], t_learn_value; /* ************************************************************ * * * 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 (!learning) return; learning = 0; Print(128, "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); fread(buf32, 4, 1, book_file); cluster = BookIn32(buf32); 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); BookClusterOut(book_file, cluster, book_buffer); fflush(book_file); } } } /* last modified 02/26/09 */ /* ******************************************************************************* * * * 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) { 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 }; float multiplier; int sd, rd; 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, 600), -600); return ((int) (sv * multiplier)); } /* last modified 02/26/09 */ /* ******************************************************************************* * * * 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; int 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 (!learning || 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; Print(128, "LearnBook() executed\n"); 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, 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); } } crafty-23.4/analyze.c0000644000175000017500000001522511466036157014141 0ustar oliveroliver#include "chess.h" #include "data.h" /* last modified 01/18/09 */ /* ******************************************************************************* * * * Analyze() is used to handle the "analyze" command. This mode basically * * puts Crafty into a "permanent pondering" state, where it reads a move from* * the input stream, and then "ponders" for the opposite side. Whenever a * * move is entered, Crafty reads this move, updates the game board, and then * * starts "pondering" for the other side. * * * * The purpose of this mode is to force Crafty to follow along in a game, * * providing analysis continually for the side on move until a move is * * entered, advancing the game to the next position. * * * ******************************************************************************* */ void Analyze() { int i, move, back_number, readstat = 1; TREE *const tree = block[0]; /* ************************************************************ * * * Initialize. * * * ************************************************************ */ int save_swindle_mode = swindle_mode; swindle_mode = 0; ponder_move = 0; analyze_mode = 1; if (!xboard) display_options |= 1 + 2 + 4; printf("Analyze Mode: type \"exit\" to terminate.\n"); /* ************************************************************ * * * Now loop waiting on input, searching the current * * position continually until a move comes in. * * * ************************************************************ */ do { do { last_pv.pathd = 0; last_pv.pathl = 0; input_status = 0; pondering = 1; tree->position[1] = tree->position[0]; (void) Iterate(wtm, think, 0); pondering = 0; if (book_move) moves_out_of_book = 0; if (!xboard) { if (wtm) printf("analyze.White(%d): ", move_number); else printf("analyze.Black(%d): ", move_number); fflush(stdout); } /* ************************************************************ * * * If we get back to here, something has been typed in and * * is in the command buffer normally, unless the search * * terminated naturally due to finding a mate or reaching * * the max depth allowable. * * * ************************************************************ */ if (!input_status) do { readstat = Read(1, buffer); if (readstat < 0) break; nargs = ReadParse(buffer, args, " ;"); Print(128, "%s\n", buffer); if (strstr(args[0], "timeleft") && !xboard) { if (wtm) printf("analyze.White(%d): ", move_number); else printf("analyze.Black(%d): ", move_number); fflush(stdout); } } while (strstr(args[0], "timeleft")); else nargs = ReadParse(buffer, args, " ;"); if (readstat < 0) break; move = 0; if (!strcmp(args[0], "exit")) break; /* ************************************************************ * * * First, check for the special analyze command "back n" * * and handle it if present, otherwise try Option() to see * * if it recognizes the input as a command. * * * ************************************************************ */ if (OptionMatch("back", args[0])) { if (nargs > 1) back_number = atoi(args[1]); else back_number = 1; for (i = 0; i < back_number; i++) { wtm = Flip(wtm); if (Flip(wtm)) move_number--; } if (move_number == 0) { move_number = 1; wtm = 1; } sprintf(buffer, "reset %d", move_number); (void) Option(tree); display = tree->pos; } else if (Option(tree)) { display = tree->pos; } /* ************************************************************ * * * If InputMove() can recognize this as a move, make it, * * swap sides, and return to the top of the loop to call * * search from this new position. * * * ************************************************************ */ else if ((move = InputMove(tree, buffer, 0, wtm, 1, 0))) { char *outmove = OutputMove(tree, move, 0, wtm); if (history_file) { fseek(history_file, ((move_number - 1) * 2 + 1 - wtm) * 10, SEEK_SET); fprintf(history_file, "%9s\n", outmove); } if (wtm) Print(128, "White(%d): ", move_number); else Print(128, "Black(%d): ", move_number); Print(128, "%s\n", outmove); if (speech) { char announce[64]; strcpy(announce, SPEAK); strcat(announce, outmove); system(announce); } MakeMoveRoot(tree, move, wtm); display = tree->pos; last_mate_score = 0; if (log_file) DisplayChessBoard(log_file, tree->pos); } /* ************************************************************ * * * If Option() didn't handle the input, then it is illegal * * and should be reported to the user. * * * ************************************************************ */ else { pondering = 0; if (Option(tree) == 0) printf("illegal move: %s\n", buffer); pondering = 1; display = tree->pos; } } while (!move); if (readstat < 0 || !strcmp(args[0], "exit")) break; wtm = Flip(wtm); if (wtm) move_number++; } while (1); analyze_mode = 0; printf("analyze complete.\n"); pondering = 0; swindle_mode = save_swindle_mode; } crafty-23.4/data.h0000644000175000017500000002724011466036157013414 0ustar oliveroliver#if !defined(DATA_INCLUDED) # define DATA_INCLUDED extern int scale; extern char version[8]; extern int presult; extern PLAYING_MODE mode; extern int batch_mode; extern int swindle_mode; extern int call_flag; extern int crafty_rating; extern int opponent_rating; extern int time_used; extern int time_used_opponent; extern BITBOARD total_moves; extern int allow_cores; extern int allow_memory; extern int initialized; extern int early_exit; extern int new_game; extern BITBOARD burner[10]; extern int burnc[10]; extern char *AK_list[128]; extern char *GM_list[128]; extern char *IM_list[128]; extern char *C_list[128]; extern char *B_list[128]; extern char *SP_list[128]; extern char *SP_opening_filename[128]; extern char *SP_personality_filename[128]; extern FILE *input_stream; extern FILE *book_file; extern FILE *books_file; extern FILE *normal_bs_file; extern FILE *computer_bs_file; extern FILE *history_file; extern FILE *log_file; extern int log_id; extern int output_format; # if !defined(NOEGTB) extern int EGTBlimit; extern int EGTB_draw; extern int EGTB_search; extern int EGTB_use; extern void *EGTB_cache; extern size_t EGTB_cache_size; extern int EGTB_setup; # endif extern int done; extern int last_mate_score; extern int last_opponent_move; extern int check_depth; extern int null_depth; extern int LMR_remaining_depth; extern int LMR_min_reduction; extern int LMR_max_reduction; extern int pgn_suggested_percent; extern char pgn_event[128]; extern char pgn_date[128]; extern char pgn_round[128]; extern char pgn_site[128]; extern char pgn_white[128]; extern char pgn_white_elo[128]; extern char pgn_black[128]; extern char pgn_black_elo[128]; extern char pgn_result[128]; extern char log_filename[256]; extern char history_filename[256]; extern int number_of_solutions; extern int solutions[10]; extern int solution_type; extern int abs_draw_score; extern int accept_draws; extern int offer_draws; extern int adaptive_hash; extern size_t adaptive_hash_min; extern size_t adaptive_hash_max; extern size_t adaptive_hashp_min; extern size_t adaptive_hashp_max; extern int over; extern int silent; extern int xboard; extern int pong; extern int channel; extern char channel_title[32]; extern char book_path[128]; extern char log_path[128]; extern char tb_path[128]; extern char rc_path[128]; extern char cmd_buffer[4096]; extern char *args[512]; extern char buffer[4096]; extern int line_length; extern unsigned char convert_buff[8]; extern int nargs; extern int kibitz; extern int wtm; extern int last_search_value; extern int lazy_eval_cutoff; extern int pruning_margin[8]; extern int pruning_depth; extern int ponder_value; extern int move_actually_played; extern int analyze_mode; extern int annotate_mode; extern int input_status; /* 0=no input; 1=predicted move read; 2=unpredicted move read; 3=something read, not executed. */ extern int resign; extern int resign_counter; extern int resign_count; extern int draw_counter; extern int draw_count; extern int draw_offer_pending; extern int draw_offered; extern char audible_alarm; extern char speech; extern char hint[512]; extern char book_hint[512]; extern int post; extern int search_depth; extern unsigned int search_nodes; extern unsigned int temp_search_nodes; extern int search_move; extern int ponder; extern int ponder_move; extern int force; extern int ponder_moves[220]; extern int num_ponder_moves; extern char initial_position[80]; extern int predicted; extern int trace_level; extern int book_move; extern int book_accept_mask; extern int book_reject_mask; extern int book_random; extern float book_weight_freq; extern float book_weight_eval; extern float book_weight_learn; extern int book_search_trigger; extern int book_selection_width; extern int show_book; extern int learning; extern int learn_value; extern int abort_after_ply1; extern int abort_search; extern int iteration_depth; extern int root_alpha; extern int root_beta; extern int root_value; extern int root_wtm; extern int last_root_value; extern ROOT_MOVE root_moves[256]; extern int n_root_moves; extern int easy_move; extern int time_limit; extern int absolute_time_limit; extern int search_time_limit; extern int burp; extern int quit; extern unsigned int opponent_start_time, opponent_end_time; extern unsigned int program_start_time, program_end_time; extern unsigned int start_time, end_time; extern unsigned int elapsed_start, elapsed_end; extern TREE *block[MAX_BLOCKS + 1]; extern TREE *volatile thread[CPUS]; # if (CPUS > 1) extern lock_t lock_smp, lock_io, lock_root; # if defined(UNIX) extern pthread_attr_t attributes; # endif # endif extern unsigned int parallel_splits; extern unsigned int parallel_aborts; extern unsigned int max_split_blocks; extern volatile int smp_idle; extern volatile int smp_threads; extern volatile int initialized_threads; extern int crafty_is_white; extern unsigned int nodes_between_time_checks; extern unsigned int nodes_per_second; extern int next_time_check; extern int transposition_age; extern int thinking; extern int pondering; extern int puzzling; extern int booking; extern int computer_opponent; extern int display_options; extern int smp_max_threads; extern int smp_max_thread_group; extern int smp_split_at_root; extern unsigned int smp_split_nodes; extern unsigned int noise_level; extern int tc_moves; extern int tc_time; extern int tc_time_remaining[2]; extern int tc_moves_remaining[2]; extern int tc_secondary_moves; extern int tc_secondary_time; extern int tc_increment; extern int tc_sudden_death; extern int tc_operator_time; extern int tc_safety_margin; extern int draw_score[2]; extern char kibitz_text[512]; extern int kibitz_depth; extern int move_number; extern int root_print_ok; extern int moves_out_of_book; extern int first_nonbook_factor; extern int first_nonbook_span; extern int smp_nice; # if defined(SKILL) extern int skill; # endif extern int book_learn_eval[LEARN_INTERVAL]; extern int book_learn_depth[LEARN_INTERVAL]; extern int learn_seekto[64]; extern BITBOARD learn_key[64]; extern int learn_nmoves[64]; extern BITBOARD book_learn_key; extern int learn_positions_count; extern int book_learn_nmoves; extern int book_learn_seekto; extern int usage_level; extern size_t hash_table_size; extern size_t hash_path_size; extern BITBOARD hash_path_mask; extern size_t pawn_hash_table_size; extern BITBOARD hash_mask; extern BITBOARD pawn_hash_mask; extern HASH_ENTRY *trans_ref; extern HPATH_ENTRY *hash_path; extern PAWN_HASH_ENTRY *pawn_hash_table; extern void *segments[MAX_BLOCKS + 32][2]; extern int nsegments; extern const int p_values[13]; extern const int pc_values[7]; extern const int p_vals[7]; extern const int pieces[2][7]; extern PATH last_pv; extern int last_value; extern const char translate[13]; extern const char empty_sqs[9]; extern const char square_color[64]; extern int knight_outpost[2][64]; extern int bishop_outpost[2][64]; extern int passed_pawn_value[2][2][8]; extern int passed_pawn_candidate[2][2][8]; extern int passed_pawn_hidden[2]; extern int doubled_pawn_value[2]; extern int outside_passed[2]; extern int pawn_defects[2][8]; extern int open_file[8]; extern int half_open_file[8]; extern int imbalance[9][9]; extern int pp_dist_bonus[8]; extern int pp_bonus[8]; extern int king_tropism_n[8]; extern int king_tropism_b[8]; extern int king_tropism_r[8]; extern int king_tropism_q[8]; extern int pval[2][2][64]; extern int nval[2][2][64]; extern int bval[2][2][64]; extern int qval[2][2][64]; extern int kval_n[2][64]; extern int kval_k[2][64]; extern int kval_q[2][64]; extern int king_safety[16][16]; extern int mob_curve_r[48]; extern int safety_vector[16]; extern int tropism_vector[16]; extern const char b_n_mate_dark_squares[64]; extern const char b_n_mate_light_squares[64]; extern const int mate[64]; extern BITBOARD magic_rook[64]; extern BITBOARD magic_rook_mask[64]; extern unsigned magic_rook_shift[64]; extern BITBOARD mobility_mask_n[4]; extern BITBOARD mobility_mask_b[4]; extern BITBOARD mobility_mask_r[4]; extern BITBOARD *magic_rook_indices[64]; extern BITBOARD magic_rook_table[102400]; extern short int *magic_rook_mobility_indices[64]; extern short int magic_rook_mobility_table[102400]; extern BITBOARD magic_bishop[64]; extern BITBOARD magic_bishop_mask[64]; extern unsigned magic_bishop_shift[64]; extern BITBOARD *magic_bishop_indices[64]; extern BITBOARD magic_bishop_table[5248]; extern short int *magic_bishop_mobility_indices[64]; extern short int magic_bishop_mobility_table[5248]; extern signed char directions[64][64]; extern BITBOARD pawn_attacks[2][64]; extern BITBOARD knight_attacks[64]; extern BITBOARD rook_attacks[64]; extern BITBOARD bishop_attacks[64]; extern POSITION display; extern BITBOARD king_attacks[64]; extern BITBOARD intervening[64][64]; extern BITBOARD randoms[2][7][64]; extern BITBOARD castle_random[2][2]; extern BITBOARD enpassant_random[65]; extern BITBOARD clear_mask[65]; extern BITBOARD set_mask[65]; extern BITBOARD file_mask[8]; extern BITBOARD rank_mask[8]; extern BITBOARD OO[2]; extern BITBOARD OOO[2]; extern BITBOARD mask_efgh, mask_fgh, mask_abc, mask_abcd; extern BITBOARD mask_advance_2_w; extern BITBOARD mask_advance_2_b; extern BITBOARD mask_left_edge; extern BITBOARD mask_right_edge; extern BITBOARD mask_not_edge; extern BITBOARD mask_kr_trapped[2][3]; extern BITBOARD mask_qr_trapped[2][3]; extern BITBOARD dark_squares; extern BITBOARD not_rook_pawns; extern BITBOARD rook_pawns; extern BITBOARD plus1dir[65]; extern BITBOARD plus7dir[65]; extern BITBOARD plus8dir[65]; extern BITBOARD plus9dir[65]; extern BITBOARD minus1dir[65]; extern BITBOARD minus7dir[65]; extern BITBOARD minus8dir[65]; extern BITBOARD minus9dir[65]; extern BITBOARD mask_eptest[64]; extern BITBOARD mask_clear_entry; # if !defined(_M_AMD64) && !defined (_M_IA64) && !defined(INLINE32) extern unsigned char msb[65536]; extern unsigned char lsb[65536]; # endif extern unsigned char msb_8bit[256]; extern unsigned char lsb_8bit[256]; extern unsigned char pop_cnt_8bit[256]; extern unsigned char is_outside[256][256]; extern BITBOARD mask_pawn_connected[64]; extern BITBOARD mask_pawn_isolated[64]; extern BITBOARD mask_passed[2][64]; extern BITBOARD mask_no_pattacks[2][64]; extern BITBOARD mask_hidden_left[2][8]; extern BITBOARD mask_hidden_right[2][8]; extern BITBOARD pawn_race[2][2][64]; extern BOOK_POSITION book_buffer[BOOK_CLUSTER_SIZE]; extern BOOK_POSITION book_buffer_char[BOOK_CLUSTER_SIZE]; extern const int rankflip[2][8]; extern const int sqflip[2][64]; extern const BITBOARD rank12[2]; extern const int sign[2]; extern int direction[2]; extern int dark_corner[2]; extern int light_corner[2]; extern int OOsqs[2][3]; extern int OOOsqs[2][3]; extern int OOfrom[2]; extern int OOto[2]; extern int OOOto[2]; extern int epsq[2]; extern int rook_A[2]; extern int rook_D[2]; extern int rook_F[2]; extern int rook_G[2]; extern int rook_H[2]; extern int capleft[2]; extern int capright[2]; extern int pawnadv1[2]; extern int pawnadv2[2]; # if !defined(NOEGTB) extern int cbEGTBCompBytes; # endif extern int piece_values[7][2]; extern int pawn_can_promote; extern int wtm_bonus[2]; extern int undeveloped_piece; extern int pawn_duo[2]; extern int pawn_isolated[2]; extern int pawn_weak[2]; extern int lower_n; extern int mobility_score_n[4]; extern int lower_b; extern int bishop_trapped; extern int bishop_with_wing_pawns[2]; extern int mobility_score_b[4]; extern int mobility_score_r[4]; extern int rook_on_7th[2]; extern int rook_open_file[2]; extern int rook_half_open_file[2]; extern int rook_behind_passed_pawn[2]; extern int rook_trapped; extern int king_king_tropism; extern int king_safety_mate_threat; extern int development_thematic; extern int development_losing_castle; extern int development_not_castled; extern struct personality_term personality_packet[256]; #endif crafty-23.4/tbdecode.h0000644000175000017500000006575511466036157014271 0ustar oliveroliver#ifndef TBDECODE /* *INDENT-OFF* */ # define TBDECODE # include # include # include # include # ifndef CLOCKS_PER_SEC # define CLOCKS_PER_SEC CLK_TCK # endif /* ---------------------------- Error codes --------------------------- */ /* ----------- */ # define COMP_ERR_NONE 0 /* everything is OK */ # define COMP_ERR_READ 2 /* input file read error */ # define COMP_ERR_NOMEM 5 /* no enough memory */ # define COMP_ERR_BROKEN 6 /* damaged compressed data */ # define COMP_ERR_PARAM 7 /* incorrect function parameter */ # define COMP_ERR_INTERNAL 9 /* everything else is internal error */ /* hopefully it should never happen */ /* Almost all functions listed further return one as its result on of */ /* codes given above: if no error occured then COMP_ERR_NONE (i.e. 0) */ /* is returned, otherwise functions return error code plus number of */ /* line in "comp.c" where the error was detected multiplied by 256; */ /* line number may be used for exact specification of a place where */ /* error was detected thus making debugging slightly simpler. */ /* */ /* Thus, "(code & 0xff)" gives proper error code, and "(code >> 8)" */ /* gives number of line where the error was raised. */ /* -------------------------------------------------------------------- */ /* */ /* Compress/decompress some chess tables */ /* */ /* Copyright (c) 1991--1998 Andrew Kadatch */ /* */ /* The Limited-Reference variant of Lempel-Ziv algorithm implemented */ /* here was first described in my B.Sc. thesis "Efficient algorithms */ /* for image compression", Novosibirsk State University, 1992, and */ /* cannot be used in any product distributed in Russia or CIS without */ /* written permission from the author. */ /* */ /* Most of the code listed below is significantly simplified code from */ /* the PRS data compression library and therefore it should not be used */ /* in any product (software or hardware, commercial or not, and so on) */ /* without written permission from the author. */ /* */ /* -------------------------------------------------------------------- */ /* ---------------------------- Debugging ----------------------------- */ /* --------- */ # ifndef DEBUG # define DEBUG 0 # endif # if DEBUG # define assert(cond) ((cond) ? (void) 0 : _local_assert (__LINE__)) static void _local_assert(int lineno) { fprintf(stderr, "assertion at line %u failed\n", lineno); exit(33); } # define debug(x) x # define dprintf(x) printf x # else # if !defined (assert) # define assert(cond) ((void) 0) # endif # define debug(x) ((void) 0) # define dprintf(x) ((void) 0) # endif /* mob_pach */ # ifndef __cplusplus int cbEGTBCompBytes = 0; # else extern "C" { int cbEGTBCompBytes = 0; } # endif /* --------------------- Constants, types, etc. ----------------------- */ /* ---------------------- */ # define MIN_BLOCK_BITS 8 /* LOG2 (min size of block to compress) */ # define MAX_BLOCK_BITS 16 /* LOG2 (max size of block to compress) */ /* max. integer we can take LOG2 by table */ # define MAX_BITS_HALF ((MAX_BLOCK_BITS + 1) >> 1) # define MAX_BITS (MAX_BITS_HALF * 2) /* assume that integer is at least 32 bits wide */ # ifndef uint # define uint unsigned # endif # ifndef uchar # define uchar unsigned char # endif # define HEADER_SIZE 80 /* number of reserved bytes */ # define STOP_SEARCH_LENGTH 256 /* terminate search if match */ /* length exceeds that value */ # define MAX_LENGTH_BITS 5 # define MAX_LENGTH (1 << MAX_LENGTH_BITS) # define LONG_BITS 1 # define LONG_LENGTH (MAX_BLOCK_BITS - LONG_BITS) # define LONG_QUICK (MAX_LENGTH - LONG_LENGTH) # if LONG_LENGTH > (MAX_BLOCK_BITS - LONG_BITS) # undef LONG_LENGTH # define LONG_LENGTH (MAX_BLOCK_BITS - LONG_BITS) # endif # if LONG_LENGTH >= MAX_LENGTH || LONG_LENGTH <= 0 # error LONG_LENGTH is out of range # endif # if LONG_BITS <= 0 # error LONG_BITS must be positive # endif # define DELTA (LONG_BITS + LONG_QUICK - 1) # if (MAX_LENGTH - 1) - (LONG_LENGTH - LONG_BITS) != DELTA # error Hmmm # endif # define MAX_DISTANCES 24 # define LOG_MAX_DISTANCES 6 /* see check below */ # if MAX_DISTANCES > (1 << LOG_MAX_DISTANCES) # error MAX_DISTANCES should not exceed (1 << LOG_MAX_DISTANCES) # endif # define ALPHABET_SIZE (256 + (MAX_DISTANCES << MAX_LENGTH_BITS)) # define MAX_ALPHABET ALPHABET_SIZE /* max. alphabet handled by */ /* Huffman coding routines */ # define USE_CRC32 1 /* 0 - use Fletcher's checksum, != 0 - use proper CRC32 */ static uchar header_title[64] = "Compressed by DATACOMP v 1.0 (c) 1991--1998 Andrew Kadatch\r\n\0"; # define RET(n) ((n) + __LINE__ * 256) /* ------------------------- CRC32 routines --------------------------- */ /* -------------- */ # if USE_CRC32 static unsigned CRC32_table[256]; static int CRC32_initialized = 0; static void CRC32_init(void) { int i, j; unsigned k, m = (unsigned) 0xedb88320L; if (CRC32_initialized) return; for (i = 0; i < 256; ++i) { k = i; j = 8; do { if ((k & 1) != 0) k >>= 1; else { k >>= 1; k ^= m; }; } while (--j); CRC32_table[i] = k; } CRC32_initialized = 1; } static unsigned CRC32(uchar * p, int n, unsigned k) { unsigned *table = CRC32_table; uchar *e = p + n; while (p + 16 < e) { # define X(i) k = table[((uchar) k) ^ p[i]] ^ (k >> 8) X(0); X(1); X(2); X(3); X(4); X(5); X(6); X(7); X(8); X(9); X(10); X(11); X(12); X(13); X(14); X(15); # undef X p += 16; } while (p < e) k = table[((uchar) k) ^ *p++] ^ (k >> 8); return (k); } # else # define CRC32_init() static unsigned CRC32(uchar * p, int n, unsigned k1) { unsigned k0 = k1 & 0xffff; uchar *e = p + n; k1 = (k1 >> 16) & 0xffff; while (p + 16 < e) { # define X(i) k0 += p[i]; k1 += k0; X(0); X(1); X(2); X(3); X(4); X(5); X(6); X(7); X(8); X(9); X(10); X(11); X(12); X(13); X(14); X(15); # undef X k0 = (k0 & 0xffff) + (k0 >> 16); k1 = (k1 & 0xffff) + (k1 >> 16); p += 16; } while (p < e) { k0 += *p++; k1 += k0; } k0 = (k0 & 0xffff) + (k0 >> 16); k1 = (k1 & 0xffff) + (k1 >> 16); k0 = (k0 & 0xffff) + (k0 >> 16); k1 = (k1 & 0xffff) + (k1 >> 16); assert(((k0 | k1) >> 16) == 0); return (k0 + (k1 << 16)); } # endif /* USE_CRC32 */ /* ------------------------ Bit IO interface -------------------------- */ /* ---------------- */ # define BITIO_LOCALS \ uint _mask; \ int _bits; \ uchar *_ptr typedef struct { BITIO_LOCALS; } bitio_t; # define BITIO_ENTER(p) do { \ _mask = (p)._mask; \ _bits = (p)._bits; \ _ptr = (p)._ptr; \ } while (0) # define BITIO_LEAVE(p) do { \ (p)._mask = _mask; \ (p)._bits = _bits; \ (p)._ptr = _ptr; \ } while (0) # define BIORD_START(from) do { \ _ptr = (uchar *) (from); \ _bits = sizeof (_mask); \ _mask = 0; \ do \ _mask = (_mask << 8) | *_ptr++; \ while (--_bits != 0); \ _bits = 16; \ } while (0) /* read [1, 17] bits at once */ # define BIORD(bits) \ (_mask >> (8 * sizeof (_mask) - (bits))) # define BIORD_MORE(bits) do { \ _mask <<= (bits); \ if ((_bits -= (bits)) <= 0) \ { \ _mask |= ((_ptr[0] << 8) + _ptr[1]) << (-_bits); \ _ptr += 2; _bits += 16; \ } \ } while (0) /* ------------------------ Huffman coding ---------------------------- */ /* -------------- */ # if MAX_ALPHABET <= 0xffff # if MAX_ALPHABET <= 1024 /* positive value takes 15 bits => symbol number occupies <= 10 bits */ # define huffman_decode_t short # else # define huffman_decode_t int # endif # else # define huffman_decode_t int # endif # define HUFFMAN_DECODE(ch,table,start_bits) do { \ (ch) = table[BIORD (start_bits)]; \ if (((int) (ch)) >= 0) \ { \ BIORD_MORE ((ch) & 31); \ (ch) >>= 5; \ break; \ } \ BIORD_MORE (start_bits); \ do \ { \ (ch) = table[BIORD (1) - (ch)]; \ BIORD_MORE (1); \ } \ while (((int) (ch)) < 0); \ } while (0) # define HUFFMAN_TABLE_SIZE(n,start_bits) \ ((1 << (start_bits)) + ((n) << 1)) static int huffman_decode_create(huffman_decode_t * table, uchar * length, int n, int start_bits) { int i, j, k, last, freq[32], sum[32]; /* calculate number of codewords */ memset(freq, 0, sizeof(freq)); for (i = 0; i < n; ++i) { if ((k = length[i]) > 31) return RET(COMP_ERR_BROKEN); ++freq[k]; } /* handle special case(s) -- 0 and 1 symbols in alphabet */ if (freq[0] == n) { memset(table, 0, sizeof(table[0]) << start_bits); return (0); } if (freq[0] == n - 1) { if (freq[1] != 1) return RET(COMP_ERR_BROKEN); for (i = 0; length[i] == 0;) ++i; i <<= 5; for (k = 1 << start_bits; --k >= 0;) *table++ = (huffman_decode_t) i; return (0); } /* save frequences */ memcpy(sum, freq, sizeof(sum)); /* check code correctness */ k = 0; for (i = 32; --i != 0;) { if ((k += freq[i]) & 1) return RET(COMP_ERR_BROKEN); k >>= 1; } if (k != 1) return RET(COMP_ERR_BROKEN); /* sort symbols */ k = 0; for (i = 1; i < 32; ++i) freq[i] = (k += freq[i]); last = freq[31]; /* preserve number of symbols in alphabet */ for (i = n; --i >= 0;) { if ((k = length[i]) != 0) table[--freq[k]] = (huffman_decode_t) i; } /* now create decoding table */ k = i = (1 << start_bits) + (n << 1); for (n = 32; --n > start_bits;) { j = i; while (k > j) table[--i] = (huffman_decode_t) - (k -= 2); for (k = sum[n]; --k >= 0;) table[--i] = table[--last]; k = j; } j = i; i = 1 << start_bits; while (k > j) table[--i] = (huffman_decode_t) - (k -= 2); for (; n > 0; --n) { for (k = sum[n]; --k >= 0;) { assert(last <= i && last > 0); j = i - (1 << (start_bits - n)); n |= table[--last] << 5; do table[--i] = (huffman_decode_t) n; while (i != j); n &= 31; } } assert((i | last) == 0); return (0); } /* -------------------- Read/write Huffman code ----------------------- */ /* ----------------------- */ # define MIN_REPT 2 # if MIN_REPT <= 1 # error MIN_REPT must exceed 1 # endif # define TEMP_TABLE_BITS 8 static int huffman_read_length(bitio_t * bitio, uchar * length, int n) { BITIO_LOCALS; huffman_decode_t table[2][HUFFMAN_TABLE_SIZE(64, TEMP_TABLE_BITS)]; uchar bits[128]; int i, j, k; BITIO_ENTER(*bitio); k = BIORD(1); BIORD_MORE(1); if (k != 0) { memset(length, 0, n); goto ret; } if (n <= 128) { k = BIORD(5); BIORD_MORE(5); for (i = 0; i < n;) { length[i] = (uchar) BIORD(k); BIORD_MORE(k); if (length[i++] == 0) { j = i + BIORD(4); BIORD_MORE(4); if (j > n) return RET(COMP_ERR_BROKEN); while (i != j) length[i++] = 0; } } goto ret; } BITIO_LEAVE(*bitio); i = huffman_read_length(bitio, bits, 128); if (i != 0) return (i); i = huffman_decode_create(table[0], bits, 64, TEMP_TABLE_BITS); if (i != 0) return (i); i = huffman_decode_create(table[1], bits + 64, 64, TEMP_TABLE_BITS); if (i != 0) return (i); BITIO_ENTER(*bitio); for (i = 0; i < n;) { HUFFMAN_DECODE(k, table[0], TEMP_TABLE_BITS); if (k <= 31) { length[i++] = (uchar) k; continue; } k &= 31; HUFFMAN_DECODE(j, table[1], TEMP_TABLE_BITS); if (j > 31) { int jj = j - 32; j = 1 << jj; if (jj != 0) { if (jj > 16) { j += BIORD(16) << (jj - 16); BIORD_MORE(16); } j += BIORD(jj); BIORD_MORE(jj); } j += 31; } j += MIN_REPT + i; if (j > n) return RET(COMP_ERR_BROKEN); do length[i] = (uchar) k; while (++i != j); } ret: BITIO_LEAVE(*bitio); return (0); } /* ----------------------- Proper compression ------------------------- */ /* ------------------ */ # if MIN_BLOCK_BITS > MAX_BLOCK_BITS || MAX_BLOCK_BITS > MAX_BITS_HALF*2 # error condition MIN_BLOCK_BITS <= MAX_BLOCK_BITS <= MAX_BITS_HALF*2 failed # endif # define DECODE_MAGIC ((int) 0x5abc947fL) # define BLOCK_MAGIC ((int) 0x79a3f29dL) # define START_BITS 13 # define SHORT_INDEX 8u typedef struct { huffman_decode_t table[HUFFMAN_TABLE_SIZE(ALPHABET_SIZE, START_BITS)]; int distance[MAX_DISTANCES]; unsigned *crc, *blk_u; unsigned short *blk_s; int block_size_log, /* block_size is integral power of 2 */ block_size, /* 1 << block_size_log */ last_block_size, /* [original] size of last block */ n_blk, /* total number of blocks */ comp_block_size, /* size of largest compressed block+32 */ check_crc; /* check CRC32? */ uchar *comp; int magic; } decode_info; typedef struct { unsigned char *ptr; /* pointer to the first decoded byte */ int decoded; /* number of bytes decoded so far */ int total; /* total number of bytes in block */ int number; /* number of this block */ } COMP_BLOCK_T; /* Pointer to compressed data block */ typedef struct { COMP_BLOCK_T b; struct { uchar *first; int size; } orig, comp; struct { uchar *ptr, *src; int rept; } emit; bitio_t bitio; int n; int magic; } decode_block; static int calculate_offset(decode_info * info, unsigned n) { unsigned i; i = n / (2 * SHORT_INDEX); if (n & SHORT_INDEX) return info->blk_u[i + 1] - info->blk_s[n]; else return info->blk_u[i] + info->blk_s[n]; } static void do_decode(decode_info * info, decode_block * block, uchar * e) { BITIO_LOCALS; uchar *p, *s = 0; int ch; if ((p = block->emit.ptr) >= e) return; if (p == block->orig.first) { BIORD_START(block->comp.first); block->emit.rept = 0; } else { BITIO_ENTER(block->bitio); if ((ch = block->emit.rept) != 0) { block->emit.rept = 0; s = block->emit.src; goto copy; } } # define OVER if (p < e) goto over; break do { over: HUFFMAN_DECODE(ch, info->table, START_BITS); if ((ch -= 256) < 0) { *p++ = (uchar) ch; OVER; } s = p + info->distance[ch >> MAX_LENGTH_BITS]; ch &= MAX_LENGTH - 1; if (ch <= 3) { p[0] = s[0]; p[1] = s[1]; p[2] = s[2]; p[3] = s[3]; p += ch + 1; OVER; } else if (ch >= LONG_LENGTH) { ch -= LONG_LENGTH - LONG_BITS; # if (MAX_BLOCK_BITS - 1) + (LONG_LENGTH - LONG_BITS) >= MAX_LENGTH if (ch == DELTA) { ch = BIORD(5); BIORD_MORE(5); ch += DELTA; } # endif { int n = 1 << ch; if (ch > 16) { n += BIORD(16) << (ch -= 16); BIORD_MORE(16); } n += BIORD(ch); BIORD_MORE(ch); ch = n; } ch += LONG_LENGTH - (1 << LONG_BITS); } ++ch; copy: if (ch > 16) { if (p + ch > e) { block->emit.rept = ch - (int) (e - p); ch = (int) (e - p); goto copy; } do { # define X(i) p[i] = s[i] X(0); X(1); X(2); X(3); X(4); X(5); X(6); X(7); X(8); X(9); X(10); X(11); X(12); X(13); X(14); X(15); # undef X p += 16; s += 16; } while ((ch -= 16) > 16); } p += ch; s += ch; switch (ch) { # define X(i) case i: p[-i] = s[-i] X(16); X(15); X(14); X(13); X(12); X(11); X(10); X(9); X(8); X(7); X(6); X(5); X(4); X(3); X(2); # undef X } p[-1] = s[-1]; } while (p < e); # undef OVER block->emit.ptr = p; block->emit.src = s; BITIO_LEAVE(block->bitio); } /* pretty ugly */ static int comp_open_file(decode_info ** res, FILE * fd, int check_crc) { BITIO_LOCALS; bitio_t Bitio; uchar temp[ALPHABET_SIZE >= HEADER_SIZE ? ALPHABET_SIZE : HEADER_SIZE]; uchar *ptr; int header_size, block_size, block_size_log, n_blk, i, n, n_s, n_u; unsigned *blk_u, *blk; unsigned short *blk_s; decode_info *info; if (res == 0) return RET(COMP_ERR_PARAM); CRC32_init(); *res = 0; if (fread(temp, 1, HEADER_SIZE, fd) != HEADER_SIZE) return RET(COMP_ERR_READ); if (memcmp(temp, header_title, 64) != 0) return RET(COMP_ERR_READ); ptr = temp; # define R4(i) \ ((ptr[i] << 24) + (ptr[(i) + 1] << 16) + (ptr[(i) + 2] << 8) + (ptr[(i) + 3])) header_size = R4(64); block_size_log = ptr[70]; if (block_size_log > MAX_BITS || header_size < 84) return RET(COMP_ERR_BROKEN); block_size = 1 << block_size_log; if (ptr[71] != MAX_DISTANCES) return RET(COMP_ERR_BROKEN); n_blk = R4(72); if (R4(76) != (ALPHABET_SIZE << 12) + (LONG_BITS << 8) + (LONG_LENGTH << 4) + MAX_LENGTH_BITS) return RET(COMP_ERR_BROKEN); if ((ptr = (uchar *) malloc(header_size)) == 0) return RET(COMP_ERR_NOMEM); if (fread(ptr + HEADER_SIZE, 1, header_size - HEADER_SIZE, fd) != (size_t) (header_size - HEADER_SIZE)) { free(ptr); return RET(COMP_ERR_NOMEM); } memcpy(ptr, temp, HEADER_SIZE); header_size -= 4; if (CRC32(ptr, header_size, 0) != (unsigned) R4(header_size)) { free(ptr); return RET(COMP_ERR_BROKEN); } header_size += 4; /* blk = (unsigned *) malloc (sizeof (unsigned) * (1 + n_blk)); */ n = sizeof(unsigned) * (1 + n_blk); if (n < 4 * 1024 * 1024) n = 4 * 1024 * 1024; blk = (unsigned *) malloc(n); if (blk == 0) { free(ptr); return RET(COMP_ERR_NOMEM); } n = sizeof(info->crc[0]) * (1 + (check_crc ? (2 * n_blk) : 0)); n_u = sizeof(unsigned) * (2 + n_blk / (2 * SHORT_INDEX)); n_s = sizeof(unsigned short) * (1 + n_blk); if ((info = (decode_info *) malloc(sizeof(*info) + n + n_u + n_s)) == 0) { free(ptr); free(blk); return RET(COMP_ERR_NOMEM); } cbEGTBCompBytes += sizeof(*info) + n + n_s + n_u; info->crc = (unsigned *) (info + 1); if (check_crc) blk_u = info->blk_u = info->crc + 2 * n_blk; else blk_u = info->blk_u = info->crc; blk_s = info->blk_s = (unsigned short *) (blk_u + 2 + n_blk / (2 * SHORT_INDEX)); info->check_crc = check_crc; info->block_size_log = block_size_log; info->block_size = block_size; info->n_blk = n_blk; if (check_crc) { n_blk <<= 1; i = HEADER_SIZE; for (n = 0; n < n_blk; ++n) { info->crc[n] = R4(i); i += 4; } n_blk >>= 1; } i = HEADER_SIZE + (n_blk << 3); BIORD_START(ptr + i); info->comp_block_size = 0; for (n = 0; n <= n_blk; ++n) { if ((blk[n] = BIORD(block_size_log)) == 0) blk[n] = block_size; if (info->comp_block_size < (int) (blk[n])) info->comp_block_size = (int) (blk[n]); BIORD_MORE(block_size_log); } info->comp_block_size += 32; for (n = 0; n < MAX_DISTANCES; ++n) { info->distance[n] = -((int) BIORD(block_size_log)); BIORD_MORE(block_size_log); } i += ((n_blk + 1 + MAX_DISTANCES) * block_size_log + 7) >> 3; BIORD_START(ptr + i); BITIO_LEAVE(Bitio); if (huffman_read_length(&Bitio, temp, ALPHABET_SIZE) != 0) { free(blk); free(info); free(ptr); return RET(COMP_ERR_BROKEN); } if (huffman_decode_create(info->table, temp, ALPHABET_SIZE, START_BITS) != 0) { free(blk); free(info); free(ptr); return RET(COMP_ERR_BROKEN); } info->last_block_size = blk[n_blk]; blk[n_blk] = 0; for (n = 0; n <= n_blk; ++n) { i = blk[n]; blk[n] = header_size; header_size += i; if (0 == n % (2 * SHORT_INDEX)) blk_u[n / (2 * SHORT_INDEX)] = blk[n]; } blk_u[n_blk / (2 * SHORT_INDEX) + 1] = blk[n_blk]; for (n = 0; n <= n_blk; ++n) { i = n / (2 * SHORT_INDEX); if (n & SHORT_INDEX) blk_s[n] = blk_u[i + 1] - blk[n]; else blk_s[n] = blk[n] - blk_u[i]; } free(blk); free(ptr); info->comp = 0; info->magic = DECODE_MAGIC; *res = info; return (COMP_ERR_NONE); } static int comp_tell_blocks(decode_info * info) { if (info == 0 || info->magic != DECODE_MAGIC) return (-1); return (info->n_blk); } static int comp_init_block(decode_block * block, int block_size, uchar * orig) { if (block == 0) return RET(COMP_ERR_PARAM); block->orig.first = orig; block->comp.first = (uchar *) (block + 1); block->b.ptr = 0; block->b.decoded = -1; block->b.total = -1; block->b.number = -1; block->n = -1; block->magic = BLOCK_MAGIC; return (COMP_ERR_NONE); } static int comp_alloc_block(decode_block ** ret_block, int block_size) { decode_block *block; if (ret_block == 0) return RET(COMP_ERR_PARAM); *ret_block = 0; if ((block = (decode_block *) malloc(sizeof(*block) + block_size)) == 0) return RET(COMP_ERR_NOMEM); cbEGTBCompBytes += sizeof(*block) + block_size; if (0 != comp_init_block(block, block_size, NULL)) return RET(COMP_ERR_PARAM); *ret_block = block; return (COMP_ERR_NONE); } # define RETURN(n) \ return ((n) == COMP_ERR_NONE ? COMP_ERR_NONE : RET (n)); static int comp_read_block(decode_block * block, decode_info * info, FILE * fd, int n) { int comp_size, orig_size, comp_start; uchar *comp, *orig; if (block == 0 || block->magic != BLOCK_MAGIC) return RET(COMP_ERR_PARAM); assert(info->magic == DECODE_MAGIC); if ((unsigned) n >= (unsigned) info->n_blk) RETURN(COMP_ERR_PARAM); comp = block->comp.first; block->n = n; orig = block->orig.first; orig_size = info->block_size; if (n == info->n_blk - 1) orig_size = info->last_block_size; block->orig.size = orig_size; comp_start = calculate_offset(info, n); block->comp.size = comp_size = calculate_offset(info, n + 1) - comp_start; if (fseek(fd, comp_start, SEEK_SET) != 0) RETURN(COMP_ERR_READ); if (fread(comp, 1, comp_size, fd) != (size_t) comp_size) RETURN(COMP_ERR_READ); if (info->check_crc && info->crc[(n << 1) + 1] != CRC32(block->comp.first, comp_size, 0)) RETURN(COMP_ERR_BROKEN); block->emit.rept = 0; if (comp_size == orig_size) { memcpy(orig, comp, comp_size); block->emit.ptr = orig + comp_size; block->b.decoded = comp_size; } else { block->emit.ptr = orig; block->b.decoded = 0; } block->b.number = n; block->b.ptr = orig; block->b.total = orig_size; RETURN(COMP_ERR_NONE); } static int comp_decode_and_check_crc(decode_block * block, decode_info * info, int n, int check_crc) { if (block == 0 || block->magic != BLOCK_MAGIC) return RET(COMP_ERR_PARAM); assert(info->magic == DECODE_MAGIC); if ((unsigned) (n - 1) > (unsigned) (block->orig.size - 1)) RETURN(COMP_ERR_PARAM); if (check_crc) n = block->orig.size; do_decode(info, block, block->orig.first + n); block->b.ptr = block->orig.first; block->b.total = block->orig.size; if (block->b.decoded >= block->b.total) { if (block->b.decoded > block->b.total) RETURN(COMP_ERR_BROKEN); if (block->emit.rept != 0) RETURN(COMP_ERR_BROKEN); } if (check_crc && info->check_crc && info->crc[block->n << 1] != CRC32(block->orig.first, block->orig.size, 0)) RETURN(COMP_ERR_BROKEN); RETURN(COMP_ERR_NONE); } # if !defined (COLOR_DECLARED) /* Test driver */ # define CRC_CHECK 1 int main(int argc, char *argv[]) { int i; int size; int result; FILE *fp; decode_info *comp_info; decode_block *comp_block; clock_t tStart, tEnd; double dSeconds; uchar rgbBuf[8192 + 32]; if (2 != argc) { printf("Invalid arguments\n"); exit(1); } fp = fopen(argv[1], "rb"); if (0 == fp) { printf("Unable to open file\n"); exit(1); } result = comp_open_file(&comp_info, fp, CRC_CHECK); if (0 != result) { printf("Unable to read file (1): %d\n", result); exit(1); } if (8192 != comp_info->block_size) { printf("Invalid block size: %d\n", comp_info->block_size); exit(1); } result = comp_alloc_block(&comp_block, comp_info->block_size); if (0 != result) { printf("Unable to allocate block: %d\n", result); exit(1); } size = 0; tStart = clock(); for (i = 0; i < comp_info->n_blk; i++) { if (0 != (result = comp_init_block(comp_block, comp_info->block_size, rgbBuf))) { printf("Unable to init block: %d\n", result); exit(1); } if (0 != (result = comp_read_block(comp_block, comp_info, fp, i))) { printf("Unable to read block: %d\n", result); exit(1); } size += comp_block->orig.size; if (0 != (result = comp_decode_and_check_crc(comp_block, comp_info, comp_block->orig.size, CRC_CHECK))) { printf("Unable to decode block: %d\n", result); exit(1); } } tEnd = clock(); dSeconds = (double) (tEnd - tStart) / CLOCKS_PER_SEC; printf("Total memory allocated: %dKb\n", (cbEGTBCompBytes + 1023) / 1024); printf("%g seconds, %dMb, %gMb/sec)\n", dSeconds, size / (1024 * 1024), size / (1024 * 1024) / dSeconds); return 0; } # endif /* *INDENT-ON* */ #endif crafty-23.4/killer.c0000644000175000017500000000331611466036157013756 0ustar oliveroliver#include "chess.h" #include "data.h" /* last modified 01/14/09 */ /* ******************************************************************************* * * * Killer() is used to maintain the two killer moves for each ply. the most * * recently used killer is always first in the list. * * * ******************************************************************************* */ void Killer(TREE * RESTRICT tree, int ply, int move) { /* ************************************************************ * * * If the best move so far is a capture or a promotion, * * return, since we try good captures and promotions * * before searching killer heuristic moves anyway. * * * ************************************************************ */ if (CaptureOrPromote(move)) return; /* ************************************************************ * * * Now, add this move to the current killer moves if it * * is not already there. If the move is already first in * * the list, leave it there, otherwise move the first one * * down to slot two and insert this move into slot one. * * * ************************************************************ */ if (tree->killers[ply].move1 != move) { tree->killers[ply].move2 = tree->killers[ply].move1; tree->killers[ply].move1 = move; } } crafty-23.4/Makefile.xp0000664000175000017500000000560311466036157014421 0ustar oliveroliver# Makefile version 3 for Crafty 16.15 # Crafty v16.x makefile for Windows NT Intel # Written by Jason Deines (jdeines@mindspring.com) April 1998 # Version modified by Gregor Overney (gregor_overney@hp.com) Nov 1998 # Version modified by Peter Karrer (pkarrer@active.ch) Dec 1998 # Version modified by Gregor Overney (gregor_overney@hp.com) Sep 1999 # # This makefile is designed to be used from the command line with # Microsoft's nmake. Either rename this # file to "Makefile" or name it # explicitly when invoking nmake: # nmake -f Makefile.xp # # The makefile is set up for Microsoft Visual C++ 6.0 Intel. # # The default target produces a file called "crafty.exe". # Build target is defined here. TARGET = NT_i386 # Command-line compiler and linker invocation commands: CC = cl LD = link # Base compiler flags needed for build: BFLAGS = /D_CONSOLE /DWIN32 /D_CRT_SECURE_NO_DEPRECATE # Compiler flags: # /O2 optimize for speed # /Oa assume no aliasing (no good for VC 6 without SP3) # /Gr fastcall calling convention # /G5 target Pentium (but will run on all x86 architectures) # /G6 target Pentium Pro (but will run on all x86 architectures) # /Ob2 inline function calls if suitable # # For debugging use these flags instead: # CFLAGS = /Od /Zi # LDFLAGS = /DEBUG /DEBUGTYPE:CV # #CFLAGS = /O2 /G6 /Gr /Ob2 #CFLAGS = /Od /Zi CFLAGS = /Ox /Gr /GL # Linker flags, normally not needed except for debug builds: LDFLAGS = /LTCG #LDFLAGS = /DEBUG /DEBUGTYPE:CV # See the default crafty makefile for a description of the options below. # With VC++, defines like COMPACT_ATTACKS, etc, makes the code slower, so # those # options are disabled by default. FAST is normally not defined # so that hash statistics are reported -- for the fastest possible # executable, define FAST below. for 6 piece EGTB support, add /DEGTB6. #COPTS = # For an SMP build use/add the following build options. # NT_INTEREX is defined if you want to use the built-in InterlockedExchange() # function for thread resource locking, instead of the inline asm routine. # (This shouldn't be needed, at least on Intel.) # /MT is a compiler flag needed for multithreaded builds. #COPTS = /MT /DSMP /DCPUS=4 /DNT_INTEREX COPTS = /MT /DSMP /DCPUS=4 # To enable assembly optimizations in vcinline.h, use /DVC_INLINE_ASM. #AOPTS = /DVC_INLINE_ASM ALLOPTS = $(COPTS) $(AOPTS) /D$(TARGET) objects = crafty.obj egtb.obj includes = chess.h data.h epd.h epddefs.h epdglue.h vcinline.h crafty : $(objects) $(LD) $(LDFLAGS) $(objects) /out:crafty.exe $(objects) : $(includes) .c.obj : $(CC) $(BFLAGS) $(CFLAGS) $(ALLOPTS) /c $*.c .cpp.obj : $(CC) $(BFLAGS) $(CFLAGS) $(ALLOPTS) /Zm500 /c $*.cpp clean: del /q $(objects) del /q log.* del /q game.* del /q *.bak crafty-23.4/epd.h0000644000175000017500000001265011466036157013252 0ustar oliveroliver#if !defined(EPDH_INCLUDED) /* *INDENT-OFF* */ # define EPDH_INCLUDED /*>>> epd.h: subprogram prototypes for epd.c */ /* Revised: 1996.06.23 */ /* Copyright (C) 1996 by Steven J. Edwards (sje@mv.mv.com) All rights reserved. This code may be freely redistibuted and used by both research and commerical applications. No warranty exists. */ /* Everything in this source file is independent of the host program. Requests for changes and additions should be communicated to the author via the e-mail address given above. */ /* This file was originally prepared on an Apple Macintosh using the Metrowerks CodeWarrior 6 ANSI C compiler. Tabs are set at every four columns. Further testing and development was performed on a generic PC running Linux 1.3.20 and using the gcc 2.7.0 compiler. */ void EPDFatal(charptrT s); void EPDSwitchFault(charptrT s); voidptrT EPDMemoryGrab(liT n); void EPDMemoryFree(voidptrT ptr); charptrT EPDStringGrab(charptrT s); void EPDStringFree(charptrT s); charptrT EPDStringAppendChar(charptrT s, char c); charptrT EPDStringAppendStr(charptrT s0, charptrT s1); liT EPDMapFromDuration(charptrT s); charptrT EPDMapToDuration(liT seconds); gamptrT EPDGameOpen(void); void EPDGameClose(gamptrT gamptr); void EPDGameAppendMove(gamptrT gamptr, mptrT mptr); void EPDTokenize(charptrT s); siT EPDTokenCount(void); charptrT EPDTokenFetch(siT n); siT EPDCICharEqual(char ch0, char ch1); pT EPDPieceFromCP(cpT cp); siT EPDCheckPiece(char ch); pT EPDEvaluatePiece(char ch); siT EPDCheckColor(char ch); cT EPDEvaluateColor(char ch); siT EPDCheckRank(char ch); rankT EPDEvaluateRank(char ch); siT EPDCheckFile(char ch); fileT EPDEvaluateFile(char ch); eovptrT EPDNewEOV(void); void EPDReleaseEOV(eovptrT eovptr); void EPDAppendEOV(eopptrT eopptr, eovptrT eovptr); eovptrT EPDCreateEOVStr(charptrT str); eovptrT EPDCreateEOVSym(charptrT sym); eovptrT EPDCreateEOVInt(liT lval); eovptrT EPDLocateEOV(eopptrT eopptr, charptrT strval); siT EPDCountEOV(eopptrT eopptr); void EPDReplaceEOVStr(eovptrT eovptr, charptrT str); eopptrT EPDNewEOP(void); void EPDReleaseEOP(eopptrT eopptr); void EPDAppendEOP(epdptrT epdptr, eopptrT eopptr); eopptrT EPDCreateEOP(charptrT opsym); eopptrT EPDCreateEOPCode(epdsoT epdso); eopptrT EPDLocateEOP(epdptrT epdptr, charptrT opsym); eopptrT EPDLocateEOPCode(epdptrT epdptr, epdsoT epdso); siT EPDCountEOP(epdptrT epdptr); void EPDDropIfLocEOP(epdptrT epdptr, charptrT opsym); void EPDDropIfLocEOPCode(epdptrT epdptr, epdsoT epdso); void EPDAddOpInt(epdptrT epdptr, epdsoT epdso, liT val); void EPDAddOpStr(epdptrT epdptr, epdsoT epdso, charptrT s); void EPDAddOpSym(epdptrT epdptr, epdsoT epdso, charptrT s); epdptrT EPDNewEPD(void); void EPDReleaseOperations(epdptrT epdptr); void EPDReleaseEPD(epdptrT epdptr); charptrT EPDFetchOpsym(epdsoT epdso); epdptrT EPDCloneEPDBase(epdptrT epdptr); eovptrT EPDCloneEOV(eovptrT eovptr); eopptrT EPDCloneEOP(eopptrT eopptr); epdptrT EPDCloneEPD(epdptrT epdptr); epdptrT EPDSet(rbptrT rbptr, cT actc, castT cast, sqT epsq); void EPDSetCurrentPosition(rbptrT rbptr, cT actc, castT cast, sqT epsq, siT hmvc, siT fmvn); epdptrT EPDGetCurrentPosition(void); cT EPDFetchACTC(void); castT EPDFetchCAST(void); sqT EPDFetchEPSQ(void); siT EPDFetchHMVC(void); siT EPDFetchFMVN(void); rbptrT EPDFetchBoard(void); cpT EPDFetchCP(sqT sq); charptrT EPDFetchBoardString(void); gtimT EPDGetGTIM(gamptrT gamptr); void EPDPutGTIM(gamptrT gamptr, gtimT gtim); charptrT EPDGenBasic(rbptrT rbptr, cT actc, castT cast, sqT epsq); charptrT EPDGenBasicCurrent(void); epdptrT EPDDecodeFEN(charptrT s); charptrT EPDEncodeFEN(epdptrT epdptr); epdptrT EPDDecode(charptrT s); charptrT EPDEncode(epdptrT epdptr); void EPDRealize(epdptrT epdptr); void EPDInitArray(void); charptrT EPDPlayerString(cT c); void EPDSANEncode(mptrT mptr, sanT san); mptrT EPDSANDecodeAux(sanT san, siT strict); siT EPDIsLegal(void); siT EPDIsCheckmate(void); siT EPDIsStalemate(void); siT EPDIsInsufficientMaterial(void); siT EPDIsFiftyMoveDraw(void); siT EPDIsThirdRepetition(gamptrT gamptr); siT EPDIsDraw(gamptrT gamptr); mptrT EPDMateInOne(void); void EPDExecuteUpdate(mptrT mptr); void EPDRetractUpdate(void); void EPDRetractAll(void); void EPDCollapse(void); void EPDReset(void); void EPDGenMoves(void); siT EPDFetchMoveCount(void); mptrT EPDFetchMove(siT index); void EPDSetMoveFlags(mptrT mptr); void EPDSortSAN(void); siT EPDPurgeOpFile(charptrT opsym, charptrT fn0, charptrT fn1); siT EPDRepairEPD(epdptrT epdptr); siT EPDRepairFile(charptrT fn0, charptrT fn1); siT EPDNormalizeFile(charptrT fn0, charptrT fn1); siT EPDScoreFile(charptrT fn, bmsptrT bmsptr); siT EPDEnumerateFile(siT depth, charptrT fn0, charptrT fn1, liptrT totalptr); charptrT EPDMoveList(gamptrT gamptr); pgnstrT EPDPGNFetchTagIndex(charptrT s); charptrT EPDPGNFetchTagName(pgnstrT pgnstr); charptrT EPDPGNGetSTR(gamptrT gamptr, pgnstrT pgnstr); void EPDPGNPutSTR(gamptrT gamptr, pgnstrT pgnstr, charptrT s); charptrT EPDPGNGenSTR(gamptrT gamptr); charptrT EPDPGNHistory(gamptrT gamptr); void EPDCopyInPTP(gamptrT gamptr, epdptrT epdptr); void EPDCopyOutPTP(gamptrT gamptr, epdptrT epdptr); charptrT EPDFetchRefcomStr(refcomT refcom); charptrT EPDFetchRefreqStr(refreqT refreq); refcomT EPDFetchRefcomIndex(charptrT s); refreqT EPDFetchRefreqIndex(charptrT s); refcomT EPDExtractRefcomIndex(epdptrT epdptr); refreqT EPDExtractRefreqIndex(epdptrT epdptr); siT EPDComm(refintptrT refintptr, charptrT pipebase); void EPDInit(void); void EPDTerm(void); /* *INDENT-ON* */ #endif /*<<< epd.h: EOF */ crafty-23.4/search.c0000644000175000017500000012112411466036157013737 0ustar oliveroliver#include "chess.h" #include "data.h" /* last modified 11/05/10 */ /* ******************************************************************************* * * * Search() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Search() is * * called whenever there is "depth" remaining so that all moves are subject * * to searching. Search() recursively calls itself so long as there is at * * least one ply of depth left, otherwise it calls Quiesce() instead. * * * ******************************************************************************* */ int Search(TREE * RESTRICT tree, int alpha, int beta, int wtm, int depth, int ply, int do_null) { BITBOARD start_nodes = tree->nodes_searched, begin_root_nodes = 0; int first_tried = 0, moves_searched = 0, repeat = 0; int o_alpha = alpha, value = 0, t_beta = beta; int extensions; /* ************************************************************ * * * Check to see if we have searched enough nodes that it * * is time to peek at how much time has been used, or if * * is time to check for operator keyboard input. This is * * usually enough nodes to force a time/input check about * * once per second, except when the target time per move * * is very small, in which case we try to check the time * * at least 10 times during the search. * * * * Note that we check for timeout in all active threads, * * but we only do I/O in thread 0 to avoid read race * * conditions that are problematic. * * * ************************************************************ */ #if defined(NODES) if (--temp_search_nodes <= 0) { abort_after_ply1 = 1; abort_search = 1; return (0); } #endif if (--next_time_check <= 0) { next_time_check = nodes_between_time_checks; if (TimeCheck(tree, 0)) { abort_after_ply1 = 1; abort_search = 1; return (0); } if (tree->thread_id == 0) { if (CheckInput()) Interrupt(ply); } } if (ply >= MAXPLY - 1) return (beta); /* ************************************************************ * * * Check for draw by repetition, which includes 50 move * * draws also. This and the next two steps are skipped * * for root moves (ply = 1). * * * ************************************************************ */ tree->rep_list[wtm][Repetition(wtm) + (ply - 1) / 2] = HashKey; if (ply > 1) { if ((repeat = RepetitionCheck(tree, ply, wtm))) { if (repeat == 1 || !tree->inchk[ply]) { value = DrawScore(wtm); if (value < beta) SavePV(tree, ply, 0); #if defined(TRACE) if (ply <= trace_level) printf("draw by repetition detected, ply=%d.\n", ply); #endif return (value); } } /* ************************************************************ * * * Now call HashProbe() to see if this position has been * * searched before. If so, we may get a real score, * * produce a cutoff, or get nothing more than a good move * * to try first. There are four cases to handle: * * * * 1. HashProbe() returns "HASH_HIT". This terminates * * the search instantly and we simply return the value * * found in the hash table. This value is simply the * * value we found when we did a real search in this * * position previously, and HashProbe() verifies that the * * value is useful based on draft and current bounds. * * * * 2. HashProbe() returns "AVOID_NULL_MOVE" which means * * the hashed score/bound was no good, but it indicated * * that trying a null-move in this position would be a * * waste of time since it will likely fail low, not high. * * * * 3. HashProbe() returns "HASH_MISS" when forces us to * * do a normal search to resolve this node. * * * ************************************************************ */ switch (HashProbe(tree, ply, depth, wtm, alpha, beta, &value)) { case HASH_HIT: return (value); case AVOID_NULL_MOVE: do_null = 0; case HASH_MISS: break; } /* ************************************************************ * * * Now it's time to try a probe into the endgame table- * * base files. This is done if we notice that there are * * 6 or fewer pieces left on the board. EGTB_use tells * * us how many pieces to probe on. Note that this can be * * zero when trying to swindle the opponent, so that no * * probes are done since we know it is a draw. * * * * Note that in "swindle mode" this can be turned off by * * Iterate() setting "EGTB_use = 0" so that we won't * * probe the EGTBs since we are searching only the root * * moves that lead to a draw and we want to play the move * * that makes the draw more difficult to reach by the * * opponent to give him a chance to make a mistake. * * * * Another special case is that we slightly fudge the * * score for draws. In a normal circumstance, draw=0.00 * * since it is "equal". However, here we add 0.01 if * * white has more material, or subtract 0.01 if black has * * more material, since in a drawn KRP vs KR we would * * prefer to have the KRP side since the opponent can * * make a mistake and convert the draw to a loss. * * * ************************************************************ */ #if !defined(NOEGTB) if (ply <= iteration_depth && TotalAllPieces <= EGTB_use && Castle(ply, white) + Castle(ply, black) == 0 && (CaptureOrPromote(tree->curmv[ply - 1]) || ply < 3)) { int egtb_value; tree->egtb_probes++; if (EGTBProbe(tree, ply, wtm, &egtb_value)) { tree->egtb_probes_successful++; alpha = egtb_value; if (Abs(alpha) > MATE - 300) alpha += (alpha > 0) ? -ply + 1 : ply; else if (alpha == 0) { alpha = DrawScore(wtm); if (Material > 0) alpha += (wtm) ? 1 : -1; else if (Material < 0) alpha -= (wtm) ? 1 : -1; } if (alpha < beta) SavePV(tree, ply, 2); HashStore(tree, ply, MAX_DRAFT, wtm, EXACT, alpha, 0); return (alpha); } } #endif /* ************************************************************ * * * We now know there is no easy way out via a hash hit, a * * repetition hit, or an EGTB hit, which leaves us one * * more way of getting out with minimal effort, where we * * try a null move to see if we can get a quick cutoff * * with only a little work. This operates as follows. * * Instead of making a legal move, the side on move * * "passes" and does nothing. The resulting position is * * searched to a shallower depth than normal (usually 3 * * plies less but settable by the operator.) This will * * result in a cutoff if our position is very good, but it * * produces the cutoff much quicker since the search is * * far shallower than a normal search that would also be * * likely to fail high. * * * * This is skipped for any of the following reasons: * * * * 1. The side on move is in check. The null move * * results in an illegal position. * * 2. No more than one null move can appear in succession * * as all this does is burn 6 plies of depth. * * 3. The side on move has only pawns left, which makes * * zugzwang positions more likely. * * 4. The transposition table probe found an entry that * * indicates that a null-move search will not fail * * high, so we avoid the wasted effort. * * * ************************************************************ */ tree->inchk[ply + 1] = 0; tree->last[ply] = tree->last[ply - 1]; if (do_null && alpha == beta - 1 && depth > 1 && !tree->inchk[ply] && TotalPieces(wtm, occupied)) { BITBOARD save_hash_key; tree->curmv[ply] = 0; tree->phase[ply] = NULL_MOVE; #if defined(TRACE) if (ply <= trace_level) Trace(tree, ply, depth, wtm, beta - 1, beta, "Search1", NULL_MOVE); #endif tree->position[ply + 1] = tree->position[ply]; Rule50Moves(ply + 1) = 0; save_hash_key = HashKey; if (EnPassant(ply)) { HashEP(EnPassant(ply + 1)); EnPassant(ply + 1) = 0; } if (depth - null_depth - 1 > 0) value = -Search(tree, -beta, -beta + 1, Flip(wtm), depth - null_depth - 1, ply + 1, NO_NULL); else value = -Quiesce(tree, -beta, -beta + 1, Flip(wtm), ply + 1, 1); HashKey = save_hash_key; if (abort_search || tree->stop) return (0); if (value >= beta) { HashStore(tree, ply, depth, wtm, LOWER, value, tree->hash_move[ply]); return (value); } } } /* ************************************************************ * * * if there is no best move from the hash table, and this * * is a PV node, then we need a good move to search * * first. while killers and history moves are good, they * * are not "good enough". the simplest action is to try * * a shallow search (depth-2) to get a move. note that * * when we call Search() with depth-2, it, too, will * * not have a hash move, and will therefore recursively * * continue this process, hence the name "internal * * iterative deepening." * * * ************************************************************ */ tree->next_status[ply].phase = HASH_MOVE; if (depth >= 6 && !tree->hash_move[ply] && do_null && ply > 1) { int abound = (ply & 1) ? root_alpha : -root_beta; int bbound = (ply & 1) ? root_beta : -root_alpha; if (alpha == abound && beta == bbound) { PATH temp_path = tree->pv[ply]; do { tree->curmv[ply] = 0; if (depth - 2 > 0) value = Search(tree, alpha, beta, wtm, depth - 2, ply, DO_NULL); else value = Quiesce(tree, alpha, beta, wtm, ply, 1); if (abort_search || tree->stop) break; if (value > alpha) { if (value < beta) { if ((int) tree->pv[ply - 1].pathl > ply) tree->hash_move[ply] = tree->pv[ply - 1].path[ply]; } else tree->hash_move[ply] = tree->curmv[ply]; tree->last[ply] = tree->last[ply - 1]; tree->next_status[ply].phase = HASH_MOVE; } } while (0); tree->pv[ply] = temp_path; } } /* ************************************************************ * * * Now iterate through the move list and search the * * resulting positions. Note that Search() culls any * * move that is not legal by using Check(). The special * * case is that we must find one legal move to search to * * confirm that it's not a mate or draw. * * * ************************************************************ */ tree->next_status[ply].phase = HASH_MOVE; while ((tree->phase[ply] = (ply > 1) ? ((tree->inchk[ply]) ? NextEvasion(tree, ply, wtm) : NextMove(tree, ply, wtm)) : NextRootMove(tree, tree, wtm))) { #if defined(TRACE) if (ply <= trace_level) Trace(tree, ply, depth, wtm, alpha, beta, "Search2", tree->phase[ply]); #endif tree->nodes_searched++; if (ply == 1) begin_root_nodes = tree->nodes_searched; if (moves_searched == 0) first_tried = tree->curmv[ply]; MakeMove(tree, ply, tree->curmv[ply], wtm); if (tree->inchk[ply] || !Check(wtm)) do { /* ************************************************************ * * * If the move to be made checks the opponent, then we * * need to remember that he's in check and also extend * * the depth by one ply for him to get out. Note that if * * the move gives check, it is not a candidate for either * * depth reduction or forward-pruning. * * * ************************************************************ */ extensions = 0; if (Check(Flip(wtm))) { tree->inchk[ply + 1] = 1; if (SwapO(tree, tree->curmv[ply], wtm) <= 0) { tree->extensions_done++; extensions = check_depth; } } else tree->inchk[ply + 1] = 0; /* ************************************************************ * * * Now it's time to try to reduce the search depth if the * * move appears to be "poor". To reduce the search, the * * following requirements must be met: * * * * (1) We must be in the REMAINING_MOVES part of the move * * ordering, so that we have nearly given up on * * failing high on any move. * * (2) We must not be too close to the horizon (this is * * the LMR_remaining_depth value). * * (3) The current move must not be a checking move and * * the side to move can not be in check. * * (4) The moving piece is not a passed pawn. * * (5) The current move can not affect the material * * balance, that is it can not be a capture or pawn * * promotion. * * * ************************************************************ */ if (tree->phase[ply] == REMAINING_MOVES && !tree->inchk[ply] && !extensions && moves_searched) { if ((Piece(tree->curmv[ply]) != pawn || mask_passed[wtm][To(tree-> curmv[ply])] & Pawns(Flip(wtm)))) { extensions = Min(-Min(depth - 1 - LMR_remaining_depth, (moves_searched > 2) ? LMR_max_reduction : LMR_min_reduction), 0); if (extensions) tree->reductions_done++; } /* ************************************************************ * * * Now for the forward-pruning stuff. The idea here is * * based on the old FUTILITY idea, where if the current * * material + a fudge factor is lower than alpha, then * * there is little promise in searching this move to make * * up for the material deficit. * * * * This is a useful idea in today's 20+ ply searches, as * * when we near the tips, if we are too far behind in * * material, there is little time left to recover it and * * moves that don't bring us closer to a reasonable * * material balance can safely be skipped. It is much * * more dangerous in shallow searches. * * * * We have an array of pruning margin values that are * * indexed by depth (remaining plies left until we drop * * into the quiescence search) and which increase with * * depth since more depth means a greater chance of * * bringing the score back up to alpha or beyond. If the * * current material + the bonus is less than alpha, we * * simply avoid searching this move at all, and skip to * * the next move without expending any more effort. Note * * that this is classic forward-pruning and can certainly * * introduce errors into the search. However, cluster * * testing has shown that this improves play in real * * games. The current implementation only prunes in the * * last 4 plies before quiescence, although this can be * * tuned with the "eval" command changing the "pruning * * depth" value to something other than 5 (test is for * * depth < pruning depth, current value is 5 which prunes * * in last 4 plies only). Testing shows no benefit in * * larger values than 5, although this might change in * * future versions as other things are modified. * * * ************************************************************ */ if (ply > 1 && depth < pruning_depth && moves_searched && MaterialSTM(wtm) + pruning_margin[depth] <= alpha) { tree->moves_pruned++; continue; } } /* ************************************************************ * * * We have determined whether the depth is to be changed * * by an extension or a reduction. If we get to this * * point, then the move is not being pruned. So off we * * go to a recursive search/quiescence call to work our * * way toward a terminal node. * * * * There are a couple of special-cases to handle. If the * * depth was reduced, and Search() returns a value >= * * beta, accepting that is risky (we reduced the move as * * we thought it was bad and expected it to fail low) so * * we repeat the search using the original (non-reduced) * * depth to see if the fail-high happens again. * * * * The other special-case is a result of the PVS idea and * * is again a result of a fail-high. Since we often * * narrow the window in PVS, if we narrow it at this ply * * and get a fail-high, we have to open it back up and * * repeat the search to see if it really fails high. In * * most searches this is not done since we almost always * * reach this point with alpha = beta-1 so that there is * * no widening required. * * * ************************************************************ */ if (depth + extensions - 1 > 0) { value = -Search(tree, -t_beta, -alpha, Flip(wtm), depth + extensions - 1, ply + 1, DO_NULL); if (value > alpha && extensions < 0) value = -Search(tree, -t_beta, -alpha, Flip(wtm), depth - 1, ply + 1, DO_NULL); } else value = -Quiesce(tree, -t_beta, -alpha, Flip(wtm), ply + 1, 1); if (abort_search || tree->stop) break; /* ************************************************************ * * * This is the PVS re-search code. If we reach this * * point and value > alpha and value < beta, then this * * can not be a null-window search. We have to re-search * * the position with the original beta value (not alpha+1 * * as is the usual case in PVS) to see if it still fails * * high before we treat this as a real fail-high and back * * up the value to the previous ply. * * * ************************************************************ */ if (value > alpha && value < beta && moves_searched) { extensions = Max(extensions, 0); if (depth + extensions - 1 > 0) value = -Search(tree, -beta, -alpha, Flip(wtm), depth + extensions - 1, ply + 1, DO_NULL); else value = -Quiesce(tree, -beta, -alpha, Flip(wtm), ply + 1, 1); if (abort_search || tree->stop) break; } if (ply == 1) root_moves[tree->root_move].nodes = tree->nodes_searched - begin_root_nodes; /* ************************************************************ * * * Search (and/or re-search) has been completed. Now we * * check for a fail high which terminates the search * * immediately as no further searching is required. * * * * Subtle code: If ply == 1, we call Output() which will * * dump the new PV. But it also backs up the PV to ply=0 * * which tells us which move to make. We often time-out * * in the last iteration of a search and don't complete * * it. We can't back up partial results, since they are * * not accurate, but when we dump a PV, it is from a full * * search and it is safe to back it up to the root PV. * * * ************************************************************ */ if (value > alpha) { if (ply == 1) { Output(tree, value, beta); root_value = alpha; } if (value >= beta) { Killer(tree, ply, tree->curmv[ply]); UnmakeMove(tree, ply, tree->curmv[ply], wtm); HashStore(tree, ply, depth, wtm, LOWER, value, tree->curmv[ply]); tree->fail_high++; if (!moves_searched) tree->fail_high_first++; return (value); } alpha = value; } if (ply == 1) root_value = alpha; t_beta = alpha + 1; moves_searched++; } while (0); UnmakeMove(tree, ply, tree->curmv[ply], wtm); if (abort_search || tree->stop) return (0); /* ************************************************************ * * * If this is an SMP search, and we have idle processors, * * now is the time to get them involved. We have now * * satisfied the "young brothers wait" condition since we * * have searched one move. All that is left is to check * * the size of the tree we have searched so far, so that * * we do not split too near the tips and drive up the * * overhead unacceptably. This has the additional effect * * that we might split after 2-3 moves have been searched * * which might sound like an issue, but the overhead is * * not so critical if we are more certain that we need to * * actually search every move. The more moves we have * * searched, the greater the probability that we are * * going to search them all. * * * ************************************************************ */ #if (CPUS > 1) if (smp_idle && moves_searched && tree->nodes_searched - start_nodes > smp_split_nodes && (ply > 1 || (smp_split_at_root && NextRootMoveParallel()))) { tree->alpha = alpha; tree->beta = beta; tree->value = alpha; tree->wtm = wtm; tree->ply = ply; tree->depth = depth; tree->moves_searched = moves_searched; if (Thread(tree)) { if (abort_search || tree->stop) return (0); if (tree->thread_id == 0 && CheckInput()) Interrupt(ply); value = tree->search_value; if (value > alpha) { if (value >= beta) { Killer(tree, ply, tree->cutmove); HashStore(tree, ply, depth, wtm, LOWER, value, tree->cutmove); tree->fail_high++; return (value); } alpha = value; break; } } } #endif } /* ************************************************************ * * * All moves have been searched. If none were legal, * * return either MATE or DRAW depending on whether the * * side to move is in check or not. * * * * Subtle code warning. "abort_after_ply1" is used to * * avoid aborting the search in the middle of searching * * any ply-1 move that has already been started. Once we * * reach the target time, the abort_after_ply1 flag is * * set so that any call to NextRootMove() will return a * * value of NONE, but we don't abort the other searches. * * Once all pending root moves are completed, we get to * * this point. Any time we find abort_search or stop set * * we have to get out without backing up anything. And * * at the root, we have the additional escape where we * * find abort_after_ply1 set and we still do not want to * * overwrite anything that has not already been backed up * * when we called Output() to display a new PV (if we did * * this.) * * * * When we reach time_limit abort_after_ply1 gets set to * * 1 and after all currently pending ply-1 moves are * * finished, we get here. If any produced a new best * * move, Output() has already backed the PV up to the * * ply=0 PV so we are done. If we go over the absolute * * max time limit, abort_search is set and that will * * immediately terminate the search and get us to this * * point. Finally, if we are in a parallel search and * * some node above this one in the tree finds a fail high * * condition, that thread will set the stop flag for any * * threads working below that node, and again we get to * * this point and do not want to back up anything. * * * ************************************************************ */ if (abort_search || tree->stop || (ply == 1 && abort_after_ply1)) return (0); if (moves_searched == 0) { value = (Check(wtm)) ? -(MATE - ply) : DrawScore(wtm); if (value >= alpha && value < beta) { SavePV(tree, ply, 0); #if defined(TRACE) if (ply <= trace_level) printf("Search() no moves! ply=%d\n", ply); #endif } return (value); } else { int bestmove, type; bestmove = (alpha == o_alpha) ? first_tried : tree->pv[ply].path[ply]; type = (alpha == o_alpha) ? UPPER : EXACT; if (repeat == 2 && alpha != -(MATE - ply - 1)) { value = DrawScore(wtm); if (value < beta) SavePV(tree, ply, 0); #if defined(TRACE) if (ply <= trace_level) printf("draw by repetition detected, ply=%d.\n", ply); #endif return (value); } else if (alpha != o_alpha) { memcpy(&tree->pv[ply - 1].path[ply], &tree->pv[ply].path[ply], (tree->pv[ply].pathl - ply) * sizeof(int)); memcpy(&tree->pv[ply - 1].pathh, &tree->pv[ply].pathh, 3); tree->pv[ply - 1].path[ply - 1] = tree->curmv[ply - 1]; Killer(tree, ply, tree->pv[ply].path[ply]); } HashStore(tree, ply, depth, wtm, type, alpha, bestmove); return (alpha); } } /* last modified 08/24/10 */ /* ******************************************************************************* * * * SearchParallel() is the recursive routine used to implement alpha/beta * * negamax search using parallel threads. When this code is called, the * * first move has already been searched, so all that is left is to search * * the remainder of the moves and then return. Note that the hash table and * * such can't be modified here since this only represents a part of the * * search at this ply. All of that is deferred until we return and reach * * the original instance of Search() where we have the complete results from * * all the threads that are helping here. * * * ******************************************************************************* */ int SearchParallel(TREE * RESTRICT tree, int alpha, int beta, int value, int wtm, int depth, int ply) { BITBOARD begin_root_nodes; int extensions; /* ************************************************************ * * * Continue iterating through the move list and search * * the resulting positions. Note that Search() culls any * * move that is not legal by using Check(). Since this * * proceeding in parallel, we use the lock in the parent * * split-block so that all threads searching for that * * parent thread use the same move list and synchronize * * selecting the next move using the parent's lock. * * * ************************************************************ */ while (1) { Lock(tree->parent->lock); if (ply == 1) { tree->phase[ply] = NextRootMove(tree->parent, tree, wtm); tree->root_move = tree->parent->root_move; } else tree->phase[ply] = (tree->inchk[ply]) ? NextEvasion((TREE *) tree->parent, ply, wtm) : NextMove((TREE *) tree->parent, ply, wtm); tree->curmv[ply] = tree->parent->curmv[ply]; Unlock(tree->parent->lock); if (!tree->phase[ply]) break; #if defined(TRACE) if (ply <= trace_level) Trace(tree, ply, depth, wtm, alpha, beta, "SearchParallel", tree->phase[ply]); #endif MakeMove(tree, ply, tree->curmv[ply], wtm); tree->nodes_searched++; if (tree->inchk[ply] || !Check(wtm)) do { /* ************************************************************ * * * If the move to be made checks the opponent, then we * * need to remember that he's in check and also extend * * the depth by one ply for him to get out. Note that if * * the move gives check, it is not a candidate for either * * depth reduction or forward-pruning. * * * ************************************************************ */ begin_root_nodes = tree->nodes_searched; extensions = 0; if (Check(Flip(wtm))) { tree->inchk[ply + 1] = 1; if (SwapO(tree, tree->curmv[ply], wtm) <= 0) { tree->extensions_done++; extensions = check_depth; } } else tree->inchk[ply + 1] = 0; /* ************************************************************ * * * Now it's time to try to reduce the search depth if the * * move appears to be "poor". To reduce the search, the * * following requirements must be met: * * * * (1) We must be in the REMAINING_MOVES part of the move * * ordering, so that we have nearly given up on * * failing high on any move. * * (2) We must not be too close to the horizon (this is * * the LMR_remaining_depth value). * * (3) The current move must not be a checking move and * * the side to move can not be in check; * * (4) The moving piece is not a passed pawn; * * (5) The current move can not affect the material * * balance, that is it can not be a capture or pawn * * promotion; * * * ************************************************************ */ if (tree->phase[ply] == REMAINING_MOVES && !tree->inchk[ply] && !extensions) { if ((Piece(tree->curmv[ply]) != pawn || mask_passed[wtm][To(tree->curmv[ply])] & Pawns(Flip(wtm)))) { extensions = Min(-Min(depth - 1 - LMR_remaining_depth, (tree->parent->moves_searched > 2) ? LMR_max_reduction : LMR_min_reduction), 0); if (extensions) tree->reductions_done++; } /* ************************************************************ * * * Now for the forward-pruning stuff. The idea here is * * based on the old FUTILITY idea, where if the current * * material + a fudge factor is lower than alpha, then * * there is little promise in searching this move to make * * up for the material deficit. * * * * We have an array of pruning margin values that are * * indexed by depth (remaining plies left until we drop * * into the quiescence search) and which increase with * * depth since more depth means a greater chance of * * bringing the score back up to alpha or beyond. If the * * current material + the bonus is less than alpha, we * * simply avoid searching this move at all, and skip to * * the next move without expending any more effort. Note * * that this is classic forward-pruning and can certainly * * introduce errors into the search. However, cluster * * testing has shown that this improves play in real * * games. The current implementation only prunes in the * * last 4 plies before quiescence, although this can be * * tuned with the "eval" command changing the "pruning * * depth" value to something other than 5 (test is for * * depth < pruning depth, current value is 5 which prunes * * in last 4 plies only). Testing shows no benefit in * * larger values than 5, although this might change in * * future versions as other things are modified. * * * ************************************************************ */ if (depth < pruning_depth && MaterialSTM(wtm) + pruning_margin[depth] <= alpha) { tree->moves_pruned++; continue; } } /* ************************************************************ * * * We have determined whether the depth is to be changed * * by an extension or a reduction. If we get to this * * point, then the move is not being pruned. So off we * * go to a recursive search/quiescence call to work our * * way toward a terminal node. * * * * If we reduce the search, and it fails high, we first * * do a verification search by using the original depth * * (no reduction) to see if it also fails high. If not, * * we ignore the fail high and continue the search. * * * ************************************************************ */ if (depth + extensions - 1 > 0) { value = -Search(tree, -alpha - 1, -alpha, Flip(wtm), depth + extensions - 1, ply + 1, DO_NULL); if (value > alpha && extensions < 0) value = -Search(tree, -alpha - 1, -alpha, Flip(wtm), depth - 1, ply + 1, DO_NULL); } else value = -Quiesce(tree, -alpha - 1, -alpha, Flip(wtm), ply + 1, 1); if (abort_search || tree->stop) break; /* ************************************************************ * * * This is the PVS re-search code. If we reach this * * point and value > alpha and value < beta, then this * * can not be a null-window search. We have to re-search * * the position with the original beta value (not alpha+1 * * as is the usual case in PVS) to see if it still fails * * high before we treat this as a real fail-high and back * * up the value to the previous ply. * * * ************************************************************ */ if (value > alpha && value < beta) { extensions = Max(extensions, 0); if (depth + extensions - 1 > 0) value = -Search(tree, -beta, -alpha, Flip(wtm), depth + extensions - 1, ply + 1, DO_NULL); else value = -Quiesce(tree, -beta, -alpha, Flip(wtm), ply + 1, 1); if (abort_search || tree->stop) break; } /* ************************************************************ * * * Now we check for an undesirable case, that of failing * * high while doing a parallel (threaded) search. This * * means our 'helpers' are doing stuff that is not needed * * so we 'stop' them now. * * * * Split-blocks are linked together so that we can walk * * this tree and terminate anyone helping us at this * * point in the tree, and also anyone helping at some * * deeper point on one of the sub-trees below this split * * point. This stops anyone working on any subtree that * * has the current split point as an ancestor node. * * * ************************************************************ */ if (ply == 1) root_moves[tree->root_move].nodes = tree->nodes_searched - begin_root_nodes; if (value > alpha) { alpha = value; if (ply == 1) { Lock(lock_root); if (value > root_value) { Output(tree, value, beta); root_value = value; } Unlock(lock_root); } if (value >= beta) { int proc; parallel_aborts++; UnmakeMove(tree, ply, tree->curmv[ply], wtm); Lock(lock_smp); Lock(tree->parent->lock); if (!tree->stop) { for (proc = 0; proc < smp_max_threads; proc++) if (tree->parent->siblings[proc] && proc != tree->thread_id) ThreadStop(tree->parent->siblings[proc]); } Unlock(tree->parent->lock); Unlock(lock_smp); return (alpha); } } tree->parent->moves_searched++; } while (0); UnmakeMove(tree, ply, tree->curmv[ply], wtm); if (abort_search || tree->stop) break; } /* ************************************************************ * * * There are no "end-of-search" things to do. We have * * searched all the remaining moves at this ply in * * parallel, and now return and let the original search * * (that started this sub-tree) clean up, and do the * * tests for mate/stalemate, update the hash table, etc. * * * * We do need to flag the root move we tried to search, * * if we were stopped early due to another root move * * failing high. Otherwise this move appears to have * * been searched already and will not be searched again * * until the next iteration. * * * ************************************************************ */ if (tree->stop && ply == 1) root_moves[tree->root_move].status &= 4095 - 256; return (alpha); } crafty-23.4/iterate.c0000644000175000017500000006110011466036157014124 0ustar oliveroliver#include "chess.h" #include "data.h" #include "epdglue.h" #if defined(UNIX) || defined(AMIGA) # include # include #endif /* last modified 06/07/09 */ /* ******************************************************************************* * * * Iterate() is the routine used to drive the iterated search. It * * repeatedly calls search, incrementing the search depth after each call, * * until either time is exhausted or the maximum set search depth is * * reached. * * * ******************************************************************************* */ int Iterate(int wtm, int search_type, int root_list_done) { ROOT_MOVE temp; int i; int value = 0, twtm; int correct, correct_count, material = 0, sorted; char *fh_indicator, *fl_indicator; TREE *const tree = block[0]; int savevalue = 0; PATH savepv; int print_ok = 0; #if (CPUS > 1) pthread_t pt; #endif /* ************************************************************ * * * Produce the root move list, which is ordered and kept * * for the duration of this search (the order may change * * as new best moves are backed up to the root of course.) * * * ************************************************************ */ tree->curmv[0] = 0; if (wtm) { draw_score[0] = -abs_draw_score; draw_score[1] = abs_draw_score; } else { draw_score[0] = abs_draw_score; draw_score[1] = -abs_draw_score; } #if defined(NODES) temp_search_nodes = search_nodes; #endif abort_after_ply1 = 0; abort_search = 0; book_move = 0; program_start_time = ReadClock(); start_time = ReadClock(); elapsed_start = ReadClock(); root_wtm = wtm; kibitz_depth = 0; tree->nodes_searched = 0; tree->fail_high = 0; tree->fail_high_first = 0; parallel_splits = 0; parallel_aborts = 0; max_split_blocks = 0; if (booking || !Book(tree, wtm, root_list_done)) do { if (abort_search) break; if (!root_list_done) RootMoveList(wtm); #if !defined(NOEGTB) if (EGTB_draw && !puzzling && swindle_mode) EGTB_use = 0; else EGTB_use = EGTBlimit; if (EGTBlimit && !EGTB_use) Print(128, "Drawn at root, trying for swindle.\n"); #endif correct_count = 0; burp = 15 * 100; transposition_age = (transposition_age + 1) & 0x1ff; next_time_check = nodes_between_time_checks; tree->evaluations = 0; tree->egtb_probes = 0; tree->egtb_probes_successful = 0; tree->extensions_done = 0; tree->qchecks_done = 0; tree->reductions_done = 0; tree->moves_pruned = 0; root_wtm = wtm; /* ************************************************************ * * * If there are no legal moves, it is either mate or draw * * depending on whether the side to move is in check or * * not (mate or stalemate.) * * * ************************************************************ */ if (n_root_moves == 0) { program_end_time = ReadClock(); tree->pv[0].pathl = 0; tree->pv[0].pathd = 0; if (Check(wtm)) root_value = -(MATE - 1); else root_value = DrawScore(wtm); Print(6, " depth time score variation\n"); Print(6, " (no moves)\n"); tree->nodes_searched = 1; if (!puzzling) last_root_value = root_value; return (root_value); } /* ************************************************************ * * * Now set the search time and iteratively call Search() * * to analyze the position deeper and deeper. Note that * * Search() is called with an alpha/beta window roughly * * 1/3 of a pawn on either side of the score last * * returned by search. Also, after the first root move * * is searched, this window is collapsed to n and n+1 * * (where n is the value for the first root move.) Often * * a better move is found, which causes search to return * * as the score. We then relax beta depending on * * its value: if beta = alpha+1, set beta to alpha+1/3 * * of a pawn; if beta = alpha+1/3 pawn, then set beta to * * + infinity. * * * ************************************************************ */ TimeSet(tree, search_type); iteration_depth = 1; if (last_pv.pathd > 1) iteration_depth = last_pv.pathd + 1; Print(6, " depth time score variation (%d)\n", iteration_depth); abort_after_ply1 = 0; abort_search = 0; /* ************************************************************ * * * Set the initial search bounds based on the last search * * or default values. * * * ************************************************************ */ tree->pv[0] = last_pv; if (iteration_depth > 1) { root_alpha = last_value - 40; root_value = last_value - 40; root_beta = last_value + 40; } else { root_alpha = -MATE - 1; root_value = -MATE - 1; root_beta = MATE + 1; } /* ************************************************************ * * * If we are using multiple threads, and they have not * * been started yet, then start them now as the search * * is ready to begin. * * * ************************************************************ */ #if (CPUS > 1) if (smp_max_threads > smp_idle + 1) { long proc; initialized_threads = 1; Print(128, "starting thread"); for (proc = smp_threads + 1; proc < smp_max_threads; proc++) { Print(128, " %d", proc); thread[proc] = 0; # if defined(_WIN32) || defined(_WIN64) NumaStartThread(ThreadInit, (void *) proc); # else pthread_create(&pt, &attributes, ThreadInit, (void *) proc); # endif Lock(lock_smp); smp_threads++; Unlock(lock_smp); } Print(128, " \n"); } WaitForAllThreadsInitialized(); #endif root_print_ok = 0; if (search_nodes) nodes_between_time_checks = search_nodes; for (; iteration_depth <= MAXPLY - 5; iteration_depth++) { /* ************************************************************ * * * Now install the old PV into the hash table so that * * these moves will be followed first. * * * ************************************************************ */ twtm = wtm; for (i = 1; i < (int) tree->pv[0].pathl; i++) { if (!VerifyMove(tree, i, twtm, tree->pv[0].path[i])) { Print(4095, "ERROR, not installing bogus pv info at ply=%d\n", i); Print(4095, "not installing from=%d to=%d piece=%d\n", From(tree->pv[0].path[i]), To(tree->pv[0].path[i]), Piece(tree->pv[0].path[i])); Print(4095, "pathlen=%d\n", tree->pv[0].pathl); break; } HashStorePV(tree, twtm, tree->pv[0].path[i]); MakeMove(tree, i, tree->pv[0].path[i], twtm); twtm = Flip(twtm); } for (i--; i > 0; i--) { twtm = Flip(twtm); UnmakeMove(tree, i, tree->pv[0].path[i], twtm); } /* ************************************************************ * * * Now we call SearchRoot() and start the next search * * iteration. We already have solid alpha/beta bounds * * set up for the aspiration search. When each iteration * * completes, these aspiration values are recomputed and * * used for the next iteration. * * * ************************************************************ */ if (trace_level) { printf("==================================\n"); printf("= search iteration %2d =\n", iteration_depth); printf("==================================\n"); } if (tree->nodes_searched) { nodes_between_time_checks = nodes_per_second / 10; if (!analyze_mode) { if (time_limit > 300); else if (time_limit > 50) nodes_between_time_checks /= 10; else nodes_between_time_checks /= 100; } else nodes_between_time_checks = Min(nodes_per_second, 100000); nodes_between_time_checks = Max(nodes_between_time_checks, 10000); } if (search_nodes) nodes_between_time_checks = search_nodes - tree->nodes_searched; nodes_between_time_checks = Min(nodes_between_time_checks, MAX_TC_NODES); while (1) { thread[0] = block[0]; for (i = 0; i < n_root_moves; i++) if (!(root_moves[i].status & 256)) break; root_moves[i].status &= 4095 - 128; tree->inchk[1] = Check(wtm); value = Search(tree, root_alpha, root_beta, wtm, iteration_depth, 1, 0); root_print_ok = tree->nodes_searched > noise_level; if (abort_search || abort_after_ply1) break; /* ************************************************************ * * * Check for the case where we get a score back that is * * greater than or equal to beta. This is called a fail * * high condition and requires a re-search with a better * * (more optimistic) beta value so that we can discover * * just how good this move really is. * * * ************************************************************ */ if (value >= root_beta) { if (!(root_moves[0].status & 8)) root_moves[0].status |= 8; else if (!(root_moves[0].status & 16)) root_moves[0].status |= 16; else root_moves[0].status |= 32; root_alpha = SetRootAlpha(root_moves[0].status, root_alpha); root_value = root_alpha; root_beta = SetRootBeta(root_moves[0].status, root_beta); root_moves[0].status &= 4095 - 256; root_moves[0].nodes = 0; if (root_print_ok) { if (wtm) { fh_indicator = "+1"; if (root_moves[0].status & 16) fh_indicator = "+3"; if (root_moves[0].status & 32) fh_indicator = "+M"; } else { fh_indicator = "-1"; if (root_moves[0].status & 16) fh_indicator = "-3"; if (root_moves[0].status & 32) fh_indicator = "-M"; } Print(2, " %2i %s %2s ", iteration_depth, DisplayTime(end_time - start_time), fh_indicator); if (display_options & 64) Print(2, "%d. ", move_number); if ((display_options & 64) && !wtm) Print(2, "... "); Print(2, "%s! \n", OutputMove(tree, tree->pv[1].path[1], 1, wtm)); kibitz_text[0] = 0; if (display_options & 64) sprintf(kibitz_text, " %d.", move_number); if ((display_options & 64) && !wtm) sprintf(kibitz_text + strlen(kibitz_text), " ..."); sprintf(kibitz_text + strlen(kibitz_text), " %s!", OutputMove(tree, tree->pv[1].path[1], 1, wtm)); Kibitz(6, wtm, iteration_depth, end_time - start_time, value, tree->nodes_searched, tree->egtb_probes_successful, kibitz_text); } /* ************************************************************ * * * Check for the case where we get a score back that is * * less than or equal to alpha. This is called a fail * * low condition and requires a re-search with a better * * more pessimistic)) alpha value so that we can discover * * just how bad this move really is. * * * ************************************************************ */ } else if (value <= root_alpha) { if (!(root_moves[0].status & 0x38)) { if (!(root_moves[0].status & 1)) root_moves[0].status |= 1; else if (!(root_moves[0].status & 2)) root_moves[0].status |= 2; else root_moves[0].status |= 4; root_alpha = SetRootAlpha(root_moves[0].status, root_alpha); root_value = root_alpha; root_beta = SetRootBeta(root_moves[0].status, root_beta); root_moves[0].status &= 4095 - 256; root_moves[0].nodes = 0; easy_move = 0; if (root_print_ok && !abort_after_ply1 && !abort_search) { if (wtm) { fl_indicator = "-1"; if (root_moves[0].status & 2) fl_indicator = "-3"; if (root_moves[0].status & 4) fl_indicator = "-M"; } else { fl_indicator = "+1"; if (root_moves[0].status & 2) fl_indicator = "+3"; if (root_moves[0].status & 4) fl_indicator = "+M"; } Print(4, " %2i %s %2s ", iteration_depth, DisplayTime(ReadClock() - start_time), fl_indicator); if (display_options & 64) Print(4, "%d. ", move_number); if ((display_options & 64) && !wtm) Print(4, "... "); Print(4, "%s? \n", OutputMove(tree, root_moves[0].move, 1, wtm)); } } else break; } else break; } if (root_value > root_alpha && root_value < root_beta) last_root_value = root_value; /* ************************************************************ * * * If we are running a test suite, check to see if we can * * exit the search. This happens when N successive * * iterations produce the correct solution. N is set by * * the test command in Option(). * * * ************************************************************ */ correct = solution_type; for (i = 0; i < number_of_solutions; i++) { if (!solution_type) { if (solutions[i] == tree->pv[0].path[1]) correct = 1; } else if (solutions[i] == tree->pv[0].path[1]) correct = 0; } if (correct) correct_count++; else correct_count = 0; /* ************************************************************ * * * If the search terminated normally, then dump the PV * * and search statistics (we don't do this if the search * * aborts because the opponent doesn't make the predicted * * move...) * * * ************************************************************ */ twtm = wtm; end_time = ReadClock(); do { sorted = 1; for (i = 1; i < n_root_moves - 1; i++) { if (root_moves[i].nodes < root_moves[i + 1].nodes) { temp = root_moves[i]; root_moves[i] = root_moves[i + 1]; root_moves[i + 1] = temp; sorted = 0; } } } while (!sorted); /* ************************************************************ * * * Notice if there are multiple moves that are producing * * large trees. If so, don't search those in parallel by * * setting the flag to avoid this. We also don't reduce * * the first 1/4 of the root moves, period, since after * * deep searches, several of them might have been best * * moves in previous iterations and need a chance to pop * * back to the top again. * * * ************************************************************ */ for (i = 0; i < n_root_moves; i++) root_moves[i].status = 0; root_moves[0].status |= 64 + 128; if (root_moves[0].nodes >= 3) for (i = 0; i < n_root_moves; i++) { if (i < Min(n_root_moves, 16) && root_moves[i].nodes > root_moves[0].nodes / 3) root_moves[i].status |= 64; if (i < n_root_moves / 4) root_moves[i].status |= 128; } /* ************************************************************ * * * If requested, print the ply=1 move list along with the * * node counts for the tree each move produced. * * * ************************************************************ */ if (display_options & 256) { BITBOARD total_nodes = 0; Print(128, " move nodes R/P\n"); for (i = 0; i < n_root_moves; i++) { total_nodes += root_moves[i].nodes; Print(128, " %10s " BMF10 " %d %d\n", OutputMove(tree, root_moves[i].move, 1, wtm), root_moves[i].nodes, (root_moves[i].status & 128) == 0, (root_moves[i].status & 64) == 0); } Print(256, " total " BMF10 "\n", total_nodes); } for (i = 0; i < n_root_moves; i++) root_moves[i].nodes = 0; if (end_time - start_time > 10) nodes_per_second = tree->nodes_searched * 100 / (BITBOARD) (end_time - start_time); else nodes_per_second = 1000000; if (!abort_after_ply1 && !abort_search && value != -(MATE - 1)) { if (root_print_ok) { DisplayPV(tree, 5, wtm, end_time - start_time, value, &tree->pv[0]); } else { savevalue = value; savepv = tree->pv[0]; print_ok = 1; } } root_alpha = value - 40; root_value = root_alpha; root_beta = value + 40; if (iteration_depth > 3 && value > MATE - 300 && value >= (MATE - iteration_depth - 1) && value > last_mate_score) break; if ((iteration_depth >= search_depth) && search_depth) break; if (abort_after_ply1 || abort_search) break; end_time = ReadClock() - start_time; if (thinking && (int) end_time >= time_limit) break; if (correct_count >= early_exit) break; #if !defined(NOEGTB) if (iteration_depth > 3 && TotalAllPieces <= EGTBlimit && EGTB_use && !EGTB_search && EGTBProbe(tree, 1, wtm, &i)) break; #endif if (search_nodes && tree->nodes_searched >= search_nodes) break; } /* ************************************************************ * * * Search done, now display statistics, depending on the * * verbosity level set. * * * ************************************************************ */ end_time = ReadClock(); elapsed_end = ReadClock() - elapsed_start; if (elapsed_end > 10) nodes_per_second = (BITBOARD) tree->nodes_searched * 100 / (BITBOARD) elapsed_end; if (!root_print_ok && print_ok) { root_print_ok = 1; DisplayPV(tree, 5, wtm, end_time - start_time, savevalue, &savepv); } tree->evaluations = (tree->evaluations) ? tree->evaluations : 1; if ((!abort_search || abort_after_ply1) && !puzzling) { tree->fail_high++; tree->fail_high_first++; material = Material / PieceValues(white, pawn); Print(8, " time=%s mat=%d", DisplayTimeKibitz(end_time - start_time), material); Print(8, " n=" BMF, tree->nodes_searched); Print(8, " fh=%u%%", (int) ((BITBOARD) tree->fail_high_first * 100 / (BITBOARD) tree->fail_high)); Print(8, " nps=%s\n", DisplayKM(nodes_per_second)); Print(16, " extensions=%s ", DisplayKM(tree->extensions_done)); Print(16, "qchecks=%s ", DisplayKM(tree->qchecks_done)); Print(16, "reduced=%s ", DisplayKM(tree->reductions_done)); Print(16, "pruned=%s\n", DisplayKM(tree->moves_pruned)); Print(16, " predicted=%d evals=%s 50move=%d", predicted, DisplayKM(tree->evaluations), Rule50Moves(0)); Print(16, " EGTBprobes=%s hits=%s\n", DisplayKM(tree->egtb_probes), DisplayKM(tree->egtb_probes_successful)); Print(16, " SMP-> splits=%d aborts=%d data=%d/%d ", parallel_splits, parallel_aborts, max_split_blocks, MAX_BLOCKS); Print(16, "elap=%s\n", DisplayTimeKibitz(elapsed_end)); } } while (0); else { last_root_value = 0; root_value = 0; book_move = 1; tree->pv[0] = tree->pv[1]; if (analyze_mode) Kibitz(4, wtm, 0, 0, 0, 0, 0, kibitz_text); } if (smp_nice && ponder == 0 && smp_threads) { int proc; Print(128, "terminating SMP processes.\n"); for (proc = 1; proc < CPUS; proc++) thread[proc] = (TREE *) - 1; while (smp_threads); smp_idle = 0; } program_end_time = ReadClock(); search_move = 0; if (quit) CraftyExit(0); return (last_root_value); } /* ******************************************************************************* * * * SetRootAlpha() is used to set the root alpha value by looking at the move * * status to see how many times this move has failed low. The first fail * * drops alpha by -1.0. The second fail low drops it by another -2.0, and * * the third fail low drops it to -infinity. * * * ******************************************************************************* */ int SetRootAlpha(unsigned char status, int old_root_alpha) { if (status & 4) return (-MATE - 1); if (status & 2) return (old_root_alpha - 200); if (status & 1) return (old_root_alpha - 100); return (old_root_alpha); } /* ******************************************************************************* * * * SetRootBeta() is used to set the root beta value by looking at the move * * status to see how many times this move has failed high. The first fail * * raises alpha by 1.0. The second fail raises it by another 2.0, and * * the third fail raises it to +infinity. * * * ******************************************************************************* */ int SetRootBeta(unsigned char status, int old_root_beta) { if (status & 32) return (MATE + 1); if (status & 16) return (old_root_beta + 200); if (status & 8) return (old_root_beta + 100); return (old_root_beta); } crafty-23.4/test.c0000644000175000017500000003256411466036157013462 0ustar oliveroliver#include "chess.h" #include "data.h" /* last modified 01/17/09 */ /* ******************************************************************************* * * * Test() is used to test the program against a suite of test positions to * * measure its performance on a particular machine, or to evaluate its skill * * after modifying it in some way. * * * * The test is initiated by using the "test " command to read in * * the suite of problems from file . The format of this file is * * as follows: * * * * Setboard : This sets the board position using the usual * * forsythe notation (see module SetBoard() in setc for a full ex- * * planation of the syntax). * * * * Solution ... : this provides a solution move (or * * set of solution moves if more than one is correct). If the search finds * * one of these moves, then the prblem is counted as correct, otherwise it * * is counted wrong. * * * * After reading these two lines, the program then searches to whatever time * * or depth limit has been set, when it reaches the end-of-file condition or * * when it reads a record containing the string "end" it then displays the * * number correct and the number missed. * * * ******************************************************************************* */ void Test(char *filename) { FILE *test_input; int i, move, right = 0, wrong = 0, correct; int time = 0, len; BITBOARD nodes = 0; char *eof, *delim; float avg_depth = 0.0; TREE *const tree = block[0]; /* ************************************************************ * * * Read in the position and then the solutions. After * * executing a search to find the best move (according * * to the program, anyway) compare it against the list * * of solutions and count it right or wrong. * * * ************************************************************ */ if (!(test_input = fopen(filename, "r"))) { printf("file %s does not exist.\n", filename); return; } Print(4095, "\n"); eof = fgets(buffer, 4096, test_input); if (strstr(buffer, "title")); else { fclose(test_input); TestEPD(filename); return; } if (book_file) { fclose(book_file); book_file = 0; } if (books_file) { fclose(books_file); books_file = 0; } while (1) { if (eof) { delim = strchr(buffer, '\n'); if (delim) *delim = 0; delim = strchr(buffer, '\r'); if (delim) *delim = ' '; } else break; nargs = ReadParse(buffer, args, " ;"); if (!strcmp(args[0], "end")) break; else if (!strcmp(args[0], "title")) { Print(4095, "==============================================" "========================\n"); Print(4095, "! "); len = 0; for (i = 1; i < nargs; i++) { Print(4095, "%s ", args[i]); len += strlen(args[i]) + 1; if (len > 65) break; } for (i = len; i < 67; i++) printf(" "); Print(4095, "!\n"); Print(4095, "==============================================" "========================\n"); } else if (strcmp(args[0], "solution")) { Option(tree); } else { number_of_solutions = 0; solution_type = 0; Print(4095, "solution "); for (i = 1; i < nargs; i++) { if (args[i][strlen(args[i]) - 1] == '?') { solution_type = 1; args[i][strlen(args[i]) - 1] = '\0'; } else if (*(args + i)[strlen(args[i]) - 1] == '!') { solution_type = 0; args[i][strlen(args[i]) - 1] = '\0'; } move = InputMove(tree, args[i], 0, wtm, 0, 0); if (move) { solutions[number_of_solutions] = move; Print(4095, "%d. %s", (number_of_solutions++) + 1, OutputMove(tree, move, 0, wtm)); if (solution_type == 1) Print(4095, "? "); else Print(4095, " "); } else DisplayChessBoard(stdout, tree->pos); } Print(4095, "\n"); InitializeHashTables(); last_pv.pathd = 0; thinking = 1; tree->position[1] = tree->position[0]; (void) Iterate(wtm, think, 0); thinking = 0; nodes += tree->nodes_searched; avg_depth += (float) iteration_depth; time += (end_time - start_time); correct = solution_type; for (i = 0; i < number_of_solutions; i++) { if (!solution_type) { if (solutions[i] == tree->pv[1].path[1]) correct = 1; } else if (solutions[i] == tree->pv[1].path[1]) correct = 0; } if (correct) { right++; Print(4095, "----------------------> solution correct (%d/%d).\n", right, right + wrong); } else { wrong++; Print(4095, "----------------------> solution incorrect (%d/%d).\n", right, right + wrong); } } eof = fgets(buffer, 4096, test_input); } /* ************************************************************ * * * Now print the results. * * * ************************************************************ */ if (right + wrong) { Print(4095, "\n\n\n"); Print(4095, "test results summary:\n\n"); Print(4095, "total positions searched..........%12d\n", right + wrong); Print(4095, "number right......................%12d\n", right); Print(4095, "number wrong......................%12d\n", wrong); Print(4095, "percentage right..................%12d\n", right * 100 / (right + wrong)); Print(4095, "percentage wrong..................%12d\n", wrong * 100 / (right + wrong)); Print(4095, "total nodes searched..............%12llu\n", nodes); Print(4095, "average search depth..............%12.1f\n", avg_depth / (right + wrong)); Print(4095, "nodes per second..................%12d\n", nodes * 100 / Max(time, 1)); Print(4095, "total time........................%12s\n", DisplayTime(time)); } input_stream = stdin; early_exit = 99; } /* last modified 01/17/09 */ /* ******************************************************************************* * * * TestEPD() is used to test the program against a suite of test positions to* * measure its performance on a particular machine, or to evaluate its skill * * after modifying it in some way. * * * * The test is initiated by using the "test " command to read in * * the suite of problems from file . The format of this file is * * as follows: * * * * am/bm move1 move2 etc; title "xxx" * * * * Am means "avoid move" and bm means "best move". Each test position may * * have multiple moves to avoid or that are best, but both am and bm may not * * appear on one position. * * * * The title is just a comment that is given in the program output to make it* * easier to match output to specific positions. * * * ******************************************************************************* */ void TestEPD(char *filename) { FILE *test_input; int i, move, right = 0, wrong = 0, correct; int time = 0, len; BITBOARD nodes = 0; char *eof, *mvs, *title; float avg_depth = 0.0; TREE *const tree = block[0]; /* ************************************************************ * * * Read in the position and then the solutions. After * * executing a search to find the best move (according * * to the program, anyway) compare it against the list * * of solutions and count it right or wrong. * * * ************************************************************ */ if (!(test_input = fopen(filename, "r"))) { printf("file %s does not exist.\n", filename); return; } if (book_file) { fclose(book_file); book_file = 0; } if (books_file) { fclose(books_file); books_file = 0; } while (1) { eof = fgets(buffer, 4096, test_input); if (eof) { char *delim; delim = strchr(buffer, '\n'); if (delim) *delim = 0; delim = strchr(buffer, '\r'); if (delim) *delim = ' '; } else break; mvs = strstr(buffer, " bm "); if (!mvs) mvs = strstr(buffer, " am "); if (!mvs) { Print(4095, "Error am/bm field missing, input string follows\n%s\n", buffer); continue; } mvs++; title = strstr(buffer, "id"); *(mvs - 1) = 0; if (title) *(title - 1) = 0; if (title) { title = strchr(title, '\"') + 1; if (title) { if (strchr(title, '\"')) { *strchr(title, '\"') = 0; } } Print(4095, "==============================================" "========================\n"); Print(4095, "! "); Print(4095, "%s ", title); len = 66 - strlen(title); for (i = 0; i < len; i++) printf(" "); Print(4095, "!\n"); Print(4095, "==============================================" "========================\n"); } Option(tree); nargs = ReadParse(mvs, args, " ;"); number_of_solutions = 0; solution_type = 0; if (!strcmp(args[0], "am")) solution_type = 1; Print(4095, "solution "); for (i = 1; i < nargs; i++) { if (!strcmp(args[i], "c0")) break; move = InputMove(tree, args[i], 0, wtm, 0, 0); if (move) { solutions[number_of_solutions] = move; Print(4095, "%d. %s", (number_of_solutions++) + 1, OutputMove(tree, move, 0, wtm)); if (solution_type == 1) Print(4095, "? "); else Print(4095, " "); } else DisplayChessBoard(stdout, tree->pos); } Print(4095, "\n"); InitializeHashTables(); last_pv.pathd = 0; thinking = 1; tree->position[1] = tree->position[0]; (void) Iterate(wtm, think, 0); thinking = 0; nodes += tree->nodes_searched; avg_depth += (float) iteration_depth; time += (end_time - start_time); correct = solution_type; for (i = 0; i < number_of_solutions; i++) { if (!solution_type) { if (solutions[i] == tree->pv[1].path[1]) correct = 1; } else if (solutions[i] == tree->pv[1].path[1]) correct = 0; } if (correct) { right++; Print(4095, "----------------------> solution correct (%d/%d).\n", right, right + wrong); } else { wrong++; Print(4095, "----------------------> solution incorrect (%d/%d).\n", right, right + wrong); } } /* ************************************************************ * * * Now print the results. * * * ************************************************************ */ if (right + wrong) { Print(4095, "\n\n\n"); Print(4095, "test results summary:\n\n"); Print(4095, "total positions searched..........%12d\n", right + wrong); Print(4095, "number right......................%12d\n", right); Print(4095, "number wrong......................%12d\n", wrong); Print(4095, "percentage right..................%12d\n", right * 100 / (right + wrong)); Print(4095, "percentage wrong..................%12d\n", wrong * 100 / (right + wrong)); Print(4095, "total nodes searched..............%12llu\n", nodes); Print(4095, "average search depth..............%12.1f\n", avg_depth / (right + wrong)); Print(4095, "nodes per second..................%12d\n", nodes * 100 / Max(1, time)); Print(4095, "total time........................%12s\n", DisplayTime(time)); } input_stream = stdin; early_exit = 99; } crafty-23.4/inline32.h0000644000175000017500000000414711466036157014127 0ustar oliveroliver/* Intel X86 inline functions for MSB(), LSB() and PopCnt(). Note that these are 32 bit functions and they use 32 bit (double-word) X86 instructions. */ /* *INDENT-OFF* */ int static __inline__ PopCnt(BITBOARD word) { /* r0=result, %1=tmp, %2=first input, %3=second input */ long dummy1, dummy2, dummy3, dummy4; asm(" xorl %0, %0 " "\n\t" " testl %2, %2 " "\n\t" " jz 2f " "\n\t" "1: leal -1(%2), %1 " "\n\t" " incl %0 " "\n\t" " andl %1, %2 " "\n\t" " jnz 1b " "\n\t" "2: testl %3, %3 " "\n\t" " jz 4f " "\n\t" "3: leal -1(%3), %1 " "\n\t" " incl %0 " "\n\t" " andl %1, %3 " "\n\t" " jnz 3b " "\n\t" "4: " "\n\t" : "=&q"(dummy1), "=&q"(dummy2), "=&q"(dummy3), "=&q"(dummy4) : "2"((int) (word >> 32)), "3"((int) word) : "cc"); return (dummy1); } int static __inline__ MSB(BITBOARD word) { int dummy1, dummy2, dummy3; asm(" bsr %1, %0 " "\n\t" " jnz 1f " "\n\t" " bsr %2, %0 " "\n\t" " jnz 2f " "\n\t" " movl $64, %0 " "\n\t" " jmp 2f " "\n\t" "1: addl $32,%0 " "\n\t" "2: " "\n\t" : "=&q"(dummy1), "=&q"(dummy2), "=&q"(dummy3) : "1"((int) (word >> 32)), "2"((int) word) : "cc"); return (dummy1); } int static __inline__ LSB(BITBOARD word) { int dummy1, dummy2, dummy3; asm(" bsf %2, %0 " "\n\t" " jnz 2f " "\n\t" " bsf %1, %0 " "\n\t" " jnz 1f " "\n\t" " movl $64, %0 " "\n\t" " jmp 2f " "\n\t" "1: addl $32,%0 " "\n\t" "2: " "\n\t" : "=&q"(dummy1), "=&q"(dummy2), "=&q"(dummy3) : "1"((int) (word >> 32)), "2"((int) word) : "cc"); return (dummy1); } /* *INDENT-ON* */ crafty-23.4/annotate.c0000644000175000017500000007776511466036157014330 0ustar oliveroliver#include "chess.h" #include "data.h" /* last modified 01/18/09 */ /* ******************************************************************************* * * * "annotate" command is used to search through the game in a pgn file, and * * provide a qualitative analysis of each move played and then creating a new * * output file (xxx.can) containing the original game + new commentary. * * * * The normal output of this command is a file, in PGN format, that contains * * the moves of the game, along with analysis when Crafty does not think that * * move was the best choice. The definition of "best choice" is somewhat * * vague, because if the move played is "close" to the best move available, * * Crafty will not comment on the move. "Close" is defined by the * * option explained below. This basic type of annotation works by first * * using the normal tree search algorithm to find the best move. If this * * move was the move played, no output is produced. If a different move is * * considered best, then the actual move played is searched to the same depth * * and if the best move and actual move scores are within of each * * other, no comment is produced, otherwise Crafty inserts the evaluation for * * the move played, followed by the eval and PV for the best continuation it * * found. You can enter suggested moves for Crafty to analyze at any point * * by simply entering a move as an analysis-type comment using (move) or * * {move}. Crafty will search that move in addition to the move actually * * played and the move it thinks is best. * * * * The format of the command is as follows: * * * * annotate filename b|w|bw|name moves margin time [n] * * * * Filename is the input file where Crafty will obtain the moves to annotate, * * and output will be written to file "filename.can". * * * * annotateh filename b|w|bw|name moves margin time [n] * * * * Can be used to produce an HTML-compatible file that includes bitmapped * * diagrams of the positions where Crafty provides analysis. This file can be* * opened by a browser to provide much easier 'reading'. * * * * annotatet filename b|w|bw|name moves margin time [n] * * * * Can be used to produce a LaTeX-compatible file that includes LaTeX chess * * fonts. This file can be read/printed by any program that can handle LaTeX * * input. * * * * Where b/w/bw indicates whether to annotate only the white side (w), the * * black side (b) or both (bw). You can also specify a name (or part of a * * name, just be sure it is unique in the name tags for clarity in who you * * mean). * * * * Moves indicates the move or moves to annotate. It can be a single move, * * which indicates the starting move number to annotate, or it can be a range,* * which indicates a range of move (1-999 gets the whole game.) * * * * Margin is the difference between Crafty's evaluation for the move actually * * played and for the move Crafty thinks is best, before Crafty will generate * * a comment in the annotation file. 1.0 is a pawn, and will only generate * * comments if the move played is 1.000 (1 pawn) worse than the best move * * found by doing a complete search. * * * * Time is time per move to search, in seconds. * * * * [n] is optional and tells Crafty to produce the PV/score for the "n" best * * moves. Crafty stops when the best move reaches the move played in the game* * or after displaying n moves, whichever comes first. If you use -n, then it* * will display n moves regardless of where the game move ranks. * * * ******************************************************************************* */ #define MIN_DECISIVE_ADV 150 #define MIN_MODERATE_ADV 70 #define MIN_SLIGHT_ADV 30 void Annotate() { FILE *annotate_in, *annotate_out; char text[128], tbuffer[4096], colors[32] = { "" }, pname[128] = { ""}; int annotate_margin, annotate_score[100], player_score, best_moves, annotate_wtm; int annotate_search_time_limit, search_player; int twtm, path_len, analysis_printed = 0; int wtm, move_num, line1, line2, move, suggested, i; int searches_done, read_status; PATH temp[100], player_pv; int temp_search_depth; TREE *const tree = block[0]; char html_br[5] = { "" }; int save_swindle_mode; int html_mode = 0; int latex = 0; /* ************************************************************ * * * First, extract the options from the command line to * * determine what the user wanted us to do. * * * ************************************************************ */ save_swindle_mode = swindle_mode; if (!strcmp(args[0], "annotateh")) { html_mode = 1; strcpy(html_br, "
"); } if (!strcmp(args[0], "annotatet")) { latex = 1; strcpy(html_br, "\\\\"); } strcpy(tbuffer, buffer); nargs = ReadParse(tbuffer, args, " ;"); if (nargs < 6) { printf ("usage: annotate