#include "includes.h" #include "knightcap.h" static int pondered_wrong_move; static int nodes, quiesce_nodes, extended_nodes, truncated_nodes, confirm_nodes; static int maxply, maxdepth; static int move_order_hits, move_order_hits1, move_order_hits2, move_order_hits3, move_order_hits4, move_order_misses; static int pondering, ponder_stop; static int search_depth; extern int mulling; extern int mull_stage; extern int demo_mode; extern int learning; extern int pop_count[256]; extern int hash_hits, hash_misses, hash_shallow; static volatile Position *real_position; static volatile Position *ponder_position; #if TREE_PRINT_PLY static int tree_print_ply = TREE_PRINT_PLY; #endif extern struct state *state; extern int testing; static int null_ply[MAX_DEPTH+10]; static int search_expired(void) { if (state->quit || state->stop_search) return 1; if (mulling) { if (state->computer) { return 1; } } else { if (state->computer == 0) if (testing) return 0; else return 1; } if (pondering) { if (ponder_stop) return 1; if (next_to_play(&state->position) == -state->computer) return 0; if (real_position->hash1 != ponder_position->hash1) { #if STORE_LEAF_POS state->predicted_move[state->stored_move_num] = -1; #endif pondered_wrong_move = 1; return 1; } lprintf(0,"predicted move\n"); #if STORE_LEAF_POS if (state->predicted_move[state->stored_move_num] == 0) state->predicted_move[state->stored_move_num] = 1; #endif pondering = 0; return 0; } /* check for timer expiry every few nodes (too expensive to check every node, and alarm isn't accurate enough for lightning) */ if ((search_depth > 2) && (!mulling || (search_depth >= state->current_depth + 1 && state->converged))) { static int last_test; if (nodes < last_test) { last_test = nodes; } if (nodes > last_test + TIMER_NODES) { if (timer_expired()) return 1; last_test = nodes; } } return 0; } static inline int depth_extensions(Position *b, int depth, int ply, Eval v1, Eval testv) { if (null_ply[ply-1]) return depth; if ((depth+ply) >= search_depth+MAX_EXTENSIONS) goto razoring; #if USE_PAWN_DEEPENING if ((b->board[b->last_move.to] == PAWN && (b->wpassed_pawn_mask & (1<pboard[b->last_move.to])) && YPOS(b->last_move.to) > 4 && b->control[b->last_move.to] >= 0 && b->board[b->last_move.to+NORTH] == 0 && b->control[b->last_move.to+NORTH] >= 0) || (b->board[b->last_move.to] == -PAWN && (b->bpassed_pawn_mask & (1<pboard[b->last_move.to])) && YPOS(b->last_move.to) < 3 && b->control[b->last_move.to] <= 0 && b->board[b->last_move.to+SOUTH] == 0 && b->control[b->last_move.to+SOUTH] <= 0)) { b->flags |= FLAG_EXTENDED; return depth+1; } #endif #if USE_CHECK_DEEPENING if (b->flags & FLAG_CHECK) { b->flags |= FLAG_EXTENDED; return depth+1; } #endif if (b->oldb && (b->oldb->flags & FLAG_EXTENDED)) { return depth; } #if USE_RECAPTURE_DEEPENING if (b->oldb) { /* if its a recapture then extend - this ensures we get to the end of capture sequences */ if (b->oldb->oldb && b->last_move.to == b->oldb->last_move.to && b->oldb->oldb->board[b->oldb->last_move.to] && abs(b->board[b->last_move.to]) != PAWN) { int mat1 = b->w_material - b->b_material; int mat2 = b->oldb->oldb->w_material - b->oldb->oldb->b_material; if (abs(mat1 - mat2) < ABS(PAWN_VALUE)/2) { b->flags |= FLAG_EXTENDED; return depth+1; } } } #endif #if USE_BEST_CAPTURE_DEEPENING if (b->oldb && same_move(&b->oldb->best_capture, &b->last_move)) { b->flags |= FLAG_EXTENDED; return depth+1; } #endif #if USE_HUNG_CAPTURE_DEEPENING if (b->oldb) { /* if its a capture of a hung piece then extend */ if (abs(b->oldb->board[b->last_move.to]) > PAWN && !(b->hung_mask & (1<pboard[b->last_move.to])) ) { b->flags |= FLAG_EXTENDED; return depth+1; } } #endif razoring: if ((b->flags & FLAG_EXTENDED) || (b->oldb && (b->oldb->flags & FLAG_EXTENDED))) return depth; #if USE_RAZORING if (depth == 1 && v1.v < (testv.v - RAZOR_THRESHOLD) && b->oldb && !(b->oldb->flags & FLAG_EXTENDED)) { return 0; } #endif #if USE_ASYMMETRIC_RAZORING if (depth == 2 && computers_move(b) && v1.v < (testv.v - RAZOR_THRESHOLD) && b->oldb && !(b->oldb->flags & FLAG_EXTENDED)) { return 0; } #endif return depth; } #if APLINUX struct slave { unsigned index; int depth; int testv, v; int nodes; int mo_hits, mo_misses; int hash_hits, hash_misses; int maxply; int busy; short x; Move move; int pad; }; struct slave slave_in; struct slave slave_out; #endif static int quiesce_start_ply; static Eval quiesce(Position *b, Eval testv, int ply); static int try_black_capture(Position *b, int i, int j, int v1, int testv, Move *m1) { #if USE_QUIESCE_SHORTCUT int v2; if (same_move(&b->best_capture, m1)) return 0; /* always try captures that might lead to check */ if (!(b->flags & FLAG_COMPUTER_WHITE)) { if (capture_map[b->board[m1->from]+KING][m1->to][BLACKPIECES(b)[IKING].pos]) return 1; /* always try a recapture */ if (m1->to == b->last_move.to) return 1; } /* estimate how much this capture is worth */ v2 = v1 + mat_value(-b->board[m1->to]); /* be optimistic about the positional values */ if (b->piece_values[i] < 0) v2 += -b->piece_values[i]; if (b->piece_values[j] < 0) v2 += -b->piece_values[j]; /* if its not hung and the capturing piece isn't hung then presume we will lose the piece we are capturing with */ if (!(b->hung_mask & (1<hung_mask & (1<board[m1->from]); /* don't capture if the estimated value is well below testv */ if (v2 < testv - FUTILE_THRESHOLD) return 0; #endif return 1; } static int try_white_capture(Position *b, int i, int j, int v1, int testv, Move *m1) { #if USE_QUIESCE_SHORTCUT int v2; if (same_move(&b->best_capture, m1)) return 0; if (b->flags & FLAG_COMPUTER_WHITE) { /* always try captures that might lead to check */ if (capture_map[b->board[m1->from]+KING][m1->to][WHITEPIECES(b)[IKING].pos]) return 1; /* always try a recapture */ if (m1->to == b->last_move.to) return 1; } /* estimate how much this capture is worth */ v2 = v1 + mat_value(b->board[m1->to]); /* be optimistic about the positional values */ if (b->piece_values[i] > 0) v2 += b->piece_values[i]; if (b->piece_values[j] > 0) v2 += b->piece_values[j]; /* if its not hung and the capturing piece isn't hung then presume we will lose the piece we are capturing with */ if (!(b->hung_mask & (1<hung_mask & (1<board[m1->from]); if (v2 < testv - FUTILE_THRESHOLD) return 0; #endif return 1; } static Eval quiesce_check(Position *b, Eval testv, int ply) { Eval g, v; Move *moves; int num_moves, m; Position b1; if (!(b->flags & FLAG_EVAL_DONE)) { eval(b, testv.v, 1); } /* check is a special case. All moves are generated and the player can't elect to just "sit" */ if (!b->moves) gen_move_list(b, ply, NULL, testv); moves = b->moves; num_moves = b->num_moves; g = makeeval(b, ILLEGAL); for (m=0;m %d (%d) testv=%d QCheck\n", whites_move(b)?"W":"B", b->move_num, short_movestr(b, &b->last_move), short_movestr(b, &moves[m]), v.v, g.v, testv.v); } #endif if (v.v > g.v) { g = v; if (g.v >= testv.v) break; } } insert_hash(b, 0, testv, g, NULL); if (g.v > FORCED_WIN) { g.v -= 1; } else if (g.v < -FORCED_WIN) { g.v += 1; } return g; } static Eval quiesce(Position *b, Eval testv, int ply) { Move m1; Eval g, v1, v; int i, j; uint32 pieces, topieces; Position b1; if (ply != quiesce_start_ply) { quiesce_nodes++; } if (ply > maxply) maxply = ply; if (testv.v > FORCED_WIN && testv.v < WIN) { testv.v += 1; } else if (testv.v < -FORCED_WIN && testv.v > -WIN) { testv.v -= 1; } #if USE_HASH_TABLES if (check_hash(b, 0, testv, &g, NULL) == HASH_HIT) { #if TREE_PRINT_PLY if (ply < tree_print_ply) { lindent(2, ply); lprintf(2,"Quiesce hash hit v=%d hash1=%08x\n", g.v, b->hash1); } #endif if (g.v > FORCED_WIN) { g.v -= 1; } else if (g.v < -FORCED_WIN) { g.v += 1; } return g; } #endif if (!do_move_part2(b)) return seteval(testv, -ILLEGAL); if ((b->flags & FLAG_CHECK) && (ply < MAX_DEPTH)) { return quiesce_check(b, testv, ply); } zero_move(&m1); v1 = eval(b, testv.v, 0); g = v1; /* in the quiesce search the player has the option to "sit" and take the current evaluation */ if (g.v >= testv.v || ply >= MAX_DEPTH) { goto evaluated; } if (!(b->flags & FLAG_EVAL_DONE)) { v1 = eval(b, testv.v, 1); g = v1; if (g.v >= testv.v) { goto evaluated; } #if TEST_QUIESCE if (!(b->flags & FLAG_EVAL_DONE)) { lprintf(0,"eval not done!\n"); } #endif } #if USE_FUTILITY if (g.v < testv.v - FUTILE_THRESHOLD/2) { g.v = testv.v - FUTILE_THRESHOLD/2; #if STORE_LEAF_POS g.pos.flags |= FLAG_FUTILE; #endif } #endif if (whites_move(b)) { /* first see if we can promote */ pieces = b->pawns7th & WHITE_MASK; while (pieces) { i = ff_one(pieces); pieces &= ~(1<board[b->pieces[i].pos+NORTH]) continue; m1.from = b->pieces[i].pos; m1.to = b->pieces[i].pos + NORTH; if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) goto evaluated; } } #if USE_PAWNPUSH_QUIESCE /* push any passed pawns beyond the third rank */ if (b->wpassed_pawn_mask) { pieces = b->wpassed_pawn_mask; while (pieces) { i = ff_one(pieces); pieces &= ~(1<pieces[i].pos) < 3 || YPOS(b->pieces[i].pos) > 5) continue; if (b->board[b->pieces[i].pos+NORTH]) continue; m1.from = b->pieces[i].pos; m1.to = b->pieces[i].pos + NORTH; if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) goto evaluated; } } } #endif /* try the "best capture" move from the tactical eval */ m1 = b->best_capture; if (!is_zero_move(&m1) && b->board[m1.to] < 0 && b->board[m1.from] > 0 && (b->topieces[m1.to] & (1<pboard[m1.from])) && do_move_part1(&b1, b, &m1)) { v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) { goto evaluated; } } } #if USE_FAST_QUIESCE goto evaluated; #endif pieces = b->material_mask & BLACK_MASK; while (pieces) { i = ff_one(pieces); pieces &= ~(1<topieces[b->pieces[i].pos] & WHITE_MASK; if (!topieces) continue; /* loop over all the capture possabilities */ while (topieces) { #if TEST_QUIESCE int is_futile = 0; #endif j = fl_one(topieces); topieces &= ~(1<pieces[j].pos; m1.to = b->pieces[i].pos; if (!try_black_capture(b, i, j, v1.v, imax(g.v, testv.v), &m1)) { #if TEST_QUIESCE is_futile = 1; #else continue; #endif } if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { #if TEST_QUIESCE if (is_futile && v.v >= testv.v) { lprintf(0,"quiesce bug: v=%d g=%d testv=%d v1=%d mat=%d p1=%d p2=%d move=%s\n", v.v, g.v, testv.v, v1.v, mat_value(-b->board[m1.to]), b->piece_values[i], b->piece_values[j], short_movestr(b, &m1)); lprintf(0,"%s\n", position_to_ppn(b)); print_board(b->board); try_black_capture(b, i, j, v1.v, imax(g.v, testv.v), &m1); } #endif g = v; if (g.v >= testv.v) { goto evaluated; } } } } /* one more chance - an enpassent capture */ if (!b->enpassent) goto evaluated; topieces = b->topieces[b->enpassent] & WHITE_MASK & ~b->piece_mask; while (topieces) { j = ff_one(topieces); topieces &= ~(1<pieces[j].pos; m1.to = b->enpassent; if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) goto evaluated; } } } else { /* first see if we can promote */ pieces = b->pawns7th & BLACK_MASK; while (pieces) { i = ff_one(pieces); pieces &= ~(1<board[b->pieces[i].pos+SOUTH]) continue; m1.from = b->pieces[i].pos; m1.to = b->pieces[i].pos + SOUTH; if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) goto evaluated; } } #if USE_PAWNPUSH_QUIESCE /* push any passed pawns beyond the third rank */ if (b->bpassed_pawn_mask) { pieces = b->bpassed_pawn_mask; while (pieces) { i = ff_one(pieces); pieces &= ~(1<pieces[i].pos) > 4 || YPOS(b->pieces[i].pos) < 2) continue; if (b->board[b->pieces[i].pos+SOUTH]) continue; m1.from = b->pieces[i].pos; m1.to = b->pieces[i].pos + SOUTH; if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) goto evaluated; } } } #endif /* try the "best capture" move from the tactical eval */ m1 = b->best_capture; if (!is_zero_move(&m1) && b->board[m1.to] > 0 && b->board[m1.from] < 0 && (b->topieces[m1.to] & (1<pboard[m1.from])) && do_move_part1(&b1, b, &m1)) { v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) { goto evaluated; } } } #if USE_FAST_QUIESCE goto evaluated; #endif pieces = b->material_mask & WHITE_MASK; while (pieces) { i = ff_one(pieces); pieces &= ~(1<topieces[b->pieces[i].pos] & BLACK_MASK; if (!topieces) continue; /* loop over all the capture possabilities */ while (topieces) { #if TEST_QUIESCE int is_futile = 0; #endif j = fl_one(topieces); topieces &= ~(1<pieces[j].pos; m1.to = b->pieces[i].pos; if (!try_white_capture(b, i, j, v1.v, imax(g.v, testv.v), &m1)) { #if TEST_QUIESCE is_futile = 1; #else continue; #endif } if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { #if TEST_QUIESCE if (is_futile && v.v >= testv.v) { lprintf(0,"quiesce bug: v=%d g=%d testv=%d v1=%d mat=%d p1=%d p2=%d move=%s\n", v.v, g.v, testv.v, v1.v, mat_value(b->board[m1.to]), b->piece_values[i], b->piece_values[j], short_movestr(b, &m1)); lprintf(0,"%s\n", position_to_ppn(b)); print_board(b->board); } #endif g = v; if (g.v >= testv.v) { goto evaluated; } } } } /* one more chance - an enpassent capture */ if (!b->enpassent) goto evaluated; topieces = b->topieces[b->enpassent] & BLACK_MASK & ~b->piece_mask; while (topieces) { j = ff_one(topieces); topieces &= ~(1<pieces[j].pos; m1.to = b->enpassent; if (!do_move_part1(&b1, b, &m1)) continue; v = quiesce(&b1, flip(testv), ply+1); v.v = -v.v; if (v.v > g.v) { g = v; if (g.v >= testv.v) goto evaluated; } } } zero_move(&m1); evaluated: #if TREE_PRINT_PLY if (ply < tree_print_ply) { lindent(2, ply); lprintf(2, "%s %d (%s) %s -> %d v1=%e testv=%e %s Quiesce\n", whites_move(b)?"W":"B", b->move_num, short_movestr(b, &b->last_move), short_movestr(b, &m1), g.v, v1.v, testv.v, (b->flags & FLAG_EVAL_DONE)?"":"quick"); } #endif if (g.v > FORCED_WIN) { g.v -= 1; } else if (g.v < -FORCED_WIN) { g.v += 1; } return g; } static Eval start_quiesce(Position *b, Eval testv, int ply) { quiesce_start_ply = ply+1; if (ply > maxdepth) maxdepth = ply; if (ply > search_depth) extended_nodes++; if (ply < search_depth) truncated_nodes++; nodes++; return quiesce(b, testv, ply+1); } /* this is like alpha-beta but only does a single null window search. It assumes that beta=testv and alpha=testv-1. */ static Eval abtest(Position *b, int depth0, Eval testv, int ply) { Move *moves; int num_moves, m; int bm = -1; Eval g; Eval v1, v; int depth = depth0; int mate_threat = 0; Move m1; if (depth <= 0) { return start_quiesce(b, testv, ply); } nodes++; if (ply > maxply) maxply = ply; v1 = makeeval(b, INFINITY); if (testv.v > FORCED_WIN && testv.v < WIN) { testv.v += 1; } else if (testv.v < -FORCED_WIN && testv.v > -WIN) { testv.v -= 1; } zero_move(&m1); #if USE_HASH_TABLES if (check_hash(b, depth, testv, &v1, &m1) == HASH_HIT) { #if TREE_PRINT_PLY if (ply < tree_print_ply) { lindent(2, ply); lprintf(2,"hash hit depth=%d v=%d move=%s hash1=%08x\n", depth, v1.v, short_movestr(b, &m1), b->hash1); } #endif if (v1.v > FORCED_WIN) { v1.v -= 1; } else if (v1.v < -FORCED_WIN) { v1.v += 1; } return v1; } #if 0 /* this lowers the number of null move trees we traverse pointlessly */ if (null_ply[ply-1] && check_hash(b,(depth-1), testv, &v1, &m1) == HASH_HIT && v1.v >= testv.v) { return v1; } #endif #endif if (!do_move_part2(b)) { return seteval(testv, -ILLEGAL); } if (v1.v == INFINITY) { v1 = eval(b, testv.v, depth); } m = -1; depth = depth_extensions(b, depth, ply, v1, testv); if (depth <= 0) { return start_quiesce(b, testv, ply); } g = makeeval(b, ILLEGAL); #if USE_NULL_MOVE if (ply < MAX_DEPTH && (b->piece_mask & player_mask(b) & ~KING_MASK) && !null_ply[ply-1] && #if USE_ASYMMETRIC_NULLMOVE (!computers_move(b) || depth > 2) && #endif !(b->flags & FLAG_CHECK) && (whites_move(b)?b->white_moves:b->black_moves) > NULL_MOVE_THRESHOLD) { int enpassent_saved = b->enpassent; int depth2; #if USE_NULLMOVE_FIXUP Move cap_saved = b->best_capture; etype tactics_saved = b->tactics; etype eval_saved = b->eval_result; uint32 flags_saved = b->flags; #endif depth2 = depth-4; #if USE_ASYMMETRIC_NULLMOVE /* this stops some of the worst null move blunders */ if (computers_move(b)) depth2 = imax(depth2, 1); #endif null_ply[ply] = 1; #if TEST_NULL_MOVE b->flags &= ~FLAG_EVAL_DONE; b->flags &= ~FLAG_DONE_TACTICS; v = eval(b,INFINITY,MAX_DEPTH); if (v.v != eval_saved*next_to_play(b)) { lprintf(0,"***Incremental error %d %d\n", v.v, eval_saved*next_to_play(b)); } #endif b->enpassent = 0; b->move_num++; moves = b->moves; num_moves = b->num_moves; b->moves = NULL; b->num_moves = 0; b->hash1 ^= 1; #if USE_NULLMOVE_FIXUP update_pawns(b); update_tactics(b); #endif #if TEST_NULL_MOVE { etype temp_result; if ((b->flags & FLAG_EVAL_DONE) && (b->flags & FLAG_DONE_TACTICS) && !(b->flags & FLAG_PROMOTE)) { b->flags &= ~FLAG_EVAL_DONE; b->flags &= ~FLAG_DONE_TACTICS; temp_result = b->eval_result*next_to_play(b); v = eval(b, INFINITY, MAX_DEPTH); if (v.v != temp_result) { lprintf(0, "****Null move error: %d %d %d\n", v.v, temp_result, whites_move(b)); if (b->fifty_count < 20) { print_board(b->board); lprintf(0,"\n***After***\n"); eval_debug(b); b->move_num--; b->flags &= ~FLAG_EVAL_DONE; b->flags &= ~FLAG_DONE_TACTICS; lprintf(0,"\n***Before***\n"); eval_debug(b); exit(1); } } } } #endif #if 0 { int t2 = b->tactics; b->flags &= ~FLAG_DONE_TACTICS; if (t2 != eval_tactics(b)) { lprintf(0,"bad tactics %d %d %d\n", tactics_saved, t2, b->tactics); } } #endif v = abtest(b, depth2, flip(testv), ply+1); v.v = -v.v; #if CONFIRM_NULL_MOVES if (v.v >= testv.v && !computers_move(b)) { int old_nodes = nodes + quiesce_nodes; b->moves = NULL; b->num_moves = 0; v = abtest(b, depth2+1, flip(testv), ply+1); v.v = -v.v; confirm_nodes += (nodes + quiesce_nodes - old_nodes); } #endif #if USE_NULLMOVE_FIXUP b->tactics = tactics_saved; b->best_capture = cap_saved; b->eval_result = eval_saved; b->flags = flags_saved; #endif b->hash1 ^= 1; b->move_num--; b->enpassent = enpassent_saved; b->moves = moves; b->num_moves = num_moves; null_ply[ply] = 0; /* don't let it see false zugzwang mates */ if (v.v >= WIN) { v.v -= STATIC_PAWN_VALUE; } if (v.v >= testv.v) { g = v; goto evaluated; } if (v.v <= -FORCED_WIN) { /* they are threatening mate! extend */ if ((depth+ply) < search_depth+MAX_EXTENSIONS) { b->flags |= FLAG_EXTENDED; mate_threat = 1; depth += 1; } } } #endif #if USE_PESSIMISTIC_PRUNING if (depth <= 3 && computers_move(b)) { v = quiesce(b, testv, ply+1); if (v.v < testv.v - STATIC_PAWN_VALUE/2) { v.v += STATIC_PAWN_VALUE/4; g = v; goto evaluated; } } #endif if (!b->moves) { gen_move_list(b, ply, &m1, testv); moves = b->moves; num_moves = b->num_moves; for (m=0;mmoves; num_moves = b->num_moves; if (num_moves == 0) { if (b->flags & FLAG_CHECK) g = makeeval(b, ILLEGAL); else g = makeeval(b, 0); goto evaluated; } #if USE_SINGULAR_EXTENSION if ((b->flags & FLAG_CHECK) && num_moves == 1 && b->oldb && !(b->oldb->flags & FLAG_EXTENDED) && (depth+ply) < search_depth+MAX_EXTENSIONS) { b->flags |= FLAG_EXTENDED; depth++; } #endif if (depth > depth0 + 1) depth = depth0 + 1; #if USE_ETTC_HASH if (ettc_check_hash(b, moves, num_moves, depth, testv, &v1, NULL)) { if (v1.v > FORCED_WIN) { v1.v -= 2; } else if (v1.v < -FORCED_WIN) { v1.v += 2; } return v1; } #endif for (m=0;m 0 && g.v > testv.v - ABS(PAWN_VALUE)/2) { v2.v = eval_move_tactics(b, &moves[m]); if (v2.v < 0 && v2.v + v1.v < testv.v - 0.8*ABS(PAWN_VALUE)) { continue; } } #endif #if APLINUX if (slave_in.index != slave_out.index) { return g; } #endif if (!do_move_part1(&b1, b, &moves[m])) { moves[m].v = ILLEGAL; continue; } if (check_repitition(&b1, 1)) { v = makeeval(b, draw_value(b) * next_to_play(b)); #if STORE_LEAF_POS v.pos.flags |= FLAG_FORCED_DRAW; #endif } else { if (depth > depth0 && !mate_threat && !(b->flags & FLAG_CHECK)) { v = abtest(&b1, depth0-1, flip(testv), ply+1); v.v = -v.v; if (computers_move(b)) { if (v.v != ILLEGAL && v.v >= testv.v) { b1.moves = NULL; b1.num_moves = 0; v = abtest(&b1, depth-1, flip(testv), ply+1); v.v = -v.v; } } else { if (v.v != ILLEGAL && v.v < testv.v) { b1.moves = NULL; b1.num_moves = 0; v = abtest(&b1, depth-1, flip(testv), ply+1); v.v = -v.v; } } } else { v = abtest(&b1, depth-1, flip(testv), ply+1); v.v = -v.v; } } moves[m].v = v.v; if (search_expired()) { return g; } if (v.v > g.v) { g = v; bm = m; } #if TREE_PRINT_PLY if (ply < tree_print_ply) { lindent(2, ply); lprintf(2, "%s %d (%s) %s -> %d (%d) testv=%d depth=%d\n", whites_move(b)?"W":"B", b->move_num, short_movestr(b, &b->last_move), short_movestr(b, &moves[m]), v.v, g.v, testv.v, depth0); } #endif if (g.v >= testv.v) { if (m == 0) { move_order_hits++; } else { move_order_misses++; } if (m==1) move_order_hits1++; else if (m==2) move_order_hits2++; else if (m==3) move_order_hits3++; else if (m!=0) move_order_hits4++; cutoff_hint(b, m, depth, ply); goto end_loop; } } end_loop: #if APLINUX if (slave_in.index != slave_out.index) { return 0; } #endif evaluated: if (search_expired()) { return g; } if (g.v == ILLEGAL && !(b->flags & FLAG_CHECK)) { g = makeeval(b, 0); } #if TREE_PRINT_PLY if (ply < tree_print_ply) { lindent(2, ply); lprintf(2,"hash insert depth=%d v=%d hash1=%08x testv=%d\n", depth0, g.v, b->hash1, testv.v); } #endif insert_hash(b, depth0, testv, g, bm>=0?&moves[bm]:NULL); if (g.v > FORCED_WIN) { g.v -= 1; } else if (g.v < -FORCED_WIN) { g.v += 1; } return g; } /* this is the abtest routine for the root node */ static Eval abroot(Position *b, int depth, Eval testv, Move *move) { Move *moves, m1; int num_moves, m; Eval g; Eval v; Position b1; search_depth = depth; g = makeeval(b, ILLEGAL); zero_move(&m1); nodes++; #if USE_HASH_TABLES if (check_hash(b, depth, testv, &v, &m1) == HASH_HIT && !is_zero_move(&m1) && legal_move(b, &m1)) { do_move(&b1, b, &m1); if (!check_repitition(&b1, 1)) { (*move) = m1; return v; } } #endif moves = b->moves; num_moves = b->num_moves; #if USE_ETTC_HASH if (ettc_check_hash(b, moves, num_moves, depth, testv, &v, move)) { return v; } #endif if (!is_zero_move(move)) m1 = (*move); order_moves(b, moves, num_moves, 0, &m1, testv); for (m=0;m g.v) { g = v; } #if TREE_PRINT_PLY if (0 < tree_print_ply) { lindent(2, 0); lprintf(2, "%s %d (%s) %s -> %d (%d) testv=%d depth=%d\n", whites_move(b)?"W":"B", b->move_num, short_movestr(b, &b->last_move), short_movestr(b, &moves[m]), v.v, g.v, testv.v, depth); } #endif if (g.v >= testv.v) { if (!same_move(&moves[m], move)) { lprintf(0,"%s %s\n", short_movestr(b, &moves[m]), evalstr(g.v)); } (*move) = moves[m]; goto end_loop; } } end_loop: if (g.v == ILLEGAL && !(b->flags & FLAG_CHECK)) { g = makeeval(b, 0); } #if TREE_PRINT_PLY if (0 < tree_print_ply) { lindent(2, 0); lprintf(2,"hash insert depth=%d v=%d hash1=%08x testv=%d\n", depth, g.v, b->hash1, testv.v); } #endif insert_hash(b, depth, testv, g, move); return g; } /* generate the root node moves list */ static void root_moves(Position *b, Move *move) { Move *moves; int num_moves, m; if (b->moves) return; /* generate the root node move list */ gen_move_list(b, 0, move, makeeval(b,0)); moves = b->moves; num_moves = b->num_moves; for (m=0;mnum_moves = num_moves; } static int is_checkmate(Position *b) { Move *moves, move; int num_moves, m, checkmate=1; Position b1; root_moves(b, &move); moves = b->moves; num_moves = b->num_moves; for (m=0;mposition; order_clear(b.move_num); hash_change_tag(b.move_num); v = abtest(&b, slave_out.depth, slave_out.testv, &m1); if (slave_in.index != slave_out.index) continue; slave_out.v = v; slave_out.nodes = nodes; slave_out.mo_hits = move_order_hits; slave_out.mo_misses = move_order_misses; slave_out.hash_hits = hash_hits; slave_out.hash_misses = hash_misses; slave_out.maxply = maxply; slave_out.move = m1; l_asend(0, 0, 0, &slave_out, sizeof(slave_out)); nodes = 0; move_order_hits = move_order_misses = 0; hash_hits = hash_misses = 0; maxply = 0; } } #endif #if APLINUX static Eval next_to_search(Eval v,struct slave *slaves, Eval alpha, Eval beta, int n) { int i; int j; Eval y; int maxwidth = 5; if (v == alpha) { v += MTD_THRESHOLD + imin((beta - alpha)/10, maxwidth); } else if (v == beta) { v -= (MTD_THRESHOLD-1) + imin((beta - alpha)/10, maxwidth); } i = 0; while (v+i <= beta || v-i > alpha) { y = v+i; if (y <= beta) { for (j=1;j= alpha) { for (j=1;jmove_num); slave_in.index = 0; for (n=1;n alpha) { v = alpha = g; (*move) = slave_in.move; } } slaves[n].busy = 0; /* abort any processors that are searching out of window */ for (n=1;n beta)) { slaves[n].busy = 0; } } return v; } #endif #if !APLINUX static int mtd_converged(Position *b, int alpha, int beta, int depth, Eval *y) { int m; int count=0, lower, upper; struct hash_entry *t; int count_alpha=0; /* its converged if only one move has an upper limit on its evaluation of >= alpha */ if (!b->moves) { return 0; } for (m=0;mnum_moves;m++) { Position b1; if (!do_move(&b1, b, &b->moves[m])) { lprintf(0, "illegal move %s in mtd_converged\n", short_movestr(b, &b->moves[m])); continue; } t = fetch_hash(&b1); if (!t) { return 0; } if (t->depth_high < (depth-1)) { return 0; } upper = (-t->low.v); lower = (-t->high.v); if (lower > -INFINITY) count_alpha++; #if 0 if (lower >= alpha && upper <= beta && lower < upper) y->v = (lower+upper+1)/2; #endif if (upper > alpha) count++; } if (count == 1) return 1; return 0; } #endif static int converged; static int small_window; static int mtd_count; static Eval mtd_search(Position *b, Move *move, int depth, Eval guess) { Eval alpha, beta, y, g; int loops=0; int alpha_count=0; int beta_count=0; #if SEARCH_DEBUG struct hash_entry *h; #endif alpha = makeeval(b, ILLEGAL); beta = makeeval(b, -ILLEGAL); g = guess; converged = 0; small_window = 0; mtd_count = 0; /* generate the move list */ root_moves(b, move); /* try to get a lower bound from the hash table */ #if USE_ETTC_HASH if (ettc_check_hash(b, b->moves, b->num_moves, depth, makeeval(b, ILLEGAL), &alpha, move)) { g = alpha; } #endif while (alpha.v == ILLEGAL && beta.v > alpha.v) { static float offsets[7] = { 0.5, 2, 6, 13, 30, 30, 30}; if (loops > 6) break; y.v = g.v - offsets[loops]*STATIC_PAWN_VALUE; if (y.v <= alpha.v) y.v = alpha.v + 0.01*STATIC_PAWN_VALUE; if (y.v >= beta.v) y.v = beta.v - 0.01*STATIC_PAWN_VALUE; g = abroot(b, depth, y, move); mtd_count++; loops++; if (search_expired()) { if (alpha.v != ILLEGAL) return alpha; return guess; } #if SEARCH_DEBUG lprintf(0,"mtd g=%d y=%d alpha=%d beta=%d nodes=%d %s\n", g.v, y.v, alpha.v, beta.v, nodes, short_movestr(b, move)); #endif if (g.v < y.v) { beta = g; } else { alpha = g; } } if (alpha.v == beta.v) { small_window = 1; return alpha; } /* we've now got a fail high, so we have a lower limit on our eval and a likely best move. */ if (mtd_count == 1 && g.v < guess.v) { g.v = emax(guess.v - 0.5*MTD_THRESHOLD, g.v); } /* if we got something from the hash table then use that to prime g */ if (mtd_count == 0) { g.v += MTD_THRESHOLD; } while (beta.v > alpha.v+10) { loops++; y = g; if (g.v == alpha.v) { alpha_count++; beta_count=0; y.v += alpha_count*alpha_count*MTD_THRESHOLD; y.v = emin(y.v, (alpha.v + beta.v)/2); } else if (g.v == beta.v) { alpha_count=0; beta_count++; y.v -= beta_count*beta_count*MTD_THRESHOLD; y.v = emax(y.v, (alpha.v + beta.v)/2); } if (alpha.v > ILLEGAL && beta.v < -ILLEGAL && !mulling && mtd_converged(b, alpha.v, beta.v, depth, &y)) break; g = abroot(b, depth, y, move); mtd_count++; if (search_expired()) { if (alpha.v != ILLEGAL) return alpha; return guess; } if (g.v < y.v) { beta = g; } else { alpha = g; } #if SEARCH_DEBUG lprintf(0,"mtd g=%d y=%d alpha=%d beta=%d nodes=%d %s\n", g.v, y.v, alpha.v, beta.v, nodes, short_movestr(b, move)); h=fetch_hash(b); if (h) { lprintf(0, "hash entry: %d/%d %d/%d %s%s\n", h->low.v, h->high.v, h->depth_low, h->depth_high, posstr(h->from), posstr(h->to)); } #endif if (alpha.v > WIN || beta.v < -WIN) { converged = 1; break; } if (alpha.v >= beta.v - MTD_THRESHOLD && (mtd_count > 30 || (abs(alpha.v) < FORCED_WIN && !mulling))) { break; } } if (alpha.v == ILLEGAL) alpha = g; if (alpha.v >= beta.v - MTD_THRESHOLD) small_window = 1; return alpha; } static Eval search_one_depth(Position *b, Move *move, int depth, Eval guess) { move_order_misses = 0; move_order_hits = 0; move_order_hits1 = 0; move_order_hits2 = 0; move_order_hits3 = 0; move_order_hits4 = 0; #if APLINUX return par_search(b, move, depth, guess); #else return mtd_search(b, move, depth, guess); #endif } static void show_pv(Position *b, Move *move, int exporting) { Move m1; int i=0; struct hash_entry *t; Position b1; if (exporting) { status_printf(0, "PV: %s ", short_movestr(b, move)); } else { lprintf(0,"PV: %s ", short_movestr(b, move)); } do_move(&b1, b, move); for (i=1;ifrom; m1.to = t->to; if (is_zero_move(&m1)) break; if (exporting) { status_printf(0, "%s(%4.2lf:%4.2lf) ", short_movestr(&b1, &m1), (double)(t->low.v)/STATIC_PAWN_VALUE, (double)(t->high.v)/STATIC_PAWN_VALUE); } else { lprintf(0,"%s(%d:%d) ", short_movestr(&b1, &m1), t->low.v, t->high.v); } if (!do_move(&b1, &b1, &m1)) break; } if (exporting) { status_printf(0, "\n"); } else { lprintf(0,"\n"); } } static void show_eval(Position *b, Eval v, int edepth) { #if LEARN_EVAL int m = state->stored_move_num; Position b1; Eval v1; float f1,f2; state->leaf_eval[m].flags = 0; /* sometimes we get an empty board (!) */ if (!v.pos.flags) { lprintf(0,"empty board in show eval\n"); return; } /* occasionally we get a futility node here */ if (v.pos.flags & FLAG_FUTILE) { lprintf(0,"****futility node\n"); return; } /* EGTB nodes are no good */ if (v.pos.flags & FLAG_EGTB_EVAL) { lprintf(0,"****EGTB node\n"); return; } /* all features are ignored for forced draws */ if ((v.pos.flags & FLAG_FORCED_DRAW) || (v.pos.flags & FLAG_ACCEPT_DRAW)) { lprintf(0,"****forced draw\n"); return; } /* leaf position is irrelevant for forced mate */ if (v.v < -FORCED_WIN || v.v > FORCED_WIN) { lprintf(0,"****forced win %d\n", v.v); return; } memset(&b1, 0, sizeof(b1)); memcpy(b1.board, v.pos.board, sizeof(b1.board)); b1.move_num = v.pos.move_num; b1.stage = v.pos.stage; b1.flags = v.pos.flags; b1.enpassent = v.pos.enpassent; b1.fifty_count = v.pos.fifty_count; b1.flags &= ~FLAG_EVAL_DONE; b1.flags &= ~FLAG_DONE_TACTICS; create_pboard(&b1); v1 = eval(&b1, INFINITY, MAX_DEPTH); lprintf(0,"eval: %d %d flags=%08x move=%d hash1=%08x\n %s\n", v.v*next_to_play(b), v1.v*next_to_play(&b1), v.pos.flags, v.pos.move_num, b1.hash1, position_to_ppn(&b1)); f1 = tanh(v.v*EVAL_SCALE*next_to_play(b)); f2 = tanh(v1.v*EVAL_SCALE*next_to_play(&b1)); if (ABS(f1-f2) < EVAL_DIFF) { td_store_pos(&b1); } else { lprintf(0,"***EVAL DIFF***\n %d %d\n", v.v*next_to_play(b), v1.v*next_to_play(&b1)); print_board(v.pos.board); eval_debug(&b1); hash_reset(); } #endif } static int terrible_move(Position *b, Move *move, Eval *v, int depth) { struct hash_entry *t; Move m1; Position b1; do_move(&b1, b, move); t = fetch_hash(&b1); if (!t) return 0; m1.from = t->from; m1.to = t->to; if (t->high.v > ABS(PAWN_VALUE)-v->v) { lprintf(0,"unbounded: %s %d low=%d high=%d %s\n", short_movestr(b, move), v->v, t->low.v, t->high.v, short_movestr(&b1, &m1)); v->v = (-t->low.v) - 2; return 1; } return 0; } static Eval search(Position *b, Move *move) { extern int display_pid; Eval v, lastv; int edepth; int depth; int tdepth; int terminate=0; Eval guess, v1, lastv1; int expired, force; int counter; struct hash_entry *t; Move last_move; nodes = maxply = maxdepth = 0; quiesce_nodes = 0; confirm_nodes = 0; extended_nodes = 0; truncated_nodes = 0; hash_reset_stats(); v.v = lastv.v = 0; order_clear(b->move_num); hash_change_tag(b->move_num); depth = 2; t = fetch_hash(b); if (t && !t->move_num) { move->from = t->from; move->to = t->to; if (!is_zero_move(move)) { depth = imax(t->depth_low, depth); v = t->low; if (t->depth_high >= t->depth_low && t->high.v <= t->low.v + MTD_THRESHOLD) depth++; } else { depth = 2; } } edepth = depth-1; zero_move(&last_move); while (!terminate && depth < MAX_DEPTH) { if (pondering && ponder_stop) break; if (!process_exists(display_pid)) break; guess = v; lastv = v; counter = 0; again: counter++; v = search_one_depth(b, move, depth, guess); expired = timer_expired(); if (pondering) expired = 0; force = 0; if (!mulling && b->num_moves == 1) { force = 1; } if (!pondering && !force && expired && !converged && !mulling && terrible_move(b, move, &v, depth)) { if (counter < 4 && timer_extend()) { guess = v; lprintf(0,"overtime (guess=%d)\n", guess.v); goto again; } } if (pondering || edepth <= 2) { terminate = 0; } else { if (!search_expired()) edepth = depth; if (!mulling || edepth >= state->current_depth) terminate = timer_terminate(depth, next_to_play(b), force); } if (!search_expired()) edepth = depth; else terminate = 1; lprintf(0, "%s%s: %s d=%d n=%d c/q/e/t=%d/%d/%d/%d%% mo=%d%% %s mtd=%d t=%.1f %s nps=%d\n", pondering?"** ":"", colorstr(next_to_play(b)), evalstr(v.v), edepth, nodes, (100*confirm_nodes)/(nodes+1), (100*quiesce_nodes)/(nodes+1), (100*extended_nodes)/(nodes+1), (100*truncated_nodes)/(nodes+1), (100*move_order_hits)/(move_order_misses+move_order_hits+1), hashstats(), mtd_count, timer_elapsed(), short_movestr(b, move), (int)((nodes+quiesce_nodes)/timer_elapsed())); hash_hits = hash_misses= hash_shallow = 0; if (edepth > 0 && depth > 4) { status_printf(1, "%s%s: %s d=%d ", pondering?"** ":"", colorstr(next_to_play(b)), evalstr(v.v), edepth); show_pv(b,move,1); } if (!mulling && next_to_play(b) != state->computer) { v1 = v; v1.v = -v1.v; lastv1 = lastv; lastv1.v = -lastv1.v; } else { v1 = v; lastv1 = lastv; } if (state->ics_robot && edepth > 3 && !mulling) { if (!pondering && v1.v > lastv1.v + 4*STATIC_PAWN_VALUE && lastv1.v < 3*STATIC_PAWN_VALUE && v1.v > -2*STATIC_PAWN_VALUE) { prog_printf("whisper Eval=%s yeah!\n", evalstr(v1.v)); } else if (!pondering && lastv1.v < 0.9*STATIC_PAWN_VALUE && v1.v > 1.3*STATIC_PAWN_VALUE) { prog_printf("whisper Eval=%s looking good\n", evalstr(v1.v)); } else if (v1.v < lastv1.v - 0.9*STATIC_PAWN_VALUE && lastv1.v < 0) { prog_printf("whisper Eval=%s uh oh\n", evalstr(v1.v)); } else if (v1.v < lastv1.v - 4*STATIC_PAWN_VALUE && lastv1.v < 4*STATIC_PAWN_VALUE) { prog_printf("whisper Eval=%s damn\n", evalstr(v1.v)); } } if (v.v <= -(WIN-edepth) || v.v >= (WIN-edepth)) terminate = 1; if (terminate && !pondering && state->ics_robot && state->move_time > 0.7 && !mulling) { prog_printf("whisper Eval=%s nps=%d depth=%d cpu=%.1f%%\n", evalstr(v1.v), (int)((nodes+quiesce_nodes)/timer_elapsed()), edepth, cpu_percent()); } if (terminate) { if (!mulling && !pondering) { show_pv(b,move,0); #if LEARN_EVAL if (v.v > -FORCED_WIN || v.v < FORCED_WIN) { if (small_window) { show_eval(b,v,edepth); } else { show_eval(b,lastv,edepth); } } #endif } } depth++; if (!pondering && (small_window || ABS(v.v) > FORCED_WIN || is_zero_move(&last_move))) { state->converged_record[state->position.move_num] = *move; } else { state->converged_record[state->position.move_num] = last_move; } last_move = *move; #if USE_PBRAIN if (mull_stage == 2) tdepth = state->current_depth; else tdepth = 6; if (!pondering && mulling && ((depth >= tdepth && (!terminate || !state->converged)) || ABS(v.v) > FORCED_WIN)) { int tmp = brain_insert(b, move); if (!tmp) state->current_depth = depth; } #endif } #if USE_PBRAIN if (!pondering && !mulling) brain_insert(b, move); #endif return v; } static int find_winner(Position *b, Move *move) { static Move moves[MAX_MOVES]; int n, i; int bkpos = BLACKPIECES(b)[IKING].pos; int wkpos = WHITEPIECES(b)[IKING].pos; Position b1; b1 = (*b); if (whites_move(b) && (b1.topieces[bkpos] & WHITE_MASK)) { lprintf(0,"black is mated\n"); b->winner = 1; return 1; } if (blacks_move(b) && (b1.topieces[wkpos] & BLACK_MASK)) { lprintf(0,"white is mated\n"); b->winner = -1; return 1; } if (check_repitition(b, 3)) { lprintf(0,"claim a draw\n"); if (!mulling) prog_printf("draw\n"); b->winner = STALEMATE; return 1; } if (state->demo_mode && (b->flags & FLAG_ACCEPT_DRAW)) { lprintf(0, "draw\n"); b->winner = STALEMATE; } if (move && legal_move(b, move)) { do_move(&b1, b, move); } if (check_repitition(&b1, 3)) { lprintf(0,"claim a draw\n"); b->winner = STALEMATE; return 1; } if (((b->material_mask & WHITE_MASK) == (b->piece_mask & WHITE_MASK) && b->w_material <= BISHOP_VALUE) && ((b->material_mask & BLACK_MASK) == (b->piece_mask & BLACK_MASK) && b->b_material <= BISHOP_VALUE)) { /* both white and black have insufficient material to mate */ lprintf(0,"insufficient material on either side\n"); if (!mulling) prog_printf("draw\n"); b->winner = STALEMATE; return 1; } n = generate_moves(&b1, moves); for (i=0;iwinner = -1; return 1; } if (blacks_move(&b1) && (b1.topieces[bkpos] & WHITE_MASK)) { lprintf(0,"black is mated\n"); b->winner = 1; return 1; } b->winner = STALEMATE; return 1; } /* this is the entry point for the chess program proper */ int make_move(Position *b, Move *move) { Position b1; Eval bestv, v1; int result; if (next_to_play(b) == 1) b->flags |= FLAG_COMPUTER_WHITE; else b->flags &= ~FLAG_COMPUTER_WHITE; ponder_stop = 0; state->stop_search = 0; b->winner = 0; zero_move(move); init_hash_table(); brain_fill_hash(NULL); if (find_winner(b, NULL)) return 0; regen_moves(b); v1 = eval(b, INFINITY, MAX_DEPTH); estimate_game_stage(b,1); b1 = (*b); timer_start(next_to_play(&b1)); lprintf(0,"searching position: %s\n", position_to_ppn(b)); #if USE_PBRAIN if ((result = brain_lookup(b, move, &bestv, 0)) > 0) { state->converged_record[state->position.move_num] = *move; if (result == 1) state->last_book_move = state->position.move_num; goto got_move; } #endif bestv = search(&b1, move); got_move: if (b1.hash1 == b->hash1) { b->evaluation = bestv.v * next_to_play(&b1); /* don't carry on when its hopeless */ if ((state->ics_robot || state->auto_exit) && !mulling && bestv.v < -RESIGN_VALUE && v1.v < -RESIGN_VALUE) { lprintf(0,"time to resign\n"); if (state->auto_exit) { if (state->computer == 1) lprintf(0,"Black wins - KnightCap.xboard resigns\n"); else lprintf(0,"White wins - KnightCap.xboard resigns\n"); } b->winner = -next_to_play(b); prog_printf("resign\n"); } } move->v = b->evaluation; timer_off(); find_winner(b, move); return 1; } /* this is used to make KnightCap think on the oppenents time */ void ponder_move(Position *b, Move *move) { Position b1; Eval bestv, v1; Move m1; if (next_to_play(b) == -1) b->flags |= FLAG_COMPUTER_WHITE; else b->flags &= ~FLAG_COMPUTER_WHITE; zero_move(move); real_position = b; ponder_position = &b1; ponder_stop = 0; state->stop_search = 0; b->winner = 0; init_hash_table(); if (find_winner(b, NULL)) return; regen_moves(b); b1 = (*b); ponder_stop = 0; lprintf(0,"pondering position: %s\n", position_to_ppn(b)); timer_off(); zero_move(&m1); if (!check_hash(&b1, 0, makeeval(&b1, ILLEGAL), &v1, &m1) || is_zero_move(&m1)) { lprintf(0,"searching for move to ponder\n"); search_one_depth(&b1, &m1, 2, makeeval(b, 0)); } if (is_zero_move(&m1) || !legal_move(&b1, &m1) || !do_move(&b1, &b1, &m1)) { lprintf(0,"no move to ponder\n"); return; } lprintf(0,"assuming move %s\n", short_movestr(b, &m1)); if (is_checkmate(&b1)) { find_winner(b,NULL); return; } if (brain_lookup(&b1, &m1, &bestv, 1) > 0) { zero_move(move); return; } timer_start(next_to_play(&b1)); pondering = 1; v1 = eval(&b1, INFINITY, MAX_DEPTH); estimate_game_stage(&b1,1); bestv = search(&b1, move); pondering = 0; timer_off(); if (b1.hash1 != b->hash1) { zero_move(move); return; } lprintf(0,"pondered right move\n"); #if STORE_LEAF_POS state->predicted_move[state->stored_move_num] = 1; #endif b->evaluation = bestv.v * next_to_play(&b1); move->v = b->evaluation; /* don't carry on when its hopeless */ if (state->ics_robot && bestv.v < -RESIGN_VALUE && v1.v < -RESIGN_VALUE) { lprintf(0,"time to resign\n"); b->winner = -next_to_play(b); if (!mulling) prog_printf("resign\n"); } find_winner(b, move); }