Search in sources :

Example 1 with ScCategory

use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.

the class ScRecordDTO method fromCategoryRecord.

@NotNull
public static ScRecordDTO fromCategoryRecord(@NotNull CategoryRecord<ScRecord, ScCategory> categoryRecord) {
    ScRecord record = categoryRecord.getRecord();
    Set<ScCategory> categories = categoryRecord.getCategories();
    return new ScRecordDTO(ScScoreDTO.fromScore(record.getScore()), record.getScore().toDisplayString(DisplayContext.plainText()), record.getAuthor(), record.getDisplayLink(), record.getDataLink(), UtilsKt.smartFormat(categories, UtilsKt.toMetricsTree(record.getPuzzle().getSupportedCategories())));
}
Also used : ScRecord(com.faendir.zachtronics.bot.sc.model.ScRecord) ScCategory(com.faendir.zachtronics.bot.sc.model.ScCategory) NotNull(org.jetbrains.annotations.NotNull)

Example 2 with ScCategory

use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.

the class ScSolution method unmarshal.

@NotNull
public static ScSolution unmarshal(@NotNull String[] fields) {
    assert fields.length == 5;
    ScScore score = Objects.requireNonNull(ScScore.parseScore(fields[0]));
    String author = fields[1];
    String displayLink = fields[2];
    boolean videoOnly = fields[3] != null;
    String categories = fields[4];
    ScSolution solution = new ScSolution(score, author, displayLink, videoOnly);
    if (categories != null)
        Pattern.compile(",").splitAsStream(categories).map(ScCategory::valueOf).forEach(solution.categories::add);
    return solution;
}
Also used : ScScore(com.faendir.zachtronics.bot.sc.model.ScScore) ScCategory(com.faendir.zachtronics.bot.sc.model.ScCategory) NotNull(org.jetbrains.annotations.NotNull)

Example 3 with ScCategory

use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.

the class ScSolutionRepository method archiveOne.

/**
 * @param solutions the list is modified with the updated state
 */
@Override
@NotNull
protected SubmitResult<ScRecord, ScCategory> archiveOne(@NotNull GitRepository.ReadWriteAccess access, @NotNull List<ScSolution> solutions, @NotNull ScSubmission submission) {
    ScPuzzle puzzle = submission.getPuzzle();
    Path puzzlePath = getPuzzlePath(access, puzzle);
    List<CategoryRecord<ScRecord, ScCategory>> beatenCategoryRecords = new ArrayList<>();
    ScSolution candidate = new ScSolution(submission.getScore(), submission.getAuthor(), submission.getDisplayLink(), false);
    try {
        for (ListIterator<ScSolution> it = solutions.listIterator(); it.hasNext(); ) {
            ScSolution solution = it.next();
            int r = dominanceCompare(candidate.getScore(), solution.getScore());
            if (r > 0) {
                // TODO actually return all of the beating sols
                CategoryRecord<ScRecord, ScCategory> categoryRecord = solution.extendToCategoryRecord(puzzle, makeArchiveLink(puzzle, solution.getScore()), makeArchivePath(puzzlePath, solution.getScore()));
                return new SubmitResult.NothingBeaten<>(Collections.singletonList(categoryRecord));
            } else if (r < 0) {
                // allow same-score changes if you bring a video or you are the original author and don't regress the video state
                if (candidate.getScore().equals(solution.getScore()) && candidate.getDisplayLink() == null && !(candidate.getAuthor().equals(solution.getAuthor()) && solution.getDisplayLink() == null)) {
                    return new SubmitResult.AlreadyPresent<>();
                }
                // remove beaten score and get categories
                candidate.getCategories().addAll(solution.getCategories());
                if (// video-only sols have no data
                !solution.isVideoOnly())
                    Files.delete(makeArchivePath(puzzlePath, solution.getScore()));
                // the beaten record has no data anymore
                beatenCategoryRecords.add(solution.extendToCategoryRecord(puzzle, null, null));
                if (candidate.getDisplayLink() == null && solution.getDisplayLink() != null) {
                    // we beat the solution, but we can't replace the video, we keep the solution entry as a video-only
                    // empty categories
                    it.set(solution.withVideoOnly(true));
                } else {
                    it.remove();
                }
            }
        }
        // the new record may have gained categories of records it didn't pareto-beat, do the transfers
        for (ScSolution solution : solutions) {
            EnumSet<ScCategory> lostCategories = EnumSet.noneOf(ScCategory.class);
            for (ScCategory category : solution.getCategories()) {
                if (category.supportsScore(candidate.getScore()) && category.getScoreComparator().compare(candidate.getScore(), solution.getScore()) < 0) {
                    lostCategories.add(category);
                }
            }
            if (!lostCategories.isEmpty()) {
                // add a CR holding the lost categories, then correct the solutions
                CategoryRecord<ScRecord, ScCategory> beatenCR = new CategoryRecord<>(solution.extendToRecord(puzzle, makeArchiveLink(puzzle, solution.getScore()), makeArchivePath(puzzlePath, solution.getScore())), lostCategories);
                beatenCategoryRecords.add(beatenCR);
                solution.getCategories().removeAll(lostCategories);
                candidate.getCategories().addAll(lostCategories);
            }
        }
        int index = Collections.binarySearch(solutions, candidate, COMPARATOR);
        if (index < 0) {
            index = -index - 1;
        }
        solutions.add(index, candidate);
        String filename = makeScoreFilename(candidate.getScore());
        Path solutionPath = puzzlePath.resolve(filename);
        // ensure there is one and only one newline at the end
        String data = submission.getData().replaceFirst("\\s*$", "\n");
        Files.writeString(solutionPath, data, StandardOpenOption.CREATE_NEW);
        marshalSolutions(solutions, puzzlePath);
    } catch (IOException e) {
        // failures could happen after we dirtied the repo, so we call reset&clean on the puzzle dir
        access.resetAndClean(puzzlePath.toFile());
        return new SubmitResult.Failure<>(e.toString());
    }
    if (access.status().isClean()) {
        // the same exact sol was already archived,
        return new SubmitResult.AlreadyPresent<>();
    }
    String result = commit(access, submission, puzzlePath);
    return new SubmitResult.Success<>(result, beatenCategoryRecords);
}
Also used : Path(java.nio.file.Path) CategoryRecord(com.faendir.zachtronics.bot.repository.CategoryRecord) SubmitResult(com.faendir.zachtronics.bot.repository.SubmitResult) IOException(java.io.IOException) ScCategory(com.faendir.zachtronics.bot.sc.model.ScCategory) NotNull(org.jetbrains.annotations.NotNull)

