Example #1
0
static int move_value(int move, const board_t * board, int height, int trans_killer, int ThreadId) {

   int value;

   ASSERT(move_is_ok(move));
   ASSERT(board!=NULL);
   ASSERT(height_is_ok(height));
   ASSERT(trans_killer==MoveNone||move_is_ok(trans_killer));

   if (false) {
   } else if (move == trans_killer) { // transposition table killer
      value = TransScore;
   } else if (move_is_tactical(move,board)) { // capture or promote
      value = capture_value(move,board);
   } else if (move == Killer[ThreadId][height][0]) { // killer 1
      value = KillerScore;
   } else if (move == Killer[ThreadId][height][1]) { // killer 2
      value = KillerScore - 2;
   } else if (height > 2 && move == Killer[ThreadId][height-2][0]) { // killer 1
      value = KillerScore - 1;
   } else if (height > 2 && move == Killer[ThreadId][height-2][1]) { // killer 2
      value = KillerScore - 3;
   } else { // quiet move
      value = quiet_move_value(move,board,ThreadId);
   }

   return value;
}
Example #2
0
void good_move(int move, const board_t * board, int depth, int height) {

   int index;
   int i;

   ASSERT(move_is_ok(move));
   ASSERT(board!=NULL);
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));


   if (move_is_tactical(move,board)) return;

   // killer

   if (Killer[height][0] != move) {
      Killer[height][1] = Killer[height][0];
      Killer[height][0] = move;
   }

   ASSERT(Killer[height][0]==move);
   ASSERT(Killer[height][1]!=move);

   // history

   index = history_index(move,board);

   History[index] += HISTORY_INC(depth);

   if (History[index] >= HistoryMax) {
      for (i = 0; i < HistorySize; i++) {
         History[i] = (History[i] + 1) / 2;
      }
   } 
}
Example #3
0
void killer_copy(int dst, int src, int height) {

   ASSERT(thread_is_ok(dst));
   ASSERT(thread_is_ok(src));
   ASSERT(height_is_ok(height));

   ASSERT(dst!=src);

   for ( ; height < SearchCurrent->max_depth[src]; height++) {
       Killer[dst][height][0] = Killer[src][height][0];
       Killer[dst][height][1] = Killer[src][height][1];
   }
}
Example #4
0
int value_from_trans(int value, int height) {

   ASSERT(value_is_ok(value));
   ASSERT(height_is_ok(height));

   if (value < -ValueEvalInf) {
      value += height;
   } else if (value > +ValueEvalInf) {
      value -= height;
   }

   ASSERT(value_is_ok(value));

   return value;
}
Example #5
0
void sort_init(sort_t * sort, board_t * board, const attack_t * attack, int depth, int height, int trans_killer) {

   ASSERT(sort!=NULL);
   ASSERT(board!=NULL);
   ASSERT(attack!=NULL);
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(trans_killer==MoveNone||move_is_ok(trans_killer));

   sort->board = board;
   sort->attack = attack;

   sort->depth = depth;
   sort->height = height;

   sort->trans_killer = trans_killer;
   sort->killer_1 = Killer[sort->height][0];
   sort->killer_2 = Killer[sort->height][1];
   if (sort->height > 2){
	  sort->killer_3 = Killer[sort->height-2][0];
      sort->killer_4 = Killer[sort->height-2][1]; 
   }
   else{
      sort->killer_3 = MoveNone;
      sort->killer_4 = MoveNone;
   }

   if (ATTACK_IN_CHECK(sort->attack)) {

      gen_legal_evasions(sort->list,sort->board,sort->attack);
      note_moves(sort->list,sort->board,sort->height,sort->trans_killer);
      list_sort(sort->list);

      sort->gen = PosLegalEvasion + 1;
      sort->test = TEST_NONE;

   } else { // not in check

      LIST_CLEAR(sort->list);
      sort->gen = PosSEE;
   }

   sort->pos = 0;
}
Example #6
0
void note_moves(list_t * list, const board_t * board, int height, int trans_killer, int ThreadId) {

   int size;
   int i, move;

   ASSERT(list_is_ok(list));
   ASSERT(board!=NULL);
   ASSERT(height_is_ok(height));
   ASSERT(trans_killer==MoveNone||move_is_ok(trans_killer));

   size = LIST_SIZE(list);

   if (size >= 2) {
      for (i = 0; i < size; i++) {
         move = LIST_MOVE(list,i);
         list->value[i] = move_value(move,board,height,trans_killer,ThreadId);
      }
   }
}
Example #7
0
void sort_init(sort_t * sort, board_t * board, const attack_t * attack, int depth, int height, int trans_killer, bool in_pv, int ThreadId) {

   ASSERT(sort!=NULL);
   ASSERT(board!=NULL);
   ASSERT(attack!=NULL);
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(trans_killer==MoveNone||move_is_ok(trans_killer));

   sort->board = board;
   sort->attack = attack;

   sort->height = height;

   sort->trans_killer = trans_killer;
   sort->killer_1 = Killer[ThreadId][sort->height][0]; // last best
   sort->killer_3 = Killer[ThreadId][sort->height][1];
   if (sort->height > 2){
      sort->killer_2 = Killer[ThreadId][sort->height-2][0];  // last best below
      sort->killer_4 = Killer[ThreadId][sort->height-2][1];
   }
   else{
      sort->killer_2 = MoveNone;
      sort->killer_4 = MoveNone;
   }

   // uniqueness added.                                                // WHM
   if (sort->killer_1 != MoveNone) {                                   // WHM
      if (sort->killer_2 == sort->killer_1) sort->killer_2 = MoveNone; // WHM
      if (sort->killer_3 == sort->killer_1) sort->killer_3 = MoveNone; // WHM
      if (sort->killer_4 == sort->killer_1) sort->killer_4 = MoveNone; // WHM
   }                                                                   // WHM
   if (sort->killer_2 != MoveNone) {                                   // WHM
      if (sort->killer_3 == sort->killer_2) sort->killer_3 = MoveNone; // WHM
      if (sort->killer_4 == sort->killer_2) sort->killer_4 = MoveNone; // WHM
   }                                                                   // WHM
      if (sort->killer_4 == sort->killer_3) sort->killer_4 = MoveNone; // WHM

   ASSERT(sort->killer_1 == MoveNone  ||  sort->killer_1 != sort->killer_2); // WHM
   ASSERT(sort->killer_1 == MoveNone  ||  sort->killer_1 != sort->killer_3); // WHM
   ASSERT(sort->killer_1 == MoveNone  ||  sort->killer_1 != sort->killer_4); // WHM
   ASSERT(sort->killer_2 == MoveNone  ||  sort->killer_2 != sort->killer_1); // WHM
   ASSERT(sort->killer_2 == MoveNone  ||  sort->killer_2 != sort->killer_3); // WHM
   ASSERT(sort->killer_2 == MoveNone  ||  sort->killer_2 != sort->killer_4); // WHM
   ASSERT(sort->killer_3 == MoveNone  ||  sort->killer_3 != sort->killer_1); // WHM
   ASSERT(sort->killer_3 == MoveNone  ||  sort->killer_3 != sort->killer_2); // WHM
   ASSERT(sort->killer_3 == MoveNone  ||  sort->killer_3 != sort->killer_4); // WHM
   ASSERT(sort->killer_4 == MoveNone  ||  sort->killer_4 != sort->killer_1); // WHM
   ASSERT(sort->killer_4 == MoveNone  ||  sort->killer_4 != sort->killer_2); // WHM
   ASSERT(sort->killer_4 == MoveNone  ||  sort->killer_4 != sort->killer_3); // WHM
   
   sort->in_pv = in_pv;
   
   if (ATTACK_IN_CHECK(sort->attack)) {

      gen_legal_evasions(sort->list,sort->board,sort->attack);
      note_moves(sort->list,sort->board,sort->height,sort->trans_killer,ThreadId);
      list_sort(sort->list);

      sort->gen = PosLegalEvasion + 1;
      sort->test = TEST_NONE;

   } else { // not in check

      LIST_CLEAR(sort->list);
      sort->gen = PosSEE;
   }

   sort->pos = 0;
}
Example #8
0
void good_move(int move, const board_t * board, int depth, int height, int thread, bool cut) {

   int index;
   int i;

   ASSERT(move_is_ok(move));
   ASSERT(board!=NULL);
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(thread_is_ok(thread));
   ASSERT(cut==true||cut==false);

   if (MOVE_IS_TACTICAL(move,board)) return;

   // killer

   if (Killer[thread][height][0] != move) {
       Killer[thread][height][1] = Killer[thread][height][0];
       Killer[thread][height][0] = move;
   }

   ASSERT(Killer[thread][height][0]==move);
   ASSERT(Killer[thread][height][1]!=move);

   // history

   index = history_index(move,board);

   History[index] += HISTORY_INC(depth);

   if (History[index] > HistoryValue) {
      for (i = 0; i < HistorySize; i++) {
         if (History[i] >= 0) {
            History[i] = (History[i] + 1) / 2;
         } else {
            History[i] = (History[i] - 1) / 2;
         }
      }
   }

   if (cut) {
      HistHit[index]++;
      HistTot[index]++;

      if (HistTot[index] >= HistoryMax) {
         HistHit[index] = (HistHit[index] + 1) / 2;
         HistTot[index] = (HistTot[index] + 1) / 2;
      }

#if DEBUG
      if (thread_number() == 1) {
         ASSERT(HistHit[index]<=HistTot[index]);
         ASSERT(HistTot[index]<HistoryMax);
      }
#endif
   }

#if DEBUG
   if (thread_number() > 1) {
      ASSERT(History[index]<=HistoryValue+8192&&History[index]>=-HistoryValue-8192);
   } else {
      ASSERT(History[index]<=HistoryValue&&History[index]>=-HistoryValue);
   }
#endif
}
Example #9
0
static int full_quiescence(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[]) {

   bool in_check;
   int old_alpha;
   int value, best_value;
   int best_move;
   int move;
   int opt_value;
   attack_t attack[1];
   sort_t sort[1];
   undo_t undo[1];
   mv_t new_pv[HeightMax];
   int cd;

   ASSERT(board!=NULL);
   ASSERT(range_is_ok(alpha,beta));
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(pv!=NULL);

   ASSERT(board_is_legal(board));
   ASSERT(depth<=0);

   // init

   SearchStack[height].best_move = MoveNone;
   SearchStack[height].move = MoveNone;
   SearchStack[height].threat_move = MoveNone;
   SearchStack[height].reduced = false;

   SearchCurrent->node_nb++;
   SearchInfo->check_nb--;
   PV_CLEAR(pv);

   if (height > SearchCurrent->max_depth) SearchCurrent->max_depth = height;

   if (SearchInfo->check_nb <= 0) {
      SearchInfo->check_nb += SearchInfo->check_inc;
      search_check();
   }

   // draw?

   if (board_is_repetition(board) || recog_draw(board)) return ValueDraw;

   // mate-distance pruning

   if (UseDistancePruning) {

      // lower bound

      value = VALUE_MATE(height+2); // does not work if the current position is mate
      if (value > alpha && board_is_mate(board)) value = VALUE_MATE(height);

      if (value > alpha) {
         alpha = value;
         if (value >= beta) return value;
      }

      // upper bound

      value = -VALUE_MATE(height+1);

      if (value < beta) {
         beta = value;
         if (value <= alpha) return value;
      }
   }

   // more init

   attack_set(attack,board);
   in_check = ATTACK_IN_CHECK(attack);

   if (in_check) {
      ASSERT(depth<0);
      depth++; // in-check extension
   }

   // height limit

   if (height >= HeightMax-1) return eval(board, alpha, beta);

   // more init

   old_alpha = alpha;
   best_value = ValueNone;
   best_move = MoveNone;

   /* if (UseDelta) */ opt_value = +ValueInf;

   if (!in_check) {

      // lone-king stalemate?

      if (simple_stalemate(board)) return ValueDraw;

      // stand pat

      value = eval(board, alpha, beta);

      ASSERT(value>best_value);
      best_value = value;
      if (value > alpha) {
         alpha = value;
         if (value >= beta) goto cut;
      }

      if (UseDelta) {
         opt_value = value + DeltaMargin;
         ASSERT(opt_value<+ValueInf);
      }
   }

   // move loop

/*    cd = CheckDepth;
   if(cd < 0 && board->piece_size[board->turn] <= 5)
	   cd++; */

   sort_init_qs(sort,board,attack, depth>=CheckDepth /* depth>=cd */);

   while ((move=sort_next_qs(sort)) != MoveNone) {

	  SearchStack[height].move = move;

      // delta pruning

      if (UseDelta && beta == old_alpha+1) {

         if (!in_check && !move_is_check(move,board) && !capture_is_dangerous(move,board)) {

            ASSERT(move_is_tactical(move,board));

            // optimistic evaluation

            value = opt_value;

            int to = MOVE_TO(move);
            int capture = board->square[to];

            if (capture != Empty) {
               value += VALUE_PIECE(capture);
            } else if (MOVE_IS_EN_PASSANT(move)) {
               value += ValuePawn;
            }

            if (MOVE_IS_PROMOTE(move)) value += ValueQueen - ValuePawn;

            // pruning

            if (value <= alpha) {

               if (value > best_value) {
                  best_value = value;
                  PV_CLEAR(pv);
               }

               continue;
            }
         }
      }

      move_do(board,move,undo);
      value = -full_quiescence(board,-beta,-alpha,depth-1,height+1,new_pv);
      move_undo(board,move,undo);

      if (value > best_value) {
         best_value = value;
         pv_cat(pv,new_pv,move);
         if (value > alpha) {
            alpha = value;
            best_move = move;
			SearchStack[height].best_move = move;
            if (value >= beta) goto cut;
         }
      }
   }

   // ALL node

   if (best_value == ValueNone) { // no legal move
      ASSERT(board_is_mate(board));
      return VALUE_MATE(height);
   }

cut:

   ASSERT(value_is_ok(best_value));

   return best_value;
}
Example #10
0
static int full_no_null(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int node_type, int trans_move, int * best_move) {

   int value, best_value;
   int move;
   int new_depth;
   attack_t attack[1];
   sort_t sort[1];
   undo_t undo[1];
   mv_t new_pv[HeightMax];

   ASSERT(board!=NULL);
   ASSERT(range_is_ok(alpha,beta));
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(pv!=NULL);
   ASSERT(node_type==NodePV||node_type==NodeCut||node_type==NodeAll);
   ASSERT(trans_move==MoveNone||move_is_ok(trans_move));
   ASSERT(best_move!=NULL);

   ASSERT(board_is_legal(board));
   ASSERT(!board_is_check(board));
   ASSERT(depth>=1);

   // init

   SearchStack[height].best_move = MoveNone;
   SearchStack[height].move = MoveNone;
   SearchStack[height].threat_move = MoveNone;
   SearchStack[height].reduced = false;

   SearchCurrent->node_nb++;
   SearchInfo->check_nb--;
   PV_CLEAR(pv);

   if (height > SearchCurrent->max_depth) SearchCurrent->max_depth = height;

   if (SearchInfo->check_nb <= 0) {
      SearchInfo->check_nb += SearchInfo->check_inc;
      search_check();
   }

   attack_set(attack,board);
   ASSERT(!ATTACK_IN_CHECK(attack));

   *best_move = MoveNone;
   best_value = ValueNone;

   // move loop

   sort_init(sort,board,attack,depth,height,trans_move);

   while ((move=sort_next(sort)) != MoveNone) {

	  SearchStack[height].move = move;

      new_depth = full_new_depth(depth,move,board,false,false,false,height);

      move_do(board,move,undo);
      value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type));
      move_undo(board,move,undo);

      if (value > best_value) {
         best_value = value;
         pv_cat(pv,new_pv,move);
         if (value > alpha) {
            alpha = value;
            *best_move = move;
			SearchStack[height].best_move = move;
            if (value >= beta) goto cut;
         }
      }
   }

   // ALL node

   if (best_value == ValueNone) { // no legal move => stalemate
      ASSERT(board_is_stalemate(board));
      best_value = ValueDraw;
   }

