From 4c7b7dc3929f623129057f2e78f07f95bffe7d67 Mon Sep 17 00:00:00 2001 From: alandau Date: Wed, 28 Oct 2015 21:23:47 -0700 Subject: [PATCH] Add caching to the Swiss format runner. --- .../tournament/impl/SwissFormat1Runner.java | 237 +++++++++++++++++- 1 file changed, 228 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/alloyggp/tournament/impl/SwissFormat1Runner.java b/src/main/java/net/alloyggp/tournament/impl/SwissFormat1Runner.java index 28a588f..5aab714 100644 --- a/src/main/java/net/alloyggp/tournament/impl/SwissFormat1Runner.java +++ b/src/main/java/net/alloyggp/tournament/impl/SwissFormat1Runner.java @@ -9,16 +9,20 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.NotThreadSafe; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multiset; +import com.google.common.collect.Multiset.Entry; import com.google.common.collect.Ordering; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; @@ -52,6 +56,7 @@ private static class SwissFormatSimulator { private final int stageNum; private final Seeding initialSeeding; private final ImmutableList rounds; + private final ImmutableSet resultsFromEarlierStages; private final ImmutableSet resultsInStage; private final Set matchesToRun = Sets.newHashSet(); //TODO: Double-check that all these stats are updated appropriately @@ -66,28 +71,39 @@ private static class SwissFormatSimulator { private final List standingsHistory = Lists.newArrayList(); private SwissFormatSimulator(String tournamentInternalName, int stageNum, Seeding initialSeeding, - ImmutableList rounds, ImmutableSet resultsSoFar) { + ImmutableList rounds, ImmutableSet resultsFromEarlierStages, + ImmutableSet resultsInStage) { this.tournamentInternalName = tournamentInternalName; this.stageNum = stageNum; this.initialSeeding = initialSeeding; this.rounds = rounds; - this.resultsInStage = resultsSoFar; + this.resultsFromEarlierStages = resultsFromEarlierStages; + this.resultsInStage = resultsInStage; } public static SwissFormatSimulator createAndRun(String tournamentInternalName, int stageNum, Seeding initialSeeding, - ImmutableList rounds, Set resultsSoFar) { - Set resultsInStage = MatchResults.filterByStage(resultsSoFar, stageNum); + ImmutableList rounds, Set allResultsSoFar) { + Set resultsFromEarlierStages = MatchResults.getResultsPriorToStage(allResultsSoFar, stageNum); + Set resultsInStage = MatchResults.filterByStage(allResultsSoFar, stageNum); SwissFormatSimulator simulator = new SwissFormatSimulator(tournamentInternalName, stageNum, initialSeeding, - rounds, ImmutableSet.copyOf(resultsInStage)); + rounds, ImmutableSet.copyOf(resultsFromEarlierStages), ImmutableSet.copyOf(resultsInStage)); simulator.run(); return simulator; } private void run() { setInitialTotalsToZero(); - + int roundNum = 0; SetMultimap matchesByRound = MatchResults.mapByRound(resultsInStage, stageNum); - for (int roundNum = 0; roundNum < rounds.size(); roundNum++) { + + @Nullable EndOfRoundState endOfRoundState = TournamentStateCache.getLatestCachedEndOfRoundState(tournamentInternalName, initialSeeding, resultsFromEarlierStages, stageNum, resultsInStage); + if (endOfRoundState != null) { + Swiss1EndOfRoundState state = (Swiss1EndOfRoundState) endOfRoundState; + roundNum = state.roundNum + 1; + loadCachedState(state); + } + + for (/* roundNum already set */; roundNum < rounds.size(); roundNum++) { RoundSpec round = rounds.get(roundNum); Set roundResults = matchesByRound.get(roundNum); runRound(round, roundNum, roundResults); @@ -96,13 +112,48 @@ private void run() { return; } //If we didn't run the round due to the number of players being too low, - //skip the standings + //skip the standings and caching if (!roundResults.isEmpty()) { standingsHistory.add(getStandings()); + Swiss1EndOfRoundState state = Swiss1EndOfRoundState.create(roundNum, + mostRecentGame, totalPointsScored, pointsScoredByGame, + pointsFromByes, totalMatchupsSoFar, matchupsSoFarByGame, + nonFixedSumMatchupsSoFarByNumPlayers, standingsHistory); + + TournamentStateCache.cacheEndOfRoundState(tournamentInternalName, initialSeeding, resultsFromEarlierStages, stageNum, resultsInStage, state); } } } + private void loadCachedState(Swiss1EndOfRoundState state) { + totalPointsScored.putAll(state.totalPointsScored); + pointsFromByes.putAll(state.pointsFromByes); + + Set possiblePlayerCounts = Sets.newHashSet(); + for (Game game : RoundSpec.getAllGames(rounds)) { + Map pointsScoredForGame = pointsScoredByGame.get(game); + for (Player player : initialSeeding.getPlayersBestFirst()) { + pointsScoredForGame.put(player, state.pointsScoredByGame.get(game).get(player)); + } + for (Entry> entry : state.matchupsSoFarByGame.get(game).entrySet()) { + matchupsSoFarByGame.get(game).add(Sets.newHashSet(entry.getElement()), entry.getCount()); + } + possiblePlayerCounts.add(game.getNumRoles()); + } + for (int playerCount : possiblePlayerCounts) { + for (Entry> entry : state.nonFixedSumMatchupsSoFarByNumPlayers.get(playerCount).entrySet()) { + nonFixedSumMatchupsSoFarByNumPlayers.get(playerCount).add(Sets.newHashSet(entry.getElement()), entry.getCount()); + } + } + + mostRecentGame = state.mostRecentGame; + standingsHistory.addAll(state.standingsHistory); + + for (Entry> entry : state.totalMatchupsSoFar.entrySet()) { + totalMatchupsSoFar.add(Sets.newHashSet(entry.getElement()), entry.getCount()); + } + } + private void runRound(RoundSpec round, int roundNum, Set roundResults) { //...there should be only one match per round, I think? //Or at least they must involve the same game? @@ -160,7 +211,6 @@ private void runRound(RoundSpec round, int roundNum, Set roundResul } //Also... updateMatchupStats(game, playerGroups); -// this.mostRecentGame = game; } } @@ -584,4 +634,173 @@ public void validateRounds(ImmutableList rounds) { getOnlyGame(round); } } + + @Immutable + private static class Swiss1EndOfRoundState implements EndOfRoundState { + private final int roundNum; + private final Game mostRecentGame; + private final ImmutableMap totalPointsScored; + private final ImmutableMap> pointsScoredByGame; + private final ImmutableMap pointsFromByes; + private final ImmutableMultiset> totalMatchupsSoFar; + private final ImmutableMap>> matchupsSoFarByGame; + private final ImmutableMap>> nonFixedSumMatchupsSoFarByNumPlayers; + private final ImmutableList standingsHistory; + + private Swiss1EndOfRoundState(int roundNum, Game mostRecentGame, ImmutableMap totalPointsScored, + ImmutableMap> pointsScoredByGame, + ImmutableMap pointsFromByes, ImmutableMultiset> totalMatchupsSoFar, + ImmutableMap>> matchupsSoFarByGame, + ImmutableMap>> nonFixedSumMatchupsSoFarByNumPlayers, + ImmutableList standingsHistory) { + this.roundNum = roundNum; + this.mostRecentGame = mostRecentGame; + this.totalPointsScored = totalPointsScored; + this.pointsScoredByGame = pointsScoredByGame; + this.pointsFromByes = pointsFromByes; + this.totalMatchupsSoFar = totalMatchupsSoFar; + this.matchupsSoFarByGame = matchupsSoFarByGame; + this.nonFixedSumMatchupsSoFarByNumPlayers = nonFixedSumMatchupsSoFarByNumPlayers; + this.standingsHistory = standingsHistory; + } + + public static Swiss1EndOfRoundState create(int roundNum, + Game mostRecentGame, + Map totalPointsScored, + Map> pointsScoredByGame, + Map pointsFromByes, + Multiset> totalMatchupsSoFar, + Map>> matchupsSoFarByGame, + Map>> nonFixedSumMatchupsSoFarByNumPlayers, + List standingsHistory) { + return new Swiss1EndOfRoundState(roundNum, + mostRecentGame, + ImmutableMap.copyOf(totalPointsScored), + toImmutableMapValuedMap(pointsScoredByGame), + ImmutableMap.copyOf(pointsFromByes), + toImmutableSetEntriedMultiset(totalMatchupsSoFar), + toImmutableMultisetOfSetsValuedMap(matchupsSoFarByGame), + toImmutableMultisetOfSetsValuedMap(nonFixedSumMatchupsSoFarByNumPlayers), + ImmutableList.copyOf(standingsHistory)); + } + + private static ImmutableMap>> toImmutableMultisetOfSetsValuedMap( + Map>> map) { + return ImmutableMap.copyOf(Maps.transformValues(map, Swiss1EndOfRoundState::toImmutableSetEntriedMultiset)); + } + + private static ImmutableMultiset> toImmutableSetEntriedMultiset( + Multiset> multiset) { + ImmutableMultiset.Builder> builder = ImmutableMultiset.builder(); + for (Entry> entry : multiset.entrySet()) { + builder.addCopies(ImmutableSet.copyOf(entry.getElement()), entry.getCount()); + } + return builder.build(); + } + + private static ImmutableMap> toImmutableMapValuedMap( + Map> map) { + return ImmutableMap.copyOf(Maps.transformValues(map, ImmutableMap::copyOf)); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((matchupsSoFarByGame == null) ? 0 : matchupsSoFarByGame.hashCode()); + result = prime * result + ((mostRecentGame == null) ? 0 : mostRecentGame.hashCode()); + result = prime * result + ((nonFixedSumMatchupsSoFarByNumPlayers == null) ? 0 + : nonFixedSumMatchupsSoFarByNumPlayers.hashCode()); + result = prime * result + ((pointsFromByes == null) ? 0 : pointsFromByes.hashCode()); + result = prime * result + ((pointsScoredByGame == null) ? 0 : pointsScoredByGame.hashCode()); + result = prime * result + roundNum; + result = prime * result + ((standingsHistory == null) ? 0 : standingsHistory.hashCode()); + result = prime * result + ((totalMatchupsSoFar == null) ? 0 : totalMatchupsSoFar.hashCode()); + result = prime * result + ((totalPointsScored == null) ? 0 : totalPointsScored.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; + } + Swiss1EndOfRoundState other = (Swiss1EndOfRoundState) obj; + if (matchupsSoFarByGame == null) { + if (other.matchupsSoFarByGame != null) { + return false; + } + } else if (!matchupsSoFarByGame.equals(other.matchupsSoFarByGame)) { + return false; + } + if (mostRecentGame == null) { + if (other.mostRecentGame != null) { + return false; + } + } else if (!mostRecentGame.equals(other.mostRecentGame)) { + return false; + } + if (nonFixedSumMatchupsSoFarByNumPlayers == null) { + if (other.nonFixedSumMatchupsSoFarByNumPlayers != null) { + return false; + } + } else if (!nonFixedSumMatchupsSoFarByNumPlayers.equals(other.nonFixedSumMatchupsSoFarByNumPlayers)) { + return false; + } + if (pointsFromByes == null) { + if (other.pointsFromByes != null) { + return false; + } + } else if (!pointsFromByes.equals(other.pointsFromByes)) { + return false; + } + if (pointsScoredByGame == null) { + if (other.pointsScoredByGame != null) { + return false; + } + } else if (!pointsScoredByGame.equals(other.pointsScoredByGame)) { + return false; + } + if (roundNum != other.roundNum) { + return false; + } + if (standingsHistory == null) { + if (other.standingsHistory != null) { + return false; + } + } else if (!standingsHistory.equals(other.standingsHistory)) { + return false; + } + if (totalMatchupsSoFar == null) { + if (other.totalMatchupsSoFar != null) { + return false; + } + } else if (!totalMatchupsSoFar.equals(other.totalMatchupsSoFar)) { + return false; + } + if (totalPointsScored == null) { + if (other.totalPointsScored != null) { + return false; + } + } else if (!totalPointsScored.equals(other.totalPointsScored)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "Swiss1EndOfRoundState [roundNum=" + roundNum + ", mostRecentGame=" + mostRecentGame + + ", totalPointsScored=" + totalPointsScored + ", pointsScoredByGame=" + pointsScoredByGame + + ", pointsFromByes=" + pointsFromByes + ", totalMatchupsSoFar=" + totalMatchupsSoFar + + ", matchupsSoFarByGame=" + matchupsSoFarByGame + ", nonFixedSumMatchupsSoFarByNumPlayers=" + + nonFixedSumMatchupsSoFarByNumPlayers + ", standingsHistory=" + standingsHistory + "]"; + } + } }