Example 4 with ScCategory

use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.

the class ScSolutionRepository method writeToRedditLeaderboard.

@Override
protected void writeToRedditLeaderboard(@NotNull ScPuzzle puzzle, Path puzzlePath, @NotNull List<ScSolution> solutions, String updateMessage) {
    Map<ScCategory, ScRecord> recordMap = new EnumMap<>(ScCategory.class);
    Map<ScCategory, ScRecord> videoRecordMap = new EnumMap<>(ScCategory.class);
    List<ScRecord> videoRecords = solutions.stream().filter(s -> s.getDisplayLink() != null).map(// no export needed
    s -> s.extendToRecord(puzzle, null, null)).toList();
    for (ScSolution solution : solutions) {
        ScRecord record = solution.extendToRecord(puzzle, makeArchiveLink(puzzle, solution.getScore()), makeArchivePath(puzzlePath, solution.getScore()));
        for (ScCategory category : solution.getCategories()) {
            recordMap.put(category, record);
            if (record.getDisplayLink() == null) {
                videoRecords.stream().filter(s -> category.supportsScore(s.getScore())).min(Comparator.comparing(ScRecord::getScore, category.getScoreComparator())).ifPresent(// bosses have no videos at all
                videoRecord -> videoRecordMap.put(category, videoRecord));
            }
        }
    }
    String[] lines = redditService.getWikiPage(Subreddit.SPACECHEM, puzzle.getGroup().getWikiPage()).split("\\r?\\n");
    Pattern puzzleRegex = Pattern.compile("^\\| \\[" + Pattern.quote(puzzle.getDisplayName()));
    int rowIdx = 0;
    // | [Puzzle - 1 Reactor](https://zlbb) | [(**ccc**/**r**/ss) author](https://li.nk) | ← | [(ccc/**r**/**ss**) author](https://li.nk) | ←
    for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
        String line = lines[lineIdx];
        if (puzzleRegex.matcher(line).find()) {
            String[] prevElems = line.trim().split("\\s*\\|\\s*", -1);
            int halfSize = (prevElems.length - 2) / 2;
            StringBuilder row = new StringBuilder("| ");
            int minReactors = Integer.MAX_VALUE;
            String rowTitle = puzzle.getDisplayName();
            if (rowIdx == 1) {
                minReactors = recordMap.get(ScCategory.RC).getScore().getReactors();
                rowTitle += " - " + minReactors + " Reactor" + (minReactors == 1 ? "" : "s");
            }
            row.append(Markdown.linkOrText(rowTitle, puzzle.getLink()));
            for (int block = 0; block < 2; block++) {
                ScCategory[] blockCategories = CATEGORIES[2 * rowIdx + block];
                ScRecord[] blockRecords = Arrays.stream(blockCategories).map(recordMap::get).toArray(ScRecord[]::new);
                ScRecord[] blockVideoRecords = Arrays.stream(blockCategories).map(videoRecordMap::get).toArray(ScRecord[]::new);
                for (int i = 0; i < halfSize; i++) {
                    ScCategory thisCategory = blockCategories[i];
                    row.append(" | ");
                    if (blockRecords[i] != null) {
                        DisplayContext<ScCategory> displayContext = new DisplayContext<>(StringFormat.REDDIT, thisCategory);
                        String cell = makeLeaderboardCell(blockRecords, i, minReactors, displayContext);
                        row.append(cell);
                        if (blockVideoRecords[i] != null) {
                            String videoCell = makeLeaderboardCell(blockVideoRecords, i, Integer.MAX_VALUE, displayContext);
                            if (!cell.equals(videoCell))
                                row.append(". Top&nbsp;video&nbsp;").append(videoCell);
                        }
                    } else
                        row.append(prevElems[2 + block * halfSize + i]);
                }
            }
            lines[lineIdx] = row.toString();
            rowIdx++;
        } else if (rowIdx != 0) {
            // we've already found the point and now we're past it, we're done
            break;
        }
    }
    redditService.updateWikiPage(Subreddit.SPACECHEM, puzzle.getGroup().getWikiPage(), String.join("\n", lines), updateMessage);
}
Also used : DisplayContext(com.faendir.zachtronics.bot.model.DisplayContext) Subreddit(com.faendir.zachtronics.bot.reddit.Subreddit) java.util(java.util) Getter(lombok.Getter) GitRepository(com.faendir.zachtronics.bot.git.GitRepository) AbstractSolutionRepository(com.faendir.zachtronics.bot.repository.AbstractSolutionRepository) RequiredArgsConstructor(lombok.RequiredArgsConstructor) Function(java.util.function.Function) AccessLevel(lombok.AccessLevel) Qualifier(org.springframework.beans.factory.annotation.Qualifier) BiConsumer(java.util.function.BiConsumer) RedditService(com.faendir.zachtronics.bot.reddit.RedditService) Path(java.nio.file.Path) Markdown(com.faendir.zachtronics.bot.utils.Markdown) Files(java.nio.file.Files) StandardOpenOption(java.nio.file.StandardOpenOption) com.faendir.zachtronics.bot.sc.model(com.faendir.zachtronics.bot.sc.model) IOException(java.io.IOException) StringFormat(com.faendir.zachtronics.bot.model.StringFormat) Component(org.springframework.stereotype.Component) CategoryRecord(com.faendir.zachtronics.bot.repository.CategoryRecord) Paths(java.nio.file.Paths) SubmitResult(com.faendir.zachtronics.bot.repository.SubmitResult) ScCategory(com.faendir.zachtronics.bot.sc.model.ScCategory) Pattern(java.util.regex.Pattern) ValidationResult(com.faendir.zachtronics.bot.validation.ValidationResult) NotNull(org.jetbrains.annotations.NotNull) Pattern(java.util.regex.Pattern) DisplayContext(com.faendir.zachtronics.bot.model.DisplayContext) ScCategory(com.faendir.zachtronics.bot.sc.model.ScCategory)