cut:

   ASSERT(value_is_ok(best_value));

   return best_value;
}
Example #11
0
static int full_search(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int node_type) {

   bool in_check;
   bool single_reply;
   bool mate_threat;
   int trans_move, trans_depth, trans_min_depth, trans_max_depth, trans_min_value, trans_max_value;
   int min_value, max_value;
   int old_alpha;
   int value, best_value;
   int move, best_move;
   int new_depth;
   int played_nb;
   int i;
   int opt_value;
   bool reduced;
   int mb;
   attack_t attack[1];
   sort_t sort[1];
   undo_t undo[1];
   mv_t new_pv[HeightMax];
   mv_t played[256];
   int FutilityMargin;

   ASSERT(board!=NULL);
   ASSERT(range_is_ok(alpha,beta));
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(pv!=NULL);
   ASSERT(node_type==NodePV||node_type==NodeCut||node_type==NodeAll);

   ASSERT(board_is_legal(board));

   // horizon?

   if (depth <= 0){
	   if (node_type == NodePV)
			CheckDepth = 1 - CheckNb - 1;
	   else
			CheckDepth = 1 - CheckNb;
	   return full_quiescence(board,alpha,beta,0,height,pv);
   }

   // init

   SearchStack[height].best_move = MoveNone;
   SearchStack[height].move = MoveNone;
   SearchStack[height].threat_move = MoveNone;
   SearchStack[height].reduced = false;
   mate_threat = false;

   SearchCurrent->node_nb++;
   SearchInfo->check_nb--;
   PV_CLEAR(pv);

   if (height > SearchCurrent->max_depth) SearchCurrent->max_depth = height;

   if (SearchInfo->check_nb <= 0) {
      SearchInfo->check_nb += SearchInfo->check_inc;
      search_check();
   }

   // draw?

   if (board_is_repetition(board) || recog_draw(board)) return ValueDraw;

   // mate-distance pruning

   if (UseDistancePruning) {

      // lower bound

      value = VALUE_MATE(height+2); // does not work if the current position is mate
      if (value > alpha && board_is_mate(board)) value = VALUE_MATE(height);

      if (value > alpha) {
         alpha = value;
         if (value >= beta) return value;
      }

      // upper bound

      value = -VALUE_MATE(height+1);

      if (value < beta) {
         beta = value;
         if (value <= alpha) return value;
      }
   }

   // transposition table

   trans_move = MoveNone;

   if (UseTrans && depth >= TransDepth) {

      if (trans_retrieve(Trans,board->key,&trans_move,&trans_min_depth,&trans_max_depth,&trans_min_value,&trans_max_value)) {

         // trans_move is now updated

         if (node_type != NodePV) {

            if (UseMateValues) {

               if (trans_min_value > +ValueEvalInf && trans_min_depth < depth) {
                  trans_min_depth = depth;
               }

               if (trans_max_value < -ValueEvalInf && trans_max_depth < depth) {
                  trans_max_depth = depth;
               }
            }

            min_value = -ValueInf;

            if (DEPTH_MATCH(trans_min_depth,depth)) {
               min_value = value_from_trans(trans_min_value,height);
               if (min_value >= beta) return min_value;
            }

            max_value = +ValueInf;

            if (DEPTH_MATCH(trans_max_depth,depth)) {
               max_value = value_from_trans(trans_max_value,height);
               if (max_value <= alpha) return max_value;
            }

            if (min_value == max_value) return min_value; // exact match
         }
      }
   }

   // height limit

   if (height >= HeightMax-1) return eval(board, alpha, beta);

   // more init

   old_alpha = alpha;
   best_value = ValueNone;
   best_move = MoveNone;
   played_nb = 0;

   attack_set(attack,board);
   in_check = ATTACK_IN_CHECK(attack);

   // null-move pruning

   if (UseNull && depth >= NullDepth && node_type != NodePV) {

      if (!in_check
       && !value_is_mate(beta)
       && do_null(board)
       && (!UseNullEval || depth <= NullReduction+1 || eval(board,alpha, beta) >= beta)) {

         // null-move search
		 
         new_depth = depth - NullReduction - 1;
		 //new_depth = depth - R_adpt(board->piece_size[board->turn]+board->pawn_size[board->turn],depth,NullReduction) - 1;
		 
	     move_do_null(board,undo);
         value = -full_search(board,-beta,-beta+1,new_depth,height+1,new_pv,NODE_OPP(node_type));
         move_undo_null(board,undo);

         // verification search

         if (UseVer && depth > VerReduction) {

            if (value >= beta && (!UseVerEndgame || do_ver(board))) {

               new_depth = depth - VerReduction;
               ASSERT(new_depth>0);

               value = full_no_null(board,alpha,beta,new_depth,height,new_pv,NodeCut,trans_move,&move);

               if (value >= beta) {
                  ASSERT(move==new_pv[0]);
                  played[played_nb++] = move;
                  best_move = move;
				  SearchStack[height].move = move;
				  SearchStack[height].best_move = move;
                  best_value = value;
                  pv_copy(pv,new_pv);
                  goto cut;
               }
            }
         }

         // pruning

         if (value >= beta) {

            if (value > +ValueEvalInf) value = +ValueEvalInf; // do not return unproven mates
            ASSERT(!value_is_mate(value));

            // pv_cat(pv,new_pv,MoveNull);

            best_move = MoveNone;
            best_value = value;
            goto cut;
         }
		 SearchStack[height].threat_move = SearchStack[height+1].best_move;
	/*	 if (SearchStack[height-1].reduced){ // Idea by Tord Romstad 
				if (MOVE_FROM(SearchStack[height+1].best_move) == MOVE_TO(SearchStack[height-1].move))
					return alpha-1; */
		    	/* if(((MOVE_TO(SearchStack[height - 2].threat_move) == MOVE_FROM(SearchStack[height - 2].move))
					&& (MOVE_TO(SearchStack[height].threat_move) == MOVE_TO(SearchStack[height - 2].move)))
				  ||
				  ((MOVE_TO(SearchStack[height - 2].threat_move) != MOVE_FROM(SearchStack[height - 2].move))
		            && (MOVE_TO(SearchStack[height].threat_move) == MOVE_TO(SearchStack[height - 2].threat_move))))
			        return alpha-1; */
	//	 }
	  }
   }

   // mate threat 
/*   mate_threat = false;
   if (value <= VALUE_MATE(height+2)){ 
			mate_threat = true;
   } */

   // Internal Iterative Deepening
   
   if (UseIID && depth >= IIDDepth && node_type == NodePV && trans_move == MoveNone) {

      // new_depth = depth - IIDReduction;
	  new_depth = MIN(depth - IIDReduction,depth/2);
      ASSERT(new_depth>0);

      value = full_search(board,alpha,beta,new_depth,height,new_pv,node_type);
      if (value <= alpha) value = full_search(board,-ValueInf,beta,new_depth,height,new_pv,node_type);

      trans_move = new_pv[0];
   }

   // move generation

   sort_init(sort,board,attack,depth,height,trans_move);

   single_reply = false;
   if (in_check && LIST_SIZE(sort->list) == 1) single_reply = true; // HACK

   // move loop

   opt_value = +ValueInf;

   while ((move=sort_next(sort)) != MoveNone) {

	  SearchStack[height].move = move;

	  // history_tried(move,board);
	  
      // extensions

      new_depth = full_new_depth(depth,move,board,single_reply,mate_threat,node_type==NodePV, height);
	  
      // futility pruning

	  if (UseFutility && depth <= 2 && node_type != NodePV) {
		  
         if (!in_check && new_depth < depth && !move_is_tactical(move,board) && !move_is_dangerous(move,board)) {

            ASSERT(!move_is_check(move,board));

            // optimistic evaluation

            if (opt_value == +ValueInf) {
				if (depth==2){
					FutilityMargin = FutilityMargin2;
				}
				else{
					FutilityMargin = FutilityMargin1;
				}
				opt_value = eval(board,alpha,beta) + FutilityMargin;
				ASSERT(opt_value<+ValueInf);
            }

            value = opt_value;

            // pruning

            if (value <= alpha) {

               if (value > best_value) {
                  best_value = value;
                  PV_CLEAR(pv);
               }

               continue;
            }
         }
      } 

	  // history pruning

/*      reduced = false;

      if (UseHistory && depth >= HistoryDepth && node_type != NodePV) {
         if (!in_check && played_nb >= HistoryMoveNb && new_depth < depth) {
            ASSERT(best_value!=ValueNone);
            ASSERT(played_nb>0);
            ASSERT(sort->pos>0&&move==LIST_MOVE(sort->list,sort->pos-1));
            if (history_reduction(move,board) == true) {
               ASSERT(value>=0&&value<16384);
               ASSERT(move!=trans_move);
               ASSERT(!move_is_tactical(move,board));
               ASSERT(!move_is_check(move,board));
               new_depth--;
               reduced = true;
            }
         }
      } */

	  // history pruning

      reduced = false;
	  value = sort->value; // history score
      if (!in_check && depth < SearchCurrent->max_extensions / 2 && node_type != NodePV && new_depth < depth && value < HistoryValue / depth) 
		  continue;


      if (UseHistory && depth >= HistoryDepth && node_type != NodePV) {
         if (!in_check && played_nb >= HistoryMoveNb && new_depth < depth) {
            ASSERT(best_value!=ValueNone);
            ASSERT(played_nb>0);
            ASSERT(sort->pos>0&&move==LIST_MOVE(sort->list,sort->pos-1));
            value = sort->value; // history score
            if (value < HistoryValue) {
               ASSERT(value>=0&&value<16384);
               ASSERT(move!=trans_move);
               ASSERT(!move_is_tactical(move,board));
               ASSERT(!move_is_check(move,board));
               new_depth--;
               reduced = true;
			   if (UseExtendedHistory && value < HistoryValue / 2 && depth >= 8){
				   new_depth--;
				   
			   }
            }
         }
      }   

	  SearchStack[height].reduced = reduced;

      // recursive search

	  move_do(board,move,undo);

      if (node_type != NodePV || best_value == ValueNone) { // first move
		 value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type));
      } else { // other moves
		 value = -full_search(board,-alpha-1,-alpha,new_depth,height+1,new_pv,NodeCut);
         if (value > alpha) { // && value < beta
			value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV);
         }
      }

      // history-pruning re-search

	  if (HistoryReSearch && reduced && value > alpha /* was >= beta */) {

         ASSERT(node_type!=NodePV);

		 //history_very_bad(move,board);
		 SearchStack[height].reduced = false;
         new_depth++;
         ASSERT(new_depth==depth-1);
		 
         value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type));
      } 

      move_undo(board,move,undo);

      played[played_nb++] = move;

      if (value > best_value) {
         best_value = value;
         pv_cat(pv,new_pv,move);
         if (value > alpha) {
            alpha = value;
            best_move = move;
			SearchStack[height].best_move = move;
            if (value >= beta){ 
				// history_success(move,board);
				goto cut;
			}
         }
      }

      if (node_type == NodeCut) node_type = NodeAll;
   }

   // ALL node

   if (best_value == ValueNone) { // no legal move
      if (in_check) {
         ASSERT(board_is_mate(board));
         return VALUE_MATE(height);
      } else {
         ASSERT(board_is_stalemate(board));
         return ValueDraw;
      }
   }

