diff --git a/build.gradle b/build.gradle index a26c60aa..1786991a 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ dependencies { implementation 'org.jsoup:jsoup:1.15.4' implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4' implementation 'org.ocpsoft.prettytime:prettytime:5.0.6.Final' + implementation group: 'org.javatuples', name: 'javatuples', version: '1.2' // test testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.3' diff --git a/src/io/github/sammers/pla/blizzard/Achievements.java b/src/io/github/sammers/pla/blizzard/Achievements.java index 6d6b4ede..6d4e4b5c 100644 --- a/src/io/github/sammers/pla/blizzard/Achievements.java +++ b/src/io/github/sammers/pla/blizzard/Achievements.java @@ -2,34 +2,43 @@ import io.github.sammers.pla.http.JsonConvertable; import io.vertx.core.json.JsonObject; +import org.javatuples.Pair; +import java.util.HashMap; +import java.util.Map; import java.util.Set; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public record Achievements(Long totalQuantity, Long totalPoints, - Set achievements + Set achievements, + TitlesHistory titlesHistory ) implements JsonConvertable { public static Achievements parse(JsonObject achievements) { + Set set = achievements.getJsonArray("achievements") + .stream() + .map(JsonObject.class::cast) + .map(Achievement::parse) + .filter(Achievements::isPvpAchievement) + .collect(Collectors.toSet()); + TitlesHistory titlesHistory = calculateTitlesHistory(set); return new Achievements( achievements.getLong("total_quantity"), achievements.getLong("total_points"), - achievements.getJsonArray("achievements") - .stream() - .map(JsonObject.class::cast) - .map(Achievement::parse) - .filter(Achievements::isPvpAchievement) - .collect(Collectors.toSet()) + set, + titlesHistory ); } public static Achievements fromJson(JsonObject json) { if (json == null || json.fieldNames().size() == 0) { - return new Achievements(0L, 0L, Set.of()); + return new Achievements(0L, 0L, Set.of(), TitlesHistory.parse(null)); } + TitlesHistory titlesHistory = TitlesHistory.parse(json.getJsonObject("titles_history")); return new Achievements( json.getLong("total_quantity"), json.getLong("total_points"), @@ -37,7 +46,8 @@ public static Achievements fromJson(JsonObject json) { .stream() .map(JsonObject.class::cast) .map(Achievement::fromJson) - .collect(Collectors.toSet()) + .collect(Collectors.toSet()), + titlesHistory ); } @@ -46,13 +56,14 @@ public JsonObject toJson() { return new JsonObject() .put("total_quantity", totalQuantity) .put("total_points", totalPoints) - .put("achievements", achievements.stream().map(Achievement::toJson).collect(Collectors.toList())); + .put("achievements", achievements.stream().map(Achievement::toJson).collect(Collectors.toList())) + .put("titles_history", titlesHistory.toJson()); } private static Pattern OldRankOnePattern = Pattern.compile("^(\\w+) Gladiator$"); private static Pattern NewRankOnePattern = Pattern.compile("^(\\w+) Gladiator: ([\\w\\s]+) Season (\\d+)$"); private static Pattern ShuffleRankOnePattern = Pattern.compile("^(\\w+) Legend: ([\\w\\s]+) Season (\\d+)$"); - private static Set ArenaRanks = Set.of("Gladiator", "Duelist", "Rival", "Challenger", "Legend"); + private static Set ArenaRanks = Set.of("Gladiator", "Duelist", "Rival", "Challenger", "Legend", "Elite" , "Combatant"); private static String ArenaRanksString = String.join("|", ArenaRanks); private static Pattern NewLowRanks = Pattern.compile("^(" + ArenaRanksString + "): ([\\w\\s]+) Season (\\d+)$"); private static Pattern OldLowRanks = Pattern.compile("^(" + ArenaRanksString + ")$"); @@ -70,4 +81,73 @@ private static boolean isPvpAchievement(Achievement achievement) { || newLowRanks.matches() || oldLowRanks.matches(); } + + private static Map rankToLong = Map.of( + "r1_3s", 120L, + "r1_shuffle", 110L, + "Gladiator", 100L, + "Legend", 90L, + "Elite", 80L, + "Duelist", 70L, + "Rival", 60L, + "Challenger", 50L, + "Combatant", 40L + ); + + private static TitlesHistory calculateTitlesHistory(Set achievements) { + // Expansion -> Map> + Map>> seasons = new HashMap<>(); + achievements.stream().forEach(achievement -> { + Matcher newR1 = NewRankOnePattern.matcher(achievement.name()); + if (newR1.find()) { + String rank = "r1_3s"; + String seasonName = newR1.group(2); + Long seasonNumber = Long.parseLong(newR1.group(3)); + updateSeasons(seasons, achievement, rank, seasonName, seasonNumber); + } + Matcher shuffleR1 = ShuffleRankOnePattern.matcher(achievement.name()); + if (shuffleR1.find()) { + String rank = "r1_shuffle"; + String seasonName = shuffleR1.group(2); + Long seasonNumber = Long.parseLong(shuffleR1.group(3)); + updateSeasons(seasons, achievement, rank, seasonName, seasonNumber); + } + Matcher newLowRanks = NewLowRanks.matcher(achievement.name()); + if (newLowRanks.find()) { + String rank = newLowRanks.group(1); + String seasonName = newLowRanks.group(2); + Long seasonNumber = Long.parseLong(newLowRanks.group(3)); + updateSeasons(seasons, achievement, rank, seasonName, seasonNumber); + } + }); + return new TitlesHistory(seasons.keySet().stream().map(expansion -> { + Map> expansionMap = seasons.get(expansion); + List seasonsList = expansionMap.keySet().stream().map(seasonNumber -> { + Pair pair = expansionMap.get(seasonNumber); + return pair.getValue1(); + }).collect(Collectors.toList()); + return new Expansion(expansion, seasonsList); + }).collect(Collectors.toList())); + } + + private static void updateSeasons(Map>> seasons, Achievement achievement, String rank, String expansion, Long seasonNumber) { + seasons.compute(expansion, (key, expansionMap) -> { + if (expansionMap == null) { + Map> ssnMap = new HashMap<>(); + ssnMap.put(seasonNumber, new Pair<>(rank, new Season(expansion, achievement, rank, List.of()))); + return ssnMap; + } else { + expansionMap.compute(seasonNumber, (key2, seasonMap) -> { + if (seasonMap == null) { + return new Pair<>(rank, new Season(expansion, achievement, rank, List.of())); + } else if (rankToLong.get(rank) > rankToLong.get(seasonMap.getValue0())) { + return new Pair<>(rank, new Season(expansion, achievement, rank, List.of())); + } else { + return seasonMap; + } + }); + return expansionMap; + } + }); + } } diff --git a/src/io/github/sammers/pla/blizzard/TitlesHistory.java b/src/io/github/sammers/pla/blizzard/TitlesHistory.java new file mode 100644 index 00000000..25cb4b47 --- /dev/null +++ b/src/io/github/sammers/pla/blizzard/TitlesHistory.java @@ -0,0 +1,102 @@ +package io.github.sammers.pla.blizzard; + +import io.github.sammers.pla.http.JsonConvertable; +import io.vertx.core.json.JsonObject; + +import java.util.List; +import java.util.stream.Collectors; + +record Spot(String bracketType, Long spot, Long rating, Long won, Long lost) implements JsonConvertable { + + public static Spot fromJson(JsonObject json) { + return new Spot( + json.getString("bracket_type"), + json.getLong("spot"), + json.getLong("rating"), + json.getLong("won"), + json.getLong("lost") + ); + } + + @Override + public JsonObject toJson() { + return new JsonObject() + .put("bracket_type", bracketType) + .put("spot", spot) + .put("rating", rating) + .put("won", won) + .put("lost", lost); + } +} + +record Season(String name, + Achievement highestAchievement, + String rank, + List spots) implements JsonConvertable { + + public static Season fromJson(JsonObject json) { + return new Season( + json.getString("name"), + Achievement.fromJson(json.getJsonObject("highest_achievement")), + json.getString("rank"), + json.getJsonArray("spots") + .stream() + .map(JsonObject.class::cast) + .map(Spot::fromJson) + .collect(Collectors.toList()) + ); + } + + @Override + public JsonObject toJson() { + return new JsonObject() + .put("name", name) + .put("highest_achievement", highestAchievement.toJson()) + .put("rank", rank) + .put("spots", spots.stream().map(Spot::toJson).collect(Collectors.toList())); + } +} + +record Expansion(String name, List seasons) implements JsonConvertable { + + public static Expansion fromJson(JsonObject json) { + return new Expansion( + json.getString("name"), + json.getJsonArray("seasons") + .stream() + .map(JsonObject.class::cast) + .map(Season::fromJson) + .collect(Collectors.toList()) + ); + } + + @Override + public JsonObject toJson() { + return new JsonObject() + .put("name", name) + .put("seasons", seasons.stream().map(Season::toJson).collect(Collectors.toList())); + } +} + +public record TitlesHistory(List expansions) implements JsonConvertable { + + public static TitlesHistory parse(JsonObject titlesHistory) { + if (titlesHistory == null || titlesHistory.fieldNames().size() == 0) { + return new TitlesHistory(List.of()); + } + return new TitlesHistory( + titlesHistory.getJsonArray("expansions") + .stream() + .map(JsonObject.class::cast) + .map(Expansion::fromJson) + .collect(Collectors.toList()) + ); + } + + @Override + public JsonObject toJson() { + return new JsonObject() + .put("expansions", expansions.stream().map(Expansion::toJson).collect(Collectors.toList())); + + } +}