<commit_before>package com.github.lstephen.ootp.ai.selection.rotation;

import com.github.lstephen.ai.search.HillClimbing;
import com.github.lstephen.ai.search.RepeatedHillClimbing;
import com.github.lstephen.ai.search.action.Action;
import com.github.lstephen.ai.search.action.ActionGenerator;
import com.github.lstephen.ootp.ai.io.Printables;
import com.github.lstephen.ootp.ai.player.Player;
import com.github.lstephen.ootp.ai.player.Slot;
import com.github.lstephen.ootp.ai.regression.Predictor;
import com.github.lstephen.ootp.ai.selection.Mode;
import com.github.lstephen.ootp.ai.selection.Selection;
import com.github.lstephen.ootp.ai.selection.rotation.Rotation.Role;
import com.google.common.base.Preconditions;
import com.google.common.collect.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class RotationSelection implements Selection {

  private final Predictor predictor;

  private final RotationDefinition definition;

  private final Multiset<Slot> slots;

  private boolean useAllAvailable = false;

  private RotationSelection(
      Predictor predictor, RotationDefinition definition, Multiset<Slot> slots) {
    this.predictor = predictor;
    this.definition = definition;
    this.slots = slots;
  }

  public RotationSelection useAllAvailable() {
    useAllAvailable = true;
    return this;
  }

  public ImmutableMultimap<Slot, Player> select(
      Iterable<Player> forced, Iterable<Player> available) {

    Rotation r = selectRotation(forced, available);

    Multimap<Slot, Player> assigned = HashMultimap.create();

    for (Player p : r.getStarters()) {
      assigned.put(Slot.SP, p);
    }

    for (Player p : r.getNonStarters()) {
      assigned.put(Slot.MR, p);
    }

    return ImmutableMultimap.copyOf(assigned);
  }

  public Rotation selectRotation(Iterable<Player> forced, Iterable<Player> available) {

    System.out.println("Selecting rotation...");
    System.out.println("Forced:" + Iterables.size(forced));
    System.out.println("Available:" + Iterables.size(available));

    HillClimbing<Rotation> hc =
        HillClimbing.<Rotation>builder()
            .validator(
                r ->
                    r.isValid()
                        && r.get(Role.SP).size() == definition.getRotationSize()
                        && (useAllAvailable || r.getAll().size() <= slots.size()))
            .heuristic(heuristic())
            .actionGenerator(actionGenerator(forced, available))
            .build();

    Rotation r =
        new RepeatedHillClimbing<Rotation>(initialStateGenerator(forced, available), hc).search();

    for (Player p : Player.byShortName().sortedCopy(r.getAll())) {
      System.out.print(p.getShortName() + "/");
    }
    System.out.println();
    return r;
  }

  private Ordering<Rotation> heuristic() {
    Ordering<Rotation> bySize =
        Ordering.natural().onResultOf((Rotation r) -> r.getAll().size()).reverse();

    return Ordering.natural().onResultOf((Rotation r) -> r.score(predictor)).compound(bySize);
  }

  private Supplier<Rotation> initialStateGenerator(
      final Iterable<Player> forced, final Iterable<Player> available) {
    return new Supplier<Rotation>() {
      @Override
      public Rotation get() {
        List<Player> ps = Lists.newArrayList(available);
        Iterables.removeAll(ps, ImmutableSet.copyOf(forced));

        Collections.shuffle(ps);

        List<Player> sps = Lists.newArrayList();
        List<Player> mrs = Lists.newArrayList();
        List<Player> rest = Lists.newArrayList();

        for (Player p : forced) {
          if (p.getSlots().contains(Slot.SP) && sps.size() < definition.getRotationSize()) {
            sps.add(p);
          } else if (mrs.size() < definition.getRelieversSize()) {<commit_msg>Force 4 man rotation if few pitchers<commit_after>package com.github.lstephen.ootp.ai.selection.rotation;

import com.github.lstephen.ai.search.HillClimbing;
import com.github.lstephen.ai.search.RepeatedHillClimbing;
import com.github.lstephen.ai.search.action.Action;
import com.github.lstephen.ai.search.action.ActionGenerator;
import com.github.lstephen.ootp.ai.io.Printables;
import com.github.lstephen.ootp.ai.player.Player;
import com.github.lstephen.ootp.ai.player.Slot;
import com.github.lstephen.ootp.ai.regression.Predictor;
import com.github.lstephen.ootp.ai.selection.Mode;
import com.github.lstephen.ootp.ai.selection.Selection;
import com.github.lstephen.ootp.ai.selection.rotation.Rotation.Role;
import com.google.common.base.Preconditions;
import com.google.common.collect.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class RotationSelection implements Selection {

  private final Predictor predictor;

  private final RotationDefinition definition;

  private final Multiset<Slot> slots;

  private boolean useAllAvailable = false;

  private RotationSelection(
      Predictor predictor, RotationDefinition definition, Multiset<Slot> slots) {
    this.predictor = predictor;
    this.definition = definition;
    this.slots = slots;
  }

  public RotationSelection useAllAvailable() {
    useAllAvailable = true;
    return this;
  }

  public ImmutableMultimap<Slot, Player> select(
      Iterable<Player> forced, Iterable<Player> available) {

    Rotation r = selectRotation(forced, available);

    Multimap<Slot, Player> assigned = HashMultimap.create();

    for (Player p : r.getStarters()) {
      assigned.put(Slot.SP, p);
    }

    for (Player p : r.getNonStarters()) {
      assigned.put(Slot.MR, p);
    }

    return ImmutableMultimap.copyOf(assigned);
  }

  public Rotation selectRotation(Iterable<Player> forced, Iterable<Player> available) {

    System.out.println("Selecting rotation...");
    System.out.println("Forced:" + Iterables.size(forced));
    System.out.println("Available:" + Iterables.size(available));

    RotationDefinition definition =
        Iterables.size(forced) + Iterables.size(available) < 10
            ? RotationDefinition.playoffs()
            : this.definition;

    HillClimbing<Rotation> hc =
        HillClimbing.<Rotation>builder()
            .validator(
                r ->
                    r.isValid()
                        && r.get(Role.SP).size() == definition.getRotationSize()
                        && (useAllAvailable || r.getAll().size() <= slots.size()))
            .heuristic(heuristic())
            .actionGenerator(actionGenerator(forced, available))
            .build();

    Rotation r =
        new RepeatedHillClimbing<Rotation>(initialStateGenerator(forced, available, definition), hc).search();

    for (Player p : Player.byShortName().sortedCopy(r.getAll())) {
      System.out.print(p.getShortName() + "/");
    }
    System.out.println();
    return r;
  }

  private Ordering<Rotation> heuristic() {
    Ordering<Rotation> bySize =
        Ordering.natural().onResultOf((Rotation r) -> r.getAll().size()).reverse();

    return Ordering.natural().onResultOf((Rotation r) -> r.score(predictor)).compound(bySize);
  }

  private Supplier<Rotation> initialStateGenerator(
      final Iterable<Player> forced,
      final Iterable<Player> available,
      RotationDefinition definition) {
    return new Supplier<Rotation>() {
      @Override
      public Rotation get() {
        List<Player> ps = Lists.newArrayList(available);
        Iterables.removeAll(ps, ImmutableSet.copyOf(forced));

        Collections.shuffle(ps);

        List<Player> sps = Lists.newArrayList();
        List<Player> mrs = Lists.newArrayList();
        List<Player> rest = Lists.newArrayList();

        for (Player p : forced) {
          if (p.getSlots().contains(Slot.SP) && sps.size() < definition.getRotationSize()) {
            sps.add(p);
          } else if (mrs.size() < definition.getRelieversSize()) {
{'fraction_non_alphanumeric': 0.12683412086545637, 'fraction_numerical': 0.0003730415319572246, 'mean_word_length': 3.4022988505747125, 'pattern_counts': {'":': 0, '<': 52, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '833910', 'n_tokens_mistral': 2452, 'n_tokens_neox': 2364, 'n_words': 436}