Exemple #1
0
static int
handle_beta_extension(struct node *node, move m, int value)
{
	if (!node->common->sd.settings.use_beta_extensions)
		return value;

	if (value >= node->beta
	    && !node[-1].is_in_null_move_search
	    && is_in_check(node[1].pos)
	    && value < mate_value
	    && node->depth > PLY
	    && node->depth < 10 * PLY
	    && node->mo->picked_count > 1
	    && !is_capture(m)
	    && !is_promotion(m)
	    && mtype(m) != mt_castle_kingside
	    && mtype(m) != mt_castle_queenside) {
		node[1].alpha = -node->beta;
		node[1].beta = -node->alpha;
		node[1].depth = node->depth;
		return negamax_child(node);
	}
	else {
		return value;
	}
}
Exemple #2
0
static char*
print_san_move_from(const struct position *pos, move m,
			char *str, enum player turn)
{
	move moves[MOVE_ARRAY_LENGTH];
	uint64_t ambig_pieces = UINT64_C(0);
	enum piece p = pos_piece_at(pos, mfrom(m));

	(void) gen_moves(pos, moves);
	for (move *im = moves; *im != 0; ++im) {
		if ((mfrom(*im) != mfrom(m)) && (mto(*im) == mto(m))
		    && (pos_piece_at(pos, mfrom(*im)) == p))
			ambig_pieces |= mfrom64(*im);
	}
	if ((p == pawn) && is_capture(m)) {
		*(str++) = index_to_file_ch(mfrom(m));
	}
	else if (is_nonempty(ambig_pieces)) {
		if (is_nonempty(ambig_pieces & file64(mfrom(m)))) {
			if (is_nonempty(ambig_pieces & rank64(mfrom(m)))) {
				*(str++) = index_to_file_ch(mfrom(m));
			}
			*(str++) = index_to_rank_ch(mfrom(m), turn);
		}
		else {
			*(str++) = index_to_file_ch(mfrom(m));
		}
	}
	return str;
}
Exemple #3
0
static char*
print_san_move(const struct position *pos, move m, char *str, enum player turn)
{
	int piece = pos_piece_at(pos, mfrom(m));

	if (mtype(m) == mt_castle_kingside)
		return str + sprintf(str, "O-O");
	else if (mtype(m) == mt_castle_queenside)
		return str + sprintf(str, "O-O-O");
	if (piece != pawn)
		*str++ = (char)toupper((unsigned char)piece_to_char(piece));
	str = print_san_move_from(pos, m, str, turn);
	if (is_capture(m))
		*str++ = 'x';
	str = index_to_str(str, mto(m), turn);
	if (mtype(m) == mt_en_passant)
		return str + sprintf(str, "e.p.");
	str = print_san_promotion(m, str);
	str = print_san_check(pos, m, str);
	*str = '\0';
	return str;
}
Exemple #4
0
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth)
{
	int palpha = alpha;
	int i, score = -MATE, highest = -MATE;
	node_t node;
	move_t cutoff = 0;
	piece_t p;

	init_node(&node);

	assert(app);

	app->search.nodes++;
	node.depth = depth;

	/* max depth */
	if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) {
		/* return evaluate(app->board); */
		return quiescent(app, parent, alpha, beta);
	}

	/* recursive base */
	if (depth == 0) {
		return evaluate(app->game.board);
	}

	/* draws */
	if (repetitions(app) || (app->game.board->half >= 100)) {
		return 0;
	}

	/* if we are checked, set the nodes checked flag */
	if (check(app->game.board, app->game.board->side)) {
		node.flags |= NODE_CHECK;
		/* extend our search by 1 depth if we are in check */
		/* NOTES: we may want to NOT extend our search here if the parent
		   is in check, because the means we already extended once */
		depth++;
	}

	/* TODO:
	     - NULL moves
	     - Late-move reduction
	     - Tactical extensions (pins & forks -> depth++)
	 */

	/* probe our table */
	if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) {
		app->hash.cut++;
		return score;
	}

	/* generate moves */
	generate_moves(app->game.board, &node.ml, &node.ml);

	/* reset score */
	score = -MATE;

	/* try to match our hash hit move */
	if (cutoff != 0) {
		for (i = 0; i < node.ml.count; i++) {
			if (node.ml.moves[i] == cutoff) {
				node.ml.scores[i] = 20000;
				break;
			}
		}
	}

	/* search negamax */
	for (i = 0; i < node.ml.count; i++) {
		/* get the next move ordered */
		next_move(i, &node.ml);

		if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i])))
			continue;

		score = -alpha_beta(app, &node, -beta, -alpha, depth - 1);

		node.made++;
		undo_move(app->game.board, &app->game.undo);

		/* score whatever is best so far */
		if (score > highest) {
			node.best = node.ml.moves[i];
			highest = score;

			/* update alpha */
			if (score > alpha) {
				if (score >= beta) {

					/* non-captures causing beta cutoffs (killers) */
					if (!is_capture(node.ml.moves[i])) {
						app->game.board->killers[1][app->game.board->ply] =
							app->game.board->killers[0][app->game.board->ply];
						app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i];
					}

					/* store this beta in our transposition table */
					store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth);

					return beta;
				}

				/* update alpha */
				alpha = score;

				/* update our history */
				if (!is_capture(node.best)) {
					p = app->game.board->pos.squares[move_from(node.best)];
					app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth;
				}
			}
		}
	}

	/* check for checkmate or stalemate */
	if (!node.made) {
		if (node.flags & NODE_CHECK) {
			return -MATE + app->game.board->ply;
		} else {
			return 0;
		}
	}

	if (alpha != palpha) {
		/* store this as an exact, since we beat alpha */
		store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth);
	} else {
		/* store the current alpha */
		store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth);
	}

	return alpha;
}
Exemple #5
0
int Search::alphaBeta(bool white_turn, int depth, int alpha, int beta, Board& board, Transposition *tt,
		bool null_move_in_branch, Move (&killers)[32][2], int (&history)[64][64], int ply) {

	// If, mate we do not need search at greater depths
	if (board.b[WHITE][KING] == 0) {
		return -10000;
	} else if (board.b[BLACK][KING] == 0) {
		return 10000;
	}
	if (depth == 0) {
		return capture_quiescence_eval_search(white_turn, alpha, beta, board);
	}
	if (depth == 1) {
		// futility pruning. we do not hope for improving a position more than 300 in one move...
		int static_eval = evaluate(board);
		if (white_turn && (static_eval + 300) < alpha) {
			return capture_quiescence_eval_search(white_turn, alpha, beta, board);
		}
		if (!white_turn && (static_eval - 300) > beta) {
			return capture_quiescence_eval_search(white_turn, alpha, beta, board);
		}
	}
	if (depth == 2) {
		// extended futility pruning. we do not hope for improving a position more than 500 in two plies...
		// not really proven to +ELO but does not worse performance at least
		int static_eval = evaluate(board);
		if ((white_turn && (static_eval + 500) < alpha) || (!white_turn && (static_eval - 500) > beta)) {
			return capture_quiescence_eval_search(white_turn, alpha, beta, board);
		}
	}

	// null move heuristic - we do this despite in check..
	if (!null_move_in_branch && depth > 3) {
		// skip a turn and see if and see if we get a cut-off at shallower depth
		// it assumes:
		// 1. That the disadvantage of forfeiting one's turn is greater than the disadvantage of performing a shallower search.
		// 2. That the beta cut-offs prunes enough branches to be worth the time searching at reduced depth
		int R = 2; // depth reduction
		int res = alphaBeta(!white_turn, depth - 1 - R, alpha, beta, board, tt, true, killers, history, ply + 1);
		if (white_turn && res > alpha) {
			alpha = res;
		} else if (!white_turn && res < beta) {
			beta = res;
		}
		if (beta <= alpha) {
			if (white_turn) {
				return alpha;
			}
			return beta;
		}
	}
	MoveList moves = get_children(board, white_turn);
	if (moves.empty()) {
		return 0;
	}
	Transposition tt_pv = tt[board.hash_key % HASH_SIZE];
	for (auto it = moves.begin(); it != moves.end(); ++it) {
		// sort pv moves first
		if (tt_pv.next_move != 0 && tt_pv.hash == board.hash_key && tt_pv.next_move == it->m) {
			it->sort_score += 1100000;
		}
		// ...then captures in MVVLVA order
		if (!is_capture(it->m)) {
			// killer moves are quite moves that has previously led to a cut-off
			if (killers[ply - 1][0].m == it->m) {
				it->sort_score += 999999;
			} else if (killers[ply - 1][1].m == it->m) {
				it->sort_score += 899999;
			} else {
				// "history heuristics"
				// the rest of the quite moves are sorted based on how often they increase score in the search tree
				it->sort_score += history[from_square(it->m)][to_square(it->m)];
			}
		}
	}
	total_generated_moves += moves.size();

	Transposition t;
	t.hash = board.hash_key;
	int next_move = 0;
	for (unsigned int i = 0; i < moves.size(); ++i) {
		// late move reduction.
		// we assume sort order is good enough to not search later moves as deep as the first 4
		if (depth > 5 && i == 10) {
			depth -= 2;
		}

		pick_next_move(moves, i);
		Move child = moves[i];
		node_count++;
		make_move(board, child);

		int res = alphaBeta(!white_turn, depth - 1, alpha, beta, board, tt, null_move_in_branch, killers, history,
				ply + 1);
		unmake_move(board, child);
		if (res > alpha && res < beta) {
			// only cache exact scores

		}

		if (res > alpha && white_turn) {
			next_move = child.m;
			alpha = res;
			//history heuristics
			if (!is_capture(child.m)) {
				history[from_square(child.m)][to_square(child.m)] += depth;
			}
			// update pv
		}
		if (res < beta && !white_turn) {
			next_move = child.m;
			beta = res;
			//history heuristics
			if (!is_capture(child.m)) {
				history[from_square(child.m)][to_square(child.m)] += depth;
			}
			// update pv
		}
		if (beta <= alpha || time_to_stop()) {
			if (!is_capture(child.m)) {
				if (killers[ply - 1][0].m != child.m) {
					killers[ply - 1][1] = killers[ply - 1][0];
					killers[ply - 1][0] = child;
				}
			}
			break;
		}
	}
	t.next_move = next_move;
	tt[board.hash_key % HASH_SIZE] = t;
	if (white_turn) {
		return alpha;
	}
	return beta;
}
Exemple #6
0
Value name_NT_InCheck(qsearch)(Pos* pos, Stack* ss, Value alpha, BETA_ARG
                               Depth depth)
{
  assert(InCheck == !!pos_checkers());
  assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
  assert(PvNode || (alpha == beta - 1));
  assert(depth <= DEPTH_ZERO);

  Move pv[MAX_PLY+1];
  TTEntry *tte;
  Key posKey;
  Move ttMove, move, bestMove;
  Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
  int ttHit, ttPv, givesCheck, evasionPrunable;
  Depth ttDepth;
  int moveCount;

  if (PvNode) {
    oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves
    (ss+1)->pv = pv;
    ss->pv[0] = 0;
  }

  bestMove = 0;
  moveCount = 0;

  // Check for an instant draw or if the maximum ply has been reached
  if (is_draw(pos) || ss->ply >= MAX_PLY)
    return ss->ply >= MAX_PLY && !InCheck ? evaluate(pos) : VALUE_DRAW;

  assert(0 <= ss->ply && ss->ply < MAX_PLY);

  // Decide whether or not to include checks: this fixes also the type of
  // TT entry depth that we are going to use. Note that in qsearch we use
  // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
  ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
                                                : DEPTH_QS_NO_CHECKS;

  // Transposition table lookup
  posKey = pos_key();
  tte = tt_probe(posKey, &ttHit);
  ttValue = ttHit ? value_from_tt(tte_value(tte), ss->ply) : VALUE_NONE;
  ttMove = ttHit ? tte_move(tte) : 0;
  ttPv = ttHit ? tte_is_pv(tte) : 0;

  if (  !PvNode
      && ttHit
      && tte_depth(tte) >= ttDepth
      && ttValue != VALUE_NONE // Only in case of TT access race
      && (ttValue >= beta ? (tte_bound(tte) &  BOUND_LOWER)
                          : (tte_bound(tte) &  BOUND_UPPER)))
    return ttValue;

  // Evaluate the position statically
  if (InCheck) {
    ss->staticEval = VALUE_NONE;
    bestValue = futilityBase = -VALUE_INFINITE;
  } else {
    if (ttHit) {
      // Never assume anything on values stored in TT
      if ((ss->staticEval = bestValue = tte_eval(tte)) == VALUE_NONE)
         ss->staticEval = bestValue = evaluate(pos);

      // Can ttValue be used as a better position evaluation?
      if (ttValue != VALUE_NONE)
        if (tte_bound(tte) & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))
          bestValue = ttValue;
    } else
      ss->staticEval = bestValue =
      (ss-1)->currentMove != MOVE_NULL ? evaluate(pos)
                                       : -(ss-1)->staticEval + 2 * Tempo;

    // Stand pat. Return immediately if static value is at least beta
    if (bestValue >= beta) {
      if (!ttHit)
        tte_save(tte, posKey, value_to_tt(bestValue, ss->ply), ttPv,
                 BOUND_LOWER, DEPTH_NONE, 0, ss->staticEval,
                 tt_generation());

      return bestValue;
    }

    if (PvNode && bestValue > alpha)
      alpha = bestValue;

    futilityBase = bestValue + 128;
  }

  ss->history = &(*pos->counterMoveHistory)[0][0];

  // Initialize move picker data for the current position, and prepare
  // to search the moves. Because the depth is <= 0 here, only captures,
  // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
  // be generated.
  mp_init_q(pos, ttMove, depth, to_sq((ss-1)->currentMove));

  // Loop through the moves until no moves remain or a beta cutoff occurs
  while ((move = next_move(pos, 0))) {
    assert(move_is_ok(move));

    givesCheck = gives_check(pos, ss, move);

    moveCount++;

    // Futility pruning
    if (   !InCheck
        && !givesCheck
        &&  futilityBase > -VALUE_KNOWN_WIN
        && !advanced_pawn_push(pos, move)) {
      assert(type_of_m(move) != ENPASSANT); // Due to !advanced_pawn_push

      futilityValue = futilityBase + PieceValue[EG][piece_on(to_sq(move))];

      if (futilityValue <= alpha) {
        bestValue = max(bestValue, futilityValue);
        continue;
      }

      if (futilityBase <= alpha && !see_test(pos, move, 1)) {
        bestValue = max(bestValue, futilityBase);
        continue;
      }
    }

    // Detect non-capture evasions that are candidates to be pruned
    evasionPrunable =    InCheck
                     && (depth != DEPTH_ZERO || moveCount > 2)
                     &&  bestValue > VALUE_MATED_IN_MAX_PLY
                     && !is_capture(pos, move);

    // Don't search moves with negative SEE values
    if (  (!InCheck || evasionPrunable)
        &&  !see_test(pos, move, 0))
      continue;

    // Speculative prefetch as early as possible
    prefetch(tt_first_entry(key_after(pos, move)));

    // Check for legality just before making the move
    if (!is_legal(pos, move)) {
      moveCount--;
      continue;
    }

    ss->currentMove = move;
    ss->history = &(*pos->counterMoveHistory)[moved_piece(move)][to_sq(move)];

    // Make and search the move
    do_move(pos, move, givesCheck);
#if PvNode
    value =  givesCheck
           ? -qsearch_PV_true(pos, ss+1, -beta, -alpha, depth - ONE_PLY)
           : -qsearch_PV_false(pos, ss+1, -beta, -alpha, depth - ONE_PLY);
#else
    value =  givesCheck
           ? -qsearch_NonPV_true(pos, ss+1, -beta, depth - ONE_PLY)
           : -qsearch_NonPV_false(pos, ss+1, -beta, depth - ONE_PLY);
#endif
    undo_move(pos, move);

    assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);

    // Check for a new best move
    if (value > bestValue) {
      bestValue = value;

      if (value > alpha) {
        bestMove = move;

        if (PvNode) // Update pv even in fail-high case
          update_pv(ss->pv, move, (ss+1)->pv);

        if (PvNode && value < beta) // Update alpha here!
          alpha = value;
        else
          break; // Fail high
      }
    }
  }

  // All legal moves have been searched. A special case: If we're in check
  // and no legal moves were found, it is checkmate.
  if (InCheck && bestValue == -VALUE_INFINITE)
    return mated_in(ss->ply); // Plies to mate from the root

  tte_save(tte, posKey, value_to_tt(bestValue, ss->ply), ttPv,
           bestValue >= beta ? BOUND_LOWER :
           PvNode && bestValue > oldAlpha  ? BOUND_EXACT : BOUND_UPPER,
           ttDepth, bestMove, ss->staticEval, tt_generation());

  assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);

  return bestValue;
}