Skip to content

Commit 4c7b7dc

Browse files
committed
Add caching to the Swiss format runner.
1 parent be48c6f commit 4c7b7dc

File tree

1 file changed

+228
-9
lines changed

1 file changed

+228
-9
lines changed

src/main/java/net/alloyggp/tournament/impl/SwissFormat1Runner.java

Lines changed: 228 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@
99
import java.util.stream.Collectors;
1010

1111
import javax.annotation.Nullable;
12+
import javax.annotation.concurrent.Immutable;
1213
import javax.annotation.concurrent.NotThreadSafe;
1314

1415
import com.google.common.base.Optional;
1516
import com.google.common.base.Preconditions;
1617
import com.google.common.collect.HashMultiset;
1718
import com.google.common.collect.ImmutableList;
19+
import com.google.common.collect.ImmutableMap;
20+
import com.google.common.collect.ImmutableMultiset;
1821
import com.google.common.collect.ImmutableSet;
1922
import com.google.common.collect.Lists;
2023
import com.google.common.collect.Maps;
2124
import com.google.common.collect.Multiset;
25+
import com.google.common.collect.Multiset.Entry;
2226
import com.google.common.collect.Ordering;
2327
import com.google.common.collect.SetMultimap;
2428
import com.google.common.collect.Sets;
@@ -52,6 +56,7 @@ private static class SwissFormatSimulator {
5256
private final int stageNum;
5357
private final Seeding initialSeeding;
5458
private final ImmutableList<RoundSpec> rounds;
59+
private final ImmutableSet<MatchResult> resultsFromEarlierStages;
5560
private final ImmutableSet<MatchResult> resultsInStage;
5661
private final Set<MatchSetup> matchesToRun = Sets.newHashSet();
5762
//TODO: Double-check that all these stats are updated appropriately
@@ -66,28 +71,39 @@ private static class SwissFormatSimulator {
6671
private final List<Ranking> standingsHistory = Lists.newArrayList();
6772

6873
private SwissFormatSimulator(String tournamentInternalName, int stageNum, Seeding initialSeeding,
69-
ImmutableList<RoundSpec> rounds, ImmutableSet<MatchResult> resultsSoFar) {
74+
ImmutableList<RoundSpec> rounds, ImmutableSet<MatchResult> resultsFromEarlierStages,
75+
ImmutableSet<MatchResult> resultsInStage) {
7076
this.tournamentInternalName = tournamentInternalName;
7177
this.stageNum = stageNum;
7278
this.initialSeeding = initialSeeding;
7379
this.rounds = rounds;
74-
this.resultsInStage = resultsSoFar;
80+
this.resultsFromEarlierStages = resultsFromEarlierStages;
81+
this.resultsInStage = resultsInStage;
7582
}
7683

7784
public static SwissFormatSimulator createAndRun(String tournamentInternalName, int stageNum, Seeding initialSeeding,
78-
ImmutableList<RoundSpec> rounds, Set<MatchResult> resultsSoFar) {
79-
Set<MatchResult> resultsInStage = MatchResults.filterByStage(resultsSoFar, stageNum);
85+
ImmutableList<RoundSpec> rounds, Set<MatchResult> allResultsSoFar) {
86+
Set<MatchResult> resultsFromEarlierStages = MatchResults.getResultsPriorToStage(allResultsSoFar, stageNum);
87+
Set<MatchResult> resultsInStage = MatchResults.filterByStage(allResultsSoFar, stageNum);
8088
SwissFormatSimulator simulator = new SwissFormatSimulator(tournamentInternalName, stageNum, initialSeeding,
81-
rounds, ImmutableSet.copyOf(resultsInStage));
89+
rounds, ImmutableSet.copyOf(resultsFromEarlierStages), ImmutableSet.copyOf(resultsInStage));
8290
simulator.run();
8391
return simulator;
8492
}
8593

8694
private void run() {
8795
setInitialTotalsToZero();
88-
96+
int roundNum = 0;
8997
SetMultimap<Integer, MatchResult> matchesByRound = MatchResults.mapByRound(resultsInStage, stageNum);
90-
for (int roundNum = 0; roundNum < rounds.size(); roundNum++) {
98+
99+
@Nullable EndOfRoundState endOfRoundState = TournamentStateCache.getLatestCachedEndOfRoundState(tournamentInternalName, initialSeeding, resultsFromEarlierStages, stageNum, resultsInStage);
100+
if (endOfRoundState != null) {
101+
Swiss1EndOfRoundState state = (Swiss1EndOfRoundState) endOfRoundState;
102+
roundNum = state.roundNum + 1;
103+
loadCachedState(state);
104+
}
105+
106+
for (/* roundNum already set */; roundNum < rounds.size(); roundNum++) {
91107
RoundSpec round = rounds.get(roundNum);
92108
Set<MatchResult> roundResults = matchesByRound.get(roundNum);
93109
runRound(round, roundNum, roundResults);
@@ -96,13 +112,48 @@ private void run() {
96112
return;
97113
}
98114
//If we didn't run the round due to the number of players being too low,
99-
//skip the standings
115+
//skip the standings and caching
100116
if (!roundResults.isEmpty()) {
101117
standingsHistory.add(getStandings());
118+
Swiss1EndOfRoundState state = Swiss1EndOfRoundState.create(roundNum,
119+
mostRecentGame, totalPointsScored, pointsScoredByGame,
120+
pointsFromByes, totalMatchupsSoFar, matchupsSoFarByGame,
121+
nonFixedSumMatchupsSoFarByNumPlayers, standingsHistory);
122+
123+
TournamentStateCache.cacheEndOfRoundState(tournamentInternalName, initialSeeding, resultsFromEarlierStages, stageNum, resultsInStage, state);
102124
}
103125
}
104126
}
105127

128+
private void loadCachedState(Swiss1EndOfRoundState state) {
129+
totalPointsScored.putAll(state.totalPointsScored);
130+
pointsFromByes.putAll(state.pointsFromByes);
131+
132+
Set<Integer> possiblePlayerCounts = Sets.newHashSet();
133+
for (Game game : RoundSpec.getAllGames(rounds)) {
134+
Map<Player, Double> pointsScoredForGame = pointsScoredByGame.get(game);
135+
for (Player player : initialSeeding.getPlayersBestFirst()) {
136+
pointsScoredForGame.put(player, state.pointsScoredByGame.get(game).get(player));
137+
}
138+
for (Entry<ImmutableSet<Player>> entry : state.matchupsSoFarByGame.get(game).entrySet()) {
139+
matchupsSoFarByGame.get(game).add(Sets.newHashSet(entry.getElement()), entry.getCount());
140+
}
141+
possiblePlayerCounts.add(game.getNumRoles());
142+
}
143+
for (int playerCount : possiblePlayerCounts) {
144+
for (Entry<ImmutableSet<Player>> entry : state.nonFixedSumMatchupsSoFarByNumPlayers.get(playerCount).entrySet()) {
145+
nonFixedSumMatchupsSoFarByNumPlayers.get(playerCount).add(Sets.newHashSet(entry.getElement()), entry.getCount());
146+
}
147+
}
148+
149+
mostRecentGame = state.mostRecentGame;
150+
standingsHistory.addAll(state.standingsHistory);
151+
152+
for (Entry<ImmutableSet<Player>> entry : state.totalMatchupsSoFar.entrySet()) {
153+
totalMatchupsSoFar.add(Sets.newHashSet(entry.getElement()), entry.getCount());
154+
}
155+
}
156+
106157
private void runRound(RoundSpec round, int roundNum, Set<MatchResult> roundResults) {
107158
//...there should be only one match per round, I think?
108159
//Or at least they must involve the same game?
@@ -160,7 +211,6 @@ private void runRound(RoundSpec round, int roundNum, Set<MatchResult> roundResul
160211
}
161212
//Also...
162213
updateMatchupStats(game, playerGroups);
163-
// this.mostRecentGame = game;
164214
}
165215
}
166216

@@ -584,4 +634,173 @@ public void validateRounds(ImmutableList<RoundSpec> rounds) {
584634
getOnlyGame(round);
585635
}
586636
}
637+
638+
@Immutable
639+
private static class Swiss1EndOfRoundState implements EndOfRoundState {
640+
private final int roundNum;
641+
private final Game mostRecentGame;
642+
private final ImmutableMap<Player, Double> totalPointsScored;
643+
private final ImmutableMap<Game, ImmutableMap<Player, Double>> pointsScoredByGame;
644+
private final ImmutableMap<Player, Double> pointsFromByes;
645+
private final ImmutableMultiset<ImmutableSet<Player>> totalMatchupsSoFar;
646+
private final ImmutableMap<Game, ImmutableMultiset<ImmutableSet<Player>>> matchupsSoFarByGame;
647+
private final ImmutableMap<Integer, ImmutableMultiset<ImmutableSet<Player>>> nonFixedSumMatchupsSoFarByNumPlayers;
648+
private final ImmutableList<Ranking> standingsHistory;
649+
650+
private Swiss1EndOfRoundState(int roundNum, Game mostRecentGame, ImmutableMap<Player, Double> totalPointsScored,
651+
ImmutableMap<Game, ImmutableMap<Player, Double>> pointsScoredByGame,
652+
ImmutableMap<Player, Double> pointsFromByes, ImmutableMultiset<ImmutableSet<Player>> totalMatchupsSoFar,
653+
ImmutableMap<Game, ImmutableMultiset<ImmutableSet<Player>>> matchupsSoFarByGame,
654+
ImmutableMap<Integer, ImmutableMultiset<ImmutableSet<Player>>> nonFixedSumMatchupsSoFarByNumPlayers,
655+
ImmutableList<Ranking> standingsHistory) {
656+
this.roundNum = roundNum;
657+
this.mostRecentGame = mostRecentGame;
658+
this.totalPointsScored = totalPointsScored;
659+
this.pointsScoredByGame = pointsScoredByGame;
660+
this.pointsFromByes = pointsFromByes;
661+
this.totalMatchupsSoFar = totalMatchupsSoFar;
662+
this.matchupsSoFarByGame = matchupsSoFarByGame;
663+
this.nonFixedSumMatchupsSoFarByNumPlayers = nonFixedSumMatchupsSoFarByNumPlayers;
664+
this.standingsHistory = standingsHistory;
665+
}
666+
667+
public static Swiss1EndOfRoundState create(int roundNum,
668+
Game mostRecentGame,
669+
Map<Player, Double> totalPointsScored,
670+
Map<Game, Map<Player, Double>> pointsScoredByGame,
671+
Map<Player, Double> pointsFromByes,
672+
Multiset<Set<Player>> totalMatchupsSoFar,
673+
Map<Game, Multiset<Set<Player>>> matchupsSoFarByGame,
674+
Map<Integer, Multiset<Set<Player>>> nonFixedSumMatchupsSoFarByNumPlayers,
675+
List<Ranking> standingsHistory) {
676+
return new Swiss1EndOfRoundState(roundNum,
677+
mostRecentGame,
678+
ImmutableMap.copyOf(totalPointsScored),
679+
toImmutableMapValuedMap(pointsScoredByGame),
680+
ImmutableMap.copyOf(pointsFromByes),
681+
toImmutableSetEntriedMultiset(totalMatchupsSoFar),
682+
toImmutableMultisetOfSetsValuedMap(matchupsSoFarByGame),
683+
toImmutableMultisetOfSetsValuedMap(nonFixedSumMatchupsSoFarByNumPlayers),
684+
ImmutableList.copyOf(standingsHistory));
685+
}
686+
687+
private static <K,T> ImmutableMap<K, ImmutableMultiset<ImmutableSet<T>>> toImmutableMultisetOfSetsValuedMap(
688+
Map<K, Multiset<Set<T>>> map) {
689+
return ImmutableMap.copyOf(Maps.transformValues(map, Swiss1EndOfRoundState::toImmutableSetEntriedMultiset));
690+
}
691+
692+
private static <T> ImmutableMultiset<ImmutableSet<T>> toImmutableSetEntriedMultiset(
693+
Multiset<Set<T>> multiset) {
694+
ImmutableMultiset.Builder<ImmutableSet<T>> builder = ImmutableMultiset.builder();
695+
for (Entry<Set<T>> entry : multiset.entrySet()) {
696+
builder.addCopies(ImmutableSet.copyOf(entry.getElement()), entry.getCount());
697+
}
698+
return builder.build();
699+
}
700+
701+
private static <K1,K2,V2> ImmutableMap<K1, ImmutableMap<K2, V2>> toImmutableMapValuedMap(
702+
Map<K1, Map<K2, V2>> map) {
703+
return ImmutableMap.copyOf(Maps.transformValues(map, ImmutableMap::copyOf));
704+
}
705+
706+
@Override
707+
public int hashCode() {
708+
final int prime = 31;
709+
int result = 1;
710+
result = prime * result + ((matchupsSoFarByGame == null) ? 0 : matchupsSoFarByGame.hashCode());
711+
result = prime * result + ((mostRecentGame == null) ? 0 : mostRecentGame.hashCode());
712+
result = prime * result + ((nonFixedSumMatchupsSoFarByNumPlayers == null) ? 0
713+
: nonFixedSumMatchupsSoFarByNumPlayers.hashCode());
714+
result = prime * result + ((pointsFromByes == null) ? 0 : pointsFromByes.hashCode());
715+
result = prime * result + ((pointsScoredByGame == null) ? 0 : pointsScoredByGame.hashCode());
716+
result = prime * result + roundNum;
717+
result = prime * result + ((standingsHistory == null) ? 0 : standingsHistory.hashCode());
718+
result = prime * result + ((totalMatchupsSoFar == null) ? 0 : totalMatchupsSoFar.hashCode());
719+
result = prime * result + ((totalPointsScored == null) ? 0 : totalPointsScored.hashCode());
720+
return result;
721+
}
722+
723+
@Override
724+
public boolean equals(Object obj) {
725+
if (this == obj) {
726+
return true;
727+
}
728+
if (obj == null) {
729+
return false;
730+
}
731+
if (getClass() != obj.getClass()) {
732+
return false;
733+
}
734+
Swiss1EndOfRoundState other = (Swiss1EndOfRoundState) obj;
735+
if (matchupsSoFarByGame == null) {
736+
if (other.matchupsSoFarByGame != null) {
737+
return false;
738+
}
739+
} else if (!matchupsSoFarByGame.equals(other.matchupsSoFarByGame)) {
740+
return false;
741+
}
742+
if (mostRecentGame == null) {
743+
if (other.mostRecentGame != null) {
744+
return false;
745+
}
746+
} else if (!mostRecentGame.equals(other.mostRecentGame)) {
747+
return false;
748+
}
749+
if (nonFixedSumMatchupsSoFarByNumPlayers == null) {
750+
if (other.nonFixedSumMatchupsSoFarByNumPlayers != null) {
751+
return false;
752+
}
753+
} else if (!nonFixedSumMatchupsSoFarByNumPlayers.equals(other.nonFixedSumMatchupsSoFarByNumPlayers)) {
754+
return false;
755+
}
756+
if (pointsFromByes == null) {
757+
if (other.pointsFromByes != null) {
758+
return false;
759+
}
760+
} else if (!pointsFromByes.equals(other.pointsFromByes)) {
761+
return false;
762+
}
763+
if (pointsScoredByGame == null) {
764+
if (other.pointsScoredByGame != null) {
765+
return false;
766+
}
767+
} else if (!pointsScoredByGame.equals(other.pointsScoredByGame)) {
768+
return false;
769+
}
770+
if (roundNum != other.roundNum) {
771+
return false;
772+
}
773+
if (standingsHistory == null) {
774+
if (other.standingsHistory != null) {
775+
return false;
776+
}
777+
} else if (!standingsHistory.equals(other.standingsHistory)) {
778+
return false;
779+
}
780+
if (totalMatchupsSoFar == null) {
781+
if (other.totalMatchupsSoFar != null) {
782+
return false;
783+
}
784+
} else if (!totalMatchupsSoFar.equals(other.totalMatchupsSoFar)) {
785+
return false;
786+
}
787+
if (totalPointsScored == null) {
788+
if (other.totalPointsScored != null) {
789+
return false;
790+
}
791+
} else if (!totalPointsScored.equals(other.totalPointsScored)) {
792+
return false;
793+
}
794+
return true;
795+
}
796+
797+
@Override
798+
public String toString() {
799+
return "Swiss1EndOfRoundState [roundNum=" + roundNum + ", mostRecentGame=" + mostRecentGame
800+
+ ", totalPointsScored=" + totalPointsScored + ", pointsScoredByGame=" + pointsScoredByGame
801+
+ ", pointsFromByes=" + pointsFromByes + ", totalMatchupsSoFar=" + totalMatchupsSoFar
802+
+ ", matchupsSoFarByGame=" + matchupsSoFarByGame + ", nonFixedSumMatchupsSoFarByNumPlayers="
803+
+ nonFixedSumMatchupsSoFarByNumPlayers + ", standingsHistory=" + standingsHistory + "]";
804+
}
805+
}
587806
}

0 commit comments

Comments
 (0)