From 993d207c0b43212e37b08de2ddcc28e705a175fe Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Thu, 10 Aug 2023 12:35:05 +0200 Subject: [PATCH] Small design improvement --- .../mpg/AbstractMercatoMpgProcess.java | 57 ++ .../org/blondin/mpg/AbstractMpgProcess.java | 136 ++++ src/main/java/org/blondin/mpg/Games.java | 456 +++++++++++++ src/main/java/org/blondin/mpg/Main.java | 614 +----------------- .../org/blondin/mpg/MercatoChampionship.java | 23 + .../java/org/blondin/mpg/MercatoLeague.java | 23 + src/main/java/org/blondin/mpg/MpgProcess.java | 9 + .../org/blondin/mpg/BonusSelectionTest.java | 60 +- src/test/java/org/blondin/mpg/MainTest.java | 4 +- 9 files changed, 740 insertions(+), 642 deletions(-) create mode 100644 src/main/java/org/blondin/mpg/AbstractMercatoMpgProcess.java create mode 100644 src/main/java/org/blondin/mpg/AbstractMpgProcess.java create mode 100644 src/main/java/org/blondin/mpg/Games.java create mode 100644 src/main/java/org/blondin/mpg/MercatoChampionship.java create mode 100644 src/main/java/org/blondin/mpg/MercatoLeague.java create mode 100644 src/main/java/org/blondin/mpg/MpgProcess.java diff --git a/src/main/java/org/blondin/mpg/AbstractMercatoMpgProcess.java b/src/main/java/org/blondin/mpg/AbstractMercatoMpgProcess.java new file mode 100644 index 0000000..bfa84ca --- /dev/null +++ b/src/main/java/org/blondin/mpg/AbstractMercatoMpgProcess.java @@ -0,0 +1,57 @@ +package org.blondin.mpg; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.blondin.mpg.out.ChampionshipOutType; +import org.blondin.mpg.out.InjuredSuspendedWrapperClient; +import org.blondin.mpg.out.model.OutType; +import org.blondin.mpg.root.model.Player; +import org.blondin.mpg.root.model.Position; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.vandermeer.asciitable.AT_Row; +import de.vandermeer.asciitable.AsciiTable; +import de.vandermeer.skb.interfaces.transformers.textformat.TextAlignment; + +public abstract class AbstractMercatoMpgProcess extends AbstractMpgProcess { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractMercatoMpgProcess.class); + + protected static void processMercato(List players, InjuredSuspendedWrapperClient outPlayersClient, ChampionshipOutType championship) { + Collections.sort(players, + Comparator.comparing(Player::getPosition).thenComparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()); + List goals = players.stream().filter(p -> p.getPosition().equals(Position.G)).collect(Collectors.toList()).subList(0, 5); + List defenders = players.stream().filter(p -> p.getPosition().equals(Position.D)).collect(Collectors.toList()).subList(0, 10); + List midfielders = players.stream().filter(p -> p.getPosition().equals(Position.M)).collect(Collectors.toList()).subList(0, 10); + List attackers = players.stream().filter(p -> p.getPosition().equals(Position.A)).collect(Collectors.toList()).subList(0, 10); + + AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE, "Auct.", "Out info"); + for (List line : Arrays.asList(goals, defenders, midfielders, attackers)) { + for (Player player : line) { + org.blondin.mpg.out.model.Player outPlayer = outPlayersClient.getPlayer(championship, player.getName(), + PositionWrapper.toOut(player.getPosition()), player.getClubName(), OutType.INJURY_GREEN); + String outInfos = ""; + if (outPlayer != null) { + outInfos = String.format("%s - %s - %s", outPlayer.getOutType(), outPlayer.getDescription(), outPlayer.getLength()); + } + AT_Row row = at.addRow(player.getPosition(), player.getName(), FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), + player.getQuotation(), player.getAuction(), outInfos); + setTableFormatRowPaddingSpace(row); + row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); + row.getCells().get(3).getContext().setTextAlignment(TextAlignment.RIGHT); + row.getCells().get(4).getContext().setTextAlignment(TextAlignment.RIGHT); + } + at.addRule(); + } + + String render = at.render(); + LOG.info(render); + LOG.info(""); + } + +} diff --git a/src/main/java/org/blondin/mpg/AbstractMpgProcess.java b/src/main/java/org/blondin/mpg/AbstractMpgProcess.java new file mode 100644 index 0000000..0729e2c --- /dev/null +++ b/src/main/java/org/blondin/mpg/AbstractMpgProcess.java @@ -0,0 +1,136 @@ +package org.blondin.mpg; + +import java.text.DecimalFormat; +import java.util.List; + +import org.blondin.mpg.config.Config; +import org.blondin.mpg.root.exception.PlayerNotFoundException; +import org.blondin.mpg.root.model.Club; +import org.blondin.mpg.root.model.Clubs; +import org.blondin.mpg.root.model.Player; +import org.blondin.mpg.stats.ChampionshipStatsType; +import org.blondin.mpg.stats.MpgStatsClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.vandermeer.asciitable.AT_Cell; +import de.vandermeer.asciitable.AT_Row; +import de.vandermeer.asciitable.AsciiTable; +import de.vandermeer.asciitable.CWC_LongestLine; +import de.vandermeer.asciithemes.a7.A7_Grids; +import de.vandermeer.skb.interfaces.transformers.textformat.TextAlignment; + +public abstract class AbstractMpgProcess implements MpgProcess { + + protected static final String TABLE_POSITION = "P"; + protected static final String TABLE_PLAYER_NAME = "Player name"; + protected static final String TABLE_QUOTE = "Q."; + protected static final String TABLE_EFFICIENCY = "Eff."; + protected static final DecimalFormat FORMAT_DECIMAL_DOUBLE = new DecimalFormat("0.00"); + + private static final Logger LOG = LoggerFactory.getLogger(AbstractMpgProcess.class); + + protected AbstractMpgProcess() { + } + + /** + * Add club name for all pool players + * + * @param players Pool + * @param clubs Clubs + */ + static void completePlayersClub(List players, Clubs clubs) { + for (Player player : players) { + Club club = clubs.getChampionshipClubs().get(player.getClubId()); + if (club == null) { + throw new UnsupportedOperationException( + String.format("Club '%s' cannot be found for player '%s'", player.getClubId(), player.getName())); + } + player.setClubName(club.getName()); + } + } + + protected static List completeAuctionAndcalculateEfficiency(List players, MpgStatsClient stats, + ChampionshipStatsType championship, Config config, boolean failIfPlayerNotFound, boolean logWarnIfPlayerNotFound) { + + // Pre-process efficiencies + calculateEfficiencies(stats, championship, config); + + // Fill MPG model + for (Player player : players) { + try { + org.blondin.mpg.stats.model.Player p = stats.getStats(championship).getPlayer(player.getName()); + if (p.getAuction() != null && p.getAuction().getNumber() > 5) { + // Feature in API only since 2021-11 + player.setAuction(p.getAuction().getAverage()); + } + if (player.getAuction() == 0 && p.getAuctionLong() != null) { + // Feature in API only since 2022-07 + player.setAuction(p.getAuctionLong().getAverage()); + } + player.setEfficiency(p.getEfficiency()); + } catch (PlayerNotFoundException e) { + if (failIfPlayerNotFound) { + throw e; + } + if (logWarnIfPlayerNotFound) { + LOG.warn("WARN: Player can't be found in statistics: {}", player.getName()); + } + player.setEfficiency(0); + } + } + return players; + } + + private static void calculateEfficiencies(MpgStatsClient stats, ChampionshipStatsType championship, Config config) { + int daysPeriod = getCurrentDay(stats, championship); + int days4efficiency = 0; + if (config.isEfficiencyRecentFocus() && stats.getStats(championship).getInfos().getAnnualStats().getCurrentDay().getDayReached() > 0) { + days4efficiency = config.getEfficiencyRecentDays(); + // If season start (=> daysPeriod < 8 when days4efficiency = 8 by default), the focus is on the started days + if (daysPeriod < days4efficiency) { + days4efficiency = daysPeriod; + } else { + daysPeriod = days4efficiency; + } + } + for (org.blondin.mpg.stats.model.Player p : stats.getStats(championship).getPlayers()) { + double efficiency = p.getStats().getMatchs(days4efficiency) / (double) daysPeriod * p.getStats().getAverage(days4efficiency) + * (1 + p.getStats().getGoals(days4efficiency) * config.getEfficiencyCoefficient(PositionWrapper.fromStats(p.getPosition()))); + // round efficiency to 2 decimals + p.setEfficiency(efficiency); + } + } + + private static int getCurrentDay(MpgStatsClient stats, ChampionshipStatsType championship) { + int daysPeriod = stats.getStats(championship).getInfos().getAnnualStats().getCurrentDay().getDayReached(); + // If league not started, we take the number of day of season, because average will be on this period + if (daysPeriod == 0) { + // The previous season statistics could be null, in this case current annual max day is used + daysPeriod = stats.getStats(championship).getInfos().getLastStats() == null + ? stats.getStats(championship).getInfos().getAnnualStats().getMaxDay() + : stats.getStats(championship).getInfos().getLastStats().getMaxDay(); + } + return daysPeriod; + } + + protected static AsciiTable getTable(Object... columnTitle) { + AsciiTable at = new AsciiTable(); + at.getContext().setGrid(A7_Grids.minusBarPlusEquals()); + at.setPaddingLeftRight(1); + at.getRenderer().setCWC(new CWC_LongestLine()); + at.addRule(); + AT_Row rowHead = at.addRow(columnTitle); + for (AT_Cell cell : rowHead.getCells()) { + cell.getContext().setTextAlignment(TextAlignment.CENTER); + } + at.addRule(); + return at; + } + + protected static void setTableFormatRowPaddingSpace(AT_Row row) { + for (AT_Cell cell : row.getCells()) { + cell.getContext().setPaddingLeftRight(1); + } + } +} diff --git a/src/main/java/org/blondin/mpg/Games.java b/src/main/java/org/blondin/mpg/Games.java new file mode 100644 index 0000000..30caf4b --- /dev/null +++ b/src/main/java/org/blondin/mpg/Games.java @@ -0,0 +1,456 @@ +package org.blondin.mpg; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.blondin.mpg.config.Config; +import org.blondin.mpg.out.ChampionshipOutType; +import org.blondin.mpg.out.InjuredSuspendedWrapperClient; +import org.blondin.mpg.out.model.OutType; +import org.blondin.mpg.root.MpgClient; +import org.blondin.mpg.root.exception.NoMoreGamesException; +import org.blondin.mpg.root.exception.PlayerNotFoundException; +import org.blondin.mpg.root.model.Coach; +import org.blondin.mpg.root.model.CoachRequest; +import org.blondin.mpg.root.model.Division; +import org.blondin.mpg.root.model.League; +import org.blondin.mpg.root.model.Mode; +import org.blondin.mpg.root.model.Player; +import org.blondin.mpg.root.model.PlayersOnPitch; +import org.blondin.mpg.root.model.PoolPlayers; +import org.blondin.mpg.root.model.Position; +import org.blondin.mpg.root.model.SelectedBonus; +import org.blondin.mpg.root.model.TacticalSubstitute; +import org.blondin.mpg.root.model.Team; +import org.blondin.mpg.stats.model.CurrentDay; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.vandermeer.asciitable.AT_Row; +import de.vandermeer.asciitable.AsciiTable; +import de.vandermeer.skb.interfaces.transformers.textformat.TextAlignment; + +public class Games extends AbstractMpgProcess { + + private static final Logger LOG = LoggerFactory.getLogger(Games.class); + + public void process(League league, ApiClients apiClients, Config config) { + try { + + // Get main bean for current league + Division division = apiClients.getMpg().getDivision(league.getDivisionId()); + Team team = apiClients.getMpg().getTeam(division.getTeam(apiClients.getMpg().getUserId())); + PoolPlayers pool = apiClients.getMpg().getPoolPlayers(league.getChampionship()); + Coach coach = apiClients.getMpg().getCoach(league.getDivisionId()); + completePlayersClub(pool.getPlayers(), apiClients.getMpg().getClubs()); + completePlayersTeam(team.getSquad(), pool); + List players = team.getSquad().values().stream().collect(Collectors.toList()); + + // Complete auction and calculate efficiency (notes should be in injured players display), and save for transactions proposal + completeAuctionAndcalculateEfficiency(players, apiClients.getStats(), ChampionshipTypeWrapper.toStats(league.getChampionship()), config, + false, true); + List playersTeam = players.stream().collect(Collectors.toList()); + + // Remove out players (and write them) + removeOutPlayers(players, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship()), true); + + // Sort by efficiency + Collections.sort(players, + Comparator.comparing(Player::getPosition).thenComparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()); + + // Write optimized team + writeTeamOptimized(players, config.isDebug()); + + // Auto-update team + if (config.isTeampUpdate()) { + updateTeamWithRetry(apiClients.getMpg(), division, team, coach, players, pool, config); + } + + if (config.isTransactionsProposal()) { + if (Mode.NORMAL.equals(league.getMode())) { + LOG.info( + "\nTransaction proposals can not be achieved, you should buy 'MPG expert mode' for this league (very fun, not expensive!)"); + } else { + LOG.info("\nTransactions proposal ..."); + CurrentDay cd = apiClients.getStats().getStats(ChampionshipTypeWrapper.toStats(league.getChampionship())).getInfos() + .getAnnualStats().getCurrentDay(); + if (!cd.isStatsDayReached()) { + LOG.info("\nWARNING: Last day stats have not fully reached! Please retry tomorrow"); + } + List playersAvailable = apiClients.getMpg().getAvailablePlayers(league.getDivisionId()).getList(); + completePlayersClub(playersAvailable, apiClients.getMpg().getClubs()); + removeOutPlayers(playersAvailable, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship()), false); + completeAuctionAndcalculateEfficiency(playersAvailable, apiClients.getStats(), + ChampionshipTypeWrapper.toStats(league.getChampionship()), config, false, false); + + Integer currentPlayersBuy = team.getBids().stream().map(Player::getPricePaid).collect(Collectors.summingInt(Integer::intValue)); + writeTransactionsProposal(cd.getDay(), playersTeam, playersAvailable, team.getBudget() - currentPlayersBuy, + apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship()), config); + } + } + } catch (NoMoreGamesException e) { + LOG.info("\nNo more games in this league ...\n"); + } + LOG.info(""); + } + + /** + * Teams players is only id and price paid => replace by real player + * + * @param teamPlayers teams + * @param pool players + */ + static void completePlayersTeam(Map teamPlayers, PoolPlayers pool) { + List players2Remove = new ArrayList<>(); + for (Entry entry : teamPlayers.entrySet()) { + try { + Player player = pool.getPlayer(entry.getKey()); + player.setPricePaid(teamPlayers.get(entry.getKey()).getPricePaid()); + teamPlayers.put(entry.getKey(), player); + } catch (PlayerNotFoundException e) { + LOG.warn("Some player in your team removed because doesn't exist in league pool players: {}", entry.getKey()); + players2Remove.add(entry.getKey()); + } + } + for (String p2r : players2Remove) { + teamPlayers.remove(p2r); + } + } + + private static void updateTeamWithRetry(MpgClient mpgClient, Division division, Team team, Coach coach, List players, PoolPlayers pool, + Config config) { + LOG.info("\nUpdating team ..."); + CoachRequest request = getCoachRequest(team, coach, players, division.getGameRemaining(), config); + if (StringUtils.isNotBlank(request.getCaptain())) { + LOG.info(" Captain: {}", pool.getPlayer(request.getCaptain()).getName()); + } + if (request.getBonusSelected() != null && StringUtils.isNotBlank(request.getBonusSelected().getName())) { + String playerPotential = ""; + if (SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(request.getBonusSelected().getName())) { + playerPotential = "(" + pool.getPlayer(request.getBonusSelected().getPlayerId()).getName() + ")"; + } + LOG.info(" Bonus : {} {}", request.getBonusSelected().getName(), playerPotential); + } + final long maxRetry = 10; + for (int i = 1; i <= 10; i++) { + try { + mpgClient.updateCoach(coach.getIdMatch(), request); + break; + } catch (UnsupportedOperationException e) { + if (i == maxRetry || !"Unsupported status code: 400 Bad Request / Content: {\"error\":\"badRequest\"}".equals(e.getMessage())) { + throw e; + } + try { + LOG.info("Retrying Team update..."); + Thread.sleep(5000); + } catch (InterruptedException e1) { // NOSONAR : Sleep wanted + throw new UnsupportedOperationException(e1); + } + } + } + } + + private static void writeTransactionsProposal(int currentDay, List playersTeam, List playersAvailable, int budget, + InjuredSuspendedWrapperClient outPlayersClient, ChampionshipOutType championship, Config config) { + + // Players with bad efficiency + List players2Sell = playersTeam.stream().filter(p -> p.getEfficiency() <= config.getEfficiencySell(p.getPosition())) + .collect(Collectors.toList()); + + // Remove goalkeeper(s) (if exist) and same team as the first + List goalkeepers = playersTeam.stream().filter(p -> p.getPosition().equals(Position.G)) + .sorted(Comparator.comparing(Player::getEfficiency).reversed()).collect(Collectors.toList()); + final Player goalFirst = goalkeepers.isEmpty() ? new Player() : goalkeepers.get(0); + if (!goalkeepers.isEmpty()) { + players2Sell.removeIf(p -> p.getPosition().equals(Position.G) && p.getClubId().equals(goalkeepers.get(0).getClubId())); + } + + int cash = budget; + if (currentDay > 2 && !players2Sell.isEmpty()) { + LOG.info("Players to sell (initial cash: {}):", budget); + AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE); + for (Player player : players2Sell) { + cash += player.getQuotation(); + AT_Row row = at.addRow(player.getPosition(), player.getName(), FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), + player.getQuotation()); + setTableFormatRowPaddingSpace(row); + row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); + } + at.addRule(); + String render = at.render(); + LOG.info(render); + } + LOG.info("Budget: {}", cash); + + Player defenderLast = playersTeam.stream().filter(p -> p.getPosition().equals(Position.D)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation)).collect(Collectors.toList()).get(0); + Player midfielderLast = playersTeam.stream().filter(p -> p.getPosition().equals(Position.M)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation)).collect(Collectors.toList()).get(0); + Player attackerLast = playersTeam.stream().filter(p -> p.getPosition().equals(Position.A)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation)).collect(Collectors.toList()).get(0); + cash += defenderLast.getQuotation() + midfielderLast.getQuotation() + attackerLast.getQuotation(); + LOG.info("Budget if last field players by line sold: {}", cash); + + final int budgetPotential = cash; + List players2buy = new ArrayList<>(); + players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.G)).filter(p -> p.getQuotation() <= budgetPotential) + .filter(p -> p.getEfficiency() > goalFirst.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.G)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) + .collect(Collectors.toList())); + players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.D)).filter(p -> p.getQuotation() <= budgetPotential) + .filter(p -> p.getEfficiency() > defenderLast.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.D)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) + .collect(Collectors.toList())); + players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.M)).filter(p -> p.getQuotation() <= budgetPotential) + .filter(p -> p.getEfficiency() > midfielderLast.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.M)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) + .collect(Collectors.toList())); + players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.A)).filter(p -> p.getQuotation() <= budgetPotential) + .filter(p -> p.getEfficiency() > attackerLast.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.A)) + .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) + .collect(Collectors.toList())); + + if (!players2buy.isEmpty()) { + LOG.info("Player(s) to buy (3 best choice by line):"); + AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE); + for (Player player : players2buy) { + org.blondin.mpg.out.model.Player outPlayer = outPlayersClient.getPlayer(championship, player.getName(), + PositionWrapper.toOut(player.getPosition()), player.getClubName(), OutType.INJURY_GREEN); + String s = player.getName(); + if (outPlayer != null) { + s += String.format(" (%s - %s - %s)", outPlayer.getOutType(), outPlayer.getDescription(), outPlayer.getLength()); + } + AT_Row row = at.addRow(player.getPosition(), s, FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), player.getQuotation()); + setTableFormatRowPaddingSpace(row); + row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); + } + at.addRule(); + String render = at.render(); + LOG.info(render); + } else { + LOG.info("No better players to buy, sorry."); + } + + } + + static List removeOutPlayers(List players, InjuredSuspendedWrapperClient outPlayersClient, ChampionshipOutType championship, + boolean displayOut) { + List outPlayers = new ArrayList<>(); + for (Player player : players) { + org.blondin.mpg.out.model.Player outPlayer = outPlayersClient.getPlayer(championship, player.getName(), + PositionWrapper.toOut(player.getPosition()), player.getClubName(), OutType.INJURY_GREEN); + if (outPlayer != null) { + outPlayers.add(player); + if (displayOut) { + String eff = FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()); + LOG.info("Out: {} ({} - Eff.:{} / Q.:{} / Paid:{}) - {} - {} - {}", player.getName(), player.getPosition(), eff, + player.getQuotation(), player.getPricePaid(), outPlayer.getOutType(), outPlayer.getDescription(), outPlayer.getLength()); + } + } + } + players.removeAll(outPlayers); + + // Check if some goalkeeper(s) always on pitch + List goals = players.stream().filter(p -> Position.G.equals(p.getPosition())).collect(Collectors.toList()); + if (goals.isEmpty()) { + LOG.warn("\nWARNING: All goalkeeper(s) are injured/absent, so maintained on the pitch!"); + players.addAll(outPlayers.stream().filter(p -> Position.G.equals(p.getPosition())).collect(Collectors.toList())); + } + + return players; + } + + private static void writeTeamOptimized(List players, boolean isDebug) { + LOG.info("\nOptimized team:"); + AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE); + Position lp = Position.G; + for (Player player : players) { + // Write position separator + if (!player.getPosition().equals(lp)) { + lp = player.getPosition(); + at.addRule(); + } + String playerName = player.getName(); + if (isDebug) { + playerName += " (" + player.getId() + ")"; + } + AT_Row row = at.addRow(player.getPosition(), playerName, FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), player.getQuotation()); + setTableFormatRowPaddingSpace(row); + row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); + } + at.addRule(); + String render = at.render(); + LOG.info(render); + } + + private static CoachRequest getCoachRequest(Team team, Coach coach, List players, int gameRemaining, Config config) { + int nbrAttackers = coach.getComposition() % 10; + int nbrMidfielders = coach.getComposition() % 100 / 10; + int nbrDefenders = coach.getComposition() / 100; + + CoachRequest request = new CoachRequest(coach.getComposition()); + + // Goals + List goals = players.stream().filter(p -> p.getPosition().equals(Position.G)).collect(Collectors.toList()); + if (!goals.isEmpty()) { + request.getPlayersOnPitch().setPlayer(1, goals.get(0).getId()); + if (goals.size() > 1) { + request.getPlayersOnPitch().setPlayer(18, goals.get(1).getId()); + } + } + + List defenders = players.stream().filter(p -> p.getPosition().equals(Position.D)).collect(Collectors.toList()); + List midfielders = players.stream().filter(p -> p.getPosition().equals(Position.M)).collect(Collectors.toList()); + List attackers = players.stream().filter(p -> p.getPosition().equals(Position.A)).collect(Collectors.toList()); + + String playerIdForBonus = midfielders.get(0).getId(); + request.setBonusSelected(selectBonus(coach.getBonusSelected(), team.getBonuses(), gameRemaining, config.isUseBonus(), playerIdForBonus)); + String playerIdForCaptain = request.getBonusSelected() != null + && SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(request.getBonusSelected().getName()) + && !midfielders.get(1).getId().equals(request.getBonusSelected().getPlayerId()) ? midfielders.get(1).getId() : playerIdForBonus; + request.setCaptain(selectCapatain(coach.getCaptain(), playerIdForCaptain, playerIdForBonus, config.isUseBonus())); + + // Main lines + setPlayersOnPitch(request, defenders, nbrDefenders, 1); + setPlayersOnPitch(request, midfielders, nbrMidfielders, 1 + nbrDefenders); + setPlayersOnPitch(request, attackers, nbrAttackers, 1 + nbrDefenders + nbrMidfielders); + + // Substitutes + int substitutes = 0; + substitutes += setPlayersOnPitch(request, defenders, 2, 11); + substitutes += setPlayersOnPitch(request, midfielders, 2, 13); + substitutes += setPlayersOnPitch(request, attackers, 2, 15); + + // Tactical Substitutes (x5) + if (config.isTacticalSubstitutes()) { + setTacticalSubstitute(request, 12, 1 + nbrDefenders, config.getNoteTacticalSubstituteDefender()); + setTacticalSubstitute(request, 14, 1 + nbrDefenders + nbrMidfielders, config.getNoteTacticalSubstituteMidfielder()); + setTacticalSubstitute(request, 15, nbrDefenders + nbrMidfielders, config.getNoteTacticalSubstituteMidfielder()); + setTacticalSubstitute(request, 16, 1 + nbrDefenders + nbrMidfielders + nbrAttackers, config.getNoteTacticalSubstituteAttacker()); + setTacticalSubstitute(request, 17, nbrDefenders + nbrMidfielders + nbrAttackers, config.getNoteTacticalSubstituteAttacker()); + } + + // No blank on substitutes' bench + if (substitutes < 6) { + List playersRemaining = new ArrayList<>(); + playersRemaining.addAll(defenders); + playersRemaining.addAll(midfielders); + playersRemaining.addAll(attackers); + Collections.sort(playersRemaining, Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()); + for (int i = 12; i <= 17; i++) { + if (playersRemaining.isEmpty()) { + break; + } + if (StringUtils.isBlank(request.getPlayersOnPitch().getPlayer(i))) { + request.getPlayersOnPitch().setPlayer(i, playersRemaining.remove(0).getId()); // NOSONAR : Pop first list item + } + } + } + + // If Bonus is player power up (boostOnePlayer), verify that player is on pitch, override otherwise + verifyBonusPlayersOverrideOnPitch(request, playerIdForBonus, playerIdForCaptain); + + return request; + } + + static void verifyBonusPlayersOverrideOnPitch(CoachRequest request, String playerIdForBonusEnforced, String playerIdForCaptainEnforced) { + // Bonus + if (request.getBonusSelected() != null && SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(request.getBonusSelected().getName()) + && !verifyPlayerOnPitch(request.getPlayersOnPitch(), request.getBonusSelected().getPlayerId())) { + request.getBonusSelected().setPlayerId(playerIdForBonusEnforced); + } + + // Captain + if (StringUtils.isNotBlank(request.getCaptain()) && !verifyPlayerOnPitch(request.getPlayersOnPitch(), request.getCaptain())) { + request.setCaptain(playerIdForCaptainEnforced); + } + } + + static boolean verifyPlayerOnPitch(PlayersOnPitch playersOnPitch, String playerId) { + if (StringUtils.isBlank(playerId)) { + return false; + } + for (int i = 1; i <= 11; i++) { + if (playerId.equals(playersOnPitch.getPlayer(i))) { + return true; + } + } + return false; + } + + static String selectCapatain(String previousCaptain, String captainIdIfNeeded, String currentPlayerBonus, boolean useBonus) { + if (!useBonus || (StringUtils.isNotBlank(previousCaptain) && !previousCaptain.equals(currentPlayerBonus))) { + return previousCaptain; + } + return captainIdIfNeeded; + } + + static SelectedBonus selectBonus(SelectedBonus previousBonus, Map bonuses, int matchsRemaining, boolean useBonus, + String playerIdIfNeeded) { + if (!useBonus || (previousBonus != null && previousBonus.getName() != null)) { + return previousBonus; + } + if (bonuses == null) { + throw new UnsupportedOperationException("Bonus is null, technical problem"); + } + + // Remove decat bonus, not supported (https://github.com/axel3rd/mpg-coach-bot/issues/234) + bonuses.remove("fourStrikers"); + + SelectedBonus bonusSelected = null; + if (bonuses.values().stream().reduce(0, Integer::sum) >= matchsRemaining) { + String bonus = getBestBonus(bonuses, matchsRemaining); + bonusSelected = new SelectedBonus(); + bonusSelected.setName(bonus); + if (SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(bonus)) { + bonusSelected.setPlayerId(playerIdIfNeeded); + } + } + return bonusSelected; + } + + private static String getBestBonus(Map bonuses, int matchsRemaining) { + if (matchsRemaining == 0) { + throw new UnsupportedOperationException("0 match remaining, using this method is not logic, bug in algorithm !"); + } + int bonusTooMuch = bonuses.values().stream().reduce(0, Integer::sum) - matchsRemaining; + List bonusLowerPriority = SelectedBonus.getBonusPriority().stream().collect(Collectors.toList()); + Collections.reverse(bonusLowerPriority); + for (String b : bonusLowerPriority) { + for (int bi = 0; bi < bonuses.get(b); bi++) { + if (bonusTooMuch > 0) { + bonusTooMuch--; + } else { + return b; + } + } + } + throw new UnsupportedOperationException("Bonus cannot be null here, bug in selection algorithm !"); + } + + private static int setPlayersOnPitch(CoachRequest request, List players, int number, int index) { + int setted = 0; + for (int i = 0; i < number; i++) { + if (!players.isEmpty()) { + request.getPlayersOnPitch().setPlayer(index + i + 1, players.remove(0).getId()); // NOSONAR : Pop first list item + setted++; + } + } + return setted; + } + + private static void setTacticalSubstitute(CoachRequest request, int playerIdSubstitutePosition, int playerIdStartPosition, float rating) { + String playerIdSubstitute = request.getPlayersOnPitch().getPlayer(playerIdSubstitutePosition); + String playerIdStart = request.getPlayersOnPitch().getPlayer(playerIdStartPosition); + if (StringUtils.isBlank(playerIdSubstitute) || StringUtils.isBlank(playerIdStart)) { + return; + } + request.getTacticalSubstitutes().add(new TacticalSubstitute(playerIdSubstitute, playerIdStart, rating)); + } +} diff --git a/src/main/java/org/blondin/mpg/Main.java b/src/main/java/org/blondin/mpg/Main.java index 2eb8953..d57bd8d 100644 --- a/src/main/java/org/blondin/mpg/Main.java +++ b/src/main/java/org/blondin/mpg/Main.java @@ -1,61 +1,18 @@ package org.blondin.mpg; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; - import org.apache.commons.lang3.StringUtils; import org.blondin.mpg.config.Config; -import org.blondin.mpg.out.ChampionshipOutType; import org.blondin.mpg.out.InjuredSuspendedWrapperClient; -import org.blondin.mpg.out.model.OutType; import org.blondin.mpg.root.MpgClient; -import org.blondin.mpg.root.exception.NoMoreGamesException; -import org.blondin.mpg.root.exception.PlayerNotFoundException; import org.blondin.mpg.root.model.ChampionshipType; -import org.blondin.mpg.root.model.Club; -import org.blondin.mpg.root.model.Clubs; -import org.blondin.mpg.root.model.Coach; -import org.blondin.mpg.root.model.CoachRequest; -import org.blondin.mpg.root.model.Division; import org.blondin.mpg.root.model.League; import org.blondin.mpg.root.model.LeagueStatus; -import org.blondin.mpg.root.model.Mode; -import org.blondin.mpg.root.model.Player; -import org.blondin.mpg.root.model.PlayersOnPitch; -import org.blondin.mpg.root.model.PoolPlayers; -import org.blondin.mpg.root.model.Position; -import org.blondin.mpg.root.model.SelectedBonus; -import org.blondin.mpg.root.model.TacticalSubstitute; -import org.blondin.mpg.root.model.Team; -import org.blondin.mpg.stats.ChampionshipStatsType; import org.blondin.mpg.stats.MpgStatsClient; -import org.blondin.mpg.stats.model.CurrentDay; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.vandermeer.asciitable.AT_Cell; -import de.vandermeer.asciitable.AT_Row; -import de.vandermeer.asciitable.AsciiTable; -import de.vandermeer.asciitable.CWC_LongestLine; -import de.vandermeer.asciithemes.a7.A7_Grids; -import de.vandermeer.skb.interfaces.transformers.textformat.TextAlignment; - public class Main { - private static final String TABLE_POSITION = "P"; - private static final String TABLE_PLAYER_NAME = "Player name"; - private static final String TABLE_QUOTE = "Q."; - private static final String TABLE_EFFICIENCY = "Eff."; - - private static final DecimalFormat FORMAT_DECIMAL_DOUBLE = new DecimalFormat("0.00"); - private static final Logger LOG = LoggerFactory.getLogger(Main.class); public static void main(String[] args) { // NOSONAR : args used as file option, no security issue @@ -101,7 +58,7 @@ static void processLeague(League league, ApiClients apiClients, Config config) { break; case CREATION: case UNKNOWN: - processMercatoChampionship(league, apiClients, config); + new MercatoChampionship().process(league, apiClients, config); break; case MERCATO: if (league.getCurrentTeamStatus() == 2) { @@ -112,581 +69,16 @@ static void processLeague(League league, ApiClients apiClients, Config config) { LOG.info("\nMercato will be ending, ready for your first match ?\n"); return; } - processMercatoLeague(league, apiClients, config); + new MercatoLeague().process(league, apiClients, config); break; case GAMES: if (league.isLive() && StringUtils.isBlank(league.getNextRealGameWeekDate())) { LOG.info("\nThis is the last live day, no next week.\n"); return; } - processGames(league, apiClients, config); + new Games().process(league, apiClients, config); break; } } - static void processMercatoLeague(League league, ApiClients apiClients, Config config) { - LOG.info("\nProposal for your mercato:\n"); - List players = apiClients.getMpg().getAvailablePlayers(league.getDivisionId()).getList(); - completePlayersClub(players, apiClients.getMpg().getClubs()); - completeAuctionAndcalculateEfficiency(players, apiClients.getStats(), ChampionshipTypeWrapper.toStats(league.getChampionship()), config, - false, true); - processMercato(players, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship())); - } - - static void processMercatoChampionship(League league, ApiClients apiClients, Config config) { - LOG.info("\nProposal for your coming soon mercato:\n"); - List players = apiClients.getMpg().getPoolPlayers(league.getChampionship()).getPlayers(); - completePlayersClub(players, apiClients.getMpg().getClubs()); - completeAuctionAndcalculateEfficiency(players, apiClients.getStats(), ChampionshipTypeWrapper.toStats(league.getChampionship()), config, - false, true); - processMercato(players, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship())); - } - - static void processMercato(List players, InjuredSuspendedWrapperClient outPlayersClient, ChampionshipOutType championship) { - Collections.sort(players, - Comparator.comparing(Player::getPosition).thenComparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()); - List goals = players.stream().filter(p -> p.getPosition().equals(Position.G)).collect(Collectors.toList()).subList(0, 5); - List defenders = players.stream().filter(p -> p.getPosition().equals(Position.D)).collect(Collectors.toList()).subList(0, 10); - List midfielders = players.stream().filter(p -> p.getPosition().equals(Position.M)).collect(Collectors.toList()).subList(0, 10); - List attackers = players.stream().filter(p -> p.getPosition().equals(Position.A)).collect(Collectors.toList()).subList(0, 10); - - AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE, "Auct.", "Out info"); - for (List line : Arrays.asList(goals, defenders, midfielders, attackers)) { - for (Player player : line) { - org.blondin.mpg.out.model.Player outPlayer = outPlayersClient.getPlayer(championship, player.getName(), - PositionWrapper.toOut(player.getPosition()), player.getClubName(), OutType.INJURY_GREEN); - String outInfos = ""; - if (outPlayer != null) { - outInfos = String.format("%s - %s - %s", outPlayer.getOutType(), outPlayer.getDescription(), outPlayer.getLength()); - } - AT_Row row = at.addRow(player.getPosition(), player.getName(), FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), - player.getQuotation(), player.getAuction(), outInfos); - setTableFormatRowPaddingSpace(row); - row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); - row.getCells().get(3).getContext().setTextAlignment(TextAlignment.RIGHT); - row.getCells().get(4).getContext().setTextAlignment(TextAlignment.RIGHT); - } - at.addRule(); - } - - String render = at.render(); - LOG.info(render); - LOG.info(""); - } - - static void processGames(League league, ApiClients apiClients, Config config) { - try { - - // Get main bean for current league - Division division = apiClients.getMpg().getDivision(league.getDivisionId()); - Team team = apiClients.getMpg().getTeam(division.getTeam(apiClients.getMpg().getUserId())); - PoolPlayers pool = apiClients.getMpg().getPoolPlayers(league.getChampionship()); - Coach coach = apiClients.getMpg().getCoach(league.getDivisionId()); - completePlayersClub(pool.getPlayers(), apiClients.getMpg().getClubs()); - completePlayersTeam(team.getSquad(), pool); - List players = team.getSquad().values().stream().collect(Collectors.toList()); - - // Complete auction and calculate efficiency (notes should be in injured players display), and save for transactions proposal - completeAuctionAndcalculateEfficiency(players, apiClients.getStats(), ChampionshipTypeWrapper.toStats(league.getChampionship()), config, - false, true); - List playersTeam = players.stream().collect(Collectors.toList()); - - // Remove out players (and write them) - removeOutPlayers(players, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship()), true); - - // Sort by efficiency - Collections.sort(players, - Comparator.comparing(Player::getPosition).thenComparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()); - - // Write optimized team - writeTeamOptimized(players, config.isDebug()); - - // Auto-update team - if (config.isTeampUpdate()) { - updateTeamWithRetry(apiClients.getMpg(), division, team, coach, players, pool, config); - } - - if (config.isTransactionsProposal()) { - if (Mode.NORMAL.equals(league.getMode())) { - LOG.info( - "\nTransaction proposals can not be achieved, you should buy 'MPG expert mode' for this league (very fun, not expensive!)"); - } else { - LOG.info("\nTransactions proposal ..."); - CurrentDay cd = apiClients.getStats().getStats(ChampionshipTypeWrapper.toStats(league.getChampionship())).getInfos() - .getAnnualStats().getCurrentDay(); - if (!cd.isStatsDayReached()) { - LOG.info("\nWARNING: Last day stats have not fully reached! Please retry tomorrow"); - } - List playersAvailable = apiClients.getMpg().getAvailablePlayers(league.getDivisionId()).getList(); - completePlayersClub(playersAvailable, apiClients.getMpg().getClubs()); - removeOutPlayers(playersAvailable, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship()), false); - completeAuctionAndcalculateEfficiency(playersAvailable, apiClients.getStats(), - ChampionshipTypeWrapper.toStats(league.getChampionship()), config, false, false); - - Integer currentPlayersBuy = team.getBids().stream().map(Player::getPricePaid).collect(Collectors.summingInt(Integer::intValue)); - writeTransactionsProposal(cd.getDay(), playersTeam, playersAvailable, team.getBudget() - currentPlayersBuy, - apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship()), config); - } - } - } catch (NoMoreGamesException e) { - LOG.info("\nNo more games in this league ...\n"); - } - LOG.info(""); - } - - /** - * Add club name for all pool players - * - * @param players Pool - * @param clubs Clubs - */ - static void completePlayersClub(List players, Clubs clubs) { - for (Player player : players) { - Club club = clubs.getChampionshipClubs().get(player.getClubId()); - if (club == null) { - throw new UnsupportedOperationException( - String.format("Club '%s' cannot be found for player '%s'", player.getClubId(), player.getName())); - } - player.setClubName(club.getName()); - } - } - - /** - * Teams players is only id and price paid => replace by real player - * - * @param teamPlayers teams - * @param pool players - */ - static void completePlayersTeam(Map teamPlayers, PoolPlayers pool) { - List players2Remove = new ArrayList<>(); - for (Entry entry : teamPlayers.entrySet()) { - try { - Player player = pool.getPlayer(entry.getKey()); - player.setPricePaid(teamPlayers.get(entry.getKey()).getPricePaid()); - teamPlayers.put(entry.getKey(), player); - } catch (PlayerNotFoundException e) { - LOG.warn("Some player in your team removed because doesn't exist in league pool players: {}", entry.getKey()); - players2Remove.add(entry.getKey()); - } - } - for (String p2r : players2Remove) { - teamPlayers.remove(p2r); - } - } - - private static void updateTeamWithRetry(MpgClient mpgClient, Division division, Team team, Coach coach, List players, PoolPlayers pool, - Config config) { - LOG.info("\nUpdating team ..."); - CoachRequest request = getCoachRequest(team, coach, players, division.getGameRemaining(), config); - if (StringUtils.isNotBlank(request.getCaptain())) { - LOG.info(" Captain: {}", pool.getPlayer(request.getCaptain()).getName()); - } - if (request.getBonusSelected() != null && StringUtils.isNotBlank(request.getBonusSelected().getName())) { - String playerPotential = ""; - if (SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(request.getBonusSelected().getName())) { - playerPotential = "(" + pool.getPlayer(request.getBonusSelected().getPlayerId()).getName() + ")"; - } - LOG.info(" Bonus : {} {}", request.getBonusSelected().getName(), playerPotential); - } - final long maxRetry = 10; - for (int i = 1; i <= 10; i++) { - try { - mpgClient.updateCoach(coach.getIdMatch(), request); - break; - } catch (UnsupportedOperationException e) { - if (i == maxRetry || !"Unsupported status code: 400 Bad Request / Content: {\"error\":\"badRequest\"}".equals(e.getMessage())) { - throw e; - } - try { - LOG.info("Retrying Team update..."); - Thread.sleep(5000); - } catch (InterruptedException e1) { // NOSONAR : Sleep wanted - throw new UnsupportedOperationException(e1); - } - } - } - } - - private static void writeTransactionsProposal(int currentDay, List playersTeam, List playersAvailable, int budget, - InjuredSuspendedWrapperClient outPlayersClient, ChampionshipOutType championship, Config config) { - - // Players with bad efficiency - List players2Sell = playersTeam.stream().filter(p -> p.getEfficiency() <= config.getEfficiencySell(p.getPosition())) - .collect(Collectors.toList()); - - // Remove goalkeeper(s) (if exist) and same team as the first - List goalkeepers = playersTeam.stream().filter(p -> p.getPosition().equals(Position.G)) - .sorted(Comparator.comparing(Player::getEfficiency).reversed()).collect(Collectors.toList()); - final Player goalFirst = goalkeepers.isEmpty() ? new Player() : goalkeepers.get(0); - if (!goalkeepers.isEmpty()) { - players2Sell.removeIf(p -> p.getPosition().equals(Position.G) && p.getClubId().equals(goalkeepers.get(0).getClubId())); - } - - int cash = budget; - if (currentDay > 2 && !players2Sell.isEmpty()) { - LOG.info("Players to sell (initial cash: {}):", budget); - AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE); - for (Player player : players2Sell) { - cash += player.getQuotation(); - AT_Row row = at.addRow(player.getPosition(), player.getName(), FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), - player.getQuotation()); - setTableFormatRowPaddingSpace(row); - row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); - } - at.addRule(); - String render = at.render(); - LOG.info(render); - } - LOG.info("Budget: {}", cash); - - Player defenderLast = playersTeam.stream().filter(p -> p.getPosition().equals(Position.D)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation)).collect(Collectors.toList()).get(0); - Player midfielderLast = playersTeam.stream().filter(p -> p.getPosition().equals(Position.M)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation)).collect(Collectors.toList()).get(0); - Player attackerLast = playersTeam.stream().filter(p -> p.getPosition().equals(Position.A)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation)).collect(Collectors.toList()).get(0); - cash += defenderLast.getQuotation() + midfielderLast.getQuotation() + attackerLast.getQuotation(); - LOG.info("Budget if last field players by line sold: {}", cash); - - final int budgetPotential = cash; - List players2buy = new ArrayList<>(); - players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.G)).filter(p -> p.getQuotation() <= budgetPotential) - .filter(p -> p.getEfficiency() > goalFirst.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.G)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) - .collect(Collectors.toList())); - players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.D)).filter(p -> p.getQuotation() <= budgetPotential) - .filter(p -> p.getEfficiency() > defenderLast.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.D)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) - .collect(Collectors.toList())); - players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.M)).filter(p -> p.getQuotation() <= budgetPotential) - .filter(p -> p.getEfficiency() > midfielderLast.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.M)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) - .collect(Collectors.toList())); - players2buy.addAll(playersAvailable.stream().filter(p -> p.getPosition().equals(Position.A)).filter(p -> p.getQuotation() <= budgetPotential) - .filter(p -> p.getEfficiency() > attackerLast.getEfficiency()).filter(p -> p.getEfficiency() > config.getEfficiencySell(Position.A)) - .sorted(Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()).limit(3) - .collect(Collectors.toList())); - - if (!players2buy.isEmpty()) { - LOG.info("Player(s) to buy (3 best choice by line):"); - AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE); - for (Player player : players2buy) { - org.blondin.mpg.out.model.Player outPlayer = outPlayersClient.getPlayer(championship, player.getName(), - PositionWrapper.toOut(player.getPosition()), player.getClubName(), OutType.INJURY_GREEN); - String s = player.getName(); - if (outPlayer != null) { - s += String.format(" (%s - %s - %s)", outPlayer.getOutType(), outPlayer.getDescription(), outPlayer.getLength()); - } - AT_Row row = at.addRow(player.getPosition(), s, FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), player.getQuotation()); - setTableFormatRowPaddingSpace(row); - row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); - } - at.addRule(); - String render = at.render(); - LOG.info(render); - } else { - LOG.info("No better players to buy, sorry."); - } - - } - - static List removeOutPlayers(List players, InjuredSuspendedWrapperClient outPlayersClient, ChampionshipOutType championship, - boolean displayOut) { - List outPlayers = new ArrayList<>(); - for (Player player : players) { - org.blondin.mpg.out.model.Player outPlayer = outPlayersClient.getPlayer(championship, player.getName(), - PositionWrapper.toOut(player.getPosition()), player.getClubName(), OutType.INJURY_GREEN); - if (outPlayer != null) { - outPlayers.add(player); - if (displayOut) { - String eff = FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()); - LOG.info("Out: {} ({} - Eff.:{} / Q.:{} / Paid:{}) - {} - {} - {}", player.getName(), player.getPosition(), eff, - player.getQuotation(), player.getPricePaid(), outPlayer.getOutType(), outPlayer.getDescription(), outPlayer.getLength()); - } - } - } - players.removeAll(outPlayers); - - // Check if some goalkeeper(s) always on pitch - List goals = players.stream().filter(p -> Position.G.equals(p.getPosition())).collect(Collectors.toList()); - if (goals.isEmpty()) { - LOG.warn("\nWARNING: All goalkeeper(s) are injured/absent, so maintained on the pitch!"); - players.addAll(outPlayers.stream().filter(p -> Position.G.equals(p.getPosition())).collect(Collectors.toList())); - } - - return players; - } - - private static void writeTeamOptimized(List players, boolean isDebug) { - LOG.info("\nOptimized team:"); - AsciiTable at = getTable(TABLE_POSITION, TABLE_PLAYER_NAME, TABLE_EFFICIENCY, TABLE_QUOTE); - Position lp = Position.G; - for (Player player : players) { - // Write position separator - if (!player.getPosition().equals(lp)) { - lp = player.getPosition(); - at.addRule(); - } - String playerName = player.getName(); - if (isDebug) { - playerName += " (" + player.getId() + ")"; - } - AT_Row row = at.addRow(player.getPosition(), playerName, FORMAT_DECIMAL_DOUBLE.format(player.getEfficiency()), player.getQuotation()); - setTableFormatRowPaddingSpace(row); - row.getCells().get(2).getContext().setTextAlignment(TextAlignment.RIGHT); - } - at.addRule(); - String render = at.render(); - LOG.info(render); - } - - private static List completeAuctionAndcalculateEfficiency(List players, MpgStatsClient stats, ChampionshipStatsType championship, - Config config, boolean failIfPlayerNotFound, boolean logWarnIfPlayerNotFound) { - - // Pre-process efficiencies - calculateEfficiencies(stats, championship, config); - - // Fill MPG model - for (Player player : players) { - try { - org.blondin.mpg.stats.model.Player p = stats.getStats(championship).getPlayer(player.getName()); - if (p.getAuction() != null && p.getAuction().getNumber() > 5) { - // Feature in API only since 2021-11 - player.setAuction(p.getAuction().getAverage()); - } - if (player.getAuction() == 0 && p.getAuctionLong() != null) { - // Feature in API only since 2022-07 - player.setAuction(p.getAuctionLong().getAverage()); - } - player.setEfficiency(p.getEfficiency()); - } catch (PlayerNotFoundException e) { - if (failIfPlayerNotFound) { - throw e; - } - if (logWarnIfPlayerNotFound) { - LOG.warn("WARN: Player can't be found in statistics: {}", player.getName()); - } - player.setEfficiency(0); - } - } - return players; - } - - private static void calculateEfficiencies(MpgStatsClient stats, ChampionshipStatsType championship, Config config) { - int daysPeriod = getCurrentDay(stats, championship); - int days4efficiency = 0; - if (config.isEfficiencyRecentFocus() && stats.getStats(championship).getInfos().getAnnualStats().getCurrentDay().getDayReached() > 0) { - days4efficiency = config.getEfficiencyRecentDays(); - // If season start (=> daysPeriod < 8 when days4efficiency = 8 by default), the focus is on the started days - if (daysPeriod < days4efficiency) { - days4efficiency = daysPeriod; - } else { - daysPeriod = days4efficiency; - } - } - for (org.blondin.mpg.stats.model.Player p : stats.getStats(championship).getPlayers()) { - double efficiency = p.getStats().getMatchs(days4efficiency) / (double) daysPeriod * p.getStats().getAverage(days4efficiency) - * (1 + p.getStats().getGoals(days4efficiency) * config.getEfficiencyCoefficient(PositionWrapper.fromStats(p.getPosition()))); - // round efficiency to 2 decimals - p.setEfficiency(efficiency); - } - } - - private static int getCurrentDay(MpgStatsClient stats, ChampionshipStatsType championship) { - int daysPeriod = stats.getStats(championship).getInfos().getAnnualStats().getCurrentDay().getDayReached(); - // If league not started, we take the number of day of season, because average will be on this period - if (daysPeriod == 0) { - // The previous season statistics could be null, in this case current annual max day is used - daysPeriod = stats.getStats(championship).getInfos().getLastStats() == null - ? stats.getStats(championship).getInfos().getAnnualStats().getMaxDay() - : stats.getStats(championship).getInfos().getLastStats().getMaxDay(); - } - return daysPeriod; - } - - private static CoachRequest getCoachRequest(Team team, Coach coach, List players, int gameRemaining, Config config) { - int nbrAttackers = coach.getComposition() % 10; - int nbrMidfielders = coach.getComposition() % 100 / 10; - int nbrDefenders = coach.getComposition() / 100; - - CoachRequest request = new CoachRequest(coach.getComposition()); - - // Goals - List goals = players.stream().filter(p -> p.getPosition().equals(Position.G)).collect(Collectors.toList()); - if (!goals.isEmpty()) { - request.getPlayersOnPitch().setPlayer(1, goals.get(0).getId()); - if (goals.size() > 1) { - request.getPlayersOnPitch().setPlayer(18, goals.get(1).getId()); - } - } - - List defenders = players.stream().filter(p -> p.getPosition().equals(Position.D)).collect(Collectors.toList()); - List midfielders = players.stream().filter(p -> p.getPosition().equals(Position.M)).collect(Collectors.toList()); - List attackers = players.stream().filter(p -> p.getPosition().equals(Position.A)).collect(Collectors.toList()); - - String playerIdForBonus = midfielders.get(0).getId(); - request.setBonusSelected(selectBonus(coach.getBonusSelected(), team.getBonuses(), gameRemaining, config.isUseBonus(), playerIdForBonus)); - String playerIdForCaptain = request.getBonusSelected() != null - && SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(request.getBonusSelected().getName()) - && !midfielders.get(1).getId().equals(request.getBonusSelected().getPlayerId()) ? midfielders.get(1).getId() : playerIdForBonus; - request.setCaptain(selectCapatain(coach.getCaptain(), playerIdForCaptain, playerIdForBonus, config.isUseBonus())); - - // Main lines - setPlayersOnPitch(request, defenders, nbrDefenders, 1); - setPlayersOnPitch(request, midfielders, nbrMidfielders, 1 + nbrDefenders); - setPlayersOnPitch(request, attackers, nbrAttackers, 1 + nbrDefenders + nbrMidfielders); - - // Substitutes - int substitutes = 0; - substitutes += setPlayersOnPitch(request, defenders, 2, 11); - substitutes += setPlayersOnPitch(request, midfielders, 2, 13); - substitutes += setPlayersOnPitch(request, attackers, 2, 15); - - // Tactical Substitutes (x5) - if (config.isTacticalSubstitutes()) { - setTacticalSubstitute(request, 12, 1 + nbrDefenders, config.getNoteTacticalSubstituteDefender()); - setTacticalSubstitute(request, 14, 1 + nbrDefenders + nbrMidfielders, config.getNoteTacticalSubstituteMidfielder()); - setTacticalSubstitute(request, 15, nbrDefenders + nbrMidfielders, config.getNoteTacticalSubstituteMidfielder()); - setTacticalSubstitute(request, 16, 1 + nbrDefenders + nbrMidfielders + nbrAttackers, config.getNoteTacticalSubstituteAttacker()); - setTacticalSubstitute(request, 17, nbrDefenders + nbrMidfielders + nbrAttackers, config.getNoteTacticalSubstituteAttacker()); - } - - // No blank on substitutes' bench - if (substitutes < 6) { - List playersRemaining = new ArrayList<>(); - playersRemaining.addAll(defenders); - playersRemaining.addAll(midfielders); - playersRemaining.addAll(attackers); - Collections.sort(playersRemaining, Comparator.comparing(Player::getEfficiency).thenComparing(Player::getQuotation).reversed()); - for (int i = 12; i <= 17; i++) { - if (playersRemaining.isEmpty()) { - break; - } - if (StringUtils.isBlank(request.getPlayersOnPitch().getPlayer(i))) { - request.getPlayersOnPitch().setPlayer(i, playersRemaining.remove(0).getId()); // NOSONAR : Pop first list item - } - } - } - - // If Bonus is player power up (boostOnePlayer), verify that player is on pitch, override otherwise - verifyBonusPlayersOverrideOnPitch(request, playerIdForBonus, playerIdForCaptain); - - return request; - } - - static void verifyBonusPlayersOverrideOnPitch(CoachRequest request, String playerIdForBonusEnforced, String playerIdForCaptainEnforced) { - // Bonus - if (request.getBonusSelected() != null && SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(request.getBonusSelected().getName()) - && !verifyPlayerOnPitch(request.getPlayersOnPitch(), request.getBonusSelected().getPlayerId())) { - request.getBonusSelected().setPlayerId(playerIdForBonusEnforced); - } - - // Captain - if (StringUtils.isNotBlank(request.getCaptain()) && !verifyPlayerOnPitch(request.getPlayersOnPitch(), request.getCaptain())) { - request.setCaptain(playerIdForCaptainEnforced); - } - } - - static boolean verifyPlayerOnPitch(PlayersOnPitch playersOnPitch, String playerId) { - if (StringUtils.isBlank(playerId)) { - return false; - } - for (int i = 1; i <= 11; i++) { - if (playerId.equals(playersOnPitch.getPlayer(i))) { - return true; - } - } - return false; - } - - static String selectCapatain(String previousCaptain, String captainIdIfNeeded, String currentPlayerBonus, boolean useBonus) { - if (!useBonus || (StringUtils.isNotBlank(previousCaptain) && !previousCaptain.equals(currentPlayerBonus))) { - return previousCaptain; - } - return captainIdIfNeeded; - } - - static SelectedBonus selectBonus(SelectedBonus previousBonus, Map bonuses, int matchsRemaining, boolean useBonus, - String playerIdIfNeeded) { - if (!useBonus || (previousBonus != null && previousBonus.getName() != null)) { - return previousBonus; - } - if (bonuses == null) { - throw new UnsupportedOperationException("Bonus is null, technical problem"); - } - - // Remove decat bonus, not supported (https://github.com/axel3rd/mpg-coach-bot/issues/234) - bonuses.remove("fourStrikers"); - - SelectedBonus bonusSelected = null; - if (bonuses.values().stream().reduce(0, Integer::sum) >= matchsRemaining) { - String bonus = getBestBonus(bonuses, matchsRemaining); - bonusSelected = new SelectedBonus(); - bonusSelected.setName(bonus); - if (SelectedBonus.BONUS_BOOT_ONE_PLAYER.equals(bonus)) { - bonusSelected.setPlayerId(playerIdIfNeeded); - } - } - return bonusSelected; - } - - private static String getBestBonus(Map bonuses, int matchsRemaining) { - if (matchsRemaining == 0) { - throw new UnsupportedOperationException("0 match remaining, using this method is not logic, bug in algorithm !"); - } - int bonusTooMuch = bonuses.values().stream().reduce(0, Integer::sum) - matchsRemaining; - List bonusLowerPriority = SelectedBonus.getBonusPriority().stream().collect(Collectors.toList()); - Collections.reverse(bonusLowerPriority); - for (String b : bonusLowerPriority) { - for (int bi = 0; bi < bonuses.get(b); bi++) { - if (bonusTooMuch > 0) { - bonusTooMuch--; - } else { - return b; - } - } - } - throw new UnsupportedOperationException("Bonus cannot be null here, bug in selection algorithm !"); - } - - private static int setPlayersOnPitch(CoachRequest request, List players, int number, int index) { - int setted = 0; - for (int i = 0; i < number; i++) { - if (!players.isEmpty()) { - request.getPlayersOnPitch().setPlayer(index + i + 1, players.remove(0).getId()); // NOSONAR : Pop first list item - setted++; - } - } - return setted; - } - - private static void setTacticalSubstitute(CoachRequest request, int playerIdSubstitutePosition, int playerIdStartPosition, float rating) { - String playerIdSubstitute = request.getPlayersOnPitch().getPlayer(playerIdSubstitutePosition); - String playerIdStart = request.getPlayersOnPitch().getPlayer(playerIdStartPosition); - if (StringUtils.isBlank(playerIdSubstitute) || StringUtils.isBlank(playerIdStart)) { - return; - } - request.getTacticalSubstitutes().add(new TacticalSubstitute(playerIdSubstitute, playerIdStart, rating)); - } - - private static AsciiTable getTable(Object... columnTitle) { - AsciiTable at = new AsciiTable(); - at.getContext().setGrid(A7_Grids.minusBarPlusEquals()); - at.setPaddingLeftRight(1); - at.getRenderer().setCWC(new CWC_LongestLine()); - at.addRule(); - AT_Row rowHead = at.addRow(columnTitle); - for (AT_Cell cell : rowHead.getCells()) { - cell.getContext().setTextAlignment(TextAlignment.CENTER); - } - at.addRule(); - return at; - } - - private static void setTableFormatRowPaddingSpace(AT_Row row) { - for (AT_Cell cell : row.getCells()) { - cell.getContext().setPaddingLeftRight(1); - } - } - } diff --git a/src/main/java/org/blondin/mpg/MercatoChampionship.java b/src/main/java/org/blondin/mpg/MercatoChampionship.java new file mode 100644 index 0000000..776422f --- /dev/null +++ b/src/main/java/org/blondin/mpg/MercatoChampionship.java @@ -0,0 +1,23 @@ +package org.blondin.mpg; + +import java.util.List; + +import org.blondin.mpg.config.Config; +import org.blondin.mpg.root.model.League; +import org.blondin.mpg.root.model.Player; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MercatoChampionship extends AbstractMercatoMpgProcess { + + private static final Logger LOG = LoggerFactory.getLogger(MercatoChampionship.class); + + public void process(League league, ApiClients apiClients, Config config) { + LOG.info("\nProposal for your coming soon mercato:\n"); + List players = apiClients.getMpg().getPoolPlayers(league.getChampionship()).getPlayers(); + completePlayersClub(players, apiClients.getMpg().getClubs()); + completeAuctionAndcalculateEfficiency(players, apiClients.getStats(), ChampionshipTypeWrapper.toStats(league.getChampionship()), config, + false, true); + processMercato(players, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship())); + } +} diff --git a/src/main/java/org/blondin/mpg/MercatoLeague.java b/src/main/java/org/blondin/mpg/MercatoLeague.java new file mode 100644 index 0000000..4c6e80b --- /dev/null +++ b/src/main/java/org/blondin/mpg/MercatoLeague.java @@ -0,0 +1,23 @@ +package org.blondin.mpg; + +import java.util.List; + +import org.blondin.mpg.config.Config; +import org.blondin.mpg.root.model.League; +import org.blondin.mpg.root.model.Player; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MercatoLeague extends AbstractMercatoMpgProcess { + + private static final Logger LOG = LoggerFactory.getLogger(MercatoLeague.class); + + public void process(League league, ApiClients apiClients, Config config) { + LOG.info("\nProposal for your mercato:\n"); + List players = apiClients.getMpg().getAvailablePlayers(league.getDivisionId()).getList(); + completePlayersClub(players, apiClients.getMpg().getClubs()); + completeAuctionAndcalculateEfficiency(players, apiClients.getStats(), ChampionshipTypeWrapper.toStats(league.getChampionship()), config, + false, true); + processMercato(players, apiClients.getOutPlayers(), ChampionshipTypeWrapper.toOut(league.getChampionship())); + } +} diff --git a/src/main/java/org/blondin/mpg/MpgProcess.java b/src/main/java/org/blondin/mpg/MpgProcess.java new file mode 100644 index 0000000..5156cc9 --- /dev/null +++ b/src/main/java/org/blondin/mpg/MpgProcess.java @@ -0,0 +1,9 @@ +package org.blondin.mpg; + +import org.blondin.mpg.config.Config; +import org.blondin.mpg.root.model.League; + +public interface MpgProcess { + + void process(League league, ApiClients apiClients, Config config); +} diff --git a/src/test/java/org/blondin/mpg/BonusSelectionTest.java b/src/test/java/org/blondin/mpg/BonusSelectionTest.java index 34a35c0..1dd7a76 100644 --- a/src/test/java/org/blondin/mpg/BonusSelectionTest.java +++ b/src/test/java/org/blondin/mpg/BonusSelectionTest.java @@ -47,69 +47,69 @@ private Map getBonus(int removeGoal, int boostAllPlayers, int n @Test public void testBonusCaptain() { - Assert.assertEquals(null, Main.selectCapatain(null, "newCaptain", null, false)); - Assert.assertEquals("previousCaptain", Main.selectCapatain("previousCaptain", "newCaptain", "somePlayer", false)); - Assert.assertEquals("previousCaptain", Main.selectCapatain("previousCaptain", "newCaptain", "somePlayer", true)); - Assert.assertEquals("previousCaptain", Main.selectCapatain("previousCaptain", "newCaptain", null, true)); - Assert.assertEquals("previousCaptain", Main.selectCapatain("previousCaptain", "", null, true)); - Assert.assertEquals("newCaptain", Main.selectCapatain("somePlayer", "newCaptain", "somePlayer", true)); - Assert.assertEquals("newCaptain", Main.selectCapatain(null, "newCaptain", "somePlayer", true)); - Assert.assertEquals("newCaptain", Main.selectCapatain("", "newCaptain", "somePlayer", true)); + Assert.assertEquals(null, Games.selectCapatain(null, "newCaptain", null, false)); + Assert.assertEquals("previousCaptain", Games.selectCapatain("previousCaptain", "newCaptain", "somePlayer", false)); + Assert.assertEquals("previousCaptain", Games.selectCapatain("previousCaptain", "newCaptain", "somePlayer", true)); + Assert.assertEquals("previousCaptain", Games.selectCapatain("previousCaptain", "newCaptain", null, true)); + Assert.assertEquals("previousCaptain", Games.selectCapatain("previousCaptain", "", null, true)); + Assert.assertEquals("newCaptain", Games.selectCapatain("somePlayer", "newCaptain", "somePlayer", true)); + Assert.assertEquals("newCaptain", Games.selectCapatain(null, "newCaptain", "somePlayer", true)); + Assert.assertEquals("newCaptain", Games.selectCapatain("", "newCaptain", "somePlayer", true)); } @Test public void testBonusSelection() { - Assert.assertEquals("mirror", Main.selectBonus(null, getBonus(1, 1, 2, 3, 1, 0, 1, 0), 8, true, "fake").getName()); - Assert.assertEquals("removeRandomPlayer", Main.selectBonus(null, getBonus(1, 1, 2, 3, 1, 0, 1, 0), 9, true, "fake").getName()); - Assert.assertEquals("nerfGoalkeeper", Main.selectBonus(null, getBonus(0, 0, 1, 1, 0, 0, 0, 0), 1, true, "fake").getName()); - Assert.assertEquals("boostOnePlayer", Main.selectBonus(null, getBonus(0, 0, 0, 1, 0, 0, 0, 0), 1, true, "fake").getName()); - Assert.assertEquals("fake", Main.selectBonus(null, getBonus(0, 0, 0, 1, 0, 0, 0, 0), 1, true, "fake").getPlayerId()); - Assert.assertNull(Main.selectBonus(null, getBonus(0, 1, 0, 1, 1, 1, 1, 0), 1, true, "fake").getPlayerId()); - Assert.assertNull(Main.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 10, true, "fake")); - Assert.assertNull(Main.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 15, true, "fake")); - Assert.assertEquals("boostAllPlayers", Main.selectBonus(null, getBonus(0, 1, 0, 1, 1, 1, 1, 0), 1, true, "fake").getName()); - Assert.assertEquals("removeGoal", Main.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 1, true, "fake").getName()); - Assert.assertEquals("removeRandomPlayer", Main.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 9, true, "fake").getName()); + Assert.assertEquals("mirror", Games.selectBonus(null, getBonus(1, 1, 2, 3, 1, 0, 1, 0), 8, true, "fake").getName()); + Assert.assertEquals("removeRandomPlayer", Games.selectBonus(null, getBonus(1, 1, 2, 3, 1, 0, 1, 0), 9, true, "fake").getName()); + Assert.assertEquals("nerfGoalkeeper", Games.selectBonus(null, getBonus(0, 0, 1, 1, 0, 0, 0, 0), 1, true, "fake").getName()); + Assert.assertEquals("boostOnePlayer", Games.selectBonus(null, getBonus(0, 0, 0, 1, 0, 0, 0, 0), 1, true, "fake").getName()); + Assert.assertEquals("fake", Games.selectBonus(null, getBonus(0, 0, 0, 1, 0, 0, 0, 0), 1, true, "fake").getPlayerId()); + Assert.assertNull(Games.selectBonus(null, getBonus(0, 1, 0, 1, 1, 1, 1, 0), 1, true, "fake").getPlayerId()); + Assert.assertNull(Games.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 10, true, "fake")); + Assert.assertNull(Games.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 15, true, "fake")); + Assert.assertEquals("boostAllPlayers", Games.selectBonus(null, getBonus(0, 1, 0, 1, 1, 1, 1, 0), 1, true, "fake").getName()); + Assert.assertEquals("removeGoal", Games.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 1, true, "fake").getName()); + Assert.assertEquals("removeRandomPlayer", Games.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 9, true, "fake").getName()); } @Test public void testBonusSelectionDecatNotSupported() { // Part of https://github.com/axel3rd/mpg-coach-bot/issues/234 - Assert.assertNull(Main.selectBonus(null, getBonus(0, 0, 0, 0, 0, 0, 0, 42), 1, true, "fake")); + Assert.assertNull(Games.selectBonus(null, getBonus(0, 0, 0, 0, 0, 0, 0, 42), 1, true, "fake")); } @Test public void testBonusAlreadySelected() { Assert.assertEquals("nerfGoalkeeper", - Main.selectBonus(getBonusSelected("nerfGoalkeeper"), getBonus(1, 1, 1, 3, 1, 1, 1, 0), 1, true, "fake").getName()); + Games.selectBonus(getBonusSelected("nerfGoalkeeper"), getBonus(1, 1, 1, 3, 1, 1, 1, 0), 1, true, "fake").getName()); } @Test public void testBonusSelectionNotUsed() { // No previous bonus - Assert.assertNull(Main.selectBonus(null, null, 0, false, "fake")); + Assert.assertNull(Games.selectBonus(null, null, 0, false, "fake")); // Previous bonus wallet SelectedBonus bs = getBonusSelected("removeGoal"); - Assert.assertNotNull(Main.selectBonus(bs, null, 0, false, "fake")); - Assert.assertEquals("removeGoal", Main.selectBonus(bs, null, 0, false, "fake").getName()); - Assert.assertNull(Main.selectBonus(bs, null, 0, false, "fake").getPlayerId()); + Assert.assertNotNull(Games.selectBonus(bs, null, 0, false, "fake")); + Assert.assertEquals("removeGoal", Games.selectBonus(bs, null, 0, false, "fake").getName()); + Assert.assertNull(Games.selectBonus(bs, null, 0, false, "fake").getPlayerId()); // Previous bonus redbull bs = getBonusSelected("boostOnePlayer", "foobar"); - Assert.assertNotNull(Main.selectBonus(bs, null, 0, false, "fake")); - Assert.assertEquals("boostOnePlayer", Main.selectBonus(bs, null, 0, false, "fake").getName()); - Assert.assertEquals("foobar", Main.selectBonus(bs, null, 0, false, "fake").getPlayerId()); + Assert.assertNotNull(Games.selectBonus(bs, null, 0, false, "fake")); + Assert.assertEquals("boostOnePlayer", Games.selectBonus(bs, null, 0, false, "fake").getName()); + Assert.assertEquals("foobar", Games.selectBonus(bs, null, 0, false, "fake").getPlayerId()); } @Test(expected = UnsupportedOperationException.class) public void testBonusSelectionBadInputBonus() { - Main.selectBonus(null, null, -1, true, "fake"); + Games.selectBonus(null, null, -1, true, "fake"); } @Test(expected = UnsupportedOperationException.class) public void testBonusSelection0MatchRemaining() { - Main.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 0, true, "fake"); + Games.selectBonus(null, getBonus(1, 1, 1, 3, 1, 1, 1, 0), 0, true, "fake"); } } diff --git a/src/test/java/org/blondin/mpg/MainTest.java b/src/test/java/org/blondin/mpg/MainTest.java index f4f99e0..f60d3b7 100644 --- a/src/test/java/org/blondin/mpg/MainTest.java +++ b/src/test/java/org/blondin/mpg/MainTest.java @@ -58,7 +58,9 @@ public void testRealWithBadCredentials() throws Exception { Assert.assertTrue("Bad credentials", e.getMessage().contains("Forbidden URL")); } catch (ProcessingException e) { // Proxy not configured or real URL not accessible - Assert.assertEquals("No network", "java.net.UnknownHostException: api.mpg.football", e.getMessage()); + Assert.assertTrue("No network: " + e.getMessage(), + Arrays.asList("java.net.UnknownHostException: api.mpg.football", "javax.net.ssl.SSLException: Read timed out") + .contains(e.getMessage())); } }