package tc.oc.pgm.goals;
import com.google.common.collect.ImmutableList;
import tc.oc.pgm.match.Competitor;
import tc.oc.pgm.match.Match;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* A measurement of progress towards completing goals in a match
*/
public class GoalProgress implements Comparable<GoalProgress> {
public final int completed; // Number of completed goals
public final int touched; // Number of completed goals
public final ImmutableList<Double> progress; // Progress of incremental goals, from highest to lowest
public final ImmutableList<Integer> completionProximity; // Distance squared to untouched goals, from lowest to highest
public final ImmutableList<Integer> touchProximity; // Distance squared to touched goals, from lowest to highest
private static <T extends Collection<?>> T shorter(T a, T b) {
return a.size() < b.size() ? a : b;
}
private static int compareProgresses(List<Double> a, List<Double> b) {
int count = Math.max(a.size(), b.size());
double aProgress = 0, bProgress = 0;
for(int i = 0; i < count; i++) {
aProgress = i < a.size() ? a.get(i) : 0;
bProgress = i < b.size() ? b.get(i) : 0;
if(aProgress != bProgress) break; // Find first differing progress
}
return Double.compare(bProgress, aProgress);
}
/**
* Only compare as many proximity scores as both teams have available.
* If one team has more proximity scores available than the other,
* ignore the extras.
*/
private static int compareProximities(List<Integer> a, List<Integer> b) {
int count = Math.min(a.size(), b.size());
int aProximity = Integer.MAX_VALUE, bProximity = Integer.MAX_VALUE;
for(int i = 0; i < count; i++) {
aProximity = a.get(i);
bProximity = b.get(i);
if(aProximity != bProximity) break;
}
return Integer.compare(aProximity, bProximity);
}
private GoalProgress(int completed, int touched, Collection<Double> progress, Collection<Integer> completionProximity, Collection<Integer> touchProximity) {
this.completed = completed;
this.touched = touched;
this.progress = ImmutableList.copyOf(progress);
this.completionProximity = ImmutableList.copyOf(completionProximity);
this.touchProximity = ImmutableList.copyOf(touchProximity);
}
GoalProgress(Competitor competitor) {
Match match = competitor.getMatch();
int completed = 0;
int touched = 0;
List<Double> progress = new ArrayList<>();
List<Integer> completionProximity = new ArrayList<>();
List<Integer> touchProximity = new ArrayList<>();
final GoalMatchModule gmm = match.needMatchModule(GoalMatchModule.class);
for(Goal<?> goal : gmm.getGoals(competitor)) {
if(goal.isRequired()) {
if(goal.isCompleted(competitor)) {
completed++;
} else {
if(goal instanceof ProximityGoal) {
ProximityGoal proximity = (ProximityGoal) goal;
TouchableGoal touchable = goal instanceof TouchableGoal ? (TouchableGoal) goal : null;
if(touchable != null && touchable.hasTouched(competitor)) {
touched++;
if(proximity.isProximityRelevant(competitor)) {
completionProximity.add(proximity.getProximity(competitor));
}
} else {
if(proximity.isProximityRelevant(competitor)) {
touchProximity.add(proximity.getProximity(competitor));
}
}
if(goal instanceof IncrementalGoal) {
IncrementalGoal incrementalGoal = (IncrementalGoal) goal;
progress.add(incrementalGoal.getCompletion());
} else if(touchable != null && touchable.hasTouched(competitor)) {
// A touched, non-incremental goal is worth 50% completion
progress.add(0.5);
}
}
}
}
}
Collections.sort(progress, Collections.reverseOrder());
Collections.sort(completionProximity);
Collections.sort(touchProximity);
this.completed = completed;
this.touched = touched;
this.progress = ImmutableList.copyOf(progress);
this.completionProximity = ImmutableList.copyOf(completionProximity);
this.touchProximity = ImmutableList.copyOf(touchProximity);
}
@Override
public int compareTo(@Nonnull GoalProgress that) {
// This team has more completed goals, so they take the lead
if(this.completed > that.completed) return -1;
if(this.completed < that.completed) return 1;
// Equal number of completed goals, compare touches
if(this.touched > that.touched) return -1;
if(this.touched < that.touched) return 1;
// Equal number of touches, compare the progress of incremental goals from highest to lowest
int compareProgress = compareProgresses(this.progress, that.progress);
if(compareProgress != 0) return compareProgress;
// All progresses are equal, compare proximity to a completion
int compareCompletionProximity = compareProximities(this.completionProximity, that.completionProximity);
if(compareCompletionProximity != 0) return compareCompletionProximity;
// This team got equally close to another completion, compare proximity to touch
int compareTouchProximity = compareProximities(this.touchProximity, that.touchProximity);
if(compareTouchProximity != 0) return compareTouchProximity;
// Both teams are equal in every measurable respect
return 0;
}
@Override
public boolean equals(Object obj) {
return obj instanceof GoalProgress && this.compareTo((GoalProgress) obj) == 0;
}
/**
* Given two tied sets of accomplishments, return the common subset of
* accomplishments that can be used to compare the two sets.
*/
public static GoalProgress commonSubset(GoalProgress a, GoalProgress b) {
return new GoalProgress(a.touched,
a.completed,
a.progress,
shorter(a.completionProximity, b.completionProximity),
shorter(a.touchProximity, b.touchProximity));
}
}