Exemple #1
0
inline void do_rjsmc (
        vsmc::Sampler<T> &sampler, const std::string &,
        std::ostream &zconst_file, const std::string &,
        typename MoveType::alpha_type::value_type alpha_config,
        typename MoveType::proposal_type::value_type proposal_config,
        std::size_t repeat)
{
    sampler.move(MoveType(alpha_config, proposal_config), false);
    std::vector<double> cn(MaxCompNum);
    std::vector<double> w(sampler.size());
    for (std::size_t i = 0; i != cn.size(); ++i)
        cn[i] = 0;

    for (std::size_t i = 0; i != repeat; ++i) {
        if (repeat > 1)
            std::cout << "Run: " << i + 1 << std::endl;
        sampler.initialize();
        while (sampler.particle().value().state(0, 0).alpha() < 1)
            sampler.iterate();
        sampler.particle().weight_set().read_weight(&w[0]);
        for (typename vsmc::Sampler<T>::size_type j = 0;
                j != sampler.size(); ++j)
            cn[sampler.particle().value().state(j, 0).comp_num() - 1] += w[j];
        for (std::size_t j = 0; j != cn.size(); ++j)
            zconst_file << cn[j] << ' ';
        zconst_file << std::endl;
    }
}
Exemple #2
0
inline void smc_do (vsmc::Sampler<T> &sampler,
        std::ostream &zconst_file, const std::string &schedule_name,
        typename MoveType::alpha_type::value_type alpha_config,
        std::size_t iter_num)
{
    sampler.move(MoveType(alpha_config, &sampler), false);
    for (std::size_t i = 0; i != Repeat; ++i) {
        if (Repeat > 1)
            std::cout << "Run: " << i + 1 << std::endl;
        zconst_file << schedule_name << ' ';
        zconst_file << alpha_config << ' ';
        smc_do(sampler, zconst_file, schedule_name, SM, iter_num);
        smc_do(sampler, zconst_file, schedule_name, CM, iter_num);
        zconst_file << std::endl;
    }
}
Exemple #3
0
void UpdateHistory(POS *p, int last_move, int move, int depth, int ply) {

  // Don't update stuff used for move ordering if a move changes material balance

  if (p->pc[Tsq(move)] != NO_PC || IsProm(move) || MoveType(move) == EP_CAP)
    return;

  // Asserts suggested by Ferdinand Mosca because history[] would overflow
  // if p->pc[Fsq(move)] == NO_PC.  If they ever fire, either move or board 
  // data are corrupt.

  assert(p->pc[Fsq(move)] != NO_PC);  // To detect no_pc
  assert(p->pc[Fsq(move)] <= NO_PC);  // To detect beyond no_pc for deeper examination 

  // Increment history counter

  history[p->pc[Fsq(move)]][Tsq(move)] += 2 * depth * depth;

  // Prevent history counters from growing too high

  if (history[p->pc[Fsq(move)]][Tsq(move)] > HIST_LIMIT)
     TrimHistory();

  // Update refutation table, saving new move in the table indexed
  // by the coordinates of last move. last_move == 0 is a null move,
  // special case of last_move == -1 denotes situations when updating
  // refutation table is switched off: at root or in QuiesceFlee()

  if (last_move >= 0)
     refutation[Fsq(last_move)][Tsq(last_move)] = move;

  // Update killer moves, taking care that they are different

  if (move != killer[ply][0]) {
    killer[ply][1] = killer[ply][0];
    killer[ply][0] = move;
  }
}
Exemple #4
0
int BadCapture(POS *p, int move) {

  int fsq = Fsq(move);
  int tsq = Tsq(move);

  // Captures that gain material or capture equal piece are good by definition

  if (tp_value[TpOnSq(p, tsq)] >= tp_value[TpOnSq(p, fsq)])
    return 0;

  // Bishop takes knight and knight takes bishop are good irrespectively from
  // the way minor pieces' values are tuned

  if ((TpOnSq(p, fsq) == B) && (TpOnSq(p, tsq) == N)) return 0;
  if ((TpOnSq(p, fsq) == N) && (TpOnSq(p, tsq) == B)) return 0;

  // En passant captures are good by definition

  if (MoveType(move) == EP_CAP) return 0;
  
  // We have to evaluate this capture using expensive Static Exchange Evaluation

  return Swap(p, fsq, tsq) < 0;
}
Exemple #5
0
int sHistory::MoveChangesMaterialBalance(sPosition *p, int move)
{
    if (p->pc[Tsq(move)] != NO_PC || IsProm(move) || MoveType(move) == EP_CAP) return 1;
	return 0;
}
void POS::UndoMove(int move, UNDO *u) {

  int sd  = Opp(side);
  int op  = side;
  int fsq = Fsq(move);
  int tsq = Tsq(move);
  int ftp = Tp(pc[tsq]);    // moving piece
  int ttp = u->ttp;

  castle_flags = u->castle_flags;
  ep_sq = u->ep_sq;
  rev_moves = u->rev_moves;
  pawn_key = u->pawn_key;
  hash_key = u->hash_key;
  head--;
  pc[fsq] = Pc(sd, ftp);
  pc[tsq] = NO_PC;
  cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq);
  tp_bb[ftp] ^= SqBb(fsq) | SqBb(tsq);
