Example #1
0
// Perform a Principle Variation Search
//   https://chessprogramming.wikispaces.com/Principal+Variation+Search
static score_t searchPV(searchNode *node, int depth, uint64_t *node_count_serial) {
  // Initialize the searchNode data structure.
  initialize_pv_node(node, depth);

  // Pre-evaluate the node to determine if we need to search further.
  leafEvalResult pre_evaluation_result = evaluate_as_leaf(node, SEARCH_PV);

  // use some information from the pre-evaluation
  int hash_table_move = pre_evaluation_result.hash_table_move;

  if (pre_evaluation_result.type == MOVE_EVALUATED) {
    return pre_evaluation_result.score;
  }
  if (pre_evaluation_result.score > node->best_score) {
    node->best_score = pre_evaluation_result.score;
    if (node->best_score > node->alpha) {
      node->alpha = node->best_score;
    }
  }

  // Get the killer moves at this node.
  move_t killer_a = killer[KMT(node->ply, 0)];
  move_t killer_b = killer[KMT(node->ply, 1)];


  // sortable_move_t move_list
  //
  // Contains a list of possible moves at this node. These moves are "sortable"
  //   and can be compared as integers. This is accomplished by using high-order
  //   bits to store a sort key.
  //
  // Keep track of the number of moves that we have considered at this node.
  //   After we finish searching moves at this node the move_list array will
  //   be organized in the following way:
  //
  //   m0, m1, ... , m_k-1, m_k, ... , m_N-1
  //
  //  where k = num_moves_tried, and N = num_of_moves
  //
  //  This will allow us to update the best_move_history table easily by
  //  scanning move_list from index 0 to k such that we update the table
  //  only for moves that we actually considered at this node.
  sortable_move_t move_list[MAX_NUM_MOVES];
  int num_of_moves = get_sortable_move_list(node, move_list, hash_table_move);
  int num_moves_tried = 0;

  // Start searching moves.
  for (int mv_index = 0; mv_index < num_of_moves; mv_index++) {
    // Incrementally sort the move list.
    sort_incremental(move_list, num_of_moves, mv_index);

    move_t mv = get_move(move_list[mv_index]);

    num_moves_tried++;
    (*node_count_serial)++;

    moveEvaluationResult result = evaluateMove(node, mv, killer_a, killer_b,
                                               SEARCH_PV,
                                               node_count_serial);

    if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE) {
      continue;
    }

    // A legal move is a move that's not KO, but when we are in quiescence
    // we only want to count moves that has a capture.
    if (result.type == MOVE_EVALUATED) {
      node->legal_move_count++;
    }

    // Check if we should abort due to time control.
    if (abortf) {
      return 0;
    }

    bool cutoff = search_process_score(node, mv, mv_index, &result, SEARCH_PV);
    if (cutoff) {
      break;
    }
  }

  if (node->quiescence == false) {
    update_best_move_history(&(node->position), node->best_move_index,
                             move_list, num_moves_tried);
  }

  tbassert(abs(node->best_score) != -INF, "best_score = %d\n",
           node->best_score);

  // Update the transposition table.
  //
  // Note: This function reads node->best_score, node->orig_alpha,
  //   node->position.key, node->depth, node->ply, node->beta,
  //   node->alpha, node->subpv
  update_transposition_table(node);

  return node->best_score;
}
static score_t scout_search(searchNode *node, int depth,
                            uint64_t *node_count_serial) {
  // Initialize the search node.
  initialize_scout_node(node, depth);

  // check whether we should abort
  if (should_abort_check() || parallel_parent_aborted(node)) {
    return 0;
  }

  // Pre-evaluate this position.
  leafEvalResult pre_evaluation_result = evaluate_as_leaf(node, SEARCH_SCOUT);

  // If we decide to stop searching, return the pre-evaluation score.
  if (pre_evaluation_result.type == MOVE_EVALUATED) {
    return pre_evaluation_result.score;
  }

  // Populate some of the fields of this search node, using some
  //  of the information provided by the pre-evaluation.
  int hash_table_move = pre_evaluation_result.hash_table_move;
  node->best_score = pre_evaluation_result.score;
  node->quiescence = pre_evaluation_result.should_enter_quiescence;

  // Grab the killer-moves for later use.
  move_t killer_a = killer[KMT(node->ply, 0)];
  move_t killer_b = killer[KMT(node->ply, 1)];

  // Store the sorted move list on the stack.
  //   MAX_NUM_MOVES is all that we need.
  sortable_move_t move_list[MAX_NUM_MOVES];

  // Obtain the sorted move list.
  int num_of_moves = get_sortable_move_list(node, move_list, hash_table_move);

  int number_of_moves_evaluated = 0;


  // A simple mutex. See simple_mutex.h for implementation details.
  simple_mutex_t node_mutex;
  init_simple_mutex(&node_mutex);

  // Sort the move list.
  sort_incremental(move_list, num_of_moves, 0);
  
  moveEvaluationResult result;

  for (int mv_index = 0; mv_index < num_of_moves; mv_index++) {
    if (mv_index == 1) {
      sort_full(move_list, num_of_moves);
     // sortable_move_t new_move_list[MAX_NUM_MOVES];
      //memcpy(new_move_list, move_list, num_of_moves*sizeof(sortable_move_t));
     //: sort_incremental_full(move_list,num_of_moves);
    }
    // Get the next move from the move list.
    int local_index = number_of_moves_evaluated++;
    move_t mv = get_move(move_list[local_index]);

    if (TRACE_MOVES) {
      print_move_info(mv, node->ply);
    }

    // increase node count
    __sync_fetch_and_add(node_count_serial, 1);

    evaluateMove(&result, node, mv, killer_a, killer_b,
                 SEARCH_SCOUT,
                 node_count_serial);
    undo_move(&result.next_node, mv);

    if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE
        || abortf || parallel_parent_aborted(node)) {
      continue;
    }

    // A legal move is a move that's not KO, but when we are in quiescence
    // we only want to count moves that has a capture.
    if (result.type == MOVE_EVALUATED) {
      node->legal_move_count++;
    }

    // process the score. Note that this mutates fields in node.
    bool cutoff = search_process_score(node, mv, local_index, &result, SEARCH_SCOUT);

    if (cutoff) {
      node->abort = true;
      break;
    }
  }

  if (parallel_parent_aborted(node)) {
    return 0;
  }

  if (node->quiescence == false) {
    update_best_move_history(node->position, node->best_move_index,
                             move_list, number_of_moves_evaluated);
  }

  tbassert(abs(node->best_score) != -INF, "best_score = %d\n",
           node->best_score);

  // Reads node->position->key, node->depth, node->best_score, and node->ply
  update_transposition_table(node);

  return node->best_score;
}
Example #3
0
static score_t scout_search(searchNode *node, int depth,
                            uint64_t *node_count_serial, full_board_t* board) {


  // Initialize the search node.
  initialize_scout_node(node, depth);

  // check whether we should abort
  if (should_abort_check() || parallel_parent_aborted(node)) {
    return 0;
  }

  // Pre-evaluate this position.
  leafEvalResult pre_evaluation_result = evaluate_as_leaf(node, SEARCH_SCOUT, board);

  // If we decide to stop searching, return the pre-evaluation score.
  if (pre_evaluation_result.type == MOVE_EVALUATED) {
    return pre_evaluation_result.score;
  }

  // Populate some of the fields of this search node, using some
  //  of the information provided by the pre-evaluation.
  int hash_table_move = pre_evaluation_result.hash_table_move;
  node->best_score = pre_evaluation_result.score;
  node->quiescence = pre_evaluation_result.should_enter_quiescence;

  // Grab the killer-moves for later use.
  move_t killer_a = killer[KMT(node->ply, 0)];
  move_t killer_b = killer[KMT(node->ply, 1)];

  // Store the sorted move list on the stack.
  //   MAX_NUM_MOVES is all that we need.
  sortable_move_t move_list[MAX_NUM_MOVES];

  // Obtain the sorted move list.
  int num_of_moves = get_sortable_move_list(node, move_list, hash_table_move, board);
  int num_of_special_moves = move_list[num_of_moves];
  int num_of_nonzero_moves = move_list[num_of_moves + 1];
  if (num_of_special_moves == 0) {
    selectionSort(move_list, num_of_moves, 1);
    num_of_special_moves = 1;
  }
  int number_of_moves_evaluated = 0;

  bool generated_early_cutoff = false;

  for (int mv_index = 0; mv_index < num_of_special_moves; mv_index++) {
    int local_index = number_of_moves_evaluated++;
    move_t mv = get_move(move_list[local_index]);

    // increase node count
    REDUCER_VIEW(node_count_reducer)++;

    smallMoveEvaluationResult result = evaluateMove(node, mv, killer_a, killer_b,
                                               SEARCH_SCOUT,
                                               node_count_serial);

    if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE) {
      continue;
    }

    if (abortf || parallel_parent_aborted(node)) {
      return 0;
    }

    // A legal move is a move that's not KO, but when we are in quiescence
    // we only want to count moves that has a capture.
    if (result.type == MOVE_EVALUATED) {
      node->legal_move_count++;
    }

    // process the score. Note that this mutates fields in node.
    bool cutoff = search_process_score(node, mv, local_index, &result, SEARCH_SCOUT);

    if (cutoff) {
      generated_early_cutoff = true;
      break;
    }
  }

