diff --git a/gemp-lotr/db/tournament-scratch.sql b/gemp-lotr/db/tournament-scratch.sql index c2e809523..7988cbfed 100644 --- a/gemp-lotr/db/tournament-scratch.sql +++ b/gemp-lotr/db/tournament-scratch.sql @@ -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' + diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java index 2d0055db5..2ce4f29c0 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java @@ -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 diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/ChampionshipSwissPairingMechanism.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/ChampionshipSwissPairingMechanism.java new file mode 100644 index 000000000..7a040d682 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/ChampionshipSwissPairingMechanism.java @@ -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 players, Set droppedPlayers) { + return round >= _targetRounds; + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java index e951069ef..de7bceba3 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java @@ -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(); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java index 2daa7b838..6b8164389 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java @@ -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; } diff --git a/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/tournament/ChampionshipSwissPairingMechanismTest.java b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/tournament/ChampionshipSwissPairingMechanismTest.java new file mode 100644 index 000000000..b51f6b887 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/test/java/com/gempukku/lotro/tournament/ChampionshipSwissPairingMechanismTest.java @@ -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 betterPlayers = new HashSet<>(); + Set worsePlayers = new HashSet<>(); + + Random rnd = ThreadLocalRandom.current(); + + for (int repeat = 0; repeat < repeatCount; repeat++) { + Set players = new HashSet<>(); + for (int i = 0; i < playerCount; i++) { + String playerName = String.valueOf(i); + players.add(playerName); + if (i droppedPlayers = new HashSet<>(); + Map byes = new HashMap<>(); + + Set matches = new HashSet<>(); + Map> 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 standings = BestOfOneStandingsProducer.produceStandings(players, matches, 3, 0, byes); + + Map newPairings = new LinkedHashMap<>(); + Set 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 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 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 betterPlayers, Set 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 players = new HashSet<>(); + for (int i = 0; i < playerCount; i++) + players.add("p" + i); + + Set droppedPlayers = new HashSet<>(); + Map byes = new HashMap<>(); + + Set matches = new HashSet<>(); + Map> 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 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 newPairings = new LinkedHashMap<>(); + Set 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 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 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 standings, String player) { + for (PlayerStanding standing : standings) { + if (standing.playerName().equals(player)) + return standing.points(); + } + return -1; + } +}