cut:

   ASSERT(value_is_ok(best_value));

   // move ordering

   if (best_move != MoveNone) {

      good_move(best_move,board,depth,height);

      if (best_value >= beta && !move_is_tactical(best_move,board)) {

		 ASSERT(played_nb>0&&played[played_nb-1]==best_move);

         for (i = 0; i < played_nb-1; i++) {
            move = played[i];
            ASSERT(move!=best_move);
            history_bad(move,board);
         }

         history_good(best_move,board);
      }
   }

   // transposition table

   if (UseTrans && depth >= TransDepth) {

      trans_move = best_move;
      trans_depth = depth;
      trans_min_value = (best_value > old_alpha) ? value_to_trans(best_value,height) : -ValueInf;
      trans_max_value = (best_value < beta)      ? value_to_trans(best_value,height) : +ValueInf;

      trans_store(Trans,board->key,trans_move,trans_depth,trans_min_value,trans_max_value);
   }

   return best_value;
}
Example #12
0
static int full_root(list_t * list, board_t * board, int alpha, int beta, int depth, int height, int search_type) {

   int old_alpha;
   int value, best_value[MultiPVMax];
   int i, move, j;
   int new_depth;
   undo_t undo[1];
   mv_t new_pv[HeightMax];
   bool found;

   ASSERT(list_is_ok(list));
   ASSERT(board_is_ok(board));
   ASSERT(range_is_ok(alpha,beta));
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(search_type==SearchNormal||search_type==SearchShort);

   ASSERT(list==SearchRoot->list);
   ASSERT(!LIST_IS_EMPTY(list));
   ASSERT(board==SearchCurrent->board);
   ASSERT(board_is_legal(board));
   ASSERT(depth>=1);

   // init

   SearchStack[height].best_move = MoveNone;
   SearchStack[height].move = MoveNone;
   SearchStack[height].threat_move = MoveNone;
   SearchStack[height].reduced = false;

   SearchCurrent->node_nb++;
   SearchInfo->check_nb--;

   if (SearchCurrent->multipv == 0)
	  for (i = 0; i < LIST_SIZE(list); i++) list->value[i] = ValueNone;

   old_alpha = alpha;
   best_value[SearchCurrent->multipv] = ValueNone;

   // move loop

   for (i = 0; i < LIST_SIZE(list); i++) {

      move = LIST_MOVE(list,i);

	  if (SearchCurrent->multipv > 0){
		  found = false;
		  for (j = 0; j < SearchCurrent->multipv; j++){
			  if (SearchBest[j].pv[0] == move){
				  found = true;
				  break;
			  }
		  }
		  if (found == true)
				continue;
	  }
	  SearchStack[height].move = move;

      SearchRoot->depth = depth;
      SearchRoot->move = move;
      SearchRoot->move_pos = i;
      SearchRoot->move_nb = LIST_SIZE(list);

      search_update_root();

      new_depth = full_new_depth(depth,move,board,board_is_check(board)&&LIST_SIZE(list)==1,false,true, height);

      move_do(board,move,undo);

      if (search_type == SearchShort || best_value[SearchCurrent->multipv] == ValueNone) { // first move
         value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV);
		 if (value <= alpha){ // research
			 old_alpha = -ValueInf;
			 value = -full_search(board,-beta,ValueInf,new_depth,height+1,new_pv,NodePV);
		 }
		 else if (value >= beta){ // research
			 value = -full_search(board,-ValueInf,-alpha,new_depth,height+1,new_pv,NodePV);
		 }
			  
      } else { // other moves
         value = -full_search(board,-alpha-1,-alpha,new_depth,height+1,new_pv,NodeCut);
         if (value > alpha) { // && value < beta
            SearchRoot->change = true;
            SearchRoot->easy = false;
            SearchRoot->flag = false;
            search_update_root();
            value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV);
         }
      }

      move_undo(board,move,undo);

      if (value <= alpha) { // upper bound
         list->value[i] = old_alpha;
      } else if (value >= beta) { // lower bound
         list->value[i] = beta;
      } else { // alpha < value < beta => exact value
         list->value[i] = value;
      }

      if (value > best_value[SearchCurrent->multipv] && (best_value[SearchCurrent->multipv] == ValueNone || value > alpha)) {

         SearchBest[SearchCurrent->multipv].move = move;
		 SearchStack[height].best_move = move;
         SearchBest[SearchCurrent->multipv].value = value;
         if (value <= alpha) { // upper bound
            SearchBest[SearchCurrent->multipv].flags = SearchUpper;
         } else if (value >= beta) { // lower bound
            SearchBest[SearchCurrent->multipv].flags = SearchLower;
         } else { // alpha < value < beta => exact value
            SearchBest[SearchCurrent->multipv].flags = SearchExact;
         }
         SearchBest[SearchCurrent->multipv].depth = depth;
         pv_cat(SearchBest[SearchCurrent->multipv].pv,new_pv,move);

         search_update_best();
      }

      if (value > best_value[SearchCurrent->multipv]) {
         best_value[SearchCurrent->multipv] = value;
         if (value > alpha) {
            if (search_type == SearchNormal) alpha = value;
            if (value >= beta) break;
         }
      }
   }

   ASSERT(value_is_ok(best_value));

   list_sort(list);

   ASSERT(SearchBest->move==LIST_MOVE(list,0));
   ASSERT(SearchBest->value==best_value);

   if (UseTrans && best_value[SearchCurrent->multipv] > old_alpha && best_value[SearchCurrent->multipv] < beta) {
      pv_fill(SearchBest[SearchCurrent->multipv].pv,board);
   }

   return best_value[SearchCurrent->multipv];
}
Example #13
0
static int full_quiescence(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int ThreadId) {

   bool in_check;
   int old_alpha;
   int value, best_value;
   int best_move;
   int move;
   int opt_value;
   attack_t attack[1];
   sort_t sort[1];
   undo_t undo[1];
   mv_t new_pv[HeightMax];
   int probe_score, probe_depth;  
   int trans_move, trans_depth, trans_flags, trans_value;
   entry_t * found_entry;

   ASSERT(board!=NULL);
   ASSERT(range_is_ok(alpha,beta));
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(pv!=NULL);

   ASSERT(board_is_legal(board));
   ASSERT(depth<=0);

   // init

   SearchCurrent[ThreadId]->node_nb++;
   SearchInfo[ThreadId]->check_nb--;
   PV_CLEAR(pv);

   if (height > SearchCurrent[ThreadId]->max_depth) SearchCurrent[ThreadId]->max_depth = height;

   if (SearchInfo[ThreadId]->check_nb <= 0) {
      SearchInfo[ThreadId]->check_nb += SearchInfo[ThreadId]->check_inc;
      search_check(ThreadId);
   }

   // draw?

   if (board_is_repetition(board)) return ValueDraw;

	/*
		Interior node recognizer from scorpio by Daniel Shawul
		-> dont probe at the leaves as this will slow down search
	    For 4/3 pieces probe there also.
		-> After captures and pawn moves assume exact score and cutoff tree,
	    because we are making progress. Note this is not done only for speed.
		-> if we are far from root (depth / 2), assume exact score and cutoff tree
	*/

/*	if (egbb_is_loaded && board->piece_nb <= 4){ 
		if (probe_bitbases(board, probe_score)){
			probe_score = value_from_trans(probe_score,height);
			return probe_score;
		}
	}*/

	if (recog_draw(board,ThreadId)) return ValueDraw;

   // mate-distance pruning

   if (UseDistancePruning) {

      // lower bound

      value = VALUE_MATE(height+2); // does not work if the current position is mate
      if (value > alpha && board_is_mate(board)) value = VALUE_MATE(height);

      if (value > alpha) {
         alpha = value;
         if (value >= beta) return value;
      }

      // upper bound

      value = -VALUE_MATE(height+1);

      if (value < beta) {
         beta = value;
         if (value <= alpha) return value;
      }
   }
   
   // transposition table: added by Jerry Donald, about +20 elo self-play
   // new in Toga II 4.0: accept hash hits in PV ~ +3 elo
   
   trans_move = MoveNone;
   
   if (UseTrans) { 

      if (trans_retrieve(Trans,&found_entry,board->key,&trans_move,&trans_depth,&trans_flags,&trans_value)) {

		 trans_value = value_from_trans(trans_value,height);

	 	 if ((UseExact && trans_value != ValueNone && TRANS_IS_EXACT(trans_flags)) 
		 	 || (TRANS_IS_LOWER(trans_flags) && trans_value >= beta)
			 || (TRANS_IS_UPPER(trans_flags) && trans_value <= alpha)) {
			 return trans_value;
		 } 
	  }
   }

   // more init

   attack_set(attack,board);
   in_check = ATTACK_IN_CHECK(attack);

   if (in_check) {
      ASSERT(depth<0);
      depth++; // in-check extension
   }

   // height limit

   if (height >= HeightMax-1) return eval(board, alpha, beta, ThreadId);

   // more init

   old_alpha = alpha;
   best_value = ValueNone;
   best_move = MoveNone;

   /* if (UseDelta) */ opt_value = +ValueInf;

   if (!in_check) {

      // lone-king stalemate?

      if (simple_stalemate(board)) return ValueDraw;

      // stand pat

      value = eval(board, alpha, beta, ThreadId);

      ASSERT(value>best_value);
      best_value = value;
      if (value > alpha) {
         alpha = value;
         if (value >= beta) goto cut;
      }

      if (UseDelta) {
         opt_value = value + DeltaMargin;
         ASSERT(opt_value<+ValueInf);
      }
   }

   // move loop

/*    cd = CheckDepth;
   if(cd < 0 && board->piece_size[board->turn] <= 5)
	   cd++; */

   sort_init_qs(sort,board,attack, depth>=SearchCurrent[ThreadId]->CheckDepth /* depth>=cd */);

   while ((move=sort_next_qs(sort)) != MoveNone) {

	  // delta pruning

      if (UseDelta && beta == old_alpha+1) { // i.e. non-PV

         if (!in_check && !move_is_check(move,board) && !capture_is_dangerous(move,board)) {

            ASSERT(move_is_tactical(move,board));

            // optimistic evaluation

            value = opt_value;

            int to = MOVE_TO(move);
            int capture = board->square[to];

            if (capture != Empty) {
               value += VALUE_PIECE(capture);
            } else if (MOVE_IS_EN_PASSANT(move)) {
               value += ValuePawn;
            }

            if (MOVE_IS_PROMOTE(move)) value += ValueQueen - ValuePawn;

            // pruning

            if (value <= alpha) {

               if (value > best_value) {
                  best_value = value;
                  PV_CLEAR(pv);
               }

               continue;
            }
         }
      }

      move_do(board,move,undo);
      value = -full_quiescence(board,-beta,-alpha,depth-1,height+1,new_pv,ThreadId);
      move_undo(board,move,undo);

      if (value > best_value) {
         best_value = value;
         pv_cat(pv,new_pv,move);
         if (value > alpha) {
            alpha = value;
            best_move = move;
			if (value >= beta) goto cut;
         }
      }
   }

   // ALL node

   if (best_value == ValueNone) { // no legal move
      ASSERT(board_is_mate(board));
      return VALUE_MATE(height);
   }

cut:
    
   // store result in hash table (JD)
    
   if (UseTrans) {

      trans_move = best_move;
      trans_depth = 0;
      trans_flags = TransUnknown;
      if (best_value > old_alpha) trans_flags |= TransLower;
      if (best_value < beta) trans_flags |= TransUpper;
      trans_value = value_to_trans(best_value,height);

      trans_store(Trans,board->key,trans_move,trans_depth,trans_flags,trans_value);

   }

   ASSERT(value_is_ok(best_value));

   return best_value;
}
Example #14
0
static int full_search(board_t * board, int alpha, int beta, int depth, int height, mv_t pv[], int node_type, bool extended, int ThreadId) {

   bool in_check;
   bool single_reply;
   bool good_cap;
   int trans_move, trans_depth, trans_flags, trans_value;
   int old_alpha;
   int value, best_value;
   int move, best_move;
   int new_depth;
   int played_nb;
   int i;
   int opt_value;
   bool reduced, cap_extended;
   attack_t attack[1];
   sort_t sort[1];
   undo_t undo[1];
   mv_t new_pv[HeightMax];
   mv_t played[256];
   int FutilityMargin;
   int probe_score, probe_depth;
   int newHistoryValue;
   int threshold;
   int reduction;
   int last_move;
   int quiet_move_count;
   entry_t * found_entry;
      
   ASSERT(board!=NULL);
   ASSERT(range_is_ok(alpha,beta));
   ASSERT(depth_is_ok(depth));
   ASSERT(height_is_ok(height));
   ASSERT(pv!=NULL);
   ASSERT(node_type==NodePV||node_type==NodeCut||node_type==NodeAll);

   ASSERT(board_is_legal(board));

   // horizon?

   if (depth <= 0){
	   if (node_type == NodePV)
			SearchCurrent[ThreadId]->CheckDepth = 1 - SearchCurrent[ThreadId]->CheckNb - 1;
	   else
			SearchCurrent[ThreadId]->CheckDepth = 1 - SearchCurrent[ThreadId]->CheckNb;
	   return full_quiescence(board,alpha,beta,0,height,pv,ThreadId);
   }

   // init

   SearchCurrent[ThreadId]->node_nb++;
   SearchInfo[ThreadId]->check_nb--;
   PV_CLEAR(pv);

   if (height > SearchCurrent[ThreadId]->max_depth) SearchCurrent[ThreadId]->max_depth = height;

   if (SearchInfo[ThreadId]->check_nb <= 0) {
      SearchInfo[ThreadId]->check_nb += SearchInfo[ThreadId]->check_inc;
      search_check(ThreadId);
   }

    // draw?

	if (board_is_repetition(board)) return ValueDraw;

		/*
		Interior node recognizer from scorpio by Daniel Shawul
		-> dont probe at the leaves as this will slow down search
	    For 4/3 pieces probe there also.
		-> After captures and pawn moves assume exact score and cutoff tree,
	    because we are making progress. Note this is not done only for speed.
		-> if we are far from root (depth / 2), assume exact score and cutoff tree
	*/

//	if (egbb_is_loaded && board->piece_nb <= 5){ 
//		probe_depth = (2 * SearchCurrent[ThreadId]->act_iteration) / 3;
//		if ((board->piece_nb <=4 || height <= probe_depth)
//           && (height >= probe_depth 
//		         || board->cap_sq != SquareNone || PIECE_IS_PAWN(board->moving_piece))
//			  ) {
//		
//            if (probe_bitbases(board, probe_score)){
//                if (probe_score < 0 && board_is_mate(board)) return VALUE_MATE(height);            
//                probe_score = value_from_trans(probe_score,height);

//			/*	trans_move = MoveNone;
//				trans_depth = depth;
//				trans_flags = TransUnknown;
//				if (probe_score > alpha) trans_flags |= TransLower;
//				if (probe_score < beta) trans_flags |= TransUpper;
//   			    trans_store(Trans,board->key,trans_move,trans_depth,trans_flags,probe_score);*/
//				return probe_score;
//			}
//		}
//	}


	if (recog_draw(board,ThreadId)) return ValueDraw;

   // mate-distance pruning

   if (UseDistancePruning) {

      // lower bound

      value = VALUE_MATE(height+2); // does not work if the current position is mate
      if (value > alpha && board_is_mate(board)) value = VALUE_MATE(height);

      if (value > alpha) {
         alpha = value;
         if (value >= beta) return value;
      }

      // upper bound

      value = -VALUE_MATE(height+1);

      if (value < beta) {
         beta = value;
         if (value <= alpha) return value;
      }
   }

   // transposition table

   trans_move = MoveNone;

   if (UseTrans && depth >= TransDepth) {

      if (trans_retrieve(Trans,&found_entry,board->key,&trans_move,&trans_depth,&trans_flags,&trans_value)) {

         // trans_move is now updated
/*		  if (found_entry->depth != trans_depth){
			  found_entry->depth = trans_depth;
		  }*/
          
		  if (node_type != NodePV /*|| ThreadId > 0*/) {

            if (UseMateValues) {

            if (trans_depth < depth) {
				   if (trans_value < -ValueEvalInf && TRANS_IS_UPPER(trans_flags)) {
					  trans_depth = depth;
					  trans_flags = TransUpper;
				   } else if (trans_value > +ValueEvalInf && TRANS_IS_LOWER(trans_flags)) {
					  trans_depth = depth;
					  trans_flags = TransLower;
				   }
			   }

				if (trans_depth >= depth) {

				   trans_value = value_from_trans(trans_value,height);

				   if ((UseExact && TRANS_IS_EXACT(trans_flags))
					|| (TRANS_IS_LOWER(trans_flags) && trans_value >= beta)
					|| (TRANS_IS_UPPER(trans_flags) && trans_value <= alpha)) {
						return trans_value;
				   } 

				 /*  if (TRANS_IS_EXACT(trans_flags))
					   return trans_value;
				   if (TRANS_IS_LOWER(trans_flags)) {
						if (trans_value >= beta)
							return trans_value;
						if (trans_value > alpha)
							alpha = trans_value;
					}
					if (TRANS_IS_UPPER(trans_flags)) {
						if (trans_value <= alpha)
							return trans_value;
						if (trans_value < beta)
							beta = trans_value;
					} */
				}
			}
		 }
	  }
   }

   // height limit

   if (height >= HeightMax-1) return eval(board, alpha, beta, ThreadId);

   // more init

   old_alpha = alpha;
   best_value = ValueNone;
   best_move = MoveNone;
   played_nb = 0;
   last_move = SearchCurrent[ThreadId]->last_move;
   
   attack_set(attack,board);
   in_check = ATTACK_IN_CHECK(attack);

   // null-move pruning

   if (UseNull && depth >= NullDepth && node_type != NodePV
   && !(trans_move != MoveNone && TRANS_IS_UPPER(trans_flags) && trans_depth >= depth - NullReduction - depth/4 && trans_value < beta)) { 
   // if hash table tells us we are < beta, the null move search probably wastes time
   // bugfix 25-3-2013 (trans_move condition added) was LOS ~90%, + 10 elo. Probably not nearly that good but it's a bugfix...)

      if (!in_check
       && !value_is_mate(beta)
       && do_null(board)
       && (!UseNullEval || depth <= NullReduction+1 || eval(board,alpha, beta, ThreadId) >= beta)) {

         // null-move search
		 
        //  if (depth < 11)
		 new_depth = depth - NullReduction - depth/4; // JD: from Stockfish, 10 elo better
		     
		// else
		//	new_depth = depth - NullReduction - 2;
		 //new_depth = depth - R_adpt(board->piece_size[board->turn]+board->pawn_size[board->turn],depth,NullReduction) - 1;
		 
	     move_do_null(board,undo);
	     SearchCurrent[ThreadId]->last_move = MoveNone; // refutation table
         value = -full_search(board,-beta,-beta+1,new_depth,height+1,new_pv,NODE_OPP(node_type),false,ThreadId);
         move_undo_null(board,undo);

         // pruning

         if (value >= beta) {

            if (value > +ValueEvalInf) value = +ValueEvalInf; // do not return unproven mates
            ASSERT(!value_is_mate(value));

            // pv_cat(pv,new_pv,MoveNull);

            best_move = MoveNone;
            best_value = value;
            goto cut;
         }
	  }
   }
    
   // Razoring: idea by Tord Romstad (Glaurung), fixed by Jerry Donald
   
   if (node_type != NodePV && !in_check && trans_move == MoveNone && depth <= 3){ 
        threshold = beta - RazorMargin - (depth-1)*39; // Values from Protector (Raimund Heid)
        if (eval(board,threshold-1, threshold, ThreadId) < threshold){
		   value = full_quiescence(board,threshold-1,threshold,0,height,pv,ThreadId); 
           if (value < threshold) // corrected - was < beta which is too risky at depth > 1
               return value;
        }
   }


   // Internal Iterative Deepening
   
   if (UseIID && depth >= IIDDepth && node_type == NodePV && trans_move == MoveNone) {

	  new_depth = MIN(depth - IIDReduction,depth/2);
      ASSERT(new_depth>0);

      value = full_search(board,alpha,beta,new_depth,height,new_pv,node_type,false,ThreadId);
      if (value <= alpha) value = full_search(board,-ValueInf,beta,new_depth,height,new_pv,node_type,false,ThreadId);

      trans_move = new_pv[0];
   }

   // move generation

   sort_init(sort,board,attack,depth,height,trans_move,last_move,ThreadId);

   single_reply = false;
   if (in_check && LIST_SIZE(sort->list) == 1) single_reply = true; // HACK

   // move loop

   opt_value = +ValueInf;
   good_cap = true;
   quiet_move_count = 0;
   
   while ((move=sort_next(sort,ThreadId)) != MoveNone) {

	  // extensions

      new_depth = full_new_depth(depth,move,board,single_reply,node_type==NodePV, height, extended, &cap_extended, ThreadId);
      
      // history pruning

      value = sort->value; // history score
	  if (!in_check && depth <= 6 && node_type != NodePV 
		  && new_depth < depth && value < 2 * HistoryValue / (depth + depth % 2) /*2*/ 
		  && played_nb >= 1+depth && !move_is_dangerous(move,board)){ 
			continue;
	  }

	  // futility pruning

	  if (UseFutility && node_type != NodePV && depth <= 5) {
		  
         if (!in_check && new_depth < depth&& !move_is_tactical(move,board) && !move_is_dangerous(move,board)) {

            ASSERT(!move_is_check(move,board));
            
            // move count based pruning (added by Jerry Donald: ~+20 elo!)
            
            if (quiet_move_count >= MoveCountLimit[depth])continue;
            
            quiet_move_count++;

            // optimistic evaluation (Chris Formula)

            if (opt_value == +ValueInf) {
				if (depth>=2){
					FutilityMargin = FutilityMargin2 + (depth % 2) * 100;
				}
				else{
					FutilityMargin = FutilityMargin1;
				}
				opt_value = eval(board,alpha-FutilityMargin,alpha,ThreadId) + FutilityMargin;
				ASSERT(opt_value<+ValueInf);
            }

            value = opt_value;

            // pruning

            if (value <= alpha) {

               if (value > best_value) {
                  best_value = value;
                  PV_CLEAR(pv);
               }

               continue;
            }
         }
      } 
      
      // Late Move Reductions
	  
	  // init	  
	  reduced = false;
	  reduction = 0;

      // lookup reduction      
	  if (UseHistory) {
			if (!in_check && new_depth < depth && played_nb >= HistoryMoveNb
				&& depth >= HistoryDepth && !move_is_dangerous(move,board)) {
                         
					if (good_cap && !move_is_tactical(move,board)){
						good_cap = false;
					}
					
					if (!good_cap){
                        reduction = (node_type == NodePV ? quietPvMoveReduction[depth<64 ? depth: 63][played_nb<64? played_nb: 63]:
                                     quietMoveReduction[depth<64 ? depth: 63][played_nb<64? played_nb: 63]);
                        
						// reduced bad captures less
						if (move_is_tactical(move,board)) reduction = reduction / 2; // bad captures
						
						// set reduction flag
						if (reduction > 0)  reduced = true;
                    }
			}
	  }

	  // recursive search

	  move_do(board,move,undo);
	  
	  SearchCurrent[ThreadId]->last_move = move;

      if (node_type != NodePV || best_value == ValueNone) { // first move or non-pv
		 value = -full_search(board,-beta,-alpha,new_depth-reduction,height+1,new_pv,NODE_OPP(node_type),cap_extended,ThreadId);
         
         // The move was reduced and fails high; so we research with full depth
         if (reduced && value >= beta){
		        value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NODE_OPP(node_type),cap_extended,ThreadId);

         }
         
      } else { // other moves (all PV children)
      
		 value = -full_search(board,-alpha-1,-alpha,new_depth-reduction,height+1,new_pv,NodeCut,cap_extended,ThreadId);
         
         // In case of fail high:
               
         // If reduced then we try a research with node_type = NodePV
         if (value > alpha && reduced){  // && value < beta
			value = -full_search(board,-beta,-alpha,new_depth-reduction,height+1,new_pv,NodePV,cap_extended,ThreadId);
		    
            // Still fails high! We research to full depth
            if (reduced && value >= beta){	
		        value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV,cap_extended,ThreadId);
            }
         
         // If not reduced we research as a PV node   
         } else if (value > alpha){	
		    value = -full_search(board,-beta,-alpha,new_depth,height+1,new_pv,NodePV,cap_extended,ThreadId);
         }
      }

      move_undo(board,move,undo);

      played[played_nb++] = move;
	  
      if (value > best_value) {
         best_value = value;
         pv_cat(pv,new_pv,move);
         if (value > alpha) {
            alpha = value;
            best_move = move;
			if (value >= beta){ 
				goto cut;
			}
         }
      }

      if (node_type == NodeCut) node_type = NodeAll;
   }

   // ALL node

   if (best_value == ValueNone) { // no legal move
      if (in_check) {
         ASSERT(board_is_mate(board));
         return VALUE_MATE(height);
      } else {
         ASSERT(board_is_stalemate(board));
         return ValueDraw;
      }
   }

