void InitCaptures(POS *p, MOVES *m) { m->phase = 0; m->p = p; m->last = GenerateCaptures(m->p, m->move); ScoreCaptures(m); m->next = m->move; }
/* ******************************************************************************** * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. then, make this move and * * do a search from the resulting position. while pondering, one of three * * things can happen: (1) a move is entered, and it matches the predicted * * move. we then switch from pondering to thinking and search as normal; * * (2) a move is entered, but it does not match the predicted move. we then * * abort the search, unmake the pondered move, and then restart with the move * * entered. (3) a command is entered. if it is a simple command, it can be * * done without aborting the search or losing time. if not, we abort the * * search, execute the command, and then attempt to restart pondering if the * * command didn't make that impossible. * * * ******************************************************************************** */ int Ponder(int wtm) { int dummy=0, i, *n_ponder_moves, *mv; /* ---------------------------------------------------------- | | | first, let's check to see if pondering is allowed, or | | if we should avoid pondering on this move since it is | | the first move of a game, or if the game is over, or | | "force" mode is active, or there is input in the queue | | that needs to be read and processed. | | | ---------------------------------------------------------- */ if (!ponder || force || over || CheckInput()) return(0); /* ---------------------------------------------------------- | | | if we don't have a predicted move to ponder, try two | | sources: (1) look up the current position in the | | transposition table and see if it has a suggested best | | move; (2) do a short tree search to calculate a move | | that we should ponder. | | | ---------------------------------------------------------- */ strcpy(hint,"none"); if (!ponder_move) { (void) LookUp(0,0,wtm,&dummy,&dummy); if (hash_move[0]) ponder_move=hash_move[0]; } if (!ponder_move) { TimeSet(puzzle); if (time_limit < 3) return(0); puzzling=1; position[1]=position[0]; printf(" puzzling over a move to ponder.\n"); last_pv.path_length=0; last_pv.path_iteration_depth=0; for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } (void) Iterate(wtm,puzzle,0); for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } puzzling=0; if (pv[0].path_length) ponder_move=pv[0].path[1]; if (!ponder_move) return(0); for (i=1;i<(int) pv[0].path_length-1;i++) last_pv.path[i]=pv[0].path[i+1]; last_pv.path_length=pv[0].path_length-1; last_pv.path_iteration_depth=0; if (!ValidMove(1,wtm,ponder_move)) { ponder_move=0; return(0); } } /* ---------------------------------------------------------- | | | display the move we are going to "ponder". | | | ---------------------------------------------------------- */ if (wtm) printf("White(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); else printf("Black(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); sprintf(hint,"%s",OutputMove(&ponder_move,0,wtm)); if (post) printf("Hint: %s\n",hint); /* ---------------------------------------------------------- | | | set the ponder move list and eliminate illegal moves. | | | ---------------------------------------------------------- */ n_ponder_moves=GenerateCaptures(0, wtm, ponder_moves); num_ponder_moves=GenerateNonCaptures(0, wtm, n_ponder_moves)-ponder_moves; for (mv=ponder_moves;mv<ponder_moves+num_ponder_moves;mv++) { MakeMove(0, *mv, wtm); if (Check(wtm)) { UnMakeMove(0, *mv, wtm); *mv=0; } else UnMakeMove(0, *mv, wtm); } /* ---------------------------------------------------------- | | | now, perform an iterated search, but with the special | | "pondering" flag set which changes the time controls | | since there is no need to stop searching until the | | opponent makes a move. | | | ---------------------------------------------------------- */ MakeMove(0,ponder_move,wtm); if (Rule50Moves(1)==90 || Rule50Moves(1)==91) ClearHashTables(); last_opponent_move=ponder_move; if (ChangeSide(wtm)) *rephead_w++=HashKey; else *rephead_b++=HashKey; if (RepetitionDraw(wtm)) printf("game is a draw by repetition\n"); if (whisper) strcpy(whisper_text,"n/a"); thinking=0; pondering=1; (void) Iterate(ChangeSide(wtm),think,0); pondering=0; thinking=0; if (ChangeSide(wtm)) rephead_w--; else rephead_b--; last_opponent_move=0; UnMakeMove(0,ponder_move,wtm); /* ---------------------------------------------------------- | | | search completed. the possible return values are: | | | | (0) no pondering was done, period. | | | | (1) pondering was done, opponent made the predicted | | move, and we searched until time ran out in a | | normal manner. | | | | (2) pondering was done, but the ponder search | | terminated due to either finding a mate, or the | | maximum search depth was reached. the result of | | this ponder search are valid, but only if the | | opponent makes the correct (predicted) move. | | | | (3) pondering was done, but the opponent either made | | a different move, or entered a command that has to | | interrupt the pondering search before the command | | (or move) can be processed. this forces Main() to | | avoid reading in a move/command since one has been | | read into the command buffer already. | | | ---------------------------------------------------------- */ if (made_predicted_move) return(1); if (abort_search) return(3); return(2); }
/* ******************************************************************************** * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. then, make this move and * * do a search from the resulting position. while pondering, one of three * * things can happen: (1) a move is entered, and it matches the predicted * * move. we then switch from pondering to thinking and search as normal; * * (2) a move is entered, but it does not match the predicted move. we then * * abort the search, unmake the pondered move, and then restart with the move * * entered. (3) a command is entered. if it is a simple command, it can be * * done without aborting the search or losing time. if not, we abort the * * search, execute the command, and then attempt to restart pondering if the * * command didn't make that impossible. * * * ******************************************************************************** */ int Ponder(int wtm) { int dummy=0, i, *n_ponder_moves; /* ---------------------------------------------------------- | | | if we don't have a predicted move to ponder, try two | | sources: (1) look up the current position in the | | transposition table and see if it has a suggested best | | move; (2) do a short tree search to calculate a move | | that we should ponder. | | | ---------------------------------------------------------- */ ponder_completed=0; if (!ponder_move) { (void) LookUp(0,0,wtm,&dummy,dummy); if (hash_move[0]) ponder_move=hash_move[0]; } if (!ponder_move) { TimeSet(puzzle); if (time_limit < 3) return(0); puzzling=1; position[1]=position[0]; Print(2," puzzling over a move to ponder.\n"); last_pv.path_length=0; last_pv.path_iteration_depth=0; for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } (void) Iterate(wtm,puzzle); for (i=0;i<MAXPLY;i++) { killer_move1[i]=0; killer_move2[i]=0; killer_count1[i]=0; killer_count2[i]=0; } puzzling=0; if (pv[0].path_length) ponder_move=pv[0].path[1]; if (!ponder_move) return(0); for (i=1;i<(int) pv[0].path_length-1;i++) last_pv.path[i]=pv[0].path[i+1]; last_pv.path_length=pv[0].path_length-1; last_pv.path_iteration_depth=0; if (!ValidMove(1,wtm,ponder_move)) { printf("puzzle returned an illegal move!\n"); DisplayChessMove("move= ",ponder_move); ponder_move=0; return(0); } } if (!ponder_move) { strcpy(hint,"none"); return(0); } /* ---------------------------------------------------------- | | | display the move we are going to "ponder". | | | ---------------------------------------------------------- */ if (wtm) Print(2,"White(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); else Print(2,"Black(%d): %s [pondering]\n", move_number,OutputMove(&ponder_move,0,wtm)); sprintf(hint,"%s",OutputMove(&ponder_move,0,wtm)); n_ponder_moves=GenerateCaptures(0, wtm, ponder_moves); num_ponder_moves=GenerateNonCaptures(0, wtm, n_ponder_moves)-ponder_moves; /* ---------------------------------------------------------- | | | now, perform an iterated search, but with the special | | "pondering" flag set which changes the time controls | | since there is no need to stop searching until the | | opponent makes a move. | | | ---------------------------------------------------------- */ MakeMove(0,ponder_move,wtm); if (Rule50Moves(1)==90 || Rule50Moves(1)==91) ClearHashTables(); last_opponent_move=ponder_move; if (ChangeSide(wtm)) *rephead_w++=HashKey; else *rephead_b++=HashKey; if (RepetitionDraw(wtm)) Print(0,"game is a draw by repetition\n"); if (whisper) strcpy(whisper_text,"n/a"); pondering=1; (void) Iterate(ChangeSide(wtm),think); pondering=0; if (!abort_search) ponder_completed=1; if (ChangeSide(wtm)) rephead_w--; else rephead_b--; last_opponent_move=0; UnMakeMove(0,ponder_move,wtm); /* ---------------------------------------------------------- | | | search completed. there are two possible reasons for | | us to get here. (1) the opponent made the predicted | | move and we have used enough time on the search, or; | | the operator typed in a command (or not-predicted | | move) that requires the search to abort and restart. | | | ---------------------------------------------------------- */ if (made_predicted_move) return(1); return(0); }
/* ******************************************************************************* * * * Ponder() is the driver for "pondering" (thinking on the opponent's time.) * * its operation is simple: Find a predicted move by (a) taking the second * * move from the principal variation, or (b) call lookup to see if it finds * * a suggested move from the transposition table. Then, make this move and * * do a search from the resulting position. While pondering, one of three * * things can happen: (1) A move is entered, and it matches the predicted * * move. We then switch from pondering to thinking and search as normal; * * (2) A move is entered, but it does not match the predicted move. We then * * abort the search, unmake the pondered move, and then restart with the * * move entered. (3) A command is entered. If it is a simple command, it * * can be done without aborting the search or losing time. If not, we abort * * the search, execute the command, and then attempt to restart pondering if * * the command didn't make that impossible. * * * ******************************************************************************* */ int Ponder(int wtm) { TREE *const tree = block[0]; int dalpha = -999999, dbeta = 999999, i; unsigned *n_ponder_moves, *mv; int save_move_number, tlom, value; /* ************************************************************ * * * First, let's check to see if pondering is allowed, or * * if we should avoid pondering on this move since it is * * the first move of a game, or if the game is over, or * * "force" mode is active, or there is input in the queue * * that needs to be read and processed. * * * ************************************************************ */ if (!ponder || force || over || CheckInput()) return 0; save_move_number = move_number; /* ************************************************************ * * * Check the ponder move for legality. If it is not a * * legal move, we have to take action to find something to * * ponder. * * * ************************************************************ */ strcpy(ponder_text, "none"); if (ponder_move) { if (!VerifyMove(tree, 1, wtm, ponder_move)) { ponder_move = 0; Print(4095, "ERROR. ponder_move is illegal (1).\n"); Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); } } /* ************************************************************ * * * First attempt, do a hash probe. However, since a hash * * collision is remotely possible, we still need to verify * * that the transposition/refutation best move is actually * * legal. * * * ************************************************************ */ if (!ponder_move) { HashProbe(tree, 0, 0, wtm, dalpha, dbeta, &value); if (tree->hash_move[0]) ponder_move = tree->hash_move[0]; if (ponder_move) { if (!VerifyMove(tree, 1, wtm, ponder_move)) { Print(4095, "ERROR. ponder_move is illegal (2).\n"); Print(4095, "ERROR. move=%d %x\n", ponder_move, ponder_move); ponder_move = 0; } } } /* ************************************************************ * * * Second attempt. If that didn't work, then we try what * * I call a "puzzling" search. Which is simply a shorter * * time-limit search for the other side, to find something * * to ponder. * * * ************************************************************ */ if (!ponder_move) { TimeSet(puzzle); if (time_limit < 20) return 0; puzzling = 1; tree->status[1] = tree->status[0]; Print(32, " puzzling over a move to ponder.\n"); last_pv.pathl = 0; last_pv.pathd = 0; for (i = 0; i < MAXPLY; i++) { tree->killers[i].move1 = 0; tree->killers[i].move2 = 0; } Iterate(wtm, puzzle, 0); for (i = 0; i < MAXPLY; i++) { tree->killers[i].move1 = 0; tree->killers[i].move2 = 0; } puzzling = 0; if (tree->pv[0].pathl) ponder_move = tree->pv[0].path[1]; if (!ponder_move) return 0; for (i = 1; i < (int) tree->pv[0].pathl; i++) last_pv.path[i] = tree->pv[0].path[i + 1]; last_pv.pathl = tree->pv[0].pathl - 1; last_pv.pathd = 0; if (!VerifyMove(tree, 1, wtm, ponder_move)) { ponder_move = 0; Print(4095, "ERROR. ponder_move is illegal (3).\n"); Print(4095, "ERROR. PV pathl=%d\n", last_pv.pathl); return 0; } } /* ************************************************************ * * * Display the move we are going to "ponder". * * * ************************************************************ */ if (wtm) Print(32, "White(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, wtm, ponder_move)); else Print(32, "Black(%d): %s [pondering]\n", move_number, OutputMove(tree, 0, wtm, ponder_move)); sprintf(ponder_text, "%s", OutputMove(tree, 0, wtm, ponder_move)); if (post) printf("Hint: %s\n", ponder_text); /* ************************************************************ * * * Set the ponder move list and eliminate illegal moves. * * This list is used to test the move entered while we are * * pondering, since we need a move list for the input * * screening process. * * * ************************************************************ */ n_ponder_moves = GenerateCaptures(tree, 0, wtm, ponder_moves); num_ponder_moves = GenerateNoncaptures(tree, 0, wtm, n_ponder_moves) - ponder_moves; for (mv = ponder_moves; mv < ponder_moves + num_ponder_moves; mv++) { MakeMove(tree, 0, wtm, *mv); if (Check(wtm)) { UnmakeMove(tree, 0, wtm, *mv); *mv = 0; } else UnmakeMove(tree, 0, wtm, *mv); } /* ************************************************************ * * * Now, perform an iterated search, but with the special * * "pondering" flag set which changes the time controls * * since there is no need to stop searching until the * * opponent makes a move. * * * ************************************************************ */ MakeMove(tree, 0, wtm, ponder_move); tree->curmv[0] = ponder_move; tree->rep_list[++rep_index] = HashKey; tlom = last_opponent_move; last_opponent_move = ponder_move; if (kibitz) strcpy(kibitz_text, "n/a"); thinking = 0; pondering = 1; if (!wtm) move_number++; ponder_value = Iterate(Flip(wtm), think, 0); rep_index--; move_number = save_move_number; pondering = 0; thinking = 0; last_opponent_move = tlom; UnmakeMove(tree, 0, wtm, ponder_move); /* ************************************************************ * * * Search completed. the possible return values are: * * * * (0) No pondering was done, period. * * * * (1) Pondering was done, opponent made the predicted * * move, and we searched until time ran out in a * * normal manner. * * * * (2) Pondering was done, but the ponder search * * terminated due to either finding a mate, or the * * maximum search depth was reached. The result of * * this ponder search are valid, but only if the * * opponent makes the correct (predicted) move. * * * * (3) Pondering was done, but the opponent either made a * * different move, or entered a command that has to * * interrupt the pondering search before the command * * (or move) can be processed. This forces Main() to * * avoid reading in a move/command since one has been * * read into the command buffer already. * * * ************************************************************ */ if (input_status == 1) return 1; if (input_status == 2) return 3; return 2; }
int NextMove(MOVES *m, int *flag) { int move; switch (m->phase) { case 0: // return transposition table move, if legal move = m->trans_move; if (move && Legal(m->p, move)) { m->phase = 1; *flag = MV_HASH; return move; } case 1: // helper phase: generate captures m->last = GenerateCaptures(m->p, m->move); ScoreCaptures(m); m->next = m->move; m->badp = m->bad; m->phase = 2; case 2: // return good captures, save bad ones on the separate list while (m->next < m->last) { move = SelectBest(m); if (move == m->trans_move) continue; if (BadCapture(m->p, move)) { *m->badp++ = move; continue; } *flag = MV_CAPTURE; return move; } case 3: // first killer move move = m->killer1; if (move && move != m->trans_move && m->p->pc[Tsq(move)] == NO_PC && Legal(m->p, move)) { m->phase = 4; *flag = MV_KILLER; return move; } case 4: // second killer move move = m->killer2; if (move && move != m->trans_move && m->p->pc[Tsq(move)] == NO_PC && Legal(m->p, move)) { m->phase = 5; *flag = MV_KILLER; return move; } case 5: // refutation move move = m->ref_move; if (move && move != m->trans_move && m->p->pc[Tsq(move)] == NO_PC && move != m->killer1 && move != m->killer2 && Legal(m->p, move)) { m->phase = 6; *flag = MV_NORMAL; return move; } case 6: // helper phase: generate quiet moves m->last = GenerateQuiet(m->p, m->move); ScoreQuiet(m); m->next = m->move; m->phase = 7; case 7: // return quiet moves while (m->next < m->last) { move = SelectBest(m); if (move == m->trans_move || move == m->killer1 || move == m->killer2 || move == m->ref_move) continue; *flag = MV_NORMAL; return move; } m->next = m->bad; m->phase = 8; case 8: // return bad captures if (m->next < m->badp) { *flag = MV_BADCAPT; return *m->next++; } } return 0; }
/* ******************************************************************************** * * * Quiesce() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Quiesce() is * * called whenever there is no "depth" remaining so that only capture moves * * are searched deeper. * * * ******************************************************************************** */ int Quiesce(int alpha, int beta, int wtm, int ply) { register int initial_alpha, value, delta; register int *next_move; register int *goodmv, *movep, moves=0, *sortv, temp; /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; next_time_check--; last[ply]=last[ply-1]; initial_alpha=alpha; /* ---------------------------------------------------------- | | | now call Evaluate() to produce the "stand-pat" score | | that will be returned if no capture is acceptable. | | if this score is > alpha, then we also have to save | | the "path" to this node as it is the PV that leads | | to this score. | | | ---------------------------------------------------------- */ value=Evaluate(ply,wtm,alpha,beta); if (value > alpha) { if (value >= beta) return(value); alpha=value; pv[ply].path_length=ply-1; pv[ply].path_hashed=0; pv[ply].path_iteration_depth=iteration_depth; } /* ---------------------------------------------------------- | | | generate captures and sort them based on (a) the value | | of the captured piece - the value of the capturing | | piece if this is > 0; or, (b) the value returned by | | Swap(). if the value of the captured piece won't | | bring the material score back up to near alpha, that | | capture is discarded as "futile." | | | ---------------------------------------------------------- */ last[ply]=GenerateCaptures(ply, wtm, last[ply-1]); delta=alpha-500-(wtm?Material:-Material); goodmv=last[ply-1]; sortv=sort_value; for (movep=last[ply-1];movep<last[ply];movep++) if (p_values[Captured(*movep)+7]+p_values[Promote(*movep)+7] >= delta) { if (Captured(*movep) == king) return(beta); if (p_values[Piece(*movep)+7] < p_values[Captured(*movep)+7] || (p_values[Piece(*movep)+7] <= p_values[Captured(*movep)+7] && delta<=0)) { *goodmv++=*movep; *sortv++=p_values[Captured(*movep)+7]; moves++; } else { temp=Swap(From(*movep),To(*movep),wtm); if (temp >= 0) { *sortv++=temp; *goodmv++=*movep; moves++; } } } /* ---------------------------------------------------------- | | | don't disdain the lowly bubble sort here. the list of | | captures is always short, and experiments with other | | algorithms are always slightly slower. | | | ---------------------------------------------------------- */ if (moves > 1) { register int done; register int *end=last[ply-1]+moves-1; do { done=1; sortv=sort_value; movep=last[ply-1]; for (;movep<end;movep++,sortv++) if (*sortv < *(sortv+1)) { temp=*sortv; *sortv=*(sortv+1); *(sortv+1)=temp; temp=*movep; *movep=*(movep+1); *(movep+1)=temp; done=0; } } while(!done); } next_move=last[ply-1]; /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. | | | ---------------------------------------------------------- */ while (moves--) { current_move[ply]=*(next_move++); #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,0,wtm,alpha,beta,"quiesce",CAPTURE_MOVES); #endif MakeMove(ply,current_move[ply],wtm); value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); UnMakeMove(ply,current_move[ply],wtm); if (value > alpha) { if(value >= beta) return(value); alpha=value; } } /* ---------------------------------------------------------- | | | all moves have been searched. return the search | | result that was found. if the result is not the | | original alpha score, then we need to return the PV | | that is associated with this score. | | | ---------------------------------------------------------- */ if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply], (pv[ply].path_length-ply+1)*sizeof(int)); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; } return(alpha); }