<commit_before>package com.ljs.ifootballmanager.ai.selection;

import com.ljs.ifootballmanager.ai.Role;
import com.ljs.ifootballmanager.ai.Tactic;
import com.ljs.ifootballmanager.ai.formation.Formation;
import com.ljs.ifootballmanager.ai.formation.SelectionCriteria;
import com.ljs.ifootballmanager.ai.league.Esl;
import com.ljs.ifootballmanager.ai.league.EliteFootballLeague;
import com.ljs.ifootballmanager.ai.league.League;
import com.ljs.ifootballmanager.ai.league.LeagueHolder;
import com.ljs.ifootballmanager.ai.player.Player;
import com.ljs.ifootballmanager.ai.report.Report;

import java.io.PrintWriter;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;

import com.github.lstephen.ai.search.HillClimbing;
import com.github.lstephen.ai.search.RepeatedHillClimbing;
import com.github.lstephen.ai.search.Validator;
import com.github.lstephen.ai.search.action.Action;
import com.github.lstephen.ai.search.action.ActionGenerator;
import com.github.lstephen.ai.search.action.SequencedAction;

import org.apache.commons.lang3.tuple.Pair;

/**
 *
 * @author lstephen
 */
public final class ChangePlan implements Report {

  private static final Integer GAME_MINUTES = 90;

  private static final Double INJURY_CHANCE_PER_MINUTE = 0.003;

  private final Formation formation;

  private final ImmutableSet<Change> changes;

  private final Map<Integer, Double> scores;

  private final Map<Pair<Player, Integer>, Player> afterMinutes;

  private final Boolean isAllowChangePos;

  private ChangePlan(Formation formation, Iterable<? extends Change> cs, Map<Integer, Double> scores, Map<Pair<Player, Integer>, Player> afterMinutes) {
    this.formation = formation;
    this.changes = ImmutableSet.copyOf(cs);
    this.scores = Maps.newHashMap(scores);
    this.afterMinutes = Maps.newHashMap(afterMinutes);

    isAllowChangePos = !(LeagueHolder.get() instanceof EliteFootballLeague
      || LeagueHolder.get() instanceof Esl);
  }

  private ImmutableList<Change> changes() {
    return Change.Meta.byMinute().immutableSortedCopy(changes);
  }

  public <C extends Change> ImmutableList<C> changes(Class<C> clazz) {
    List<C> cs = Lists.newArrayList();

    for (Change c : changes()) {
      if (clazz.isInstance(c)) {
        cs.add(clazz.cast(c));
      }
    }

    return ImmutableList.copyOf(Change.Meta.byMinute().immutableSortedCopy(cs));
  }

  public ImmutableList<Change> changesMadeAt(Integer minute) {
    List<Change> cs = Lists.newArrayList();

    for (Change c : changes()) {
      if (c.getMinute() <= minute) {
        cs.add(c);
      }
    }

    return ImmutableList.copyOf(cs);
  }

  public <C extends Change> ImmutableList<C> changesMadeAt(Integer minute, Class<C> clazz) {
    List<C> cs = Lists.newArrayList();

    for (Change c : changesMadeAt(minute)) {
      if (clazz.isInstance(c)) {
        cs.add(clazz.cast(c));
      }

    }

    return ImmutableList.copyOf(cs);
  }

  public Boolean isChangeAt(Integer minute) {
    for (Change c : changes()) {
      if (c.getMinute().equals(minute)) {
        return true;
      }
    }
    return false;
  }

  public void print(PrintWriter w) {
    for (Change c : changes()) {
      c.print(w);
    }

    ScoreTactics.create(formation, this).print(w);
  }

  public void print(PrintWriter w, Function<Player, Integer> playerIdx) {
    for (Change c : changes()) {
      c.print(w, playerIdx);
    }

    ScoreTactics.create(formation, this).print(w);
  }

  public Tactic getBestScoringTactic() {
    return Ordering
      .natural()
      .onResultOf(new Function<Tactic, Double>() {
        public Double apply(Tactic t) {
          return scoring(t);
        }
      })
    .max(Arrays.asList(Tactic.values()));
  }

  public Tactic getBestDefensiveTactic() {
    return Ordering
      .natural()
      .onResultOf(new Function<Tactic, Double>() {
        public Double apply(Tactic t) {
          return defending(t);
        }
      })
    .max(Arrays.asList(Tactic.values()));
  }

  private Double scoring(Tactic t) {
    Double score = 0.0;

    for (Integer minute = 1; minute < GAME_MINUTES; minute++) {
      Formation f = getFormationAt(minute);
      score += weightedAtMinute(f.scoring(t) + f.score(t), minute);
    }

    return score;
  }

  private Double defending(Tactic t) {
    Double score = 0.0;
    for (Integer minute = 1; minute <= GAME_MINUTES; minute++) {
      Formation f = getFormationAt(minute);
      score += weightedAtMinute(f.defending(t) + f.score(t), minute);
    }
    return score;
  }

  private Double weightedAtMinute(Double score, Integer minute) {
    return score * weightingAtMinute(minute);
  }

  private Double weightingAtMinute(Integer minute) {
    return Math.pow((double) minute / GAME_MINUTES, 2);
  }

