All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.alloyggp.tournament.spec.TournamentSpec Maven / Gradle / Ivy
Go to download
A library for GGP tournament specification and scheduling.
package net.alloyggp.tournament.spec;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.alloyggp.tournament.api.Game;
import net.alloyggp.tournament.api.MatchResult;
import net.alloyggp.tournament.api.NextMatchesResult;
import net.alloyggp.tournament.api.Player;
import net.alloyggp.tournament.api.PlayerScore;
import net.alloyggp.tournament.api.Ranking;
import net.alloyggp.tournament.api.Score;
import net.alloyggp.tournament.api.Seeding;
import net.alloyggp.tournament.api.Tournament;
import net.alloyggp.tournament.api.TournamentSpecParser;
import net.alloyggp.tournament.impl.MatchResults;
import net.alloyggp.tournament.impl.StandardNextMatchesResult;
import net.alloyggp.tournament.impl.StandardRanking;
import net.alloyggp.tournament.impl.TimeUtils;
import net.alloyggp.tournament.impl.YamlUtils;
@Immutable
public class TournamentSpec implements Tournament {
private final String tournamentInternalName;
private final String tournamentDisplayName;
private final ImmutableList stages;
private TournamentSpec(String tournamentInternalName, String tournamentDisplayName,
ImmutableList stages) {
Preconditions.checkNotNull(tournamentInternalName);
Preconditions.checkNotNull(tournamentDisplayName);
Preconditions.checkArgument(!stages.isEmpty());
Preconditions.checkArgument(tournamentInternalName.matches("[a-zA-Z0-9_]+"),
"Tournament internal name should consist of alphanumerics and underscores, but was %s", tournamentInternalName);
this.tournamentInternalName = tournamentInternalName;
this.tournamentDisplayName = tournamentDisplayName;
this.stages = stages;
}
private static final ImmutableSet ALLOWED_KEYS = ImmutableSet.of(
"games",
"nameInternal",
"nameDisplay",
"stages"
);
/**
* Parses an already-loaded YAML object containing a tournament specification.
*
* For general use, see {@link TournamentSpecParser} instead.
*/
@SuppressWarnings("unchecked")
public static TournamentSpec parseYamlRootObject(Object yamlRoot) {
Map rootMap = (Map) yamlRoot;
YamlUtils.validateKeys(rootMap, "root", ALLOWED_KEYS);
Map games = parseGames(rootMap.get("games"));
String tournamentInternalName = (String) rootMap.get("nameInternal");
String tournamentDisplayName = (String) rootMap.get("nameDisplay");
List stages = Lists.newArrayList();
int stageNum = 0;
for (Object yamlStage : (List) rootMap.get("stages")) {
stages.add(StageSpec.parseYaml(yamlStage, stageNum, games));
stageNum++;
}
return new TournamentSpec(tournamentInternalName, tournamentDisplayName,
ImmutableList.copyOf(stages));
}
@SuppressWarnings("unchecked")
private static Map parseGames(Object gamesYaml) {
Preconditions.checkNotNull(gamesYaml, "The YAML file must have a 'games' section.");
Map results = Maps.newHashMap();
for (Object gameYaml : (List) gamesYaml) {
Map gameMap = (Map) gameYaml;
String name = (String) gameMap.get("name");
String repository = (String) gameMap.get("repository");
int numRoles = (int) gameMap.get("numRoles");
boolean fixedSum = (boolean) gameMap.get("fixedSum");
Game game = Game.create(repository, name, numRoles, fixedSum);
if (results.containsKey(name)) {
throw new IllegalArgumentException("Can't have two games with the same name defined");
}
results.put(name, game);
}
return results;
}
/* (non-Javadoc)
* @see net.alloyggp.swiss.api.Tournament#getTournamentInternalName()
*/
@Override
public String getTournamentInternalName() {
return tournamentInternalName;
}
/* (non-Javadoc)
* @see net.alloyggp.swiss.api.Tournament#getTournamentDisplayName()
*/
@Override
public String getTournamentDisplayName() {
return tournamentDisplayName;
}
public ImmutableList getStages() {
return stages;
}
/* (non-Javadoc)
* @see net.alloyggp.swiss.api.Tournament#getMatchesToRun(net.alloyggp.swiss.api.Seeding, com.google.common.collect.ImmutableList)
*/
@Override
public NextMatchesResult getMatchesToRun(Seeding initialSeeding, Set resultsSoFar) {
Seeding seeding = initialSeeding;
for (int stageNum = 0; stageNum < stages.size(); stageNum++) {
StageSpec stage = stages.get(stageNum);
Set resultsInStage = MatchResults.filterByStage(resultsSoFar, stageNum);
NextMatchesResult matchesForStage = stage.getMatchesToRun(tournamentInternalName,
initialSeeding, resultsInStage);
if (!matchesForStage.getMatchesToRun().isEmpty()) {
return matchesForStage;
}
Ranking standings = stage.getCurrentStandings(tournamentInternalName,
seeding, resultsInStage);
seeding = stage.getSeedingsFromFinalStandings(standings);
}
//No stages had matches left; the tournament is over
return StandardNextMatchesResult.createEmpty();
}
/* (non-Javadoc)
* @see net.alloyggp.swiss.api.Tournament#getCurrentStandings(net.alloyggp.swiss.api.Seeding, com.google.common.collect.ImmutableList)
*/
@Override
public Ranking getCurrentStandings(Seeding initialSeeding,
Set resultsSoFar) {
Seeding seeding = initialSeeding;
Ranking standings = null;
for (int stageNum = 0; stageNum < stages.size(); stageNum++) {
StageSpec stage = stages.get(stageNum);
NextMatchesResult matchesForStage = stage.getMatchesToRun(tournamentInternalName,
initialSeeding, resultsSoFar);
standings = mixInStandings(standings,
stage.getCurrentStandings(tournamentInternalName, seeding, resultsSoFar));
if (!matchesForStage.getMatchesToRun().isEmpty()) {
return standings;
}
seeding = stage.getSeedingsFromFinalStandings(standings);
}
//No stages had matches left; the tournament is over; use the last set of standings
Preconditions.checkNotNull(standings);
return standings;
}
private Ranking mixInStandings(Ranking oldStandings,
Ranking newStandings) {
if (oldStandings == null) {
return newStandings;
}
//preserve old standings for players that didn't make the cut
Set allPlayerScores = Sets.newHashSet();
Set playersInNewerStandings = Sets.newHashSet();
for (PlayerScore score : newStandings.getScores()) {
allPlayerScores.add(PlayerScore.create(score.getPlayer(),
CutoffScore.madeCutoff(score),
score.getSeedFromRoundStart()));
playersInNewerStandings.add(score.getPlayer());
}
for (PlayerScore score : oldStandings.getScores()) {
if (!playersInNewerStandings.contains(score.getPlayer())) {
allPlayerScores.add(PlayerScore.create(score.getPlayer(),
CutoffScore.failedCutoff(score),
score.getSeedFromRoundStart()));
}
}
return StandardRanking.create(allPlayerScores);
}
private static class CutoffScore implements Score {
private final boolean madeCutoff;
private final Score score;
private CutoffScore(boolean madeCutoff, Score score) {
this.madeCutoff = madeCutoff;
this.score = score;
}
public static CutoffScore failedCutoff(PlayerScore score) {
return new CutoffScore(false, score.getScore());
}
public static CutoffScore madeCutoff(PlayerScore score) {
return new CutoffScore(true, score.getScore());
}
@Override
public int compareTo(Score other) {
if (!(other instanceof CutoffScore)) {
throw new IllegalArgumentException();
}
CutoffScore otherCutoff = (CutoffScore) other;
if (!madeCutoff && otherCutoff.madeCutoff) {
return -1;
} else if (madeCutoff && !otherCutoff.madeCutoff) {
return 1;
} else {
return score.compareTo(otherCutoff.score);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (madeCutoff ? 1231 : 1237);
result = prime * result + ((score == null) ? 0 : score.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
CutoffScore other = (CutoffScore) obj;
if (madeCutoff != other.madeCutoff) {
return false;
}
if (score == null) {
if (other.score != null) {
return false;
}
} else if (!score.equals(other.score)) {
return false;
}
return true;
}
@Override
public String toString() {
if (!madeCutoff) {
return "eliminated";
} else {
return score.toString();
}
}
}
@Override
public List getStandingsHistory(Seeding initialSeeding, Set resultsSoFar) {
List result = Lists.newArrayList();
result.add(StandardRanking.createForSeeding(initialSeeding));
Seeding seeding = initialSeeding;
for (int stageNum = 0; stageNum < stages.size(); stageNum++) {
StageSpec stage = stages.get(stageNum);
NextMatchesResult matchesForStage = stage.getMatchesToRun(tournamentInternalName,
initialSeeding, resultsSoFar);
result.addAll(stage.getStandingsHistory(tournamentInternalName, initialSeeding, resultsSoFar));
if (!matchesForStage.getMatchesToRun().isEmpty()) {
return result;
}
Ranking standings = stage.getCurrentStandings(tournamentInternalName,
seeding, resultsSoFar);
seeding = stage.getSeedingsFromFinalStandings(standings);
}
return result;
}
@Override
public Optional getInitialStartTime() {
return stages.get(0).getRounds().get(0).getStartTime();
}
@Override
public long getSecondsToWaitUntilInitialStartTime() {
return TimeUtils.getSecondsToWaitUntilStartTime(getInitialStartTime());
}
}