From 847cade7811da2cdf8a751755e58a436764e1908 Mon Sep 17 00:00:00 2001 From: Sammers21 Date: Sat, 20 Apr 2024 19:12:18 +0300 Subject: [PATCH] releaseb add predictions --- .../github/sammers/pla/blizzard/Cutoffs.java | 10 ++ src/io/github/sammers/pla/db/Snapshot.java | 156 ++++++++++++++---- src/io/github/sammers/pla/http/Http.java | 2 +- src/io/github/sammers/pla/logic/Ladder.java | 11 +- 4 files changed, 142 insertions(+), 37 deletions(-) diff --git a/src/io/github/sammers/pla/blizzard/Cutoffs.java b/src/io/github/sammers/pla/blizzard/Cutoffs.java index 4b8f2e2b..b0a9253e 100644 --- a/src/io/github/sammers/pla/blizzard/Cutoffs.java +++ b/src/io/github/sammers/pla/blizzard/Cutoffs.java @@ -15,6 +15,7 @@ public class Cutoffs implements JsonConvertable { public final String region; public final String season; public final Map cutoffs; + public final Map cutoffsPredictions = new HashMap<>(); public final Long timestamp; public final Map spotsCounts = new HashMap<>(); public final Map spotWithNoAlts = new HashMap<>(); @@ -38,6 +39,10 @@ public Cutoffs( this.spotWithNoAlts.putAll(spotWithNoAlts); } + public void setPrediction(String bracket, long rating) { + cutoffsPredictions.put(bracket, rating); + } + public void setSpotWithNoAlts(String bracket, long count) { spotWithNoAlts.put(bracket, (long)count); } @@ -148,6 +153,11 @@ public JsonObject toJson() { .put("spotWithNoAlts", new JsonObject(spotWithNoAlts.entrySet().stream().map(x -> Map.entry(x.getKey(), x.getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))); } + public JsonObject toJsonWithPredictions() { + return toJson() + .put("predictions", new JsonObject(cutoffsPredictions.entrySet().stream().map(x -> Map.entry(x.getKey(), x.getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))); + } + public static Cutoffs fromJson(JsonObject json) { return new Cutoffs( json.getString("region"), diff --git a/src/io/github/sammers/pla/db/Snapshot.java b/src/io/github/sammers/pla/db/Snapshot.java index aae65735..574c7222 100644 --- a/src/io/github/sammers/pla/db/Snapshot.java +++ b/src/io/github/sammers/pla/db/Snapshot.java @@ -1,6 +1,5 @@ package io.github.sammers.pla.db; - import io.github.sammers.pla.Main; import io.github.sammers.pla.blizzard.Cutoffs; import io.github.sammers.pla.blizzard.Realms; @@ -21,6 +20,8 @@ public record Snapshot(List characters, Long timestamp, String region, String dateTime) implements Resp { + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Snapshot.class); + public List findChar(String fullName) { return characters.stream().filter(c -> c.fullName().equals(fullName)).toList(); } @@ -44,7 +45,7 @@ public Snapshot filter(final List specs) { boolean res = false; for (String spec : specs) { res = res || c.fullSpec().toLowerCase().replaceAll(" ", "").replaceAll("'", "") - .contains(spec.toLowerCase().replaceAll(" ", "").replaceAll("'", "")); + .contains(spec.toLowerCase().replaceAll(" ", "").replaceAll("'", "")); } return res; }).sorted(Comparator.comparing(Character::rating).reversed()).toList(); @@ -52,25 +53,26 @@ public Snapshot filter(final List specs) { } public JsonObject toJson(Long page) { - List chars = characters.stream().skip((page - 1) * 100L).limit(100).map(JsonConvertable::toJson).toList(); + List chars = characters.stream().skip((page - 1) * 100L).limit(100).map(JsonConvertable::toJson) + .toList(); return new JsonObject() - .put("characters", new JsonArray(chars)) - .put("timestamp", timestamp) - .put("date_time", dateTime) - .put("region", region) - .put("page", page) - .put("total_pages", Calculator.totalPages(this.characters().size(), 100)) - .put("last_seen", Main.PRETTY_TIME.format(new Date(timestamp))); + .put("characters", new JsonArray(chars)) + .put("timestamp", timestamp) + .put("date_time", dateTime) + .put("region", region) + .put("page", page) + .put("total_pages", Calculator.totalPages(this.characters().size(), 100)) + .put("last_seen", Main.PRETTY_TIME.format(new Date(timestamp))); } public JsonObject toJson() { List chars = characters.stream().map(JsonConvertable::toJson).toList(); return new JsonObject() - .put("characters", new JsonArray(chars)) - .put("timestamp", timestamp) - .put("date_time", dateTime) - .put("region", region) - .put("last_seen", Main.PRETTY_TIME.format(new Date(timestamp))); + .put("characters", new JsonArray(chars)) + .put("timestamp", timestamp) + .put("date_time", dateTime) + .put("region", region) + .put("last_seen", Main.PRETTY_TIME.format(new Date(timestamp))); } public static Snapshot fromJson(JsonObject entries) { @@ -78,7 +80,92 @@ public static Snapshot fromJson(JsonObject entries) { Instant instant = Instant.ofEpochMilli(ts); ZonedDateTime zonedDateTime = instant.atZone(UTC); String format = Main.DATA_TIME.format(zonedDateTime); - return new Snapshot(entries.getJsonArray("characters").stream().map(x -> (JsonObject) x).map(Character::fromJson).toList(), ts, entries.getString("region"), format); + return new Snapshot( + entries.getJsonArray("characters").stream().map(x -> (JsonObject) x).map(Character::fromJson).toList(), + ts, entries.getString("region"), format); + } + + public void predictCutoffs(String bracket, Cutoffs cutoffs) { + if (cutoffs == null) { + return; + } + if (bracket.equals(THREE_V_THREE)) { + Map> countedAlready = new HashMap<>(); + Character last = null; + Long spots = cutoffs.spotWithNoAlts.get("ARENA_3v3"); + for (int i = 0; i < characters.size() && countedAlready.size() < spots; i++) { + last = characters.get(i); + Character finalLast = last; + Integer petHash = last.pethash().map(p -> p == -1 ? finalLast.fullNameWSpec().hashCode() : p) + .orElse(last.fullNameWSpec().hashCode()); + countedAlready.compute(petHash, (k, v) -> { + if (v == null) { + v = new ArrayList<>(); + } + v.add(finalLast); + return v; + }); + } + if (last != null) { + cutoffs.setPrediction("ARENA_3v3", last.rating()); + } else { + log.error("No characters found for 3v3"); + } + } else if (bracket.equals(RBG)) { + Map> countedAlready = new HashMap<>(); + Character last = null; + Long spots = cutoffs.spotWithNoAlts.get("BATTLEGROUNDS/alliance"); + for (int i = 0; i < characters.size() && countedAlready.size() < spots; i++) { + last = characters.get(i); + Character finalLast = last; + Integer petHash = last.pethash().map(p -> p == -1 ? finalLast.fullNameWSpec().hashCode() : p) + .orElse(last.fullNameWSpec().hashCode()); + countedAlready.compute(petHash, (k, v) -> { + if (v == null) { + v = new ArrayList<>(); + } + v.add(finalLast); + return v; + }); + } + if (last != null) { + cutoffs.setPrediction("BATTLEGROUNDS/alliance", last.rating()); + cutoffs.setPrediction("BATTLEGROUNDS/horde", last.rating()); + } else { + log.error("No characters found for RBG"); + } + } else if (bracket.equals(SHUFFLE)) { + Map>> countedAlready = new HashMap<>(); + Map last = new HashMap<>(); + for (Character ch : characters) { + String fullSpec = ch.fullSpec(); + String specCode = Cutoffs.specCodeByFullName(fullSpec); + Long spots = cutoffs.spotWithNoAlts.get("SHUFFLE/" + specCode); + Map> countedG = countedAlready.get(specCode); + if (countedG != null && countedG.size() >= spots) { + continue; + } + Integer petHash = ch.pethash().map(p -> p == -1 ? ch.fullNameWSpec().hashCode() : p) + .orElse(ch.fullNameWSpec().hashCode()); + countedAlready.compute(specCode, (k, v) -> { + if (v == null) { + v = new HashMap<>(); + } + v.compute(petHash, (k1, v1) -> { + if (v1 == null) { + v1 = new ArrayList<>(); + } + v1.add(ch); + return v1; + }); + return v; + }); + last.put(specCode, ch); + } + last.forEach((spec, ch) -> { + cutoffs.setPrediction("SHUFFLE/" + spec, ch.rating()); + }); + } } public Snapshot applyCutoffs(String bracket, Cutoffs cutoffs) { @@ -99,12 +186,12 @@ public Snapshot applyCutoffs(String bracket, Cutoffs cutoffs) { List charsInCutoff = chars.stream().filter(Character::inCutoff).toList(); cutoffs.setSpotCount("ARENA_3v3", charsWithCutoff.intValue()); Map> petHashes = charsInCutoff.stream() - .collect(Collectors.groupingBy(ch -> { - if (ch.pethash().isEmpty() || ch.pethash().get() == -1) { - return ch.fullNameWSpec().hashCode(); - } - return ch.pethash().get(); - })); + .collect(Collectors.groupingBy(ch -> { + if (ch.pethash().isEmpty() || ch.pethash().get() == -1) { + return ch.fullNameWSpec().hashCode(); + } + return ch.pethash().get(); + })); cutoffs.setSpotWithNoAlts("ARENA_3v3", petHashes.size()); return new Snapshot(chars, this.timestamp(), this.region(), this.dateTime()); } else if (bracket.equals(RBG)) { @@ -119,13 +206,13 @@ public Snapshot applyCutoffs(String bracket, Cutoffs cutoffs) { } }).collect(Collectors.toList()); Map> petHashes = chars.stream() - .filter(Character::inCutoff) - .collect(Collectors.groupingBy(ch -> { - if (ch.pethash().isEmpty() || ch.pethash().get() == -1) { - return ch.fullNameWSpec().hashCode(); - } - return ch.pethash().get(); - })); + .filter(Character::inCutoff) + .collect(Collectors.groupingBy(ch -> { + if (ch.pethash().isEmpty() || ch.pethash().get() == -1) { + return ch.fullNameWSpec().hashCode(); + } + return ch.pethash().get(); + })); cutoffs.setSpotCount("BATTLEGROUNDS/alliance", charsWithCutoff.intValue()); cutoffs.setSpotCount("BATTLEGROUNDS/horde", charsWithCutoff.intValue()); cutoffs.setSpotWithNoAlts("BATTLEGROUNDS/alliance", petHashes.size()); @@ -147,7 +234,8 @@ public Snapshot applyCutoffs(String bracket, Cutoffs cutoffs) { if (v == null) { v = new HashMap<>(); } - Integer petHash = ch.pethash().map(p -> p == -1 ? ch.fullNameWSpec().hashCode() : p).orElse(ch.fullNameWSpec().hashCode()); + Integer petHash = ch.pethash().map(p -> p == -1 ? ch.fullNameWSpec().hashCode() : p) + .orElse(ch.fullNameWSpec().hashCode()); v.compute(petHash, (k1, v1) -> { if (v1 == null) { v1 = new ArrayList<>(); @@ -163,10 +251,10 @@ public Snapshot applyCutoffs(String bracket, Cutoffs cutoffs) { } }).collect(Collectors.toList()); specCodeAndSpotCount.entrySet() - .forEach((var cutoff) -> { - cutoffs.setSpotWithNoAlts("SHUFFLE/" + cutoff.getKey(), petHashes.get(cutoff.getKey()).size()); - cutoffs.setSpotCount("SHUFFLE/" + cutoff.getKey(), cutoff.getValue().intValue()); - }); + .forEach((var cutoff) -> { + cutoffs.setSpotWithNoAlts("SHUFFLE/" + cutoff.getKey(), petHashes.get(cutoff.getKey()).size()); + cutoffs.setSpotCount("SHUFFLE/" + cutoff.getKey(), cutoff.getValue().intValue()); + }); return new Snapshot(chars, this.timestamp(), this.region(), this.dateTime()); } return this; diff --git a/src/io/github/sammers/pla/http/Http.java b/src/io/github/sammers/pla/http/Http.java index 26cd1ece..ed4bbc03 100644 --- a/src/io/github/sammers/pla/http/Http.java +++ b/src/io/github/sammers/pla/http/Http.java @@ -107,7 +107,7 @@ public void start() { JsonObject res = new JsonObject().put("2v2", twos).put("3v3", threes).put("rbg", rbgs).put("shuffle", shuffle); Cutoffs cutoffs = ladder.regionCutoffFromDb.get(region); if (cutoffs != null) { - res.put("cutoffs", cutoffs.toJson()); + res.put("cutoffs", cutoffs.toJsonWithPredictions()); } ctx.response().end(res.encode()); }); diff --git a/src/io/github/sammers/pla/logic/Ladder.java b/src/io/github/sammers/pla/logic/Ladder.java index 0534c7fd..678f486f 100644 --- a/src/io/github/sammers/pla/logic/Ladder.java +++ b/src/io/github/sammers/pla/logic/Ladder.java @@ -516,8 +516,15 @@ private Completable loadCutoffsFromDb(String region) { log.info("Load cutoffs from DB for region " + region); return db.getLastCutoffs(realRegion(region)).map(cutoffs -> { if (cutoffs.isPresent()) { - regionCutoffFromDb.put(oldRegion(region), cutoffs.get()); - regionCutoffFromDb.put(realRegion(region), cutoffs.get()); + Cutoffs ctf = cutoffs.get(); + List.of(TWO_V_TWO, THREE_V_THREE, RBG, SHUFFLE).forEach(bracket -> { + Snapshot s = refs.refByBracket(bracket, region).get(); + if (s != null) { + s.predictCutoffs(bracket, ctf); + } + }); + regionCutoffFromDb.put(oldRegion(region), ctf); + regionCutoffFromDb.put(realRegion(region), ctf); } return cutoffs; }).doOnSuccess(cutoffs -> log.info("Cutoffs from DB for region={} has been loaded", region))