package geniusweb.protocol.tournament.allpermutations; import java.math.BigInteger; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.function.BiFunction; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import geniusweb.protocol.session.SessionSettings; import geniusweb.protocol.session.TeamInfo; import geniusweb.protocol.session.saop.SAOPSettings; import geniusweb.protocol.session.shaop.SHAOPSettings; import geniusweb.protocol.tournament.ProfileList; import geniusweb.protocol.tournament.Team; import geniusweb.protocol.tournament.TournamentProtocol; import geniusweb.protocol.tournament.TournamentSettings; import geniusweb.references.PartyWithParameters; import geniusweb.references.PartyWithProfile; import geniusweb.references.ProfileRef; import tudelft.utilities.immutablelist.FixedList; import tudelft.utilities.immutablelist.ImmutableList; import tudelft.utilities.immutablelist.MapList; import tudelft.utilities.immutablelist.MapThreadList; import tudelft.utilities.immutablelist.Permutations; import tudelft.utilities.immutablelist.PermutationsOrderedWithoutReturn; import tudelft.utilities.immutablelist.PermutationsWithReturn; import tudelft.utilities.immutablelist.PermutationsWithoutReturn; import tudelft.utilities.immutablelist.Repeat; import tudelft.utilities.immutablelist.Tuple; import tudelft.utilities.immutablelist.Tuples; import tudelft.utilities.logging.Reporter; /** * Takes a number of parties and profiles and makes all permutations of them * with the given number of parties and profiles. All profiles must be in the * same domain. *

* All sessions are based on the given {@link SessionSettings} (can be * {@link SAOPSettings} or {@link SHAOPSettings}). The parties for each * tournament session are added to this basic sessionsettings. sessionsettings * contains basically all the normal run-sessionsettings. This is used as a * "template" for all sessions of the tournament. You can put any use any * session setting here, and each session will be run according to the protocol * you select here. * * The participants list usually is empty. The AllPermutationsProtocol adds the * the required parties to this list. So if you provide a non-empty list here, * then these parties would be present in every session in the tournament. *

* If the tournament is used with a SHAOP sessionsettings, the first member of * each team is assumed to be the SHAOP party, the second the COB party. To * match this, the first element of the ProfileList will have to be a partial * profile to fit the needs of the SHAOP party. The second profile will have to * be a normal profile, to fit the needs of the COB party. Note that you can * generate a partial profile using the partial=N option of the * profiles server. */ public class AllPermutationsSettings implements TournamentSettings { protected final List teams; protected final List profileslists; protected final boolean reuseTeams; protected final int teamsPerSession; protected final SessionSettings sessionsettings; protected final int numberTournaments; /** * * @param teams a list of {@link Team}s. Must contain at least * {@link #teamsPerSession} elements. The * {@link #teamsPerSession} must match the protocol: * (SAOP:1, SHAOP:2) * @param reuseTeams if true, we use PermutationsWithReturn, if false * we use PermutationsWithoutReturn to create the * teams. * @param plists list of available {@link ProfileList}s to be * permutated over the teams. Each * {@link ProfileList} must contain * {@link #teamsPerSession} elements. * @param teamsPerSession number of parties per session, must be at least 2. * @param sesettings The generic {@link SessionSettings}. * {@link SessionSettings#with(TeamInfo)} will be * used to add the required {@link TeamInfo}s * @param nTournaments the number of times the tournament should be run. */ @JsonCreator public AllPermutationsSettings(@JsonProperty("teams") List teams, @JsonProperty("profileslists") List plists, @JsonProperty("reuseTeams") boolean reuseTeams, @JsonProperty("teamsPerSession") int teamsPerSession, @JsonProperty("sessionsettings") SessionSettings sesettings, @JsonProperty("numberTournaments") int nTournaments) { if (teamsPerSession < 2) throw new IllegalArgumentException("teamsPerSession must be >=2"); if (teams == null || teams.size() < teamsPerSession) throw new IllegalArgumentException("teams must contain at least " + teamsPerSession + " teams"); if (plists == null || plists.size() < 2) throw new IllegalArgumentException( "profileslist must contain at least " + teamsPerSession + " ProfileList's"); if (nTournaments <= 0) throw new IllegalArgumentException("nTournaments must be >0"); int teamsize = sesettings.getTeamSize(); for (Team team : teams) { if (team.getParties().size() != teamsize) { throw new IllegalArgumentException("All teams should have size " + teamsize + " but found " + team.getParties().size()); } } for (ProfileList profile : plists) { if (profile.getProfiles().size() != teamsize) { throw new IllegalArgumentException( "All profiles should have size equal to the team size " + teamsize + " but found " + profile.getProfiles().size()); } } this.teamsPerSession = teamsPerSession; this.reuseTeams = reuseTeams; this.teams = teams; this.profileslists = plists; this.sessionsettings = sesettings; this.numberTournaments = nTournaments; } @Override public Double getMaxRunTime() { return permutations().size().doubleValue() * (sessionsettings.getMaxRunTime() + 2d); } @Override public TournamentProtocol getProtocol(Reporter logger) { AllPermutationsState state = new AllPermutationsState(this, permutations(), Collections.emptyList()); return new AllPermutationsProtocol(state, logger); } /** * @return {@link ImmutableList} containing all {@link SessionSettings} for * all Sessions in the correct order. */ public ImmutableList permutations() { ImmutableList> partylistlist; ImmutableList partieslist = new FixedList(teams); ImmutableList profileslist = new FixedList( profileslists); partylistlist = getParticipants(partieslist, profileslist, teamsPerSession, reuseTeams); return new MapList<>(partyproflist -> createSetting(partyproflist), partylistlist); } @Override public String toString() { return "AllPermutationsSettings[" + contentString() + "]"; } protected String contentString() { return teams + "," + reuseTeams + "," + profileslists + "," + teamsPerSession + "," + sessionsettings + "," + numberTournaments; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + numberTournaments; result = prime * result + ((profileslists == null) ? 0 : profileslists.hashCode()); result = prime * result + (reuseTeams ? 1231 : 1237); result = prime * result + ((sessionsettings == null) ? 0 : sessionsettings.hashCode()); result = prime * result + ((teams == null) ? 0 : teams.hashCode()); result = prime * result + teamsPerSession; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AllPermutationsSettings other = (AllPermutationsSettings) obj; if (numberTournaments != other.numberTournaments) return false; if (profileslists == null) { if (other.profileslists != null) return false; } else if (!profileslists.equals(other.profileslists)) return false; if (reuseTeams != other.reuseTeams) return false; if (sessionsettings == null) { if (other.sessionsettings != null) return false; } else if (!sessionsettings.equals(other.sessionsettings)) return false; if (teams == null) { if (other.teams != null) return false; } else if (!teams.equals(other.teams)) return false; if (teamsPerSession != other.teamsPerSession) return false; return true; } /** * * @param partyproflist a list of {@link PartyWithProfile} settings * @return {@link SessionSettings} */ protected SessionSettings createSetting( ImmutableList partyproflist) { SessionSettings settings = sessionsettings; for (TeamInfo partyprof : partyproflist) { settings = settings.with(partyprof); } return settings; } /** * * @param parties the parties in the tournament * @param profiles the profiles in the tournament * @param n number of items to draw. Assumed ≥1. * @param drawPartyWithPutback if parties can be drawn multiple times * @return list of sessionsets where each sessionset contains settings for a * session: a list with {@link TeamInfo}s. The sessionsets are made * by making all combinations of parties and profiles. Profiles are * drawn with replace. */ protected ImmutableList> getParticipants( ImmutableList parties, ImmutableList profiles, int n, boolean drawPartyWithPutback) { Permutations partiesPermutations; if (drawPartyWithPutback) { partiesPermutations = new PermutationsWithReturn<>(parties, n); } else { partiesPermutations = new PermutationsWithoutReturn<>(parties, n); } Permutations profilesPermutations = new PermutationsOrderedWithoutReturn<>( profiles, n); // each tuple contains session info: a list of Team and a list of // ProfileList. Tuples, ImmutableList> tuples = new Tuples<>( partiesPermutations, profilesPermutations); MapList, ImmutableList>, ImmutableList> tournamentsettings = new MapList, ImmutableList>, ImmutableList>( tuple -> teamlist(tuple), tuples); return new Repeat>(tournamentsettings, BigInteger.valueOf(numberTournaments), true); } /** * * @param tuple a tuple with (1) ImmutableList (2) * ImmutableList * @return list of TeamOfPartiesAndProfiles, each picked from the two lists * in order. */ private ImmutableList teamlist( Tuple, ImmutableList> tuple) { BiFunction makeparty = new BiFunction() { @Override public TeamInfo apply(Team team, ProfileList profilelist) { List parties = team.getParties(); List profiles = profilelist.getProfiles(); if (parties.size() != profiles.size()) throw new IllegalArgumentException( "Parties and profiles list must match, but found " + team + " and " + profilelist); List theteams = new LinkedList<>(); for (int n = 0; n < parties.size(); n++) { theteams.add(new PartyWithProfile(parties.get(n), profiles.get(n))); } return new TeamInfo(theteams); } }; return new MapThreadList(makeparty, tuple.get1(), tuple.get2()); } }