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());
}
}