private BattleSelector newBattleSelector(List<BotList> initialBattles,
      final ChallengeConfig challenge, final String challenger,
      final Map<String, ScoreError> errorMap) {
    final LinkedList<BotList> battleList = Lists.newLinkedList(initialBattles);
    return new BattleSelector() {
      @Override
      public BotList nextBotList() {
        if (!battleList.isEmpty()) {
          return battleList.remove();
        };

        int minBattles = getMinBattles(errorMap);
        double randomBattleChance =
            SMART_BATTLE_RANDOM_RATE / power(2, minBattles - 2);
        String nextBotListString = null;
        if (Math.random() < randomBattleChance) {
          List<String> minBotLists = Lists.newArrayList();
          for (Map.Entry<String, ScoreError> entry : errorMap.entrySet()) {
            if (entry.getValue().numBattles == minBattles) {
              minBotLists.add(entry.getKey());
            }
          }
          nextBotListString =
              minBotLists.get((int) Math.random() * minBotLists.size());
        } else {
          double bestGain = Double.NEGATIVE_INFINITY;
          for (Map.Entry<String, ScoreError> entry : errorMap.entrySet()) {
            String botListString = entry.getKey();
            if (challenge.allReferenceBots.size() <= _config.threads
                || !_runningBotLists.contains(botListString)) {
              double accuracyGain = entry.getValue().getAccuracyGainRate();
              if (accuracyGain > bestGain) {
                bestGain = accuracyGain;
                nextBotListString = botListString;
              }
            }
          }
        }

        if (nextBotListString == null) {
          throw new RuntimeException("Failed to select a battle!");
        }
        _runningBotLists.add(nextBotListString);
        List<String> nextBotList =
            Lists.newArrayList(nextBotListString.split(","));
        nextBotList.add(challenger);
        return new BotList(nextBotList);
      }
    };
  }