Adding Championship Swiss pairing mode
- Champ Swiss runs for a fixed number of rounds (8) as opposed to basing it on the number of players - Adjusted automatic swiss between-round delay to 1 minute instead of 2 - Added Paused stage to be used by kickoff-enabled tournaments
This commit is contained in:
@@ -9,12 +9,12 @@ WHERE id = 9
|
||||
|
||||
INSERT INTO gemp_db.scheduled_tournament
|
||||
(tournament_id, name, format, start_date, cost, playoff, tiebreaker, prizes, minimum_players, manual_kickoff, started)
|
||||
VALUES('test-tourney-4', 'Test4', 'pc_movie', '2023-08-26 05:29:00', 0, 'swiss', 'owr', 'daily', 2, true, false);
|
||||
VALUES('2023-wc-am-pc-expanded-walk-on', 'AM PC-Expanded Walk-on Qualifier', 'pc_expanded', '2023-09-23 17:00:00', 0, 'swiss', 'owr', 'daily', 2, true, false);
|
||||
|
||||
|
||||
SELECT *
|
||||
FROM player p
|
||||
WHERE name = 'Pokec'
|
||||
WHERE name LIKE 'Chad%'
|
||||
|
||||
SELECT *
|
||||
FROM deck d
|
||||
@@ -22,6 +22,23 @@ where player_id = 29802
|
||||
AND name = 'Rohan/Uruk Towers standard'
|
||||
|
||||
|
||||
SELECT *
|
||||
FROM league l
|
||||
ORDER BY id DESC
|
||||
|
||||
UPDATE league
|
||||
SET parameters = '20230908,default,0.69,3,2,open,7,5,rev_tow_sta,7,10'
|
||||
WHERE id = 642
|
||||
|
||||
|
||||
SELECT *
|
||||
,CONCAT('https://play.lotrtcgpc.net/gemp-lotr/game.html?replayId=',REPLACE(winner, '_', '%5F'), '$', win_recording_id) AS winner_replay
|
||||
,CONCAT('https://play.lotrtcgpc.net/gemp-lotr/game.html?replayId=',REPLACE(loser, '_', '%5F'), '$', lose_recording_id) AS loser_replay
|
||||
FROM game_history gh
|
||||
WHERE tournament = 'Constructed - Weekend Qualifier PC-Movie - Serie 1'
|
||||
AND (winner = 'Chadwick537' OR loser = 'Chadwick537')
|
||||
ORDER BY id DESC
|
||||
|
||||
|
||||
SELECT *
|
||||
FROM tournament t
|
||||
@@ -34,7 +51,7 @@ WHERE name IS NULL
|
||||
|
||||
UPDATE tournament
|
||||
SET stage = 'Preparing'
|
||||
WHERE id = 1386
|
||||
WHERE id = 1422
|
||||
|
||||
|
||||
UPDATE tournament
|
||||
@@ -48,7 +65,11 @@ ORDER BY ID DESC
|
||||
|
||||
UPDATE tournament_player
|
||||
SET dropped = 1
|
||||
WHERE id = 10387
|
||||
WHERE id = 11284
|
||||
|
||||
SELECT *
|
||||
FROM tournament_match tm
|
||||
ORDER BY id DESC
|
||||
|
||||
|
||||
INSERT INTO gemp_db.tournament_player
|
||||
@@ -67,11 +88,54 @@ FROM game_history gh
|
||||
ORDER BY id DESC
|
||||
|
||||
|
||||
UPDATE tournament_match
|
||||
SET winner = 'dstaley'
|
||||
WHERE id = 14638
|
||||
|
||||
SELECT *
|
||||
UPDATE tournament_match
|
||||
SET winner = stephan77
|
||||
WHERE id = 14637
|
||||
|
||||
|
||||
UPDATE game_history
|
||||
SET winner = 'dstaley', winnerId = 33114, loser = 'johnec', loserId = 35804
|
||||
WHERE id = 1214409
|
||||
|
||||
UPDATE game_history
|
||||
SET winner = 'stephan77', winnerId = 30969, loser = 'balrog69', loserId = 35908
|
||||
WHERE id = 1214410
|
||||
|
||||
|
||||
|
||||
SELECT FROM_UNIXTIME('1695509366664')
|
||||
|
||||
SELECT *, FROM_UNIXTIME(floor(transfer_date / 1000)) AS transfer_time
|
||||
FROM transfer t
|
||||
WHERE player = 'Aaron_Brutcher'
|
||||
AND collection LIKE '%2_125%'
|
||||
ORDER BY id DESC
|
||||
|
||||
SELECT *
|
||||
FROM collection c
|
||||
INNER JOIN player p
|
||||
ON p.id = c.player_id
|
||||
INNER JOIN collection_entries ce
|
||||
ON ce.collection_id = c.id
|
||||
WHERE p.name = 'Aaron_Brutcher'
|
||||
AND c.id IN (199929, 202698)
|
||||
AND ce.product = '2_125'
|
||||
ORDER BY IFNULL(ce.modified_date, ce.created_date)
|
||||
|
||||
|
||||
UPDATE collection_entries
|
||||
SET quantity = 4
|
||||
WHERE collection_id = 199929 AND product = '2_125'
|
||||
|
||||
|
||||
|
||||
SELECT c.*
|
||||
FROM collection c
|
||||
INNER JOIN player p
|
||||
ON p.id = c.player_id
|
||||
WHERE p.name = 'Aaron_Brutcher'
|
||||
|
||||
|
||||
@@ -805,7 +805,7 @@ public class HallServer extends AbstractServer {
|
||||
null, null, true, false, false, false, GameTimer.TOURNAMENT_TIMER, null);
|
||||
|
||||
wcGameSettings = new GameSettings(null, _formatLibrary.getFormat(_tournament.getFormat()),
|
||||
null, null, true, false, false, false, GameTimer.EXPANDED_CHAMPIONSHIP_TIMER, null);
|
||||
null, null, true, false, false, false, GameTimer.CHAMPIONSHIP_TIMER, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.gempukku.lotro.tournament;
|
||||
|
||||
import com.gempukku.lotro.competitive.PlayerStanding;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class ChampionshipSwissPairingMechanism extends SwissPairingMechanism {
|
||||
|
||||
private final Integer _targetRounds;
|
||||
|
||||
|
||||
public ChampionshipSwissPairingMechanism(String registryRepresentation, int targetRounds) {
|
||||
super(registryRepresentation, targetRounds);
|
||||
_targetRounds = targetRounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayOffSystem() {
|
||||
return "WC Swiss";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished(int round, Set<String> players, Set<String> droppedPlayers) {
|
||||
return round >= _targetRounds;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
public class DefaultTournament implements Tournament {
|
||||
// 10 minutes
|
||||
private final int _deckBuildTime = 10 * 60 * 1000;
|
||||
private long _waitForPairingsTime = 1000 * 60 * 2;
|
||||
private long _waitForPairingsTime = 1000 * 60 * 1;
|
||||
|
||||
private final PairingMechanism _pairingMechanism;
|
||||
private final TournamentPrizes _tournamentPrizes;
|
||||
@@ -107,7 +107,7 @@ public class DefaultTournament implements Tournament {
|
||||
_players);
|
||||
} else if (_tournamentStage == Stage.DECK_BUILDING) {
|
||||
_deckBuildStartTime = System.currentTimeMillis();
|
||||
} else if (_tournamentStage == Stage.AWAITING_KICKOFF) {
|
||||
} else if (_tournamentStage == Stage.AWAITING_KICKOFF || _tournamentStage == Stage.PAUSED) {
|
||||
|
||||
} else if (_tournamentStage == Stage.FINISHED) {
|
||||
_finishedTournamentMatches.addAll(_tournamentService.getMatches(_tournamentId));
|
||||
@@ -313,7 +313,7 @@ public class DefaultTournament implements Tournament {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
if (_tournamentStage == Stage.AWAITING_KICKOFF) {
|
||||
if (_tournamentStage == Stage.AWAITING_KICKOFF || _tournamentStage == Stage.PAUSED) {
|
||||
|
||||
}
|
||||
else if (_tournamentStage == Stage.PREPARING) {
|
||||
@@ -325,7 +325,7 @@ public class DefaultTournament implements Tournament {
|
||||
if (_pairingMechanism.isFinished(_tournamentRound, _players, _droppedPlayers)) {
|
||||
finishTournament(tournamentCallback, collectionsManager);
|
||||
} else {
|
||||
tournamentCallback.broadcastMessage("Tournament " + _tournamentName + " will start round "+(_tournamentRound+1)+" in 2 minutes");
|
||||
tournamentCallback.broadcastMessage("Tournament " + _tournamentName + " will start round "+(_tournamentRound+1)+" in 1 minute.");
|
||||
_nextTask = new PairPlayers();
|
||||
}
|
||||
result = true;
|
||||
@@ -482,7 +482,7 @@ public class DefaultTournament implements Tournament {
|
||||
|
||||
if(game == null)
|
||||
continue;
|
||||
|
||||
|
||||
var gameStart = game.GetUTCStartDate();
|
||||
var gameEnd = game.GetUTCEndDate();
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ public interface Tournament {
|
||||
DRAFT("Drafting"),
|
||||
DECK_BUILDING("Deck building"),
|
||||
AWAITING_KICKOFF("Awaiting kickoff"),
|
||||
PAUSED("Paused"),
|
||||
PREPARING("Preparing"),
|
||||
PLAYING_GAMES("Playing games"),
|
||||
FINISHED("Finished");
|
||||
@@ -59,6 +60,8 @@ public interface Tournament {
|
||||
return new SwissPairingMechanism("swiss");
|
||||
if (pairingType.equals("swiss-3"))
|
||||
return new SwissPairingMechanism("swiss-3", 3);
|
||||
if (pairingType.equals("wc-swiss"))
|
||||
return new ChampionshipSwissPairingMechanism("wc-swiss", 8);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
package com.gempukku.lotro.tournament;
|
||||
|
||||
import com.gempukku.lotro.at.AbstractAtTest;
|
||||
import com.gempukku.lotro.competitive.BestOfOneStandingsProducer;
|
||||
import com.gempukku.lotro.competitive.PlayerStanding;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ChampionshipSwissPairingMechanismTest extends AbstractAtTest {
|
||||
// @Test
|
||||
// public void testPairingLargeTournament() {
|
||||
// int repeatCount = 1;
|
||||
// int playerCount = 4096*2;
|
||||
//
|
||||
// for (int repeat = 0; repeat < repeatCount; repeat++) {
|
||||
// testSwissPairingForPlayerCount(playerCount);
|
||||
// }
|
||||
// }
|
||||
|
||||
public void calculateSallyJackChances() {
|
||||
// 10 Sallys and 250 Jacks play in a tournament with 9 rounds of Swiss and Top 8.
|
||||
// Sally has 66% chance of winning a game against Jack and 50% against anotherSally, Jack has 50% of winning
|
||||
// a game against another Jack
|
||||
|
||||
int repeatCount = 100000;
|
||||
int roundCount = 9;
|
||||
|
||||
int betterPlayerCount = 10;
|
||||
int worsePlayerCount = 250;
|
||||
float chanceToWin = 0.66666f;
|
||||
|
||||
int playerCount = betterPlayerCount+worsePlayerCount;
|
||||
|
||||
int betterPlayerWins =0;
|
||||
|
||||
Set<String> betterPlayers = new HashSet<>();
|
||||
Set<String> worsePlayers = new HashSet<>();
|
||||
|
||||
Random rnd = ThreadLocalRandom.current();
|
||||
|
||||
for (int repeat = 0; repeat < repeatCount; repeat++) {
|
||||
Set<String> players = new HashSet<>();
|
||||
for (int i = 0; i < playerCount; i++) {
|
||||
String playerName = String.valueOf(i);
|
||||
players.add(playerName);
|
||||
if (i<betterPlayerCount)
|
||||
betterPlayers.add(playerName);
|
||||
else
|
||||
worsePlayers.add(playerName);
|
||||
}
|
||||
|
||||
Set<String> droppedPlayers = new HashSet<>();
|
||||
Map<String, Integer> byes = new HashMap<>();
|
||||
|
||||
Set<TournamentMatch> matches = new HashSet<>();
|
||||
Map<String, Set<String>> previouslyPaired = new HashMap<>();
|
||||
for (String player : players)
|
||||
previouslyPaired.put(player, new HashSet<>());
|
||||
|
||||
var pairing = new ChampionshipSwissPairingMechanism("swiss", 8);
|
||||
for (int i = 1; i <= roundCount; i++) {
|
||||
if (!pairing.isFinished(i - 1, players, droppedPlayers)) {
|
||||
List<PlayerStanding> standings = BestOfOneStandingsProducer.produceStandings(players, matches, 3, 0, byes);
|
||||
|
||||
Map<String, String> newPairings = new LinkedHashMap<>();
|
||||
Set<String> newByes = new HashSet<>();
|
||||
|
||||
pairing.pairPlayers(i, players, droppedPlayers, byes, standings, previouslyPaired, newPairings, newByes);
|
||||
if (newByes.size() > 0) {
|
||||
for (String newBye : newByes) {
|
||||
byes.put(newBye, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> newPairing : newPairings.entrySet()) {
|
||||
String playerOne = newPairing.getKey();
|
||||
String playerTwo = newPairing.getValue();
|
||||
|
||||
String winner = getWinner(rnd, betterPlayers, worsePlayers, playerOne, playerTwo, chanceToWin);
|
||||
|
||||
previouslyPaired.get(playerOne).add(playerTwo);
|
||||
previouslyPaired.get(playerTwo).add(playerOne);
|
||||
|
||||
matches.add(new TournamentMatch(playerOne, playerTwo, winner, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
List<PlayerStanding> standings = BestOfOneStandingsProducer.produceStandings(players, matches, 1, 0, byes);
|
||||
|
||||
String firstSemi = getWinner(rnd, betterPlayers, worsePlayers, standings.get(0).playerName(), standings.get(7).playerName(), chanceToWin);
|
||||
String secondSemi = getWinner(rnd, betterPlayers, worsePlayers, standings.get(1).playerName(), standings.get(6).playerName(), chanceToWin);
|
||||
String thirdSemi = getWinner(rnd, betterPlayers, worsePlayers, standings.get(2).playerName(), standings.get(5).playerName(), chanceToWin);
|
||||
String fourthSemi = getWinner(rnd, betterPlayers, worsePlayers, standings.get(3).playerName(), standings.get(4).playerName(), chanceToWin);
|
||||
|
||||
String firstFinalist = getWinner(rnd, betterPlayers, worsePlayers, firstSemi, fourthSemi, chanceToWin);
|
||||
String secondFinalist = getWinner(rnd, betterPlayers, worsePlayers, secondSemi, thirdSemi, chanceToWin);
|
||||
|
||||
String winner = getWinner(rnd, betterPlayers, worsePlayers, firstFinalist, secondFinalist, chanceToWin);
|
||||
|
||||
if (betterPlayers.contains(winner))
|
||||
betterPlayerWins++;
|
||||
}
|
||||
|
||||
System.out.println(betterPlayerWins);
|
||||
}
|
||||
|
||||
private String getWinner(Random rnd, Set<String> betterPlayers, Set<String> worsePlayers, String playerOne, String playerTwo, float winChance) {
|
||||
if (betterPlayers.contains(playerOne) && worsePlayers.contains(playerTwo)) {
|
||||
if (rnd.nextFloat() < winChance)
|
||||
return playerOne;
|
||||
else
|
||||
return playerTwo;
|
||||
} else if (betterPlayers.contains(playerTwo) && worsePlayers.contains(playerOne)) {
|
||||
if (rnd.nextFloat() < winChance)
|
||||
return playerTwo;
|
||||
else
|
||||
return playerOne;
|
||||
} else {
|
||||
if (rnd.nextBoolean())
|
||||
return playerOne;
|
||||
else
|
||||
return playerTwo;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPairingSmallTournament() {
|
||||
int repeatCount = 10;
|
||||
int playerCount = 12;
|
||||
|
||||
for (int repeat = 0; repeat < repeatCount; repeat++) {
|
||||
testSwissPairingForPlayerCount(playerCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPairingVerySmallTournament() {
|
||||
int repeatCount = 10;
|
||||
int playerCount = 8;
|
||||
|
||||
for (int repeat = 0; repeat < repeatCount; repeat++) {
|
||||
testSwissPairingForPlayerCount(playerCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPairingWCTournament() {
|
||||
int repeatCount = 10;
|
||||
int playerCount = 30;
|
||||
|
||||
for (int repeat = 0; repeat < repeatCount; repeat++) {
|
||||
testSwissPairingForPlayerCount(playerCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPairingSmallTournamentWithOddNumberOfPlayers() {
|
||||
int repeatCount = 10;
|
||||
int playerCount = 9;
|
||||
|
||||
for (int repeat = 0; repeat < repeatCount; repeat++) {
|
||||
testSwissPairingForPlayerCount(playerCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void testSwissPairingForPlayerCount(int playerCount) {
|
||||
Set<String> players = new HashSet<>();
|
||||
for (int i = 0; i < playerCount; i++)
|
||||
players.add("p" + i);
|
||||
|
||||
Set<String> droppedPlayers = new HashSet<>();
|
||||
Map<String, Integer> byes = new HashMap<>();
|
||||
|
||||
Set<TournamentMatch> matches = new HashSet<>();
|
||||
Map<String, Set<String>> previouslyPaired = new HashMap<>();
|
||||
for (String player : players)
|
||||
previouslyPaired.put(player, new HashSet<>());
|
||||
|
||||
var pairing = new ChampionshipSwissPairingMechanism("swiss", Math.floorDiv(playerCount, 2));
|
||||
for (int i = 1; i < 20; i++) {
|
||||
if (!pairing.isFinished(i - 1, players, droppedPlayers)) {
|
||||
System.out.println("Pairing round " + i);
|
||||
List<PlayerStanding> standings = BestOfOneStandingsProducer.produceStandings(players, matches, 1, 0, byes);
|
||||
for (PlayerStanding standing : standings) {
|
||||
String player = standing.playerName();
|
||||
log(player + " points - " + standing.points() + " played against: " + StringUtils.join(previouslyPaired.get(player), ","));
|
||||
}
|
||||
|
||||
Map<String, String> newPairings = new LinkedHashMap<>();
|
||||
Set<String> newByes = new HashSet<>();
|
||||
|
||||
assertFalse("Unable to pair for round " + i, pairing.pairPlayers(i, players, droppedPlayers, byes, standings, previouslyPaired, newPairings, newByes));
|
||||
assertEquals("Invalid number of pairings", playerCount / 2, newPairings.size());
|
||||
if (playerCount % 2 == 0)
|
||||
assertEquals("Invalid number of byes", 0, newByes.size());
|
||||
else {
|
||||
assertEquals("Invalid number of byes", 1, newByes.size());
|
||||
String newBye = newByes.iterator().next();
|
||||
log("Bye - " + newBye);
|
||||
assertNull("Player already received bye", byes.get(newBye));
|
||||
byes.put(newBye, 1);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> newPairing : newPairings.entrySet()) {
|
||||
String playerOne = newPairing.getKey();
|
||||
String playerTwo = newPairing.getValue();
|
||||
|
||||
assertFalse(previouslyPaired.get(playerOne).contains(playerTwo));
|
||||
assertFalse(previouslyPaired.get(playerTwo).contains(playerOne));
|
||||
|
||||
System.out.println("Paired " + playerOne + " against " + playerTwo + " points - " + getPlayerPoints(standings, playerOne) + " vs " + getPlayerPoints(standings, playerTwo));
|
||||
String winner = ThreadLocalRandom.current().nextBoolean() ? playerOne : playerTwo;
|
||||
log("Winner - " + winner);
|
||||
|
||||
previouslyPaired.get(playerOne).add(playerTwo);
|
||||
previouslyPaired.get(playerTwo).add(playerOne);
|
||||
|
||||
matches.add(new TournamentMatch(playerOne, playerTwo, winner, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("Final standings:");
|
||||
List<PlayerStanding> standings = BestOfOneStandingsProducer.produceStandings(players, matches, 1, 0, byes);
|
||||
for (PlayerStanding standing : standings) {
|
||||
String player = standing.playerName();
|
||||
System.out.println(standing.standing() + ". " + player + " points - " + standing.points() + " played against: " + StringUtils.join(previouslyPaired.get(player), ","));
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String s) {
|
||||
// System.out.println(s);
|
||||
}
|
||||
|
||||
private int getPlayerPoints(List<PlayerStanding> standings, String player) {
|
||||
for (PlayerStanding standing : standings) {
|
||||
if (standing.playerName().equals(player))
|
||||
return standing.points();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user