  public ImmutableSet<Player> getSubstitutes() {
    Set<Player> subs = Sets.newHashSet();
<commit_msg>Avoid some boxing<commit_after>package com.ljs.ifootballmanager.ai.selection;

import com.ljs.ifootballmanager.ai.Role;
import com.ljs.ifootballmanager.ai.Tactic;
import com.ljs.ifootballmanager.ai.formation.Formation;
import com.ljs.ifootballmanager.ai.formation.SelectionCriteria;
import com.ljs.ifootballmanager.ai.league.Esl;
import com.ljs.ifootballmanager.ai.league.EliteFootballLeague;
import com.ljs.ifootballmanager.ai.league.League;
import com.ljs.ifootballmanager.ai.league.LeagueHolder;
import com.ljs.ifootballmanager.ai.player.Player;
import com.ljs.ifootballmanager.ai.report.Report;

import java.io.PrintWriter;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;

import com.github.lstephen.ai.search.HillClimbing;
import com.github.lstephen.ai.search.RepeatedHillClimbing;
import com.github.lstephen.ai.search.Validator;
import com.github.lstephen.ai.search.action.Action;
import com.github.lstephen.ai.search.action.ActionGenerator;
import com.github.lstephen.ai.search.action.SequencedAction;

import org.apache.commons.lang3.tuple.Pair;

/**
 *
 * @author lstephen
 */
public final class ChangePlan implements Report {

  private static final Integer GAME_MINUTES = 90;

  private static final Double INJURY_CHANCE_PER_MINUTE = 0.003;

  private final Formation formation;

  private final ImmutableSet<Change> changes;

  private final Map<Integer, Double> scores;

  private final Map<Pair<Player, Integer>, Player> afterMinutes;

  private final Boolean isAllowChangePos;

  private ChangePlan(Formation formation, Iterable<? extends Change> cs, Map<Integer, Double> scores, Map<Pair<Player, Integer>, Player> afterMinutes) {
    this.formation = formation;
    this.changes = ImmutableSet.copyOf(cs);
    this.scores = Maps.newHashMap(scores);
    this.afterMinutes = Maps.newHashMap(afterMinutes);

    isAllowChangePos = !(LeagueHolder.get() instanceof EliteFootballLeague
      || LeagueHolder.get() instanceof Esl);
  }

  private ImmutableList<Change> changes() {
    return Change.Meta.byMinute().immutableSortedCopy(changes);
  }

  public <C extends Change> ImmutableList<C> changes(Class<C> clazz) {
    List<C> cs = Lists.newArrayList();

    for (Change c : changes()) {
      if (clazz.isInstance(c)) {
        cs.add(clazz.cast(c));
      }
    }

    return ImmutableList.copyOf(Change.Meta.byMinute().immutableSortedCopy(cs));
  }

  public ImmutableList<Change> changesMadeAt(Integer minute) {
    List<Change> cs = Lists.newArrayList();

    for (Change c : changes()) {
      if (c.getMinute() <= minute) {
        cs.add(c);
      }
    }

    return ImmutableList.copyOf(cs);
  }

  public <C extends Change> ImmutableList<C> changesMadeAt(Integer minute, Class<C> clazz) {
    List<C> cs = Lists.newArrayList();

    for (Change c : changesMadeAt(minute)) {
      if (clazz.isInstance(c)) {
        cs.add(clazz.cast(c));
      }

    }

    return ImmutableList.copyOf(cs);
  }

  public Boolean isChangeAt(Integer minute) {
    for (Change c : changes()) {
      if (c.getMinute().equals(minute)) {
        return true;
      }
    }
    return false;
  }

  public void print(PrintWriter w) {
    for (Change c : changes()) {
      c.print(w);
    }

    ScoreTactics.create(formation, this).print(w);
  }

  public void print(PrintWriter w, Function<Player, Integer> playerIdx) {
    for (Change c : changes()) {
      c.print(w, playerIdx);
    }

    ScoreTactics.create(formation, this).print(w);
  }

  public Tactic getBestScoringTactic() {
    return Ordering
      .natural()
      .onResultOf(new Function<Tactic, Double>() {
        public Double apply(Tactic t) {
          return scoring(t);
        }
      })
    .max(Arrays.asList(Tactic.values()));
  }

  public Tactic getBestDefensiveTactic() {
    return Ordering
      .natural()
      .onResultOf(new Function<Tactic, Double>() {
        public Double apply(Tactic t) {
          return defending(t);
        }
      })
    .max(Arrays.asList(Tactic.values()));
  }

  private double scoring(Tactic t) {
    double score = 0.0;

    for (Integer minute = 1; minute < GAME_MINUTES; minute++) {
      Formation f = getFormationAt(minute);
      score += weightedAtMinute(f.scoring(t) + f.score(t), minute);
    }

    return score;
  }

  private double defending(Tactic t) {
    double score = 0.0;
    for (Integer minute = 1; minute <= GAME_MINUTES; minute++) {
      Formation f = getFormationAt(minute);
      score += weightedAtMinute(f.defending(t) + f.score(t), minute);
    }
    return score;
  }

  private double weightedAtMinute(double score, Integer minute) {
    return score * weightingAtMinute(minute);
  }

  private double weightingAtMinute(Integer minute) {
    return Math.pow((double) minute / GAME_MINUTES, 2);
  }

  public ImmutableSet<Player> getSubstitutes() {
    Set<Player> subs = Sets.newHashSet();

{'fraction_non_alphanumeric': 0.1213924963924964, 'fraction_numerical': 0.0025252525252525255, 'mean_word_length': 3.7449721865639707, 'pattern_counts': {'":': 0, '<': 57, '<?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': '258584', 'n_tokens_mistral': 3685, 'n_tokens_neox': 3557, 'n_words': 718}