#ifndef LEAF_PST
  mg_pst[sd] += Param.mg_pst_data[sd][ftp][fsq] - Param.mg_pst_data[sd][ftp][tsq];
  eg_pst[sd] += Param.eg_pst_data[sd][ftp][fsq] - Param.eg_pst_data[sd][ftp][tsq];
#endif

  // Update king location

  if (ftp == K) king_sq[sd] = fsq;

  // Undo capture

  if (ttp != NO_TP) {
    pc[tsq] = Pc(op, ttp);
    cl_bb[op] ^= SqBb(tsq);
    tp_bb[ttp] ^= SqBb(tsq);
    phase += phase_value[ttp];
#ifndef LEAF_PST
    mg_pst[op] += Param.mg_pst_data[op][ttp][tsq];
    eg_pst[op] += Param.eg_pst_data[op][ttp][tsq];
#endif
    cnt[op][ttp]++;
  }

  switch (MoveType(move)) {

  case NORMAL:
    break;

  case CASTLE:

    // define complementary rook move

    switch (tsq) {
      case C1: { fsq = A1; tsq = D1; break; }
      case G1: { fsq = H1; tsq = F1; break; }
      case C8: { fsq = A8; tsq = D8; break; }
      case G8: { fsq = H8; tsq = F8; break; }
    }

    pc[tsq] = NO_PC;
    pc[fsq] = Pc(sd, R);
    cl_bb[sd] ^= SqBb(fsq) | SqBb(tsq);
    tp_bb[R] ^= SqBb(fsq) | SqBb(tsq);
#ifndef LEAF_PST
    mg_pst[sd] += Param.mg_pst_data[sd][R][fsq] - Param.mg_pst_data[sd][R][tsq];
    eg_pst[sd] += Param.eg_pst_data[sd][R][fsq] - Param.eg_pst_data[sd][R][tsq];
#endif
    break;

  case EP_CAP:
    tsq ^= 8;
    pc[tsq] = Pc(op, P);
    cl_bb[op] ^= SqBb(tsq);
    tp_bb[P] ^= SqBb(tsq);
    phase += phase_value[P];
#ifndef LEAF_PST
    mg_pst[op] += Param.mg_pst_data[op][P][tsq];
    eg_pst[op] += Param.eg_pst_data[op][P][tsq];
#endif
    cnt[op][P]++;
    break;

  case EP_SET:
    break;

  case N_PROM: case B_PROM: case R_PROM: case Q_PROM:
    pc[fsq] = Pc(sd, P);
    tp_bb[P] ^= SqBb(fsq);
    tp_bb[ftp] ^= SqBb(fsq);
    phase += phase_value[P] - phase_value[ftp];
#ifndef LEAF_PST
    mg_pst[sd] += Param.mg_pst_data[sd][P][fsq] - Param.mg_pst_data[sd][ftp][fsq];
    eg_pst[sd] += Param.eg_pst_data[sd][P][fsq] - Param.eg_pst_data[sd][ftp][fsq];
#endif
    cnt[sd][P]++;
    cnt[sd][ftp]--;
    break;
  }
  side ^= 1;
}
Exemple #7
0
/*-----------------------------------------------------------------------------*/
bool Notation::AddMove(const MoveKif& move)
{
	const MoveKif* prev_move = this->getPrevMove();
	const MoveKif& move_current = this->current_moves_->back();

	if (move.number() != 0 && move.number() != (this->move_current_->number() + 1))
	{
		// 番号が違う
		return false;
	}


	if (move_current.number() != this->number())
	{
		// 指し手の末尾と番号が違う場合 ブランチとして作成する
		this->current_moves_ = this->move_current_->MakeBranch(this->current_moves_);
	}

	int number = this->move_current_->number();
	
	// 要素追加
	this->current_moves_->emplace_back(move);
	this->move_current_ = &this->current_moves_->back();

	MoveKif& move_kif = this->current_moves_->back();

	if (move_kif.number() == 0)
	{
		move_kif.set_number(number + 1);
	}

	move_kif.set_side(this->position_.side_to_move());

	if ((move_kif.move_type() & MOVE_FLAG))
	{
		if (move_kif.to() == move_current.to())
		{
			// 同ほげをたてる
			move_kif.set_move_type(MoveType(move_kif.move_type() | MoveType::SAME_FLAG));
		}

		Piece capture_piece = position_.GetPiece(move_kif.to());
		if (capture_piece != NO_PIECE)
		{
			// 捕獲する場合 captureフラグを建てる
			move_kif.set_capture_piece(capture_piece);
			move_kif.set_move_type(MoveType(move_kif.move_type() | MoveType::CAPTURE_FLAG));
		}
	}
	else if (is_result(move_kif.move_type()))
	{
		this->result_ = move_kif.move_type();
		this->winner_ = this->getWinPlayer(move_kif);
	}

	if (move_kif.totalTime == 0)
	{
		// トータル時間の計算
		move_kif.totalTime = ((prev_move != nullptr) ? prev_move->totalTime : 0) + move_kif.time;
	}

	if (is_move(move_kif.move_type()))
	{
		// 盤面をすすめる
		this->position_.DoMove(move_kif);
	}

	return true;
}
Exemple #8
0
void sManipulator::DoMove(sPosition *p, int move, UNDO *u)
{
  int side = p->side;         // moving side 
  int fsq  = Fsq(move);       // start square
  int tsq  = Tsq(move);       // target square
  int ftp  = TpOnSq(p, fsq);  // moving piece
  int ttp  = TpOnSq(p, tsq);  // captured piece

  U64 bbMove = SqBb(fsq) | SqBb(tsq); // optimization from Stockfish

  // save data for undoing a move
  u->ttp = ttp;
  u->castleFlags = p->castleFlags;
  u->epSquare = p->epSquare;
  u->reversibleMoves = p->reversibleMoves;
  u->hashKey = p->hashKey;
  u->pawnKey = p->pawnKey;

  p->repetitionList[p->head++] = p->hashKey;

  // update reversible move counter (zeroing is done on captures and pawn moves)
  p->reversibleMoves++;

  p->hashKey     ^= zobCastle[p->castleFlags];
  p->castleFlags &= castleMask[fsq] & castleMask[tsq];
  p->hashKey     ^= zobCastle[p->castleFlags];

  // clear en passant square
  if (p->epSquare != NO_SQ) {
    p->hashKey ^= zobEp[File(p->epSquare)];
    p->epSquare = NO_SQ;
  }

  // move a piece from start square
  p->pc[fsq]      = NO_PC;
  p->pc[tsq]      = Pc(side, ftp);
  p->hashKey     ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq];
  if (ftp == P) {
	  p->reversibleMoves = 0;
	  p->pawnKey ^= zobPiece[Pc(side, ftp)][fsq] ^ zobPiece[Pc(side, ftp)][tsq];
  } 
  p->bbCl[side]  ^= bbMove;
  p->bbTp[ftp]   ^= bbMove;
  p->pstMg[side] += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][ftp][fsq];
  p->pstEg[side] += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][ftp][fsq];

  // on a king move update king location data
  if (ftp == K) p->kingSquare[side] = tsq;
  
  // capture
  if (ttp != NO_TP) {
	p->reversibleMoves = 0;
    p->hashKey ^= zobPiece[Pc(Opp(side), ttp)][tsq];
	if (ttp == P) 
		p->pawnKey     ^= zobPiece[Pc(Opp(side), ttp)][tsq];
    p->bbCl[Opp(side)] ^= SqBb(tsq);
    p->bbTp[ttp]       ^= SqBb(tsq);
	p->pcCount[Opp(side)][ttp]--;
    p->pieceMat[Opp(side)] -= Data.matValue[ttp];
	p->phase               -= Data.phaseValue[ttp]; 
    p->pstMg[Opp(side)]    -= Data.pstMg[Opp(side)][ttp][tsq];
	p->pstEg[Opp(side)]    -= Data.pstEg[Opp(side)][ttp][tsq];
  }
  
  switch (MoveType(move)) {

  case NORMAL:
    break;

  case CASTLE:
    if (tsq > fsq) {
      fsq += 3;
      tsq -= 1;
    } else {
      fsq -= 4;
      tsq += 1;
    }
    p->pc[fsq]      = NO_PC;
    p->pc[tsq]      = Pc(side, R);
    p->hashKey     ^= zobPiece[Pc(side, R)][fsq] ^ zobPiece[Pc(side, R)][tsq];
    p->bbCl[side]  ^= SqBb(fsq) | SqBb(tsq);
    p->bbTp[R]     ^= SqBb(fsq) | SqBb(tsq);
    p->pstMg[side] += Data.pstMg[side][R][tsq] - Data.pstMg[side][R][fsq];
	p->pstEg[side] += Data.pstEg[side][R][tsq] - Data.pstEg[side][R][fsq];
    break;

  case EP_CAP:
    tsq ^= 8;
    p->pc[tsq]  = NO_PC;
    p->hashKey ^= zobPiece[Pc(Opp(side), P)][tsq];
	p->pawnKey ^= zobPiece[Pc(Opp(side), P)][tsq];
    p->bbCl[Opp(side)] ^= SqBb(tsq);
    p->bbTp[P] ^= SqBb(tsq);
	p->pcCount[Opp(side)][P]--;
	p->phase             -= Data.phaseValue[P];
    p->pstMg[Opp(side)] -= Data.pstMg[Opp(side)][P][tsq];
	p->pstEg[Opp(side)] -= Data.pstEg[Opp(side)][P][tsq];
    break;

  case EP_SET:
    tsq ^= 8;
    if (bbPawnAttacks[side][tsq] & bbPc(p, Opp(side), P)) {
      p->epSquare = tsq;
      p->hashKey ^= zobEp[File(tsq)];
    }
    break;

  // promotion:    (1) add promoted piece and add values associated with it
  case N_PROM:  // (2) remove promoted pawn and substract values associated with it 
  case B_PROM: 
  case R_PROM: 
  case Q_PROM:
    ftp = PromType(move);
    p->pc[tsq]   = Pc(side, ftp);
    p->hashKey  ^= zobPiece[Pc(side, P)][tsq] ^ zobPiece[Pc(side, ftp)][tsq];
	p->pawnKey  ^= zobPiece[Pc(side, P)][tsq];
    p->bbTp[P]  ^= SqBb(tsq);
    p->bbTp[ftp]^= SqBb(tsq);
	p->pcCount[side][ftp]++;
	p->pcCount[side][P]--;
	p->pieceMat[side] += Data.matValue[ftp];
	p->phase          += Data.phaseValue[ftp]       - Data.phaseValue[P];
    p->pstMg[side]    += Data.pstMg[side][ftp][tsq] - Data.pstMg[side][P][tsq];
	p->pstEg[side]    += Data.pstEg[side][ftp][tsq] - Data.pstEg[side][P][tsq];
    break;

  }
  p->side ^= 1;
  p->hashKey ^= SIDE_RANDOM;
}
int Search(POS *p, int ply, int alpha, int beta, int depth, int was_null, int last_move, int last_capt_sq, int *pv) {

  int best, score, null_score, move, new_depth, new_pv[MAX_PLY];
  int fl_check, fl_prunable_node, fl_prunable_move, mv_type, reduction;
  int is_pv = (beta > alpha + 1);
  int mv_tried = 0, quiet_tried = 0, fl_futility = 0;

  int mv_played[MAX_MOVES];
  int mv_hist_score;
  int victim, last_capt;

  MOVES m[1];
  UNDO u[1];

  assert(ply > 0);

  // Quiescence search entry point

  if (depth <= 0)
    return QuiesceChecks(p, ply, alpha, beta, pv);

  // Periodically check for timeout, ponderhit or stop command

  nodes++;
  CheckTimeout();

  // Quick exit on a timeout or on a statically detected draw
  
  if (abort_search) return 0;
  if (ply) *pv = 0;
  if (IsDraw(p)) return DrawScore(p);

  // Mate distance pruning

  int checkmatingScore = MATE - ply;
  if (checkmatingScore < beta) {
    beta = checkmatingScore;
    if (alpha >= checkmatingScore)
    return alpha;
  }

  int checkmatedScore = -MATE + ply;
  if (checkmatedScore > alpha) {
    alpha = checkmatedScore;
    if (beta <= checkmatedScore)
    return beta;
  }

  // Retrieving data from transposition table. We hope for a cutoff
  // or at least for a move to improve move ordering.

  move = 0;
  if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply)) {
    
    // For move ordering purposes, a cutoff from hash is treated
    // exactly like a cutoff from search

    if (score >= beta) UpdateHistory(p, last_move, move, depth, ply);

    // In pv nodes only exact scores are returned. This is done because
    // there is much more pruning and reductions in zero-window nodes,
    // so retrieving such scores in pv nodes works like retrieving scores
    // from slightly lower depth.

    if (!is_pv || (score > alpha && score < beta))
      return score;
  }

  // Safeguard against exceeding ply limit
  
  if (ply >= MAX_PLY - 1)
    return Eval.Return(p, 1);

  // Are we in check? Knowing that is useful when it comes 
  // to pruning/reduction decisions

  fl_check = InCheck(p);

  // INTERNAL ITERATIVE DEEPENING - we try to get a hash move to improve move ordering

  if (!move && is_pv && depth >= 6 && !fl_check) {
    Search(p, ply, alpha, beta, depth - 2, 0, 0, -1, new_pv);
    if (abort_search) return 0;
    TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply);
  }

  // Can we prune this node?

  fl_prunable_node = !fl_check 
                   && !is_pv 
                   && alpha > -MAX_EVAL
                   && beta < MAX_EVAL;

  // Beta pruning / static null move

  if (use_beta_pruning
  && fl_prunable_node
  && depth <= 3
  && !was_null) {
    int sc = Eval.Return(p, 1) - 120 * depth; // TODO: Tune me!
    if (sc > beta) return sc;
  }

  // Null move

  if (use_nullmove
  && fl_prunable_node
  && depth > 1
  && !was_null
  && MayNull(p)
  ) {
    int eval = Eval.Return(p, 1);
    if (eval > beta) {

      new_depth = depth - ((823 + 67 * depth) / 256); // simplified Stockfish formula

      // omit null move search if normal search to the same depth wouldn't exceed beta
      // (sometimes we can check it for free via hash table)

      if (TransRetrieve(p->hash_key, &move, &null_score, alpha, beta, new_depth, ply)) {
        if (null_score < beta) goto avoid_null;
      }

      p->DoNull(u);
      if (new_depth > 0) score = -Search(p, ply + 1, -beta, -beta + 1, new_depth, 1, 0, -1, new_pv);
      else               score = -QuiesceChecks(p, ply + 1, -beta, -beta + 1, new_pv);
      p->UndoNull(u);

      // Verification search (nb. immediate null move within it is prohibited)

      if (new_depth > 6 && score >= beta && use_null_verification)
         score = Search(p, ply, alpha, beta, new_depth - 5, 1, move, -1, new_pv);

      if (abort_search ) return 0;
      if (score >= beta) return score;
    }
  } 
  
  avoid_null:

  // end of null move code

  // Razoring based on Toga II 3.0

  if (use_razoring
  && fl_prunable_node
  && !move
  && !was_null
  && !(p->Pawns(p->side) & bbRelRank[p->side][RANK_7]) // no pawns to promote in one move
  && depth <= 3) {
    int threshold = beta - razor_margin[depth];
    int eval = Eval.Return(p, 1);

    if (eval < threshold) {
      score = QuiesceChecks(p, ply, alpha, beta, pv);
      if (score < threshold) return score;
    }
  }

  // end of razoring code 

  // Init moves and variables before entering main loop
  
  best = -INF;
  InitMoves(p, m, move, Refutation(last_move), ply);
  
  // Main loop
  
  while ((move = NextMove(m, &mv_type))) {

    // Gather data about the move

    mv_hist_score = history[p->pc[Fsq(move)]][Tsq(move)];
	victim = TpOnSq(p, Tsq(move));
	if (victim != NO_TP) last_capt = Tsq(move);
	else last_capt = -1;

    // Set futility pruning flag before the first applicable move is tried

    if (mv_type == MV_NORMAL && quiet_tried == 0) {
      if (use_futility
      && fl_prunable_node
      && depth <= 6) {
        if (Eval.Return(p, 1) + fut_margin[depth] < beta) fl_futility = 1;
      }
    }

    p->DoMove(move, u);
    if (Illegal(p)) { p->UndoMove(move, u); continue; }

  // Update move statistics 
  // (needed for reduction/pruning decisions and for updating history score)

  mv_played[mv_tried] = move;
  mv_tried++;
  if (mv_type == MV_NORMAL) quiet_tried++;

  // Can we prune this move?

  fl_prunable_move = !InCheck(p)
                  && (mv_type == MV_NORMAL)
                  && (mv_hist_score < hist_limit);

  // Set new search depth

  new_depth = depth - 1;

  // Check extension (pv node or low depth)

  if (is_pv || depth < 9) {
	  new_depth += InCheck(p);
	  if (is_pv && Tsq(move) == last_capt_sq) new_depth += 1;
  }

  // Futility pruning

  if (fl_futility
  &&  fl_prunable_move
  &&  mv_tried > 1) {
    p->UndoMove(move, u); continue;
  }

  // Late move pruning

  if (use_lmp
  && fl_prunable_node
  && fl_prunable_move
  && quiet_tried > lmp_limit[depth]
  && depth <= 3
  && MoveType(move) != CASTLE ) {
    p->UndoMove(move, u); continue;
  }

  // Late move reduction

  reduction = 0;

  if (use_lmr 
  && depth >= 2
  && mv_tried > 3
  && alpha > -MAX_EVAL && beta < MAX_EVAL
  && !fl_check 
  &&  fl_prunable_move
  && lmr_size[is_pv][depth][mv_tried] > 0
  && MoveType(move) != CASTLE ) {
    
    // read reduction size from the table

    reduction = lmr_size[is_pv][depth][mv_tried];

    // increase reduction on bad history score

    if (mv_hist_score < 0
    && new_depth - reduction > 2
    && lmr_hist_adjustement)
       reduction++;

    // reduce search depth

    new_depth -= reduction;
  }

  // a place to come back if reduction looks suspect

  re_search:
   
  // PVS

  if (best == -INF)
    score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv);
  else {
    score = -Search(p, ply + 1, -alpha - 1, -alpha, new_depth, 0, move, last_capt, new_pv);
    if (!abort_search && score > alpha && score < beta)
      score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv);
  }

  // Reduced move scored above alpha - we need to re-search it

  if (reduction
  && score > alpha) {
    new_depth += reduction;
    reduction = 0;
    goto re_search;
  }

  // Undo move

  p->UndoMove(move, u);
  if (abort_search) return 0;

  // Beta cutoff

    if (score >= beta) {
      if (!fl_check) {
        UpdateHistory(p, last_move, move, depth, ply);
        for (int mv = 0; mv < mv_tried; mv++)
          DecreaseHistory(p, mv_played[mv], depth);
      }
      TransStore(p->hash_key, move, score, LOWER, depth, ply);

      return score;
    }

  // Updating score and alpha

    if (score > best) {
      best = score;
      if (score > alpha) {
        alpha = score;
        BuildPv(pv, new_pv, move);
      }
    }

  } // end of the main loop

  // Return correct checkmate/stalemate score

  if (best == -INF)
    return InCheck(p) ? -MATE + ply : DrawScore(p);

  // Save score in the transposition table

  if (*pv) {
    if (!fl_check) {
      UpdateHistory(p, last_move, *pv, depth, ply);
      for (int mv = 0; mv < mv_tried; mv++)
        DecreaseHistory(p, mv_played[mv], depth);
    }
    TransStore(p->hash_key, *pv, best, EXACT, depth, ply);
  } else
    TransStore(p->hash_key, 0, best, UPPER, depth, ply);

  return best;
}
int SearchRoot(POS *p, int ply, int alpha, int beta, int depth, int *pv) {

  int best, score, move, new_depth, new_pv[MAX_PLY];
  int fl_check, fl_prunable_move, mv_type, reduction;
  int mv_tried = 0, quiet_tried = 0;
  int mv_played[MAX_MOVES];
  int mv_hist_score;
  int victim, last_capt;
  int move_change = 0;
  
  fl_has_choice = 0;
  
  MOVES m[1];
  UNDO u[1];

  // Periodically check for timeout, ponderhit or stop command

  nodes++;
  CheckTimeout();

  // Quick exit
  
  if (abort_search) return 0;

  // Retrieving data from transposition table. We hope for a cutoff
  // or at least for a move to improve move ordering.

  move = 0;
  if (TransRetrieve(p->hash_key, &move, &score, alpha, beta, depth, ply)) {
    
    // For move ordering purposes, a cutoff from hash is treated
    // exactly like a cutoff from search

    if (score >= beta) UpdateHistory(p, -1, move, depth, ply);

    // Root node is a pv node, so we return only exact scores

    if (score > alpha && score < beta)
      return score;
  }
  
  // Are we in check? Knowing that is useful when it comes 
  // to pruning/reduction decisions

  fl_check = InCheck(p);

  // Init moves and variables before entering main loop
  
  best = -INF;
  InitMoves(p, m, move, Refutation(-1), ply);
  
  // Main loop
  
  while ((move = NextMove(m, &mv_type))) {

    mv_hist_score = history[p->pc[Fsq(move)]][Tsq(move)];
	victim = TpOnSq(p, Tsq(move));
	if (victim != NO_TP) last_capt = Tsq(move);
	else last_capt = -1;

    p->DoMove(move, u);
    if (Illegal(p)) { p->UndoMove(move, u); continue; }

    // Update move statistics (needed for reduction/pruning decisions)

    mv_played[mv_tried] = move;
    mv_tried++;
    if (mv_tried > 1) fl_has_choice = 1; // we have a choice between at least two root moves
    if (depth > 16 && verbose) DisplayCurrmove(move, mv_tried);
    if (mv_type == MV_NORMAL) quiet_tried++;
    fl_prunable_move = !InCheck(p) && (mv_type == MV_NORMAL);

    // Set new search depth

    new_depth = depth - 1 + InCheck(p);

    // Late move reduction
  
    reduction = 0;
  
    if (use_lmr
    && depth >= 2
    && mv_tried > 3
    && mv_hist_score < hist_limit
    && alpha > -MAX_EVAL && beta < MAX_EVAL
    && !fl_check 
    &&  fl_prunable_move
    && lmr_size[1][depth][mv_tried] > 0
    && MoveType(move) != CASTLE ) {

    reduction = lmr_size[1][depth][mv_tried];

    // increase reduction on bad history score

    if (mv_hist_score < 0 
    && new_depth - reduction > 2 
    && lmr_hist_adjustement) 
       reduction++;

    new_depth -= reduction;
  }

  re_search:
   
  // PVS

  if (best == -INF)
    score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv);
  else {
    score = -Search(p, ply + 1, -alpha - 1, -alpha, new_depth, 0, move, last_capt, new_pv);
    if (!abort_search && score > alpha && score < beta)
      score = -Search(p, ply + 1, -beta, -alpha, new_depth, 0, move, last_capt, new_pv);
  }

  // Reduced move scored above alpha - we need to re-search it

  if (reduction && score > alpha) {
    new_depth += reduction;
    reduction = 0;
    goto re_search;
  }

    p->UndoMove(move, u);
    if (abort_search) return 0;

  // Beta cutoff

    if (score >= beta) {
      if (!fl_check) {
        UpdateHistory(p, -1, move, depth, ply);
        for (int mv = 0; mv < mv_tried; mv++)
          DecreaseHistory(p, mv_played[mv], depth);
      }
      TransStore(p->hash_key, move, score, LOWER, depth, ply);

      // Update search time depending on whether the first move has changed

      if (depth > 4) {
        if (pv[0] != move) Timer.OnNewRootMove();
        else               Timer.OnOldRootMove();
      }

	  // Change the best move and show the new pv

      BuildPv(pv, new_pv, move);
      DisplayPv(score, pv);

      return score;
    }

    // Updating score and alpha

    if (score > best) {
      best = score;

      if (score > alpha) {
        alpha = score;

        // Update search time depending on whether the first move has changed

        if (depth > 4) {
          if (pv[0] != move) Timer.OnNewRootMove();
          else               Timer.OnOldRootMove();
        }

	    // Change the best move and show the new pv

        BuildPv(pv, new_pv, move);
        DisplayPv(score, pv);
      }
    }

  } // end of the main loop

  // Return correct checkmate/stalemate score

  if (best == -INF)
    return InCheck(p) ? -MATE + ply : DrawScore(p);

  // Save score in the transposition table

  if (*pv) {
    if (!fl_check) {
      UpdateHistory(p, -1, *pv, depth, ply);
      for (int mv = 0; mv < mv_tried; mv++)
        DecreaseHistory(p, mv_played[mv], depth);
    }
    TransStore(p->hash_key, *pv, best, EXACT, depth, ply);
    
  } else
    TransStore(p->hash_key, 0, best, UPPER, depth, ply);

  return best;
}