void Initialize(int continuing) { /* ---------------------------------------------------------- | | | perform routine initialization. | | | ---------------------------------------------------------- */ int i=0; InitializeZeroMasks(); InitializeMasks(); InitializeRandomHash(); InitializeAttackBoards(); InitializePawnMasks(); InitializePieceMasks(); InitializeChessBoard(&position[0]); #if defined(NT_i386) || defined(NT_AXP) _fmode = _O_BINARY; /* set global file mode to binary to avoid text translation */ #endif last[0]=move_list; if (continuing) { history_file=fopen("game.001","r+"); if (!log_file || !history_file) { printf("\nsorry. nothing to continue.\n\n"); history_file=fopen("game.001","w+"); } else { sprintf(buffer,"read game.001"); (void) Option(); } } else { history_file=fopen("game.001","w+"); } trans_ref_wa=(HASH_ENTRY*)malloc(16*hash_table_size); trans_ref_wb=(HASH_ENTRY*)malloc(16*2*hash_table_size); trans_ref_ba=(HASH_ENTRY*)malloc(16*hash_table_size); trans_ref_bb=(HASH_ENTRY*)malloc(16*2*hash_table_size); pawn_hash_table=(PAWN_HASH_ENTRY*)malloc(sizeof(PAWN_HASH_ENTRY)*pawn_hash_table_size); InitializeHashTables(); if (!trans_ref_wa || !trans_ref_wb || !trans_ref_ba || !trans_ref_bb ) { printf("malloc() failed, not enough memory.\n"); free(trans_ref_wa); free(trans_ref_wb); free(trans_ref_ba); free(trans_ref_bb); free(pawn_hash_table); hash_table_size=0; pawn_hash_table_size=0; log_hash=0; log_pawn_hash=0; trans_ref_wa=0; trans_ref_wb=0; trans_ref_ba=0; trans_ref_bb=0; pawn_hash_table=0; } hash_maska=(1<<log_hash)-1; hash_maskb=(1<<(log_hash+1))-1; pawn_hash_mask=((unsigned int) 037777777777)>>(32-log_pawn_hash); }
/* ******************************************************************************* * * * SetBoard() is used to set up the board in any position desired. It uses * * a forsythe-like string of characters to describe the board position. * * * * The standard piece codes p,n,b,r,q,k are used to denote the type of piece * * on a square, upper/lower case are used to indicate the side (program/opp.)* * of the piece. * * * * The pieces are entered with square a8 first, then b8, ... until the full * * 8th rank is completed. A "/" terminates that rank. This is repeated for * * each of the 8 ranks, with the last (1st) rank not needing a terminating * * "/". For empty squares, a number between 1 and 8 can be used to indicate * * the number of adjacent empty squares. * * * * That board description must be followed by a "b" or "w" to indicate which * * side is on move. * * * * Next, up to 4 characters are used to indicate which side can castle and * * to which side. An uppercase K means white can castle kingside, while a * * lowercase q means black can castle queenside. * * * * Finally, if there is an enpassant capture possible (the last move was a * * double pawn move and there was an enemy pawn that could capture it. The * * square is the square the capturing pawn ends up on. * * * * K2R/PPP////q/5ppp/7k/ b - - * * * * this assumes that k represents a white king and -q represents a black * * queen. * * * * k * * r * * * * * * p p p * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -q * * * * * * * * * * * * * * -p -p -p * * * * * * * * * -k * * * ******************************************************************************* */ void SetBoard(TREE * tree, int nargs, char *args[], int special) { int twtm, i, match, num, pos, square, tboard[64]; int bcastle, ep, wcastle, error = 0; char input[80]; static const char bdinfo[] = { 'k', 'q', 'r', 'b', 'n', 'p', '*', 'P', 'N', 'B', 'R', 'Q', 'K', '*', '1', '2', '3', '4', '5', '6', '7', '8', '/' }; static const char status[13] = { 'K', 'Q', 'k', 'q', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', ' ' }; int whichsq; static const int firstsq[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; if (special) strcpy(input, initial_position); else strcpy(input, args[0]); for (i = 0; i < 64; i++) tboard[i] = 0; /* ************************************************************ * * * Scan the input string searching for pieces, numbers * * [empty squares], slashes [end-of-rank] and a blank * * [end of board, start of castle status]. * * * ************************************************************ */ whichsq = 0; square = firstsq[whichsq]; num = 0; for (pos = 0; pos < (int) strlen(args[0]); pos++) { for (match = 0; match < 23 && args[0][pos] != bdinfo[match]; match++); if (match > 22) break; /* "/" -> end of this rank. */ else if (match == 22) { num = 0; if (whichsq > 6) break; square = firstsq[++whichsq]; } /* "1-8" -> empty squares. */ else if (match >= 14) { num += match - 13; square += match - 13; if (num > 8) { printf("more than 8 squares on one rank\n"); error = 1; break; } continue; } /* piece codes. */ else { if (++num > 8) { printf("more than 8 squares on one rank\n"); error = 1; break; } tboard[square++] = match - 6; } } /* ************************************************************ * * * Now extract (a) side to move [w/b], (b) castle status * * [KkQq for white/black king-side ok, white/black queen- * * side ok], (c) enpassant target square. * * * ************************************************************ */ twtm = 0; ep = 0; wcastle = 0; bcastle = 0; /* ************************************************************ * * * Side to move. * * * ************************************************************ */ if (args[1][0] == 'w') twtm = 1; else if (args[1][0] == 'b') twtm = 0; else { printf("side to move is bad\n"); error = 1; } /* ************************************************************ * * * Castling/enpassant status. * * * ************************************************************ */ if (nargs > 2 && strlen(args[2])) { if (strcmp(args[2], "-")) { for (pos = 0; pos < (int) strlen(args[2]); pos++) { for (match = 0; (match < 13) && (args[2][pos] != status[match]); match++); if (match == 0) wcastle += 1; else if (match == 1) wcastle += 2; else if (match == 2) bcastle += 1; else if (match == 3) bcastle += 2; else if (args[2][0] != '-') { printf("castling status is bad.\n"); error = 1; } } } } if (nargs > 3 && strlen(args[3])) { if (strcmp(args[3], "-")) { if (args[3][0] >= 'a' && args[3][0] <= 'h' && args[3][1] > '0' && args[3][1] < '9') { ep = (args[3][1] - '1') * 8 + args[3][0] - 'a'; } else if (args[3][0] != '-') { printf("enpassant status is bad.\n"); error = 1; } } } for (i = 0; i < 64; i++) PcOnSq(i) = tboard[i]; Castle(0, white) = wcastle; Castle(0, black) = bcastle; EnPassant(0) = 0; if (ep) { if (Rank(ep) == RANK6) { if (PcOnSq(ep - 8) != -pawn) ep = 0; } else if (Rank(ep) == RANK3) { if (PcOnSq(ep + 8) != pawn) ep = 0; } else ep = 0; if (!ep) { printf("enpassant status is bad (must be on 3rd/6th rank only.\n"); ep = 0; error = 1; } EnPassant(0) = ep; } Rule50Moves(0) = 0; /* ************************************************************ * * * Now check the castling status and enpassant status to * * make sure that the board is in a state that matches * * these. * * * ************************************************************ */ if (((Castle(0, white) & 2) && (PcOnSq(A1) != rook)) || ((Castle(0, white) & 1) && (PcOnSq(H1) != rook)) || ((Castle(0, black) & 2) && (PcOnSq(A8) != -rook)) || ((Castle(0, black) & 1) && (PcOnSq(H8) != -rook))) { printf("ERROR-- castling status does not match board position\n"); error = 1; } /* ************************************************************ * * * Now set the bitboards so that error tests can be done. * * * ************************************************************ */ if ((twtm && EnPassant(0) && (PcOnSq(EnPassant(0) + 8) != -pawn) && (PcOnSq(EnPassant(0) - 7) != pawn) && (PcOnSq(EnPassant(0) - 9) != pawn)) || (Flip(twtm) && EnPassant(0) && (PcOnSq(EnPassant(0) - 8) != pawn) && (PcOnSq(EnPassant(0) + 7) != -pawn) && (PcOnSq(EnPassant(0) + 9) != -pawn))) { EnPassant(0) = 0; } SetChessBitBoards(tree); /* ************************************************************ * * * Now check the position for a sane position, which * * means no more than 8 pawns, no more than 10 knights, * * bishops or rooks, no more than 9 queens, no pawns on * * 1st or 8th rank, etc. * * * ************************************************************ */ wtm = twtm; error += InvalidPosition(tree); if (!error) { if (log_file) DisplayChessBoard(log_file, tree->pos); tree->rep_index[white] = 0; tree->rep_index[black] = 0; Rule50Moves(0) = 0; if (!special) { last_mate_score = 0; InitializeKillers(); last_pv.pathd = 0; last_pv.pathl = 0; tree->pv[0].pathd = 0; tree->pv[0].pathl = 0; moves_out_of_book = 0; } } else { if (special) Print(4095, "bad string = \"%s\"\n", initial_position); else Print(4095, "bad string = \"%s\"\n", args[0]); InitializeChessBoard(tree); Print(4095, "Illegal position, using normal initial chess position\n"); } }
/* ******************************************************************************** * * * "annotate" command is used to search through the game in the "history" file * * (often set by the "read" command which reads moves in, skipping non-move * * information such as move numbers, times, etc.) * * * * 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 <margin> * * 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 <margin> 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. * * * * the format of the command is as follows: * * * * annotate filename b|w|bw 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.ann". * * * * where b/w/bw indicates whether to annotate only the white side (w), the * * black side (b) or both (bw). * * * * 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. * * * ******************************************************************************** */ void Annotate() { FILE *annotate_in, *annotate_out; char command[80], text[128], colors[32], next; int annotate_margin, annotate_score[100], player_score, best_moves; int annotate_search_time_limit, search_player; int twtm, path_len, analysis_printed=0, *mv; int wtm, move_number, move_num, line1, line2, move, suggested, i; int temp_draw_score_is_zero, searches_done; CHESS_PATH temp[100], player_pv; /* ---------------------------------------------------------- | | | first, quiz the user for the options needed to | | successfully annotate a game. | | | ---------------------------------------------------------- */ Print(0,"\nannotate filename: "); fscanf(input_stream,"%s",text); annotate_in = fopen(text,"r"); if (annotate_in == NULL) { Print(0,"unable to open %s for input\n", text); return; } sprintf(command,"read=%s",text); Option(command); strcpy(text+strlen(text),".can"); annotate_out = fopen(text,"w"); if (annotate_out == NULL) { Print(0,"unable to open %s for output\n", text); return; } Print(0,"\ncolor(s): "); fscanf(input_stream,"%s",colors); line1=1; line2=999; Print(0,"\nstarting move number or range: "); fscanf(input_stream,"%s",text); if(strchr(text,'-')) sscanf(text,"%d-%d",&line1,&line2); else { sscanf(text,"%d",&line1); line2=999; } Print(0,"\nannotation margin: "); fscanf(input_stream,"%s",text); annotate_margin=atof(text)*PAWN_VALUE; Print(0,"\ntime per move: "); fscanf(input_stream,"%s",text); annotate_search_time_limit=atoi(text)*100; next=getc(input_stream); best_moves=1; if (next == ' ') { fscanf(input_stream,"%s",text); best_moves=atoi(text); } /* ---------------------------------------------------------- | | | reset the game to "square 0" to start the annotation | | procedure. then we read moves from the input file, | | make them on the game board, and annotate if the move | | is for the correct side. if we haven't yet reached | | the starting move to annotate, we skip the Search() | | stuff and read another move. | | | ---------------------------------------------------------- */ annotate_mode=1; temp_draw_score_is_zero=draw_score_is_zero; draw_score_is_zero=1; ponder_completed=0; ponder_move=0; last_pv.path_iteration_depth=0; last_pv.path_length=0; InitializeChessBoard(&position[0]); wtm=1; move_number=1; fprintf(annotate_out,"[Event \"%s\"]\n",pgn_event); fprintf(annotate_out,"[Site \"%s\"]\n",pgn_site); fprintf(annotate_out,"[Date \"%s\"]\n",pgn_date); fprintf(annotate_out,"[Round \"%s\"]\n",pgn_round); fprintf(annotate_out,"[White \"%s\"]\n",pgn_white); fprintf(annotate_out,"[WhiteElo \"%s\"]\n",pgn_white_elo); fprintf(annotate_out,"[Black \"%s\"]\n",pgn_black); fprintf(annotate_out,"[BlackElo \"%s\"]\n",pgn_black_elo); fprintf(annotate_out,"[Result \"%s\"]\n",pgn_result); fprintf(annotate_out,"[Annotator \"Crafty v%s\"]\n",version); if (!strcmp(colors,"bw") || !strcmp(colors,"wb")) fprintf(annotate_out,"{annotating both black and white moves.}\n"); else if (strchr(colors,'b')) fprintf(annotate_out,"{annotating only black moves.}\n"); else if (strchr(colors,'w')) fprintf(annotate_out,"{annotating only white moves.}\n"); fprintf(annotate_out,"{using a scoring margin of %s pawns.}\n", DisplayEvaluationWhisper(annotate_margin)); fprintf(annotate_out,"{search time limit is %s}\n\n", DisplayTimeWhisper(annotate_search_time_limit)); do { fflush(annotate_out); move=ReadChessMove(annotate_in,wtm,0); if (move <= 0) break; strcpy(text,OutputMove(&move,0,wtm)); fseek(history_file,((move_number-1)*2+1-wtm)*10,SEEK_SET); fprintf(history_file,"%10s ",text); if (wtm) Print(0,"White(%d): %s\n",move_number,text); else Print(0,"Black(%d): %s\n",move_number,text); if (analysis_printed) fprintf(annotate_out,"%3d.%s%6s\n",move_number,(wtm?"":" ..."),text); else { if (wtm) fprintf(annotate_out,"%3d.%6s",move_number,text); else fprintf(annotate_out,"%6s\n",text); } analysis_printed=0; if (move_number >= line1) { if ((!wtm && strchr(colors,'b')) | ( wtm && strchr(colors,'w'))) { last_pv.path_iteration_depth=0; last_pv.path_length=0; thinking=1; RootMoveList(wtm); /* ---------------------------------------------------------- | | | now search the position to see if the move played is | | the best move possible. if not, then search just the | | move played to get a score for it as well, so we can | | determine if annotated output is appropriate. | | | ---------------------------------------------------------- */ search_time_limit=annotate_search_time_limit; search_depth=0; player_score=-999999; search_player=1; for (searches_done=0;searches_done<abs(best_moves);searches_done++) { if (searches_done > 0) { search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; } Print(0,"\n Searching all legal moves."); Print(0,"----------------------------------\n"); position[1]=position[0]; InitializeHashTables(); annotate_score[searches_done]=Iterate(wtm,annotate); if (pv[0].path[1] == move) { player_score=annotate_score[searches_done]; player_pv=pv[0]; search_player=0; } temp[searches_done]=pv[0]; for (mv=last[0];mv<last[1];mv++) { if (*mv == pv[0].path[1]) { for (;mv<last[1]-1;mv++) *mv=*(mv+1); last[1]--; break; } } if (last[1] < last[0] || (player_score+annotate_margin>annotate_score[searches_done] && best_moves>0)) break; } if (search_player) { Print(0,"\n Searching only the move played in game."); Print(0,"--------------------\n"); position[1]=position[0]; search_move=move; search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; if (search_depth==0) search_time_limit=annotate_search_time_limit; InitializeHashTables(); player_score=Iterate(wtm,annotate); player_pv=pv[0]; search_depth=0; search_time_limit=annotate_search_time_limit; search_move=0; } /* ---------------------------------------------------------- | | | output the score/pv for the move played unless it | | matches what Crafty would have played. if it doesn't | | then output the pv for what Crafty thinks is best. | | | ---------------------------------------------------------- */ thinking=0; if (player_pv.path_iteration_depth>1 && player_pv.path_length>=1 && player_score+annotate_margin<annotate_score[0] && (temp[0].path[1]!=player_pv.path[1] || annotate_margin<0.0 || best_moves!=1)) { if (wtm) { analysis_printed=1; fprintf(annotate_out,"\n"); } twtm = wtm; fprintf(annotate_out," {%d:%s", player_pv.path_iteration_depth, DisplayEvaluationWhisper(player_score)); path_len=player_pv.path_length; for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s", OutputMove(&player_pv.path[i],i,twtm)); MakeMove(i,player_pv.path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,player_pv.path[i],twtm); } fprintf(annotate_out,"}\n"); for (move_num=0;move_num<searches_done;move_num++) { twtm = wtm; if (move != temp[move_num].path[1]) { fprintf(annotate_out," {%d:%s", temp[move_num].path_iteration_depth, DisplayEvaluationWhisper(annotate_score[move_num])); path_len=temp[move_num].path_length; for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s", OutputMove(&temp[move_num].path[i],i,twtm)); MakeMove(i,temp[move_num].path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,temp[move_num].path[i],twtm); } fprintf(annotate_out,"}\n"); } } } } } /* ---------------------------------------------------------- | | | before going on to the next move, see if the user has | | included a set of other moves that require a search. | | if so, search them one at a time and produce the ana- | | lysis for each one. | | | ---------------------------------------------------------- */ do { next=getc(annotate_in); } while (next==' ' || next=='\n'); ungetc(next,annotate_in); if (next == EOF) break; if (next == '{') do { do { next=getc(annotate_in); } while (next==' '); ungetc(next,annotate_in); if (next == EOF || next == '}') break; suggested=ReadChessMove(annotate_in,wtm,1); if (suggested < 0) break; if (suggested > 0) { thinking=1; Print(0,"\n Searching only the move suggested."); Print(0,"--------------------\n"); position[1]=position[0]; search_move=suggested; search_time_limit=3*annotate_search_time_limit; search_depth=temp[0].path_iteration_depth; InitializeHashTables(); annotate_score[0]=Iterate(wtm,annotate); search_depth=0; search_time_limit=annotate_search_time_limit; search_move=0; thinking=0; twtm = wtm; path_len = pv[0].path_length; if (pv[0].path_iteration_depth > 1 && path_len >= 1) { if (wtm && !analysis_printed) { analysis_printed=1; fprintf(annotate_out,"\n"); } fprintf(annotate_out," {suggested %d:%s", pv[0].path_iteration_depth, DisplayEvaluationWhisper(annotate_score[0])); for (i=1;i<=path_len;i++) { fprintf(annotate_out," %s",OutputMove(&pv[0].path[i],i,twtm)); MakeMove(i,pv[0].path[i],twtm); twtm=ChangeSide(twtm); } for (i=path_len;i>0;i--) { twtm=ChangeSide(twtm); UnMakeMove(i,pv[0].path[i],twtm); } fprintf(annotate_out,"}\n"); } } } while(1); MakeMoveRoot(move,wtm); wtm=ChangeSide(wtm); if (wtm) move_number++; if (move_number > line2) break; } while (1); fprintf(annotate_out,"\n"); if (annotate_out) fclose(annotate_out); if (annotate_in) fclose(annotate_in); search_time_limit=0; draw_score_is_zero=temp_draw_score_is_zero; annotate_mode=0; }