//  ABOVE IS YOUNG BROTHER'S WAIT CODE. IT SHOULD RUN IN SERIAL
//
//  BELOW IS PARALLELIZED SCOUT SEARCH. IT SHOULD RUN IN PARALLEL


  // if this happens, we generated an early cutoff in young brother's wait and can just skip to the end
  if (generated_early_cutoff)
    goto finish_scout_search;

  sort_incremental(move_list + num_of_special_moves, num_of_nonzero_moves - num_of_special_moves);

  // A simple mutex. See simple_mutex.h for implementation details.
  simple_mutex_t node_mutex;
  init_simple_mutex(&node_mutex);
  // TODO: see if changing this depth is better
  if (depth >= 2) {
    cilk_for (int mv_index = num_of_special_moves; mv_index < num_of_moves; mv_index++) {
      do {
        if (node->abort) continue;

        int local_index = __sync_fetch_and_add(&number_of_moves_evaluated, 1);

        move_t mv = get_move(move_list[local_index]);

        // increase node count
        REDUCER_VIEW(node_count_reducer)++;

        smallMoveEvaluationResult result = evaluateMove(node, mv, killer_a, killer_b,
                                                   SEARCH_SCOUT,
                                                   node_count_serial);

        // TODO: change this to break if we want to abort
        if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE) {
          continue;
        }

        if (abortf || parallel_parent_aborted(node)) {
          node->abort = true;
          continue;
        }

        // A legal move is a move that's not KO, but when we are in quiescence
        // we only want to count moves that has a capture.
        if (result.type == MOVE_EVALUATED) {
          __sync_fetch_and_add(&node->legal_move_count, 1);
        }

        bool cutoff = false;
        if (result.score > node->best_score) {
          simple_acquire(&node_mutex);
          // process the score. Note that this mutates fields in node.
          cutoff = search_process_score(node, mv, local_index, &result, SEARCH_SCOUT);
          simple_release(&node_mutex);
        }

        if (cutoff) {
          node->abort = true;
          continue;
        }
      } while (false);
    }
  } else {