cut:

   ASSERT(value_is_ok(best_value));

   // move ordering

   if (best_move != MoveNone) {

      good_move(best_move,board,depth,height,ThreadId);

      if (best_value >= beta && !move_is_tactical(best_move,board)) { // check is 204b and d, e
         
         // refutation table ~5 elo
         if (last_move != MoveNull && last_move != MoveNone)refutation_update(best_move, last_move, board, ThreadId);

		 ASSERT(played_nb>0&&played[played_nb-1]==best_move);

	 	 for (i = 0; i < played_nb-1; i++) {
			move = played[i];
			ASSERT(move!=best_move);
			history_bad(move,board,ThreadId);
			bad_move(move,board,depth,height,ThreadId); // added JD ~ 5 elo
		 }

		 history_good(best_move,board,ThreadId);
		
      }
   }

   // transposition table

   if (UseTrans && depth >= TransDepth) {

      trans_move = best_move;
      trans_depth = depth;
      trans_flags = TransUnknown;
      if (best_value > old_alpha) trans_flags |= TransLower;
      if (best_value < beta) trans_flags |= TransUpper;
      trans_value = value_to_trans(best_value,height);

      trans_store(Trans,board->key,trans_move,trans_depth,trans_flags,trans_value);

   }

   return best_value;
}