int CheckLadderUseSearch()

in go/board.cc [203:317]


int CheckLadderUseSearch(Board *board, Stone victim, int *num_call, int depth) {
  (*num_call) ++;
  Coord c = board->_last_move;
  Coord c2 = board->_last_move2;
  unsigned short id = board->_infos[c].id;
  unsigned short lib = board->_groups[id].liberties;
  // char buf[30];
  GroupId4 ids;

  if (victim == OPPONENT(board->_next_player)) {
    // Capturer to play. He can choose two ways to capture.

    // Captured.
    if (lib == 1) return depth;
    // Not able to capture.
    if (lib >= 3) return 0;
    // Check if c's vicinity's two empty locations.
    Coord escape[2];
    int num_escape = 0;
    FOR4(c, _, cc) {
      if (board->_infos[cc].color == S_EMPTY) {
        escape[num_escape ++] = cc;
      }
    } ENDFOR4
    // Not a ladder.
    if (num_escape <= 1) return 0;

    // Play each possibility and recurse.
    // We can avoid copying if we are sure one branch cannot be right and only trace down the other.
    int freedom[2];
    Coord must_block = M_PASS;
    for (int i = 0; i < 2; ++i) {
      freedom[i] = 0;
      FOR4(escape[i], _, cc) {
        if (board->_infos[cc].color == S_EMPTY) {
          freedom[i] ++;
        }
      } ENDFOR4
      if (freedom[i] == 3) {
        // Then we have to block this.
        must_block = escape[i];
        break;
      }
    }

    // Check if we have too many branches. If so, stopping the branching.
    if (must_block == M_PASS && *num_call >= MAX_LADDER_SEARCH) {
      must_block = escape[0];
    }

    if (must_block != M_PASS) {
      // It suffices to only play must_block.
      if (TryPlay2(board, must_block, &ids)) {
        Play(board, &ids);
        int final_depth = CheckLadderUseSearch(board, victim, num_call, depth + 1);
        if (final_depth > 0) return final_depth;
      }
    } else {
      // printf("IsLadderUseSearch: Branching:\n");
      // printf("Choice 1: %s\n", get_move_str(escape[0], board->_next_player, buf));
      // printf("Choice 2: %s\n", get_move_str(escape[1], board->_next_player, buf));
      // ShowBoard(board, SHOW_ALL);

      // We need to play both. This should seldomly happen.
      Board b_next;
      CopyBoard(&b_next, board);
      if (TryPlay2(&b_next, escape[0], &ids)) {
        Play(&b_next, &ids);
        int final_depth = CheckLadderUseSearch(&b_next, victim, num_call, depth + 1);
        if (final_depth > 0) return final_depth;
      }

      if (TryPlay2(board, escape[1], &ids)) {
        Play(board, &ids);
        int final_depth = CheckLadderUseSearch(board, victim, num_call, depth + 1);
        if (final_depth > 0) return final_depth;
      }
    }
  } else {
    // Victim to play. In general he only has one choice because he is always in atari.
    // If the capturer place a stone in atari, then the capture fails.
    if (lib == 1) return 0;
    // Otherwise the victim need to continue fleeing.
    Coord flee_loc = M_PASS;
    FOR4(c2, _, cc) {
      if (board->_infos[cc].color == S_EMPTY) {
        flee_loc = cc;
        break;
      }
    } ENDFOR4
    // Make sure flee point is not empty
    if (flee_loc == M_PASS) {
      ShowBoard(board, SHOW_ALL);
      error("Error!! IsLadderUseSearch is wrong!\n");
      return 0;
    }
    if (TryPlay2(board, flee_loc, &ids)) {
      Play(board, &ids);
      unsigned char id = board->_infos[flee_loc].id;
      if (board->_groups[id].liberties >= 3) return 0;
      if (board->_groups[id].liberties == 2) {
        // Check if the neighboring enemy stone has only one liberty, if so, then it is not a ladder.
        FOR4(flee_loc, _, cc) {
          if (board->_infos[cc].color != OPPONENT(victim)) continue;
          unsigned char id2 = board->_infos[cc].id;
          // If the enemy group is in atari but our group has 2 liberties, then it is not a ladder.
          if (board->_groups[id2].liberties == 1) return 0;
        } ENDFOR4
      }
      int final_depth = CheckLadderUseSearch(board, victim, num_call, depth + 1);
      if (final_depth > 0) return final_depth;
    }
  }
  return 0;
}