Example 5 with ScCategory

use of com.faendir.zachtronics.bot.sc.model.ScCategory in project zachtronics-leaderboard-bot by F43nd1r.

the class SolRepoFindTest method testFindCategoryHolders.

@Test
public void testFindCategoryHolders() {
    ScPuzzle puzzle = ScPuzzle.research_example_1;
    List<CategoryRecord<ScRecord, ScCategory>> categoryHolders = repository.findCategoryHolders(puzzle, false);
    assertEquals(2, categoryHolders.size());
    List<ScCategory> coveredCategories = categoryHolders.stream().map(CategoryRecord::getCategories).flatMap(Set::stream).sorted().toList();
    assertEquals(puzzle.getSupportedCategories(), coveredCategories);
}
Also used : Set(java.util.Set) ScPuzzle(com.faendir.zachtronics.bot.sc.model.ScPuzzle) CategoryRecord(com.faendir.zachtronics.bot.repository.CategoryRecord) ScCategory(com.faendir.zachtronics.bot.sc.model.ScCategory) Test(org.junit.jupiter.api.Test) BotTest(com.faendir.zachtronics.bot.BotTest)

Aggregations

ScCategory (com.faendir.zachtronics.bot.sc.model.ScCategory)6 NotNull (org.jetbrains.annotations.NotNull)5 CategoryRecord (com.faendir.zachtronics.bot.repository.CategoryRecord)3 SubmitResult (com.faendir.zachtronics.bot.repository.SubmitResult)2 ScPuzzle (com.faendir.zachtronics.bot.sc.model.ScPuzzle)2 IOException (java.io.IOException)2 Path (java.nio.file.Path)2 BotTest (com.faendir.zachtronics.bot.BotTest)1 GitRepository (com.faendir.zachtronics.bot.git.GitRepository)1 DisplayContext (com.faendir.zachtronics.bot.model.DisplayContext)1 StringFormat (com.faendir.zachtronics.bot.model.StringFormat)1 RedditService (com.faendir.zachtronics.bot.reddit.RedditService)1 Subreddit (com.faendir.zachtronics.bot.reddit.Subreddit)1 AbstractSolutionRepository (com.faendir.zachtronics.bot.repository.AbstractSolutionRepository)1 com.faendir.zachtronics.bot.sc.model (com.faendir.zachtronics.bot.sc.model)1 ScRecord (com.faendir.zachtronics.bot.sc.model.ScRecord)1 ScScore (com.faendir.zachtronics.bot.sc.model.ScScore)1 Markdown (com.faendir.zachtronics.bot.utils.Markdown)1 ValidationResult (com.faendir.zachtronics.bot.validation.ValidationResult)1 Files (java.nio.file.Files)1